339. 复制水平滚动位

把水平滚动字段从临时地址 t 复制到当前地址 v。

339 / 356 · tests/chapter_13_scrolling/test_339_copy_horizontal_scroll_bits.py

待更新文件

emulator/ppu/ppu.py

参考

https://www.nesdev.org/wiki/PPU_scrolling#During_rendering
https://www.nesdev.org/wiki/PPU_scrolling#At_dot_257_of_each_scanline

此步骤存在的原因

CPU 写入会把滚动字段准备到临时地址 t 中,但背景渲染使用的是当前地址 v。PPU 在水平重载点只传输水平字段:

yyy NN YYYYY XXXXX
    ||       +++++-- coarse X: bits 0-4
    |+-------------- horizontal nametable: bit 10
    +--------------- vertical nametable: bit 11, not copied here

水平掩码

0b000_01_00000_11111 = 0x041F

所需结果

coarse X                    <- t
horizontal nametable       <- t
coarse Y                    <- original v
fine Y                      <- original v
vertical nametable         <- original v
every other unrelated bit  <- original v

最小示例

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 A

常见误解

水平重载并不是 v = t。复制整个 t 会在错误的时机替换垂直状态。后续步骤会在第 257 点应用这个已经过测试的操作。

范围之外

  • 修改 PPU.step()
  • 第 257 点的时序
  • 水平地址递增
  • 垂直 t 到 v 复制
  • 帧缓冲区渲染

完整示例实现

# 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)
    )

运行本课

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