the_ninja-method:nmis_and_distributed_jitter-correction_routines
Differences
This shows you the differences between two versions of the page.
the_ninja-method:nmis_and_distributed_jitter-correction_routines [2015-04-17 04:35] – external edit 127.0.0.1 | the_ninja-method:nmis_and_distributed_jitter-correction_routines [2015-10-07 22:16] (current) – removed ftc | ||
---|---|---|---|
Line 1: | Line 1: | ||
- | ====== The Ninja-Method ====== | ||
- | |||
- | The basic idea has a pretty long history. | ||
- | |||
- | |||
- | ===== Idea of a NMI-driven 4x4-routine ===== | ||
- | |||
- | We'll set up CIA #2 to trigger a NMI every 8th rasterline (on PAL: a counter of 8*63-1 = 503 cycles). | ||
- | |||
- | jmp $XX00 | ||
- | |||
- | where XX == lo-value of Timer B counter in cycle 3 of the jmp command. | ||
- | |||
- | Now imagine we let the NMI execute this $dc04. | ||
- | |||
- | For this amount of precision all we need is precision while setting it all up. Beware: the exact timing while setting all up is extremely crucial, because any $dc04-jmp to undefined memory will most likely crash. | ||
- | |||
- | |||
- | ===== Caveats ===== | ||
- | |||
- | ==== $dd0d ==== | ||
- | To not mess around with $dd0d badly, you'll have to follow two simple rules: | ||
- | Activate the NMI by | ||
- | lda #$81 | ||
- | bit $dd0d | ||
- | sta $dd0d | ||
- | Deactivate the NMI by | ||
- | lda #$7f | ||
- | sta $dd0d | ||
- | Just do not '' | ||
- | |||
- | ==== 6526 vs. 6526A ==== | ||
- | Another caveat is the difference between 6526 and 6526A. | ||
- | if (6526) start NMI-timer one cycle earlier | ||
- | As on both CIA-types the " | ||
- | |||
- | Actually JackAsser mentioned in the CSDB-forums, | ||
- | ===== the C0DE (49374) ===== | ||
- | I'm sorry some labels have more or less German names. | ||
- | < | ||
- | ; | ||
- | ;TODO: declare variables before !src' | ||
- | ; (must be solvable even in the first pass!) | ||
- | ; | ||
- | ;NMI_base = address of the first NMI-routine in memory (ATTENTION: theoretically | ||
- | ; those might be located anywhere (except underneath the IO), if the | ||
- | ; | ||
- | ; demo coders like to make one of the timers reference-count exactly | ||
- | ; one rasterline, which would make $0100-$3700 the range for NMI_base) | ||
- | ; | ||
- | ;d018wert1= D018-value of the upper 4 pixel-rows | ||
- | ;d018wert2= D018-value of the lower 4 pixel-rows | ||
- | ;d011wert1= D011-value für D018wert1 | ||
- | ;d011wert2= D011-value für D018wert2 | ||
- | ; | ||
- | ;ATTENTION: this routine just does the syncing and starting of all timers. | ||
- | ;still need to create some raster-IRQs that start and stop the timer-NMIs. | ||
- | ; | ||
- | !ifdef NMI_base { | ||
- | |||
- | .wartung inx ; | ||
- | ldy #7 ;(incl. jsr .wartung) | ||
- | .check6 dey | ||
- | bne .check6 | ||
- | .check_6 nop | ||
- | .rts rts | ||
- | |||
- | init4x4 | ||
- | ;FIRST UP: CIA-Detection and Initialization | ||
- | and #0 | ||
- | sta .CIA_type | ||
- | sta $dd05 | ||
- | sta $dc0e ;stop all timers | ||
- | sta $dc0f | ||
- | sta $dd0e | ||
- | sta $dd0f | ||
- | ldy # | ||
- | sty $dc0d | ||
- | cmp $dc0d | ||
- | sty $dd0d | ||
- | cmp $dd0d | ||
- | lda # | ||
- | sta $dd04 | ||
- | bit $d011 ;wait for border, then start ... | ||
- | bpl *-3 | ||
- | lda #< | ||
- | sta $fffa | ||
- | lda #> | ||
- | sta $fffb | ||
- | lda #$81 | ||
- | ldx #%10011001 | ||
- | stx $dd0e | ||
- | sta $dd0d | ||
- | bit $dd0d | ||
- | dec .CIA_type | ||
- | .CIA_detect_nmi pla | ||
- | pla | ||
- | pla | ||
- | sty $dd0d ; | ||
- | cmp $dd0d | ||
- | |||
- | ;ATTN: for mathematical purposes a line starts at cycle 0 and ends at cycle 62! | ||
- | |||
- | ldx #$03 ;half variance delay: | ||
- | .check0 cpx $d012 ; | ||
- | bne .check0 | ||
- | ;ending this command: 2 3 4 5 6 7 8 | ||
- | .check_0 jsr .wartung ; | ||
- | nop | ||
- | cpx $d012 ;now check in cycle: 60 61 62 0 1 2 3 | ||
- | .check1 beq .check_1 | ||
- | cmp ($00), | ||
- | .check_1 jsr .wartung | ||
- | nop | ||
- | .check2 cpx $d012 ;now check in cycle: 62 0 1 61 62 0 1 | ||
- | beq .check_2 | ||
- | bit $ea ; | ||
- | .check_2 jsr .wartung | ||
- | bit $ea | ||
- | cpx $d012 ;now check in cycle: | ||
- | .check3 bne .check_3 ; | ||
- | |||
- | ; | ||
- | ; | ||
- | ;- the first STA $d011 MUST end in cycle 13 (when starting to count at 0) | ||
- | ;- that means, it starts on cycle 9, | ||
- | ;- that means LDA #D018WERT starts at cycle 1 | ||
- | ;- with 7 cycles jitter another sta zp (3 cycles) happens before | ||
- | ;means: 3 (save accu) + 7 (Jitter) + 3 (jmp) + 7 (NMI itself) = 20 | ||
- | ;-> NMI must execute at cycle 44, so the (ForceLoad+Run) command has to | ||
- | ; do its write in cycle 42 (as the nmi then happens after cycle 43) | ||
- | .check_3 ; | ||
- | ;on cycle 3 ... | ||
- | lda .CIA_type ; | ||
- | .check4 bpl .check_4 ; | ||
- | |||
- | .check_4 lda #< | ||
- | sta $dc06 ;4 | ||
- | sta $dd04 ;4 | ||
- | lda #> | ||
- | sta $dc07 ;4 | ||
- | sta $dd05 ;4 | ||
- | lda #$4c ;2 | ||
- | sta $dc04 ;4 | ||
- | lda # | ||
- | sta $dd0e ;4 = 38/39 -> cycle 42 of this RL on 6526A | ||
- | |||
- | .CIA_type = *+1 | ||
- | ldx #0 ;2 | ||
- | .check5 bmi .check_5 ; | ||
- | .check_5 ; | ||
- | |||
- | ;2nd calculation: | ||
- | ; | ||
- | ;NMI = 7 cycles, jmp = 3 cycles, max.Jitter = 7 cycles | ||
- | ;means: 17+1 cycles later Timer B shall be started to "land at" $0000. This also | ||
- | ;means that we have to start Timer B 17+1+hi(NMI_base) cycles later. | ||
- | |||
- | !set .rest = (> | ||
- | |||
- | !do while .rest > 5 { | ||
- | nop | ||
- | !set .rest = .rest - 2 | ||
- | } | ||
- | !if .rest = 4 { | ||
- | sta $dc0f | ||
- | } else { | ||
- | sta $dc0f,y | ||
- | } | ||
- | ;use CIA#1timerA as simple memory location for jmp $xx00 | ||
- | !set .jmpval = (< | ||
- | +mv16im .jmpval, | ||
- | ;use that as NMI-vector | ||
- | +mv16im $dc04,$fffa | ||
- | rts | ||
- | |||
- | ;IMPORTANT: check all the " | ||
- | ;page boundary was crossed whithin a branch command. | ||
- | !if ((> | ||
- | !serious "Page boundary crossed where it was a bad thing to happen. | ||
- | } | ||
- | ; | ||
- | !macro flinmi jitter, offset { | ||
- | * = offset + $0100*(8-jitter) | ||
- | !if (jitter & 1) = 0 { | ||
- | sta zpreg ;3 | ||
- | } else { | ||
- | sta .thisreg ; | ||
- | } | ||
- | !if jitter < 5 { | ||
- | bit $dd0d ;4 | ||
- | !if (jitter = 0) { | ||
- | nop ;2 | ||
- | nop ;2 | ||
- | } | ||
- | } | ||
- | !if ((jitter & 3) = 1) OR ((jitter & 3) = 2) { | ||
- | nop ;2 | ||
- | } | ||
- | ;Der eigentliche FLI-IRQ | ||
- | lda # | ||
- | sta $d018 | ||
- | lda #d011wert2 | ||
- | sta $d011 ; | ||
- | lda #d011wert1 | ||
- | sta $d011 | ||
- | lda #d018wert1 | ||
- | sta $d018 | ||
- | |||
- | !if jitter >= 5 { | ||
- | bit $dd0d | ||
- | } | ||
- | !if (jitter & 1) = 0 { | ||
- | lda zpreg | ||
- | } else { | ||
- | .thisreg = *+1 | ||
- | lda #0 | ||
- | } | ||
- | rti | ||
- | } | ||
- | ; | ||
- | ;create the NMI-Routines right on their spot | ||
- | !set .oldaddr = * | ||
- | !set antijitter = 0 | ||
- | !do { | ||
- | !set jitterVal = 8 - antijitter | ||
- | +flinmi jitterVal, NMI_base | ||
- | !set antijitter = antijitter+1 | ||
- | } while antijitter < 8 | ||
- | * = .oldaddr | ||
- | ; | ||
- | } else { | ||
- | !serious " | ||
- | } | ||
- | </ | ||
- | |||
- | ===== Advantages ===== | ||
- | |||
- | ==== no preparation for the next interrupt, no static starting cycle ==== | ||
- | If we create a " | ||
- | |||
- | Take the NMI instead: the CIA just reloads its timer - 0 cycles wasted, the only thing to do is create a starting and an ending condtion. | ||
- | |||
- | ==== Advantage brought up by JackAsser ==== | ||
- | We could actually care less about the CIA-differences by adding another Jitter-Routine. | ||
- | Now it's up to you if you can waste or abuse (whatever makes you happy) another page for another routine. | ||
- | |||
- | ===== Disadvantages ===== | ||
- | |||
- | Any Interrupt can jitter 0 to 7 cycles. | ||
- | |||
- | ---- | ||
- | To make the code-part work {{: | ||
- | Please assemble test4x4fli.a with ACME 0.93. If you're missing any of the library-routines I use or get errors while compiling: {{: | ||
- | ---- | ||
- | |||
- | St0fF / Neoplasia | ||