gbdk-2020 | GameBoy Development Kit |
| download: https://git.y1.nz/archives/gbdk.tar.gz | |
| README | Files | Log | Refs | LICENSE |
gbdk-lib/libc/targets/mos6502/nes/crt0.s
1 ;
2 ; crt0.s for NES, using UNROM-512 (mapper30) with single-screen mirroring variant
3 ;
4 ; Provides:
5 ; * Start-up code clearing RAM and VRAM
6 ; * Constant-cycle-time NMI handler, performing sprite DMA and VRAM writes via transfer buffer at $100
7 ; * 16-bit frame counter _sys_time, to support VM routines
8 .module crt0
9 .include "global.s"
10 .include "mapper_macros.s"
11
12 ; OAM CPU page
13 _shadow_OAM = 0x200
14
15 .macro WRITE_PALETTE_SHADOW
16 lda #>0x3F00
17 sta PPUADDR
18 lda #<0x3F00
19 sta PPUADDR
20 ldx __crt0_paletteShadow
21 i = 0
22 .rept 8
23 stx PPUDATA
24 lda (__crt0_paletteShadow+1+3*i+0)
25 sta PPUDATA
26 lda (__crt0_paletteShadow+1+3*i+1)
27 sta PPUDATA
28 lda (__crt0_paletteShadow+1+3*i+2)
29 sta PPUDATA
30 i = i + 1
31 .endm
32 .endm
33
34 ;; ****************************************
35
36 ;; Ordering of segments for the linker
37 ;; Code that really needs to be in the fixed bank
38 .area _CODE
39 .area _HOME
40 ;; Similar to _HOME
41 .area _BASE
42 ;; Constant data
43 .area _LIT
44 .area _RODATA
45 ;; Constant data, used to init _DATA
46 .area _INITIALIZER
47 .area _XINIT
48 ;; Code, used to init _DATA
49 .area _GSINIT
50 .area _GSFINAL
51 ;; Uninitialised ram data
52 .area _DATA
53 .area _BSS
54 ;; Initialised in ram data
55 .area _INITIALIZED
56 ;; For malloc
57 .area _HEAP
58 .area _HEAP_END
59
60 .area OSEG (PAG, OVR)
61 .area GBDKOVR (PAG, OVR)
62 .area _ZP (PAG)
63 __shadow_OAM_base:: .ds 1
64 __current_bank:: .ds 1
65 _sys_time:: .ds 2
66 _shadow_PPUCTRL:: .ds 1
67 _shadow_PPUMASK:: .ds 1
68 _bkg_scroll_x:: .ds 1
69 _bkg_scroll_y:: .ds 1
70 _attribute_row_dirty:: .ds NUM_NT
71 _attribute_column_dirty:: .ds NUM_NT
72 .ifdef NES_WINDOW_LAYER
73 _attribute_row_dirty_win:: .ds NUM_NT
74 _attribute_column_dirty_win:: .ds NUM_NT
75 .endif
76 __oam_valid_display_on:: .ds 1
77 __SYSTEM:: .ds 1
78 __hblank_writes_index:: .ds 1
79
80 .ifdef NES_WINDOW_LAYER
81 _win_pos_x:: .ds 1
82 _win_pos_y:: .ds 1
83 .endif
84 .ifdef VRAM_MAPPER_CFG_ISR
85 __lcd_isr_save_s: .ds 1
86 __current_vram_cfg_write:: .ds 1
87 .define __crt0_MAPPER_VRAM_CFG_TEMP "___SDCC_m6502_ret4"
88 .endif
89
90 .define __crt0_NMITEMP "___SDCC_m6502_ret4"
91
92 .area _BSS
93 .ifdef NES_LOMEM
94 ; For LOMEM configuration use part of stack page for attribute shadow, leaving 56 bytes for subroutine calls
95 _attribute_shadow = 0x188
96 .else
97 ; Otherwise allocate attribute shadow in data segment, with 64 bytes for each NT/AT
98 _attribute_shadow:: .ds (64*NUM_NT)
99 .endif
100 .ifdef NES_WINDOW_LAYER
101 _attribute_shadow_win:: .ds (64*NUM_NT)
102 .endif
103 __crt0_paletteShadow:: .ds 25
104 .mode:: .ds 1
105 __lcd_isr_PPUCTRL:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
106 __lcd_isr_PPUMASK:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
107 __lcd_isr_scroll_x:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
108 __lcd_isr_scroll_y:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
109 __lcd_isr_delay_num_scanlines:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
110 __lcd_isr_ppuaddr_lo:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
111 .ifdef VRAM_MAPPER_CFG_ISR
112 __lcd_isr_mapper:: .ds (2*.MAX_DEFERRED_ISR_CALLS)
113 .endif
114
115 .area _CODE
116
117 .bndry 0x100
118 .identity::
119 _identity::
120 i = 0
121 .rept 256
122 .db i
123 i = i + 1
124 .endm
125
126 .define ProcessDrawList_tempX "__crt0_NMITEMP+2"
127 .define ProcessDrawList_addr "__crt0_NMITEMP+0"
128
129 .bndry 0x100
130 nop ; Pad to offset, to support zero-terminator value
131 ProcessDrawList_UnrolledCopyLoop:
132 .rept 32
133 pla ; +4
134 sta PPUDATA ; +4
135 .endm
136 ProcessDrawList_DoOneTransfer:
137 pla ; +4
138 beq ProcessDrawList_EndOfList ; +2/3
139 sta *ProcessDrawList_addr ; +3
140 pla ; +4
141 sta PPUCTRL ; +4
142 pla ; +4
143 sta PPUADDR ; +4
144 pla ; +4
145 sta PPUADDR ; +4
146 tay ; +2
147 jmp [ProcessDrawList_addr] ; +5
148 ; Total = 4 + 2 + 2 + 4 + 3 + 6*4 + 2 + 2 + 5 = 48 for each transfer (...+ 8*NumBytesCopied)
149 ; 4 + 3 + 14 = 7 + 14 = 21 fixed-cost exit
150
151 .ifdef VRAM_MAPPER_CFG_TRANSFER
152 ProcessDrawList_mapper_switch::
153 sta MAPPER_WRITE_REG,y ; +5
154 jmp ProcessDrawList_DoOneTransfer ; +3
155 ; Total = 4 + 4 + 4 + 2 + 5 + 5 = 24 for each mapper switch
156 .endif
157
158 ; .bndry 0x100 (skip alignment as previous alignment means page-cross won't happen)
159 __crt0_doSpriteDMA:
160 bit *__oam_valid_display_on
161 bpl __crt0_doSpriteDMA_spritePageInvalid
162 lda #0 ; +2
163 sta OAMADDR ; +4
164 lda #>_shadow_OAM ; +2
165 sta OAMDMA ; +512/513
166 rts
167 __crt0_doSpriteDMA_spritePageInvalid:
168 ; Delay 520 cycles to keep timing consistent
169 ldx #104
170 __crt0_doSpriteDMA_loop:
171 dex
172 bne __crt0_doSpriteDMA_loop
173 rts
174
175 ProcessDrawList_EndOfList:
176 ldx *ProcessDrawList_tempX ; +3
177 txs ; +2
178 lda #0 ; +2
179 sta *__vram_transfer_buffer_pos_w ; +3
180 sta *__vram_transfer_buffer_valid ; +3
181 rts ; +6
182 ; = 3 + 2 + 2 + 3 + 3 + 6 = 19
183
184 ;
185 ; Number of cycles spent = 19 + 21 + 48*NumTransfers + 8*NumBytesTransferred
186 ; = 56 + 48*NumTransfers + 8*NumBytesTransferred
187 ; = 8 * (7 + 6*NumTransfers + NumBytesTransferred)
188 ; = 8 * (6*NumTransfers + NumBytesTransferred + 7)
189 ;
190 ProcessDrawList:
191 lda #>ProcessDrawList_UnrolledCopyLoop ; +2
192 sta *ProcessDrawList_addr+1 ; +3
193 tsx ; +2
194 stx *ProcessDrawList_tempX ; +3
195 ldx #0xFF ; +2
196 txs ; +2
197 jmp ProcessDrawList_DoOneTransfer ; +3
198 ; Total = 2 + 3 + 2 + 3 + 2 + 2 + 3 = 17 fixed-cost entry
199
200 ;
201 ; Delays until specified (non-zero) scanline is reached
202 ;
203 ; First scanline's delay needs adjusting in coordination with .do_hblank_writes
204 ;
205 .define .acc "___SDCC_m6502_ret4"
206 .delay_to_lcd_scanline::
207 jmp 2$
208 1$:
209 jsr .delay_28_cycles
210 jsr .delay_28_cycles
211 jsr .delay_12_cycles ; -> 28 + 28 + 12 = 68 cycles
212 clc
213 2$:
214
215 jsr .delay_fractional ; -> 35.666 NTSC cycles 28.5625 PAL cycles
216 lda *0x00
217
218 dex
219 bne 1$ ; -> 5 cycles
220 rts
221
222 .delay_28_cycles:
223 jsr .delay_12_cycles
224 .delay_16_cycles:
225 nop
226 .delay_14_cycles:
227 nop
228 .delay_12_cycles:
229 rts
230
231 ;
232 ; Takes 35.666 NTSC cycles / 28.5626 PAL cycles
233 ;
234 ; Note: does NOT clear carry - this needs to be handled by caller
235 ;
236 .delay_fractional:
237 lda #144 ; Initialize A with PAL fractional cycle count
238 ; +7 cycles for NTSC scanlines
239 bit *__SYSTEM
240 bvs 3$
241 lda #171 ; NTSC fractional cycle count
242 nop
243 nop
244 nop
245 3$: ; -> 15 NTSC cycles / 8 PAL cycles
246 ; Add fractional cycles and branch on carry
247 adc *.acc
248 bcs 4$
249 4$:
250 sta *.acc ; -> 8.666 NTSC cycles / 8.5625 PAL cycles
251 rts ; -> 6 cycles for RTS, 6 cycles for JSR = 12 cycles
252
253 __crt0_NMI:
254 pha
255 txa
256 pha
257 tya
258 pha
259
260 ; Skip graphics updates if blanked, to allow main code to do VRAM address / scroll updates
261 nop
262 bit *__oam_valid_display_on
263 bvs __crt0_NMI_skip
264 ; Do Sprite DMA or delay equivalent cycles
265 jsr __crt0_doSpriteDMA
266 ; Update VRAM
267 lda PPUSTATUS
268 lda #PPUCTRL_SPR_CHR
269 sta PPUCTRL
270 jsr DoUpdateVRAM
271
272 ; First index is from main code / VBL ISR
273 ldy *__hblank_writes_index
274 ; Set scroll address
275 lda __lcd_isr_scroll_x,y
276 sta PPUSCROLL
277 lda __lcd_isr_scroll_y,y
278 sta PPUSCROLL
279
280 ; Write PPUCTRL and force NMI enabled to avoid deadlock from buggy isr handlers
281 lda __lcd_isr_PPUCTRL,y
282 ora #PPUCTRL_NMI
283 sta PPUCTRL
284
285 ; Write PPUMASK, in case it was disabled
286 lda __lcd_isr_PPUMASK,y
287 sta PPUMASK
288
289 .ifdef VRAM_MAPPER_CFG_ISR
290 ; Write mapper reg (note: assumes no bus conflicts!)
291 lda __lcd_isr_mapper,y
292 sta MAPPER_WRITE_REG
293 sta *__crt0_MAPPER_VRAM_CFG_TEMP
294 sta *__crt0_MAPPER_VRAM_CFG_TEMP
295 nop
296 .endif
297
298 ; Delay and call fake LCD isr if list not empty
299 lda __lcd_isr_delay_num_scanlines+1,y ; Check for first potential LCD write
300 beq __crt0_NMI_skip_hblank_writes
301 ; First delay until end-of-vblank, depending on transfer buffer contents...
302 ; (X set to correct delay value by DoUpdateVRAM)
303 1$:
304 lda *0x00
305 dex
306 bne 1$
307 ; Do additional delay of 5186 cycles if running on a PAL system, and -5*7 + 2 = -33 for alignment
308 ; This is to compensate for the longer vblank period of 7459 vs NTSC's 2273
309 bit *__SYSTEM
310 bvc 2$
311 nop
312 nop
313 ldy #5
314 ldx #(14-7)
315 3$:
316 dex
317 bne 3$
318 dey
319 bne 3$
320 2$:
321 ldy *__hblank_writes_index
322 iny ; Move index past VBL write, to first potential LCD write
323 ; Call the write reg subroutine
324 jsr .do_hblank_writes
325 __crt0_NMI_skip_hblank_writes:
326 .ifdef VRAM_MAPPER_CFG_ISR
327 lda *__current_bank
328 ora *__crt0_MAPPER_VRAM_CFG_TEMP
329 SWITCH_PRG0_A
330 .endif
331 __crt0_NMI_skip:
332
333
334 jsr .tim_emulation
335
336 ; Update frame counter
337 lda *_sys_time
338 clc
339 adc #1
340 sta *_sys_time
341 bcc 9$
342 inc *(_sys_time+1)
343 9$:
344
345 pla
346 tay
347 pla
348 tax
349 pla
350 rti
351
352 DoUpdateVRAM:
353 WRITE_PALETTE_SHADOW
354 bit *__vram_transfer_buffer_valid
355 bmi DoUpdateVRAM_drawListValid
356 DoUpdateVRAM_drawListInvalid:
357 ; Delay for all unused cycles and ProcessDrawList overhead to keep timing consistent
358 lda *0x00
359 nop
360 ldx #(VRAM_DELAY_CYCLES_X8+6)
361 bne DoUpdateVRAM_end
362 DoUpdateVRAM_drawListValid:
363 jsr ProcessDrawList
364 ; Delay for remaining unused cycles to keep timing consistent
365 ldx *__vram_transfer_buffer_num_cycles_x8
366 ; Reset available cycles to initial value
367 lda #VRAM_DELAY_CYCLES_X8
368 sta *__vram_transfer_buffer_num_cycles_x8
369 DoUpdateVRAM_end:
370 rts
371
372 __crt0_IRQ:
373 jmp __crt0_IRQ
374
375 __crt0_setPalette:
376 ; Set background color to 30 (white)
377 lda #0x30
378 sta __crt0_paletteShadow
379 ; set all background / sprite sub-palettes to 10, 00, 1D
380 ldx #0x18
381 1$:
382 lda #0x1D
383 sta __crt0_paletteShadow,x
384 dex
385 lda #0x00
386 sta __crt0_paletteShadow,x
387 dex
388 lda #0x10
389 sta __crt0_paletteShadow,x
390 dex
391 bne 1$
392 rts
393
394 ;
395 ; Waits for vblank flag to be set. This macro should only be
396 ; used during the PPU warm-up phase at reset, as a hardware
397 ; flaw can cause the flag to be cleared in the register
398 ; without returning a set flag on the CPU data bus.
399 ;
400 ; On Dendy-like Famiclones there is an additional problem
401 ; that this pathological case can occur *every* frame if the wait
402 ; loop is exactly 8 cycles long, causing a soft-lock at reset.
403 ; For this reason, code before the "bpl .loop" instruction must
404 ; be aligned so the branch does not cross a 256-byte page.
405 ;
406 ; https://www.nesdev.org/wiki/PPU_power_up_state
407 ;
408 .macro CRT0_WAIT_PPU ?.loop;
409 .loop:
410 lda PPUSTATUS
411 bpl .loop
412 .endm
413
414 ;
415 ; Detects system. After execution, A contains the following values:
416 ;
417 ; 0: NTSC NES/Famicom
418 ; 1: PAL NES
419 ; 2: Dendy-like Famiclone
420 ;
421 .macro CRT0_WAIT_PPU_AND_DETECT_SYSTEM ?.loop, ?.end_of_loop, ?.end
422 ldx #0
423 ldy #0
424 ; 256 iterations of the inner loop (X) takes 256 * (4 + 2 + 2 + 3) - 1 = 2816 cycles
425 ; 1 iteration of the outer loop takes 2816 + 2 + 3 = 2821 cycles
426 ; And different systems will have the following contents in Y:
427 ; NTSC: 29780 / 2821 = 10
428 ; PAL: 33247 / 2821 = 11
429 ; Dendy: 35464 / 2821 = 12
430 .loop:
431 bit PPUSTATUS
432 bmi .end_of_loop
433 inx
434 bne .loop
435 iny
436 bne .loop
437 .end_of_loop:
438 tya
439 sec
440 sbc #10
441 .end:
442 .endm
443
444 .macro CRT0_CLEAR_RAM
445 ldx #0x00
446 txa
447 __crt0_clearRAM_loop:
448 sta 0x0000,x
449 sta 0x0100,x
450 sta 0x0200,x
451 sta 0x0300,x
452 sta 0x0400,x
453 sta 0x0500,x
454 sta 0x0600,x
455 sta 0x0700,x
456 inx
457 bne __crt0_clearRAM_loop
458 .endm
459
460 __crt0_clearVRAM:
461 lda #0x00
462 sta PPUADDR
463 sta PPUADDR
464 ldy #64
465 ldx #0
466 __crt0_clearVRAM_loop:
467 sta PPUDATA
468 dex
469 bne __crt0_clearVRAM_loop
470 dey
471 bne __crt0_clearVRAM_loop
472 rts
473
474 .wait_vbl_done::
475 _wait_vbl_done::
476 _vsync::
477 jsr _flush_shadow_attributes
478
479 ; if display is on, run deferred ISR handlers
480 bit *__oam_valid_display_on
481 bvs 1$
482 jsr .deferred_isr_run
483 1$:
484 ; Enable OAM DMA in next NMI
485 lda *__oam_valid_display_on
486 ora #OAM_VALID_MASK
487 sta *__oam_valid_display_on
488
489 _wait_vbl_done_waitForNextFrame:
490 lda *_sys_time
491 _wait_vbl_done_waitForNextFrame_loop:
492 cmp *_sys_time
493 beq _wait_vbl_done_waitForNextFrame_loop
494
495 ; Disable OAM DMA in next NMI
496 lda *__oam_valid_display_on
497 and #~OAM_VALID_MASK
498 sta *__oam_valid_display_on
499
500 rts
501
502 .display_off::
503 _display_off::
504 ; Skip entirely if display_off is called repeatedly
505 bit *__oam_valid_display_on
506 bvs 1$
507 ; Reset deferred ISR buffers
508 jsr .deferred_isr_reset
509 ; Clear BG and SPR in first __lcd_isr_PPUMASK, to cause
510 ; NMI code to clear PPUMASK *after* 1 normal execution
511 lda *_shadow_PPUMASK
512 and #~(PPUMASK_SHOW_BG|PPUMASK_SHOW_SPR)
513 sta __lcd_isr_PPUMASK
514 ; Wait for 1 execution of NMI to drain vram transfer buffer
515 jsr _wait_vbl_done_waitForNextFrame
516 ; Set forced blanking bit
517 lda *__oam_valid_display_on
518 ora #DISPLAY_OFF_MASK
519 sta *__oam_valid_display_on
520 1$:
521 rts
522
523 .display_on::
524 _display_on::
525 ; Skip entirely if display_on is called repeatedly
526 bit *__oam_valid_display_on
527 bvc 1$
528 ; Reset deferred ISR buffers
529 jsr .deferred_isr_reset_and_init
530 ; Set DISPLAY_ON bits
531 lda *__oam_valid_display_on
532 and #~DISPLAY_OFF_MASK
533 sta *__oam_valid_display_on
534 1$:
535 rts
536
537 __crt0_RESET:
538 ; Disable IRQs
539 sei
540 ; Set stack pointer
541 ldx #0xff
542 txs
543 ; Set switchable bank to first
544 __crt0_RESET_bankSwitchValue:
545 lda #0x00
546 sta __crt0_RESET_bankSwitchValue+1
547 ; Disable NMIs and rendering
548 sta PPUCTRL
549 sta PPUMASK
550 ; Clear RAM
551 CRT0_CLEAR_RAM
552 ; Wait for PPU warm-up / detect system
553 bit PPUSTATUS
554 CRT0_WAIT_PPU
555 CRT0_WAIT_PPU_AND_DETECT_SYSTEM
556 ; Store system in upper two bits of __SYSTEM, to allow bit instruction to quickly test for PAL
557 clc
558 ror
559 ror
560 ror
561 sta *__SYSTEM
562 ; Clear VRAM
563 jsr __crt0_clearVRAM
564 ; Hide sprites in shadow OAM, and perform OAM DMA
565 ldx #0
566 txa
567 jsr _hide_sprites_range
568 stx OAMADDR
569 lda #>_shadow_OAM
570 sta OAMDMA
571
572 ; Perform initialization of DATA area
573 lda #<s__XINIT
574 sta ___memcpy_PARM_2
575 lda #>s__XINIT
576 sta ___memcpy_PARM_2+1
577 lda #<l__XINIT
578 sta ___memcpy_PARM_3
579 lda #>l__XINIT
580 sta ___memcpy_PARM_3+1
581 lda #<s__DATA
582 ldx #>s__DATA
583 jsr ___memcpy
584
585 ; For a PAL / Dendy system, override _TMA_REG to be correctly initialized for a 50Hz vblank rate
586 lda *__SYSTEM
587 beq 1$
588 lda #.TIMER_VBLANK_PARITY_MODE_SYSTEM_50HZ
589 sta _TMA_REG
590 1$:
591
592 ; Set bank to first
593 lda #0x00
594 sta *__current_bank
595 ; Set palette shadow
596 jsr __crt0_setPalette
597 lda #VRAM_DELAY_CYCLES_X8
598 sta *__vram_transfer_buffer_num_cycles_x8
599 lda #0
600 sta *__vram_transfer_buffer_pos_w
601 ;
602 lda #(PPUMASK_SHOW_BG | PPUMASK_SHOW_SPR | PPUMASK_SHOW_BG_LC | PPUMASK_SHOW_SPR_LC)
603 sta *_shadow_PPUMASK
604 lda #OAM_VALID_MASK
605 sta *__oam_valid_display_on
606 ; enable NMI
607 lda #(PPUCTRL_NMI | PPUCTRL_SPR_CHR)
608 sta *_shadow_PPUCTRL
609 sta PPUCTRL
610 jsr .deferred_isr_reset
611 ; Call main
612 jsr _main
613 ; main finished - loop forever
614 __crt0_waitForever:
615 jmp __crt0_waitForever
616
617 .bndry 0x100
618 .do_hblank_writes:
619 .define .reg_write_index "__crt0_NMITEMP+1"
620 .define .lda_PPUADDR "__crt0_NMITEMP+2"
621 .define .ldx_PPUMASK "__crt0_NMITEMP+3"
622
623 ; Delay to make hblank at end of scanline 0
624 .ifdef VRAM_MAPPER_CFG_ISR
625 ; Save stack pointer (5 cycles)
626 tsx
627 stx *__lcd_isr_save_s
628 ; 6 cycles to adjust for no-RTS of .delay_fractional (now a macro)
629 ldx #0x00
630 txs
631 nop
632 ; -5 cycles in loop, to account for cycles spent saving stack pointer above
633 ldx #9
634 .else
635 ldx #10
636 .endif
637 0$:
638 dex
639 bne 0$
640 clc
641 nop
642
643 sty *.reg_write_index
644
645 lda #0
646 sta *.acc
647 1$:
648 ldx __lcd_isr_delay_num_scanlines,y
649 cpx #1
650 beq 3$ ; Skip delay if next scanline
651 cpx #0
652 beq 2$ ; Exit if empty buffer (no calls were made within frame)
653 dex
654 .ifdef VRAM_MAPPER_CFG_ISR
655 txa
656 ldx *__lcd_isr_save_s
657 txs
658 tax
659 nop
660 jsr .delay_to_lcd_scanline
661 jsr .delay_12_cycles
662 jsr .delay_28_cycles
663 .else
664 jsr .delay_to_lcd_scanline
665 jsr .delay_12_cycles
666 jsr .delay_28_cycles
667 nop
668 nop
669 nop
670 nop
671 lda *0x00
672 .endif
673 3$:
674
675 .ifdef VRAM_MAPPER_CFG_ISR
676 ; Delay for 23.666 NTSC cycles / 16.5625 PAL cycles
677 ldx __lcd_isr_mapper,y
678 txs
679 ;
680 lda #144 ; Initialize A with PAL fractional cycle count
681 ; +7 cycles for NTSC scanlines
682 bit *__SYSTEM
683 bvs 30$
684 lda #171 ; NTSC fractional cycle count
685 nop
686 nop
687 nop
688 30$: ; -> 15 NTSC cycles / 8 PAL cycles
689 ; Add fractional cycles and branch on carry
690 adc *.acc
691 bcs 40$
692 40$:
693 sta *.acc ; -> 8.666 NTSC cycles / 8.5625 PAL cycles
694 .else
695 ; Delay for 35.666 NTSC cycles / 28.5625 PAL cycles
696 jsr .delay_fractional
697 .endif
698
699 ; Pre-write PPUADDR (1st write) and y-scroll
700 sty PPUADDR
701 lda __lcd_isr_scroll_y,y
702 sta PPUSCROLL
703 ; A <- PPUADDR (2nd write)
704 lda __lcd_isr_ppuaddr_lo,y
705 sta *.lda_PPUADDR
706 ldx __lcd_isr_PPUMASK,y
707 stx *.ldx_PPUMASK
708 ; X <- SCROLLX
709 ldx __lcd_isr_scroll_x,y
710 ; Y <- PPUCTRL
711 lda __lcd_isr_PPUCTRL,y
712 tay
713 lda *.lda_PPUADDR
714 ;
715 ; Write 4 PPU registers in following order.
716 ;
717 ; 1. PPUSCROLL (needs to be written to set fine-x)
718 ; 2. PPUADDR 2nd write (highest priority as needs to happen before the two-tile pre-fetch)
719 ; 3. PPUCTRL (PPU pattern table switch can affect two-tile pre-fetch)
720 ; 4. PPUMASK (emphasis and render on/off are maybe less distracting?)
721 ;
722 ; TODO: Self-modifying code could build a non-redundant write sequence in RAM.
723 ;
724 stx PPUSCROLL
725 sta PPUADDR
726 ldx *.ldx_PPUMASK
727 stx PPUMASK
728 .ifdef VRAM_MAPPER_CFG_ISR
729 ; Mapper write (note: assumes no bus conflicts!)
730 tsx
731 stx MAPPER_WRITE_REG
732 .endif
733 sty PPUCTRL
734
735 inc *.reg_write_index
736 ldy *.reg_write_index
737 jmp 1$
738 2$:
739 .ifdef VRAM_MAPPER_CFG_ISR
740 ; Store MAPPER VRAM CFG
741 tsx
742 stx *__crt0_MAPPER_VRAM_CFG_TEMP
743 ; Restore stack pointer
744 ldx *__lcd_isr_save_s
745 txs
746 .endif
747 rts
748
749 ; Interrupt / RESET vector table
750 .area VECTORS (ABS)
751 .org 0xfffa
752 .dw __crt0_NMI
753 .dw __crt0_RESET
754 .dw __crt0_IRQ
This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.