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/deferred_isr.s

      1     .include    "global.s"
      2 
      3     .area   _HOME
      4 
      5     .define .lcd_scanline_previous "REGTEMP"
      6     .define .lcd_buf_index "REGTEMP+1"
      7     .define .lcd_buf_end "REGTEMP+2"
      8     .define .last_scanline_value
      9 
     10 ;
     11 ; Writes shadow registers to buffer
     12 ;
     13 ; Input:
     14 ;  X: Scanline number
     15 ;
     16 .write_shadow_registers_to_buffer::
     17     inx
     18     cpx #.SCREENHEIGHT
     19     bcc 6$
     20     txa
     21     sec
     22     sbc #.SCREENHEIGHT
     23     tax
     24 6$:
     25     ; Copy shadow registers
     26     ldy *.lcd_buf_index
     27 .ifdef NES_WINDOW_LAYER
     28     lda #0
     29     sta __lcd_isr_mapper,y
     30 .endif
     31     lda *_shadow_PPUMASK
     32     sta __lcd_isr_PPUMASK,y
     33     lda *_shadow_PPUCTRL
     34     sta __lcd_isr_PPUCTRL,y
     35 .ifdef NES_WINDOW_LAYER
     36     ; is WIN in front of BKG?
     37     ; Never in front if not enabled
     38     lda *__oam_valid_display_on
     39     and #WINDOW_ON_MASK
     40     beq 0$
     41     ; always in front if _win_pos_y == 0
     42     lda *_win_pos_y
     43     beq 9$
     44     ; in front if current_scanline >= win_pos_y
     45     txa
     46     cmp #.SCREENHEIGHT
     47     bcc 7$
     48     sbc #.SCREENHEIGHT
     49 7$:
     50     cmp *_win_pos_y
     51     bcc 0$
     52 9$:
     53     ; Enable window
     54     lda __lcd_isr_mapper,y
     55     ora #PPUHI_WIN
     56     sta __lcd_isr_mapper,y
     57     ; Write window registers
     58     lda #0
     59     sec
     60     sbc *_win_pos_x
     61     sta __lcd_isr_scroll_x,y
     62     lsr
     63     lsr
     64     lsr
     65     sta __lcd_isr_ppuaddr_lo,y
     66     txa
     67     sec
     68     sbc *_win_pos_y
     69     cmp #.SCREENHEIGHT
     70     bcc 8$
     71     sbc #.SCREENHEIGHT
     72 8$:
     73     jmp 2$
     74 .endif
     75 0$:
     76     lda *_bkg_scroll_x
     77     sta __lcd_isr_scroll_x,y
     78     lsr
     79     lsr
     80     lsr
     81     sta __lcd_isr_ppuaddr_lo,y
     82     ; Add _bkg_scroll_y to current scanline to generate final Y-scroll, with 239->0 wrap-around
     83     txa
     84     clc
     85     adc *_bkg_scroll_y
     86     bcc 1$
     87     sbc #.SCREENHEIGHT
     88 1$:
     89     cmp #.SCREENHEIGHT
     90     bcc 2$
     91     sbc #.SCREENHEIGHT
     92 2$:
     93     sta __lcd_isr_scroll_y,y
     94     and #0xF8
     95     asl
     96     asl
     97     ora __lcd_isr_ppuaddr_lo,y
     98     sta __lcd_isr_ppuaddr_lo,y
     99     rts
    100 
    101 ;
    102 ; Resets the deferred ISR.
    103 ;
    104 ; This effectively omits the double-buffering delay and writes the shadow registers to the first buffer.
    105 ; It is intended to be used when display is re-enabled after being turned off, to display with reasonable values.
    106 ;
    107 ; Note that in contrast to .run_deferred_isr_handlers, this will NOT actually run VBL / LCD handlers, so
    108 ; can still cause glitches.
    109 ;
    110 .deferred_isr_reset::
    111     ; Prepare VBL buffer data (need to start at scanline -1 = SCREENHEIGHT-1 for correct Y scroll)
    112     lda #0
    113     sta *__hblank_writes_index
    114     sta *.lcd_buf_index
    115     sta __lcd_isr_delay_num_scanlines+1
    116     ldx #.SCREENHEIGHT-1
    117     jmp .write_shadow_registers_to_buffer
    118 
    119 ;
    120 ; Resets the deferred ISR, then runs VBL and LCD handlers twice to initialize buffers with valid data.
    121 ;
    122 .deferred_isr_reset_and_init::
    123     jsr .deferred_isr_reset
    124     jsr .deferred_isr_run
    125     jmp .deferred_isr_run
    126 
    127 ;
    128 ; Executes the deferred VBL/LCD handlers.
    129 ;
    130 ; After each ISR handler has run, PPU shadow registers are written to a buffer 
    131 ; which is consumed by the vblank NMI handler.
    132 ; Double-buffering is used to avoid buffer locking / race conditions.
    133 ;
    134 .deferred_isr_run::
    135     ; Save shadow registers that VBL or LCD isr could change
    136     lda *_shadow_PPUMASK
    137     pha
    138     lda *_shadow_PPUCTRL
    139     pha
    140     lda *_bkg_scroll_x
    141     pha
    142     lda *_bkg_scroll_y
    143     pha
    144 .ifdef NES_WINDOW_LAYER
    145     lda *_win_pos_x
    146     pha
    147     lda *_win_pos_y
    148     pha
    149     lda *__oam_valid_display_on
    150     pha
    151 .endif
    152 
    153     ; Allow VBL isr to modify shadow registers if present
    154     jsr .jmp_to_VBL_isr
    155 
    156     ; Set initial scanline value
    157     lda #0xFF
    158     sta *.lcd_scanline_previous
    159 
    160     lda *__hblank_writes_index
    161     clc
    162     adc #.MAX_DEFERRED_ISR_CALLS
    163     cmp #(2*.MAX_DEFERRED_ISR_CALLS)
    164     bcc 20$
    165     lda #0
    166 20$:
    167     sta *.lcd_buf_index
    168     clc
    169     adc #.MAX_DEFERRED_ISR_CALLS
    170     sta *.lcd_buf_end
    171 
    172     ; Write shadow registers as first LCD buffer entry (actually VBL)
    173     ldy *.lcd_buf_index
    174     ldx #.SCREENHEIGHT-1
    175     jsr .write_shadow_registers_to_buffer
    176     iny
    177     sty *.lcd_buf_index
    178 
    179     ; Ensure second entry (actual LCD) starts off with zero (end-of-list)
    180     lda #0
    181     sta __lcd_isr_delay_num_scanlines,y
    182     ; Skip to end if LCD isr functionality is disabled (0x60 = RTS means LCD isr disabled)
    183     lda .jmp_to_LCD_isr
    184     cmp #0x60
    185     beq .deferred_isr_run_done
    186 
    187     lda *.lcd_scanline_previous
    188 
    189     jmp 2$
    190 1$:
    191     pla
    192     sta *.lcd_scanline_previous
    193 2$:
    194     ; We are done if next scanline is <= the previous one
    195     cmp #0xFF
    196     beq 3$
    197     cmp *__lcd_scanline
    198     bcs .deferred_isr_run_done
    199 3$:
    200     ;
    201     ldy *.lcd_buf_index
    202     lda *__lcd_scanline
    203     ; We are done if next LCD scanline >= SCREENHEIGHT
    204     cmp #.SCREENHEIGHT
    205     bcs .deferred_isr_run_done
    206     pha
    207 
    208 .ifdef NES_WINDOW_LAYER
    209     ldx *__lcd_scanline
    210     jsr .deferred_isr_handle_window
    211     pla
    212     txa
    213     pha
    214 .endif
    215 
    216     sec
    217     sbc *.lcd_scanline_previous
    218     sta __lcd_isr_delay_num_scanlines,y
    219     ; Call LCD isr
    220     jsr .jmp_to_LCD_isr
    221     ; Grab LCD scanline value from stack and store in X
    222     pla
    223     tax
    224     pha
    225     jsr .write_shadow_registers_to_buffer
    226 
    227 30$:
    228     iny
    229     sty *.lcd_buf_index
    230     cpy *.lcd_buf_end
    231     bne 1$
    232     
    233     ; Clear last-scanline-value from stack
    234     pla
    235 
    236 .deferred_isr_run_done:
    237 
    238 .ifdef NES_WINDOW_LAYER
    239     ldx #.SCREENHEIGHT
    240     jsr .deferred_isr_handle_window
    241 .endif
    242 
    243     ; Flip __hblank_writes_index for NMI handler
    244     ldy #0
    245     lda *__hblank_writes_index
    246     bne 21$
    247     ldy #.MAX_DEFERRED_ISR_CALLS
    248 21$:
    249     sty *__hblank_writes_index
    250 
    251 .ifdef NES_WINDOW_LAYER
    252     pla
    253     sta *__oam_valid_display_on
    254     pla
    255     sta *_win_pos_y
    256     pla
    257     sta *_win_pos_x
    258 .endif
    259     pla
    260     sta *_bkg_scroll_y
    261     pla
    262     sta *_bkg_scroll_x
    263     pla
    264     sta *_shadow_PPUCTRL
    265     pla
    266     sta *_shadow_PPUMASK
    267 
    268     rts
    269 
    270 .ifdef NES_WINDOW_LAYER
    271 ;
    272 ; Injects an extra scanline for window switch
    273 ;
    274 .deferred_isr_handle_window:
    275 ;;;
    276     ; If window is enabled and WY_REG is smaller than next LCD scanline, inject a deferred ISR for Window start / finish	
    277     lda *__oam_valid_display_on
    278     and #WINDOW_ON_MASK
    279     beq 21$
    280     txa
    281     cmp *_win_pos_y
    282     bcs 22$
    283     ; lcd_scanline_next < win_pos_y
    284     jmp 21$
    285 22$:
    286     ; lcd_scanline_next >= win_pos_y -> win_pos_y < lcd_scanline_next
    287     lda *_win_pos_y
    288     beq 21$
    289     sec
    290     sbc *.lcd_scanline_previous
    291     sta __lcd_isr_delay_num_scanlines,y
    292 
    293     ; Make window scroll Y coordinate source data from top of nametable
    294     ldx *_win_pos_y
    295     dex
    296     jsr .write_shadow_registers_to_buffer
    297 
    298     ; Grab LCD scanline value from win_pos_y and store in X
    299     dex
    300     iny
    301     sty *.lcd_buf_index
    302     ; TODO: Check to not overflow deferred ISR buffer
    303 21$:
    304     rts
    305 .endif

This webpage is intended to be an accessible preview of this repository. To get a fuller picture, clone it and use the git CLI.