git.y1.nz

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.