====== 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 $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.