342. Copy vertical scroll bits

Copy vertical scrolling fields from temporary address t into current address v.

Lesson 342 of 356 · tests/chapter_13_scrolling/test_342_copy_vertical_scroll_bits.py

File to update

emulator/ppu/ppu.py

References

https://www.nesdev.org/wiki/PPU_scrolling#During_dots_280_to_304_of_the_pre-render_scanline_(end_of_vblank)

Why this step exists

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 ---
def copy_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