341. Increment vertical vram addr
Increment the vertical component of current rendering address v.
Lesson 341 of 356 · tests/chapter_13_scrolling/test_341_increment_vertical_vram_addr.py
File to update
emulator/ppu/ppu.pyReferences
https://www.nesdev.org/wiki/PPU_scrolling#Y_increment
https://www.nesdev.org/wiki/PPU_scrolling#Wrapping_aroundWhy this step exists
At dot 256, the PPU advances the rendering address by one pixel row. Unlike the horizontal increment, vertical movement first advances fine Y inside the current 8-pixel tile:
yyy NN YYYYY XXXXX
+++ +++++
| +---- coarse Y: bits 5-9
+------------ fine Y: bits 12-14
vertical nametable: bit 11Fine-Y behavior
0 -> 1 -> 2 -> ... -> 7When fine Y is already 7, it wraps to 0 and coarse Y advances.
Coarse-Y behavior after fine-Y wrapping
coarse Y 0-28 -> increment by one
coarse Y 29 -> 0 and toggle vertical nametable
coarse Y 30 -> 31 through the normal increment branch
coarse Y 31 -> 0 without toggling vertical nametableRows 0-29 are the 30 visible tile rows of a nametable. Values 30 and 31 are not normal visible rows, but they can exist because coarse Y is a five-bit field and CPU address operations can load those values.
Why add $1000 for a normal fine-Y increment? Fine Y begins at bit 12, so adding 1 << 12 increments that packed field by one. This does not conceptually mean moving 4096 bytes through PPU memory.
Important invariants
- horizontal coarse X and nametable selection remain unchanged
- unrelated internal address bits remain unchanged
- vertical nametable toggles only for row-29 wrapping
- the helper is pure and does not mutate PPU
Common misconception
Vertical increment is not simply symmetrical with horizontal increment. Horizontal movement advances one tile column; vertical movement advances one fine pixel row and only sometimes advances the coarse tile row.
Out of scope
- calling this helper at dot 256
- vertical t-to-v copying
- pre-render timing
- scanline viewport recording
- framebuffer rendering
Complete example implementation
# emulator/ppu/ppu.py
# --- NEW BLOCK: PURE VERTICAL v INCREMENT ---
def increment_vertical_vram_addr(vram_addr: int) -> int:
fine_y = (vram_addr >> 12) & 0b111
if fine_y < 7:
return vram_addr + (1 << 12)
vram_addr &= ~0b111_00_00000_00000
coarse_y = (vram_addr >> 5) & 0b1_1111
if coarse_y == 29:
coarse_y = 0
vram_addr ^= 0b000_10_00000_00000
elif coarse_y == 31:
coarse_y = 0
else:
coarse_y += 1
return (
(vram_addr & ~0b000_00_11111_00000)
| (coarse_y << 5)
)Run this lesson
uv run pytest tests/chapter_13_scrolling/test_341_increment_vertical_vram_addr.py -v