259. PPU 时序计数器

实现基本的 PPU 时序计数器。

259 / 356 · tests/chapter_04_ppu_timing_and_vblank/test_259_ppu_timing_counters.py

参考资料

https://www.nesdev.org/wiki/PPU_rendering#Line-by-line_timing

需要更新的文件

emulator/ppu/ppu.py

需要添加的常量

PPU_CYCLES_PER_SCANLINE = 341
PPU_SCANLINES_PER_FRAME = 262

需要添加的状态

cycle: int = 0
scanline: int = 0
frame: int = 0

需要添加的方法

PPU.step(cycles: int = 1) -> None

为什么需要这一步

PPU 是一个基于时间的设备。之后,VBlank、NMI、渲染以及帧节奏都要依赖于知道 PPU 当前处于这一帧内的什么位置。

在这一步,只需要添加计数器

cycle    -> position inside the current scanline
scanline -> current scanline inside the frame
frame    -> completed frame count

初始的时序模型

341 PPU cycles per scanline
262 scanlines per frame

建议的实现示例

def step(self, cycles: int = 1) -> None:
    for _ in range(cycles):
        self.cycle += 1

        if self.cycle >= PPU_CYCLES_PER_SCANLINE:
            self.cycle = 0
            self.scanline += 1

            if self.scanline >= PPU_SCANLINES_PER_FRAME:
                self.scanline = 0
                self.frame += 1

未来的兼容性

这些测试有意只检查计数器的行为,暂时不要求精确的 VBlank/NMI 副作用。之后的步骤可能会在 PPU.step() 内部添加这些副作用,但这些计数器的不变式应该始终成立。

不在本步骤范围内

  • VBlank 的产生
  • NMI 请求
  • 渲染
  • 0 号精灵命中(sprite 0 hit)
  • 精灵溢出
  • 奇数帧的周期跳过

运行本课

uv run pytest tests/chapter_04_ppu_timing_and_vblank/test_259_ppu_timing_counters.py -v