Dispatch-on-type and Data-Directed Assembler

Most of the time, you care about function pointers because you've arranged them in some kind of table. You hand it an index representing the type of your argument, or which method it is you're calling, or some other determinator, and then you index into an array of routines and execute the right one.

Writing a generic routine to do this is kind of a pain. First you have to pass a 16-bit pointer in, then you have to dereference it to figure out where your table is, then you have to do an indexed dereference on that to get the routine you want to run, then you need to copy it out to somewhere fixed so that you can write your jump instruction. And making this non-generic doesn't help a whole lot, since that only saves you the first two steps, but now you have to write them out in every single indexed jump instruction. If only there were some way to easily and quickly pass in a local pointer directly...

Something, say, like the JSR instruction, only not for program code.

Or we could just use the JSR statement itself, but only call this routine at the ends of other routines, much like we were organizing for indirect jumps to begin with. This lets us set up routines that look like this:

jump'table'alpha:
    jsr do'jump'table
    .word alpha'0, alpha'1, alpha'2

Where the alpha'x routines are the ones to be called when the index has that value. This leaves the implementation of do'jump'table, which in this case uses the Y register to hold the index:

do'jump'table:
    sta _scratch
    pla
    sta _jmpptr
    pla
    sta _jmpptr+1
    tya
    asl
    tay
    iny
    lda (_jmpptr), y
    sta _target
    iny
    lda (_jmpptr), y
    sta _target+1
    lda _scratch
    jmp (_target)

The TYA:ASL:TAY:INY sequence can actually be omitted if you don't mind having your Y indices be 1, 3, 5, 7, 9, etc., instead of 0, 1, 2, 3, 4, etc. Likewise, the instructions dealing with _scratch can be omitted if you don't mind trashing the accumulator. Keeping the accumulator and X register pristine for the target call comes in handy, though, because it means we can pass in a pointer argument purely in registers. This will come in handy soon...