CPU writes prepare vertical scrolling fields in temporary address t, while rendering uses current address v. During the pre-render scanline, the PPU selectively refreshes the vertical fields:
yyy NN YYYYY XXXXX
||| | |||||
||| | +++++-- coarse Y: bits 5-9
||| +--------- vertical nametable: bit 11
+++----------- fine Y: bits 12-14
Vertical mask
0b111_10_11111_00000 = 0x7BE0
Required result
fine Y <- t
vertical nametable <- t
coarse Y <- t
coarse X <- original v
horizontal nametable <- original v
every other unrelated bit <- original v
Minimal example
v: horizontal state A, vertical state B
t: horizontal state C, vertical state D
result: horizontal state A, vertical state D
Common misconception
The vertical reload is not v = t. Copying all of t would overwrite horizontal state after it was independently prepared by the horizontal reload.
Out of scope
modifying PPU.step()
dot-256 vertical increment
pre-render dots 280-304
scanline state recording
framebuffer rendering
Complete example implementation
# emulator/ppu/ppu.py# --- NEW LINE: FINE Y, VERTICAL NAMETABLE, AND COARSE Y ---
VERTICAL_SCROLL_BITS = 0b111_10_11111_00000# --- NEW BLOCK: PURE VERTICAL t-TO-v COPY ---defcopy_vertical_scroll_bits(
vram_addr: int,
temp_vram_addr: int,
) -> int:
return (
(vram_addr & ~VERTICAL_SCROLL_BITS)
| (temp_vram_addr & VERTICAL_SCROLL_BITS)
)
Run this lesson
uv run pytest tests/chapter_13_scrolling/test_342_copy_vertical_scroll_bits.py -v