338. 递增水平 VRAM 地址

递增当前渲染地址 v 的水平分量。

338 / 356 · tests/chapter_13_scrolling/test_338_increment_horizontal_vram_addr.py

待更新文件

emulator/ppu/ppu.py

参考

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

此步骤存在的原因

在背景抓取过程中,PPU 每次前进一个 8 像素的图块列。当前渲染地址 v 同时存储了粗 X 图块列和水平名称表选择:

v: yyy NN YYYYY XXXXX
                   |
                   +-- coarse X, bits 0-4

horizontal nametable selection: bit 10

正常行为

coarse X 0  -> 1
coarse X 30 -> 31

边界行为

coarse X 31, horizontal nametable 0
    -> coarse X 0, horizontal nametable 1

coarse X 31, horizontal nametable 1
    -> coarse X 0, horizontal nametable 0

该边界切换会使渲染跨越逻辑上的左右名称表接缝。当这些逻辑地址被读取时,PpuBus 随后会应用卡带镜像。

重要不变量

  • 只有粗 X,以及在环绕时的水平名称表,才可能改变
  • 粗 Y、细 Y 和垂直名称表保持不变
  • 细 X 是独立的,不是该操作的参数
  • 该辅助函数返回一个值,不会修改 PPU

常见误解

这并不是移动一个像素。它前进的是一个图块列,代表 8 个像素。细 X 提供了第一个图块内部单独的 0-7 像素偏移。

范围之外

  • 从 PPU.step() 调用该辅助函数
  • 选择背景抓取点
  • 把水平 t 位复制到 v
  • 垂直递增
  • 帧缓冲区渲染

完整示例实现

# emulator/ppu/ppu.py

# --- NEW BLOCK: PURE HORIZONTAL v INCREMENT ---
def increment_horizontal_vram_addr(vram_addr: int) -> int:
    # Advance v by one background tile column.
    coarse_x = vram_addr & 0b1_1111

    if coarse_x == 31:
        vram_addr &= ~0b1_1111
        vram_addr ^= 0b100_0000_0000
        return vram_addr

    return vram_addr + 1

运行本课

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