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

      1 ;
      2 ; Routines for performing safe buffered writes via the vram transfer buffer.
      3 ;
      4 .include    "global.s"
      5 
      6 .title  "VRAMBuffer"
      7 .module VRAMBuffer
      8 
      9 .include "mapper_macros.s"
     10 
     11 ;
     12 ; Format of transfer buffer
     13 ;
     14 ; 0: Data length
     15 ; 1: 4 if inc-by-32, 0 if inc-by-1
     16 ; 2: PPUADDR_HI
     17 ; 3: PPUADDR_LO
     18 ; 4: ...N data bytes...
     19 ;
     20 VRAM_HDR_SIZEOF         = 4
     21 VRAM_HDR_JMPADDR        = 0
     22 VRAM_HDR_DIRECTION      = 1
     23 VRAM_HDR_PPUHI          = 2
     24 VRAM_HDR_PPULO          = 3
     25 VRAM_MAX_BYTES          = 32
     26 VRAM_MAX_STRIPE_SIZE    = VRAM_HDR_SIZEOF + VRAM_MAX_BYTES
     27 
     28 ;
     29 ; Locks the VRAM buffer for writing, causing the vblank handler to ignore it.
     30 ;
     31 .macro VRAM_BUFFER_LOCK
     32     clc
     33     ror *__vram_transfer_buffer_valid
     34 .endm
     35 
     36 ;
     37 ; Unlocks the VRAM buffer for writing.
     38 ;
     39 .macro VRAM_BUFFER_UNLOCK
     40     sec
     41     ror *__vram_transfer_buffer_valid
     42 .endm
     43 
     44 ;
     45 ; Converts data length into JMP address for unrolled loop
     46 ;
     47 ; jmp addr = 128-4*num_bytes + 1 = NOT(4*num_bytes)+1+128 + 1 = NOT(4*num_bytes)+130
     48 ;
     49 .macro LENGTH_TO_JMPADDR
     50     asl
     51     asl
     52     eor #0xFF
     53     adc #130
     54 .endm
     55 
     56 .area   GBDKOVR (PAG, OVR)
     57 _set_vram_byte_PARM_2:: .ds 1
     58     
     59 .area _ZP (PAG)
     60 __vram_transfer_buffer_valid::          .ds 1
     61 __vram_transfer_buffer_num_cycles_x8::  .ds 1
     62 __vram_transfer_buffer_pos_w::          .ds 1
     63 __vram_transfer_buffer_pos_old::        .ds 1
     64 
     65 .define __vram_transfer_buffer_temp     "(REGTEMP+6)"
     66 .define ppu_addr ".tmp"
     67 
     68 .area   _HOME
     69 
     70 ;
     71 ; Begin a horizontal stripe
     72 ;
     73 .ppu_stripe_begin_horizontal::
     74     clc
     75     bit *__oam_valid_display_on
     76     bvc .ppu_stripe_begin_indirect
     77     ; Direct write
     78     stx PPUADDR
     79     sta PPUADDR
     80 .ifdef VRAM_MAPPER_CFG_TRANSFER
     81     jsr __switch_win_addr_temp
     82 .endif
     83     ; Clear inc-by-32 bit in both PPUCTRL and _shadow_PPUCTRL, as NMI may re-write PPUCTRL
     84     lda *_shadow_PPUCTRL
     85     and #~PPUCTRL_INC32
     86     sta *_shadow_PPUCTRL
     87     sta PPUCTRL
     88     rts
     89 
     90 ;
     91 ; Begin a vertical stripe
     92 ;
     93 .ppu_stripe_begin_vertical::
     94     sec
     95     bit *__oam_valid_display_on
     96     bvc .ppu_stripe_begin_indirect
     97     ; Direct write
     98     stx PPUADDR
     99     sta PPUADDR
    100 .ifdef VRAM_MAPPER_CFG_TRANSFER
    101     jsr __switch_win_addr_temp
    102 .endif
    103     ; Set inc-by-32 bit in both PPUCTRL and _shadow_PPUCTRL, as NMI may re-write PPUCTRL
    104     lda *_shadow_PPUCTRL
    105     ora #PPUCTRL_INC32
    106     sta *_shadow_PPUCTRL
    107     sta PPUCTRL
    108     rts
    109 
    110 .ppu_stripe_begin_indirect:
    111     ; Indirect write via transfer buffer
    112     sty *__vram_transfer_buffer_temp
    113     pha
    114     txa
    115     pha
    116 .ifdef NES_WINDOW_LAYER
    117     lda *__current_vram_cfg_write
    118     and #MAPPER_CFG_NT_MASK
    119     ora .identity,x
    120     sta *ppu_addr+1
    121     tay
    122 .endif
    123     lda #0
    124     rol
    125     asl
    126     asl
    127     pha
    128     ; Ensure there's at least VRAM_MAX_STRIPE_SIZE bytes remaining to write before progressing
    129     ; This conservative limit simplifies conditions for rest of stripe in order to write single bytes with no checks
    130 2$:
    131     lda *__vram_transfer_buffer_pos_w
    132     cmp #128-VRAM_MAX_STRIPE_SIZE
    133     bcs 2$
    134 .ifdef NES_WINDOW_LAYER
    135     ldx *__vram_transfer_buffer_pos_w
    136     ; if transfer buffer is empty, we must always re-initialize mapper with fresh write
    137     beq 3$
    138     ; otherwise check the previous address. if there's a difference in bank bits, we need to re-initialize mapper 
    139     ldx *__vram_transfer_buffer_pos_old
    140     tya
    141     eor __vram_transfer_buffer+VRAM_HDR_PPUHI,x
    142     and #PPUHI_WIN
    143     beq 4$
    144 3$:
    145     jsr .ppu_write_mapper_indirect
    146 4$:
    147 .endif
    148     ; Lock buffer and store current write pointer for later
    149     VRAM_BUFFER_LOCK
    150     ldy *__vram_transfer_buffer_pos_w
    151     sty *__vram_transfer_buffer_pos_old
    152     ; Write direction
    153     pla
    154     sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
    155     ; Write address
    156     pla
    157     sta __vram_transfer_buffer+VRAM_HDR_PPUHI,y
    158     pla
    159     sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
    160     tya
    161     clc
    162     adc #VRAM_HDR_SIZEOF
    163     sta *__vram_transfer_buffer_pos_w
    164     ; __vram_transfer_buffer_num_cycles_x8 -= 5 (assumes carry clear)
    165     lda *__vram_transfer_buffer_num_cycles_x8
    166     sbc #4
    167     sta *__vram_transfer_buffer_num_cycles_x8
    168     ldy *__vram_transfer_buffer_temp
    169     rts
    170 
    171 ;
    172 ; End a stripe (be it direct or via transfer buffer)
    173 ;
    174 .ppu_stripe_end::
    175     bit *__oam_valid_display_on
    176     bvc 1$
    177     ; For direct writes there's nothing more to do
    178     rts
    179 1$:
    180 .ppu_stripe_end_indirect:
    181     sty *__vram_transfer_buffer_temp
    182     ldy *__vram_transfer_buffer_pos_w
    183     ; Write terminator byte
    184     lda #0
    185     sta __vram_transfer_buffer,y
    186     ; Write number of data bytes
    187     tya
    188     sec
    189     sbc *__vram_transfer_buffer_pos_old
    190     sbc #VRAM_HDR_SIZEOF
    191     pha
    192     LENGTH_TO_JMPADDR
    193     ldy *__vram_transfer_buffer_pos_old
    194     sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
    195     pla
    196     eor #0xFF
    197     sec
    198     adc *__vram_transfer_buffer_num_cycles_x8
    199     sta *__vram_transfer_buffer_num_cycles_x8
    200     VRAM_BUFFER_UNLOCK
    201     ldy *__vram_transfer_buffer_temp
    202     rts
    203 
    204 ;
    205 ; Writes a byte of an in-progress horizontal / vertical stripe (be it direct or via transfer buffer)
    206 ;
    207 .ppu_stripe_write_byte::
    208     bit *__oam_valid_display_on
    209     bvc 1$
    210     sta PPUDATA     ; Direct write
    211     rts
    212 1$:
    213 .ppu_stripe_write_byte_indirect:
    214     sty *__vram_transfer_buffer_temp
    215     ldy *__vram_transfer_buffer_pos_w
    216     sta __vram_transfer_buffer,y
    217     inc *__vram_transfer_buffer_pos_w
    218     ldy *__vram_transfer_buffer_temp
    219     rts
    220 
    221 ;
    222 ; Appends a single byte to the last finished stripe when possible, temporarily re-opening it.
    223 ;
    224 ; The last stripe will only be re-used if it is possible to do so. 
    225 ; The following conditions must be true:
    226 ; - The transfer buffer is not empty (a "last stripe" must exist in buffer)
    227 ; - The last stripe's number of bytes isn't already VRAM_MAX_BYTES number of bytes
    228 ; - The last stripe is either horizontal or single-byte, and the new byte's address is last_address+1
    229 ; - The last stripe is either vertical or single-byte, and the next byte's address is last_address+32
    230 ;
    231 ; If any of these conditions aren't true, a new stripe will be created instead.
    232 ;
    233 ; The current version has a further simplification: The high byte of PPU address must be unchanged.
    234 ; This simplification is there to avoid doing exhaustive and slow 16-bit comparisons.
    235 ; This means a vertical stripe which is automatically appended to by this code can be no longer than 8 bytes.
    236 ;
    237 _set_vram_byte::
    238 .ppu_stripe_append::
    239     ;
    240     bit *__oam_valid_display_on
    241     bvc .ppu_stripe_append_indirect
    242     ; Direct write
    243     stx PPUADDR
    244     sta PPUADDR
    245 .ifdef VRAM_MAPPER_CFG_TRANSFER
    246     jsr __switch_win_addr_temp
    247 .endif
    248     ldy *_set_vram_byte_PARM_2
    249     sty PPUDATA
    250     rts
    251 
    252 .ppu_stripe_wait_for_flush:
    253     ; Unlock and wait for flush by NMI handler
    254     VRAM_BUFFER_UNLOCK
    255 1$:
    256     ldx *__vram_transfer_buffer_pos_w
    257     bmi 1$
    258     ; Re-lock buffer and jump straight to new-stripe code
    259     VRAM_BUFFER_LOCK
    260     jmp .ppu_stripe_append_failed
    261     
    262 .ppu_stripe_append_indirect:
    263     ; Indirect write via transfer buffer
    264     sta *ppu_addr
    265     stx *ppu_addr+1
    266     ; Lock buffer first, to make sure our checks don't get invalidated by NMI
    267     VRAM_BUFFER_LOCK
    268     ; Now that buffer is safely locked, first check if it's almost full
    269     ldx *__vram_transfer_buffer_pos_w
    270     bmi .ppu_stripe_wait_for_flush
    271     ; check that it's not empty
    272     beq .ppu_stripe_append_failed
    273     ldx *__vram_transfer_buffer_pos_old
    274     ; Don't handle appending when high address changed
    275     lda __vram_transfer_buffer+VRAM_HDR_PPUHI,x
    276     cmp *ppu_addr+1
    277     bne .ppu_stripe_append_failed
    278     ; Don't handle appending if old size has reached VRAM_MAX_BYTES
    279     lda __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    280     cmp #(128-4*VRAM_MAX_BYTES + 1)
    281     beq .ppu_stripe_append_failed
    282     ; if last stripe only contains a single byte, branch to go-either-direction routine
    283     cmp #(128-4*1 + 1)
    284     beq .ppu_stripe_append_second_byte
    285     ; Test for horizontal stripe
    286     lda __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
    287     bne 2$
    288     ; Horizontal stripe - check if new address is 1*LENGTH
    289     lda *ppu_addr
    290     sec
    291     sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
    292     LENGTH_TO_JMPADDR
    293     cmp __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    294     beq 0$
    295     jmp .ppu_stripe_append_failed
    296 0$:
    297     ; Append byte
    298     jmp .ppu_stripe_append_1byte
    299 2$:
    300     ; Vertical stripe - check if new address is 32*LENGTH
    301     lda *ppu_addr
    302     lsr
    303     lsr
    304     lsr
    305     lsr
    306     lsr
    307     sec
    308     sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
    309     LENGTH_TO_JMPADDR
    310     cmp __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    311     bne .ppu_stripe_append_failed
    312     ; Append 1 byte
    313     jmp .ppu_stripe_append_1byte
    314 
    315 .ppu_stripe_append_failed:
    316 .ifdef NES_WINDOW_LAYER
    317     jsr .ppu_write_mapper_indirect
    318 .endif
    319     ; Just create a new stripe with a single byte
    320     ldy *__vram_transfer_buffer_pos_w
    321     sty *__vram_transfer_buffer_pos_old
    322     ; Write direction (assume horizontal) and new terminator byte
    323     lda #0
    324     sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
    325     sta __vram_transfer_buffer+VRAM_HDR_SIZEOF+1,y
    326     ; Write jmp addr
    327     lda #(128-4*1 + 1)
    328     sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
    329     ; Write address
    330     lda *ppu_addr+1
    331     sta __vram_transfer_buffer+VRAM_HDR_PPUHI,y
    332     lda *ppu_addr
    333     sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
    334     ; write data byte
    335     lda *_set_vram_byte_PARM_2
    336     sta __vram_transfer_buffer+VRAM_HDR_SIZEOF,y
    337     ; Increase write pointer
    338     tya
    339     clc
    340     adc #VRAM_HDR_SIZEOF+1
    341     ; store new write pointer
    342     sta *__vram_transfer_buffer_pos_w
    343     ; __vram_transfer_buffer_num_cycles_x8 -= 6 (assumes carry clear)
    344     lda *__vram_transfer_buffer_num_cycles_x8
    345     sbc #5
    346     sta *__vram_transfer_buffer_num_cycles_x8
    347     VRAM_BUFFER_UNLOCK
    348     ; Return PPU address
    349     lda *ppu_addr
    350     ldx *ppu_addr+1
    351     rts
    352 
    353 .ppu_stripe_append_second_byte:
    354     ; We only have one previous byte, so ppu_addr being +1 or +32 would both work
    355     lda *ppu_addr
    356     sec
    357     sbc __vram_transfer_buffer+VRAM_HDR_PPULO,x
    358     cmp #1
    359     bne 1$
    360     ; Convert to horizontal stripe
    361     lda #0
    362     sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
    363     jmp .ppu_stripe_append_1byte
    364 1$:
    365     cmp #32
    366     bne 2$
    367     ; Convert to vertical stripe
    368     lda #4
    369     sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,x
    370     jmp .ppu_stripe_append_1byte
    371 2$:
    372     jmp .ppu_stripe_append_failed
    373 
    374 .ppu_stripe_append_1byte:
    375     ; Append 1 byte
    376     dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    377     dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    378     dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    379     dec __vram_transfer_buffer+VRAM_HDR_JMPADDR,x
    380     lda *_set_vram_byte_PARM_2
    381     ldx *__vram_transfer_buffer_pos_w
    382     sta __vram_transfer_buffer,x
    383     inx
    384     ; Write terminator byte
    385     lda #0
    386     sta __vram_transfer_buffer,x
    387     stx *__vram_transfer_buffer_pos_w
    388     ; Decrease x8 cycle delay by 1
    389     dec *__vram_transfer_buffer_num_cycles_x8
    390     ; unlock buffer
    391     VRAM_BUFFER_UNLOCK
    392     ; Return PPU address
    393     lda *ppu_addr
    394     ldx *ppu_addr+1
    395     rts
    396 
    397 .ifdef NES_WINDOW_LAYER
    398 .ppu_write_mapper:
    399     clc
    400     bit *__oam_valid_display_on
    401     bvc .ppu_write_mapper_indirect
    402 .ppu_write_mapper_direct:
    403     ; Direct write
    404     SWITCH_PRG0_A
    405     rts
    406 
    407 .ppu_write_mapper_indirect:
    408     pha
    409     ; Unlock buffer (if this routine was called while unlocked)
    410     VRAM_BUFFER_UNLOCK
    411     ; Ensure there's at least VRAM_MAX_STRIPE_SIZE bytes remaining to write before progressing
    412     ; This conservative limit simplifies conditions for rest of stripe in order to write single bytes with no checks
    413 2$:
    414     lda *__vram_transfer_buffer_pos_w
    415     cmp #128-VRAM_MAX_STRIPE_SIZE
    416     bcs 2$
    417     ; Lock buffer and store current write pointer for later
    418     VRAM_BUFFER_LOCK
    419     ldy *__vram_transfer_buffer_pos_w
    420     sty *__vram_transfer_buffer_pos_old
    421 
    422     ; Write direction (assume horizontal) and new terminator byte
    423     lda #0
    424     sta __vram_transfer_buffer+VRAM_HDR_DIRECTION,y
    425     sta __vram_transfer_buffer+VRAM_HDR_SIZEOF+1,y
    426     ; Write jmp addr
    427     lda #<ProcessDrawList_mapper_switch
    428     sta __vram_transfer_buffer+VRAM_HDR_JMPADDR,y
    429     ; Write mapper value to PPUADDR_LO
    430     lda *ppu_addr+1
    431     ora *__current_vram_cfg_write
    432     and #PPUHI_WIN
    433     sta __vram_transfer_buffer+VRAM_HDR_PPULO,y
    434 
    435     ; Increase write pointer
    436     tya
    437     clc
    438     adc #VRAM_HDR_SIZEOF
    439     ; store new write pointer
    440     sta *__vram_transfer_buffer_pos_w
    441 
    442     ; __vram_transfer_buffer_num_cycles_x8 -= 5 (assumes carry clear)
    443     lda *__vram_transfer_buffer_num_cycles_x8
    444     sbc #4
    445     sta *__vram_transfer_buffer_num_cycles_x8
    446     ldy *__vram_transfer_buffer_temp
    447 
    448     dec *__vram_transfer_buffer_num_cycles_x8
    449 
    450     VRAM_BUFFER_UNLOCK
    451     pla
    452     rts
    453 .endif
    454 
    455 .ifdef NES_WINDOW_LAYER
    456 __switch_bkg::
    457     lda *__current_vram_cfg_write
    458     and #~PPUHI_WIN
    459     sta *__current_vram_cfg_write
    460     rts
    461 
    462 __switch_win::
    463     lda *__current_vram_cfg_write
    464     ora #PPUHI_WIN
    465     sta *__current_vram_cfg_write
    466     rts
    467 .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.