VBlank routine
Shadow variables
A register shadow variable is a RAM variable that will hold (shadow) the intended value of a PPU register. This variable can be read or written to at any time. When it is safe to write to the register, usually during the Vertical Blanking period, the shadow variable will be transferred to its associated register.
Shadow variables offer many advantages over directly writing to the register:
- Shadow variables can be read or written at any time.
- Shadow variables allow the code to read the shadowed-state of write-only registers.
- Write-twice registers can store their state in a 16 bit (word) variable. This allows the main-loop to access the shadow with a 16 bit Accumulator or Index.
- Reading a shadow variable is significantly faster then calculating the intended value of a register. The slow calculation can be preformed in the main-loop and the write to the PPU register can be preformed during VBlank.
Shadow variables should be allocated in Low-RAM. This allows the VBlank routine to access both the shadow-variable and the register with the addr addressing mode.
Write-only byte register example
The following is an example of a shadow variable for the MOSAIC register. Notice how shadow variable allows the main-loop to read the shadowed register state and how tiny the VBlank code is.
Main-loop code:
; mosaicShadow u8 - shadow of the MOSAIC register ; ; zpTmpByte u8 - zeropage temporary byte variable ; Increase mosaic size by 1 (with clamping), leave mosaic enable bits unchanged ; ; DB access Low RAM ($00-$3f, $7e, $80-$bf) .a8 .i16 ; extract mosaic enable bits lda mosaicShadow and #0xf0 sta zpTmpByte ; increase mosaic size bits (with clamping) lda mosaicShadow inc and #0x0f bne :+ ; mosaic size overflowed lda #0xf : ; combine enable and size bits ora zpTmpByte sta mosaicShadow
VBlank code:
; mosaicShadow u8 - shadow of the MOSAIC register ; In VBlank ; DB access registers ($00-$3f, $80-$bf) .a8 .i16 lda mosaicShadow sta MOSAIC
Write-twice register example
The following is an example of a shadow variable for the BG1HOFS and BG1VOFS scroll registers. Notice how the main-loop code writes to the shadow variables in 16 bit mode, while the VBlank code is required to read the shadow variables one byte at a time.
Main-loop code:
; bg1HOffset u16 - shadow of the BG1HOFS register ; bg1VOffset u16 - shadow of the BG1VOFS register ; DB access Low RAM ($00-$3f, $7e, $80-$bf) .a8 .i16 ; Set BG1 scroll offset with a 16 bit Index register ldx #0 stx bg1HOffset ldx #.loword(-1) stx bg1VOffset
VBlank code:
; bg1HOffset u16 - shadow of the BG1HOFS register ; bg1VOffset u16 - shadow of the BG1VOFS register ; In VBlank ; DB access registers ($00-$3f, $80-$bf) .a8 .i16 ; Transfer a 16 bit variable to a write twice register lda .lobyte(bg1HOffset) sta BG1HOFS lda .hibyte(bg1HOffset) sta BG1HOFS lda .lobyte(bg1VOffset) sta BG1VOFS lda .hibyte(bg1VOffset) sta BG1VOFS