User Tools

Site Tools


base:variable_speedcode_runlength

Variable speedcode runlength

After you entered a segment of speed code at a certain spot by making use of some method discussed in the Article “Dispatch on a byte” we now want to exit that code at a certain spot.

Some code can be written that way, that we can also determine the runlength by the point where we enter and let it run until its end. But if we have explicit target addresses in our speedcode this just won't work.

                        ;<- enter here
        sta $0400
        sta $0401
        sta $0402
        sta $0403
        sta $0404
        sta $0405
        sta $0406
        sta $0407
        sta $0408
                        ;<- leave here
        sta $0409
        sta $040a
        sta $040b
        sta $040c
        sta $040d
        sta $040e
        sta $040f

So given the above example, we might want to enter at sta $0400 and leave after sta $0408, to do so we can modify the code and transform the upcoming sta to an rts command. After exiting the speedcode we then modify the rts back to a sta. Pretty much overhead and we would even need to call our speedcode as a subroutine. We can also leave the speedcode by a timed NMI, but that will also mean overhead in setting up and it will cost extra cycles for the IRQ and RTI.

We need speedcode with predictable runlengths (so no random penalty cycles apply) to do so, The setup is quite simple and no code has to be restored.

All you need is setting up a IRQ/NMI handler once beforehand as the exit point of your routine, setup the timer with 2 writes to e.g. $dd04/$dd05 and start a single shot timer run by setting $dd0e to $09. After that, jump to your speedcode segment and wait for the time interrupt to happen at the right spot.

An example could look like this:

          ;setup
          lda #$08
          sta $dd0e
          lda #$00
          sta $dd04
          sta $dd05
          lda $dd0d
          lda #$81
          sta $dd0d
          lda #<exit
          sta $fffa
          lda #>exit
          sta $fffb
          
          ...
          
          lda runlength_lo,x
          sta $dd04
          ;this can even be ommitted if we do not run more than 255 cycles
          ;lda runlength_hi,x
          ;sta $dd05

          tsx
          
          lda #$09
          sta $dd0e
          
ptr       jmp speedcode

speedcode
          sta $0400
          sta $0401
          sta $0402
          sta $0403
          sta $0404
          sta $0405
          sta $0406
          sta $0407
          sta $0408
          sta $0409
          sta $040a
          sta $040b
          sta $040c
          sta $040d
          sta $040e
          sta $040f

runlength_lo
          !byte $04,$08,$0c,$10,$14 ...
runlength_hi          
          !byte $00,$00,$00,$00,$00 ...
exit
          txs
          cli
          lda $dd0d
          ...

Regarding the cli instruction: This is important, as else normal irqs might be blocked until we restore the I-flag on the terminating rti, so find out for yourself if it is needed in your case. If so, you can just pull the original PC and flags from stack via pla/plp, instead of manipulating the stack pointer, or simply forgo on the cli if no further irq needs to happen.

base/variable_speedcode_runlength.txt · Last modified: 2022-01-14 12:45 by bitbreaker