261. VBlank 时 PPU 发起 NMI 请求

实现 VBlank 时 PPU 端的 NMI 请求。

261 / 356 · tests/chapter_04_ppu_timing_and_vblank/test_261_ppu_nmi_request_on_vblank.py

参考资料

https://www.nesdev.org/wiki/PPU_registers#PPUCTRL
https://www.nesdev.org/wiki/PPU_registers#Vblank_NMI

需要更新的文件

emulator/ppu/ppu.py

需要添加的状态

nmi_requested: bool = False

此步骤存在的原因

许多 NES 游戏并不会在主循环中轮询 PPUSTATUS。相反,它们通过 PPUCTRL 的第 7 位启用 NMI,并期望 PPU 在 VBlank 开始时请求一次中断。

在这一步中,只实现 PPU 端的信号

if VBlank starts and PPUCTRL bit 7 is set:
    ppu.nmi_requested = True

CPU 目前还不会消费这个 NMI。那是之后的集成步骤要做的事情。

建议的实现示例

nmi_requested: bool = False
...
...

if self.scanline == PPU_VBLANK_START_SCANLINE:
    self.status |= VBLANK_STARTED

    if self.ctrl & CTRL_NMI_ENABLE:
        self.nmi_requested = True

重要概念

NMI 请求是在 VBlank 开始时产生的事件,不应该在 VBlank 期间的每个周期都持续产生。

不在本步骤范围内

  • CPU 的中断处理
  • 读取 NMI 向量
  • 把 PC/状态寄存器压入 CPU 栈
  • 由 CPU/系统集成来清除 nmi_requested
  • 在 VBlank 已经处于激活状态时再次启用 NMI
  • 逐点/逐周期精确的 NMI 时序

运行本课

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