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.py

References

https://www.nesdev.org/wiki/PPU_scrolling#Details

Why 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 7

Later scanline recording will copy v and rewind that copy twice

7 -> 6 -> 5

This 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  -> 0

Boundary behavior

coarse X 0 -> 31 and toggle horizontal nametable

Important 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 - 1

Run this lesson

uv run pytest tests/chapter_13_scrolling/test_344_decrement_horizontal_vram_addr.py -v