Re: [stella] Intelligent Job-System idea

Subject: Re: [stella] Intelligent Job-System idea
From: Christopher Tumber <christophertumber@xxxxxxxxxx>
Date: Fri, 03 Jan 2003 16:07:14 -0500
>I'm currently thinking some advanced almost AI-like job system, since in Star Fire I've regular troubles of exceeding VBLANK time.

Here's an idea you may be able to use, it's worked well for me with the Vectrex - Some short background, low-level Vectrex programming tends to consist of "Tell the electron gun where to move - Wait for the electron gun to finish moving  - Tell the electron gun where to move - Repeat" and then when you're done drawing the frame, you get to do your game calcs (rather like doing game calcs for the 2600 during VBLANK/Overscan). If you let the Vectrex's BIOS routines handle the drawing for you, then the instances of "Wait for the electron gun" are just lost time. However, if you're programming the hardware directly you can recover and use those cycles and depending upon the length of the lines you're drawing that can be a couple hundred cycles per line. Which can really add up...

So, what you tend to do is bury short ($63 or $127 cycle tend to be the usual) routines within the longer drawing waits. The problem with this is that it's then difficult to generalise, loop and/or subroutine these functions - ie: If you have a drawing routine to draw an enemy "sprite" and you want to bury some bookeeping (or whatever) routines in there it's dificult to loop or subroutine that drawing routine for each or 6 or 8 or 10 enemies because then you wind up executing that bookeeping routine 6 or 8 or10 times a frame. A common solution is to tie the "bookeeping" routine to the drawing routine, so you might put the collision detection routine for that enemy inside the drawing routine. But you can't always match routines up like that (ie: If you're drawing backgrounds) and sometimes you're not always sure a drawing routine is going to get called (ie: if something's alive or dead) and you may still need to be able to be sure the bookeeping routines all get called.

So I came up with a short little subroutine handler that looked something like this that I'd call during any unused wait state:

     lda NextRoutine
     cmp NumberOfRoutines
     beq done_all_routines
     lda RoutineTable,y
     sta RoutineOffset
     lda RoutineTable+1,y
     sta RoutineOffset+1
     inc NextRoutine
     jmp (RoutineOffset)   ;Routine just needs an RTS

RoutineTable: .word FirstRoutineStartingAddress
                     .word SecondRoutineStartingAddress
                     .word ThirdRoutineStartingAddress
                     .word FourthRoutineStartingAddress

At the start of every frame, NextRoutine is zeroed. There's a little added overhead (The 6809 allows indexed JSR and JMP so the Vectrex version actually has quite a bit less overhead), including a byte of RAM but I found this kind of handler really helpfull for non-specific bookeeping routines.

For your purposes, I'd suggest a table for low-priority routines, ones that don't need to be called every frame. And then your NextRoutine offset variable would only be reset when the end of the table is reached. So calling the routines could be spread out over several frames. Something like:

     jsr mandatory_routine1
     jsr mandatory_routine2
     jsr mandatory_routine3
     jsr mandatory_routine4


     CMP #$05       ; more than 6*64 cycles left?
     BCS end_VBLANK

     lda NextLowPriorityRoutine
     cmp NumberOfLowPriorityRoutines
     beq done_low_priority_routines
     lda LowPriorityRoutineTable,y
     sta RoutineOffset
     lda LowPriorityRoutineTable+1,y
     sta RoutineOffset+1
     inc NumberOfLowPriorityRoutines
     jmp (RoutineOffset)   ;Routines need to end by with: jmp low_priority_routine_handler
     lda #0
     sta NextLowPriorityRoutine


;... (BTW - This is off the top of my head, may be buggy as hell...)

LowPriorityRoutineTable: .word FirstLowPriorityRoutineStartingAddress
                                     .word SecondLowPriorityRoutineStartingAddress
                                     .word ThirdLowPriorityRoutineStartingAddress
                                     .word FourthLowPriorityRoutineStartingAddress

You are adding the overhead of around 30 cycles per routine plus 1 byte RAM over you original idea, but otherwise you're probably going to be adding:

     CMP #$05       ; more than 6*64 cycles left?
     BCC Continue

To the start of a bunch of routines (it'll only take a few before you're using more ROM than my routine) and you may have some problems ensuring that all those routines get called in a timely manor (Supposed you have 6 of those routines, each with the above timer check, it may sometimes take several frames to get down to the last couple routines while the first couple routines get called every frame. With my table you at least know they'll get called as part of the sequence of routines...)

Anyway, there's my two cents...


Archives (includes files) at
Unsub & more at

Current Thread