342. 复制垂直滚动位

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

342 / 356 · tests/chapter_13_scrolling/test_342_copy_vertical_scroll_bits.py

要更新的文件

emulator/ppu/ppu.py

参考资料

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

为什么需要这一步

CPU 写入会在临时地址 t 中准备垂直滚动字段,而渲染使用的是当前地址 v。在预渲染扫描线期间,PPU 会有选择地刷新垂直字段:

yyy NN YYYYY XXXXX
||| |  |||||
||| |  +++++-- coarse Y: bits 5-9
||| +--------- vertical nametable: bit 11
+++----------- fine Y: bits 12-14

垂直掩码

0b111_10_11111_00000 = 0x7BE0

所需结果

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

最小示例

v: horizontal state A, vertical state B
t: horizontal state C, vertical state D

result: horizontal state A, vertical state D

常见误解

垂直重载并不是 v = t。复制 t 的全部内容,会覆盖已经由水平重载独立准备好的水平状态。

超出范围

  • 修改 PPU.step()
  • 点 256 的垂直递增
  • 预渲染的点 280-304
  • 扫描线状态记录
  • 帧缓冲渲染

完整示例实现

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

运行本课

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