339. Copy horizontal scroll bits
Copy horizontal scrolling fields from temporary address t into current address v.
Lesson 339 of 356 · tests/chapter_13_scrolling/test_339_copy_horizontal_scroll_bits.py
File to update
emulator/ppu/ppu.pyReferences
https://www.nesdev.org/wiki/PPU_scrolling#During_rendering
https://www.nesdev.org/wiki/PPU_scrolling#At_dot_257_of_each_scanlineWhy this step exists
CPU writes prepare scrolling fields in temporary address t, but background rendering uses current address v. The PPU transfers only the horizontal fields at the horizontal reload point:
yyy NN YYYYY XXXXX
|| +++++-- coarse X: bits 0-4
|+-------------- horizontal nametable: bit 10
+--------------- vertical nametable: bit 11, not copied hereHorizontal mask
0b000_01_00000_11111 = 0x041FRequired result
coarse X <- t
horizontal nametable <- t
coarse Y <- original v
fine Y <- original v
vertical nametable <- original v
every other unrelated bit <- original vMinimal example
v: coarse X 3, horizontal nametable 0, vertical state A
t: coarse X 20, horizontal nametable 1, vertical state B
result: coarse X 20, horizontal nametable 1, vertical state ACommon misconception
The horizontal reload is not v = t. Copying all of t would replace vertical state at the wrong time. A later step will apply this already-tested operation at dot 257.
Out of scope
- modifying PPU.step()
- dot-257 timing
- horizontal address increments
- vertical t-to-v copies
- framebuffer rendering
Complete example implementation
# emulator/ppu/ppu.py
# --- NEW LINE: COARSE X AND HORIZONTAL NAMETABLE FIELDS ---
HORIZONTAL_SCROLL_BITS = 0b000_01_00000_11111
# --- NEW BLOCK: PURE HORIZONTAL t-TO-v COPY ---
def copy_horizontal_scroll_bits(
vram_addr: int,
temp_vram_addr: int,
) -> int:
return (
(vram_addr & ~HORIZONTAL_SCROLL_BITS)
| (temp_vram_addr & HORIZONTAL_SCROLL_BITS)
)Run this lesson
uv run pytest tests/chapter_13_scrolling/test_339_copy_horizontal_scroll_bits.py -v