344. Decrement horizontal vram addr
Reverse one horizontal background-fetch increment on a copied address.
Lesson 344 of 356 · tests/chapter_13_scrolling/test_344_decrement_horizontal_vram_addr.py
File to update
emulator/ppu/ppu.pyReferences
https://www.nesdev.org/wiki/PPU_scrolling#DetailsWhy this step exists
Dots 321-336 prefetch the first two background tiles for the next scanline. Each tile fetch advances horizontal v, so at dot 1 the address is already two tile columns ahead of the pixels stored in the background shifters:
intended visible coarse X = 5
prefetch tile 5 -> v advances to 6
prefetch tile 6 -> v advances to 7
dot 1:
shifters begin displaying tile 5
v already contains coarse X 7Later scanline recording will copy v and rewind that copy twice
7 -> 6 -> 5This helper reverses one increment. It is not a timed PPU operation, and it must never move the real PPU.vram_addr backward.
Normal behavior
coarse X 31 -> 30
coarse X 7 -> 6
coarse X 1 -> 0Boundary behavior
coarse X 0 -> 31 and toggle horizontal nametableImportant invariants
- only coarse X and, at wrapping, horizontal nametable may change
- every vertical and unrelated field remains unchanged
- decrement is the inverse of the tested horizontal increment
- the function is pure and does not mutate PPU
Common misconception
The NES PPU does not perform this decrement during rendering. It exists only to translate the ahead-of-display fetch address into a visible viewport address for the existing high-level renderer.
Out of scope
- calling the helper from PPU.step()
- recording 240 scanline positions
- framebuffer or opacity-mask changes
Complete example implementation
# emulator/ppu/ppu.py
# --- NEW BLOCK: REVERSE ONE HORIZONTAL FETCH INCREMENT ---
def decrement_horizontal_vram_addr(vram_addr: int) -> int:
coarse_x = vram_addr & 0b1_1111
if coarse_x == 0:
vram_addr = (
(vram_addr & ~0b1_1111)
| 0b1_1111
)
vram_addr ^= 0b000_01_00000_00000
return vram_addr
return vram_addr - 1Run this lesson
uv run pytest tests/chapter_13_scrolling/test_344_decrement_horizontal_vram_addr.py -v