294. 手动 core_validator ROM 循环

添加手动 core_validator.py ROM 启动循环。

294 / 356 · tests/chapter_08_manual_main/test_294_manual_core_validator_rom_loop.py

在根目录创建的文件

core_validator.py

为什么需要这一步

模拟器现在已具备足够的启动存活行为,可以创建一个对开发者有用的手动入口点:

iNES parsing
Mapper000/NROM
CPU reset vector
Console frame stepping
APU/audio no-op for out-of-scope audio
OAMDMA $4014
controller port 1 through $4016

本步骤将 core_validator.py 打造为一个手动入口点,用于尝试加载本地 ROM 并在不使用 pygame 的情况下逐帧运行模拟器。

为什么用 core_validator.py 而不是 main.py?main.py 保留给可视化 pygame 运行器使用。core_validator.py 是一个小型非可视化运行器,在调试模拟器核心时无需窗口或前端事件循环即可使用。

重要的法律/测试规则

教程仓库不得包含商业 ROM 文件。自动化测试不得要求 MarioBros.nes。开发者如需手动运行 Mario Bros.,必须自行提供合法的本地副本:

MarioBros.nes

教程开发期间使用的参考哈希 [Mario Bros. (World).nes]:

MD5 5d7bcc400a2fb5fa27346da345d3bb62  MarioBros.nes
SHA1 314b6e46e814f955b52ac954f67dab849582fe77

此哈希仅供手动参考。测试不得要求此文件或此确切哈希,因为用户可能拥有不同的合法转储/版本。

建议的实现示例

from pathlib import Path

from emulator.bus.cpu_bus import CpuBus
from emulator.cartridge.cartridge import Cartridge
from emulator.console import Console
from emulator.cpu.cpu import CPU


ROM_PATH = Path("MarioBros.nes")
debug_mode = True


def main() -> None:
    if not ROM_PATH.exists():
        raise FileNotFoundError(
            "MarioBros.nes not found. Provide your own legal local copy. "
            "This file is intentionally not included in the tutorial repository."
        )

    cartridge = Cartridge.from_ines_bytes(ROM_PATH.read_bytes())

    cpu_bus = CpuBus(cartridge=cartridge)
    cpu = CPU(cpu_bus)
    console = Console(cpu=cpu, ppu=cpu_bus.ppu)

    cpu.reset()

    print(f"Loaded {ROM_PATH}")
    print(f"CPU reset PC = ${cpu.pc:04X}")
    print("Starting frame loop. Press Ctrl+C to stop.")

    try:
        while True:
            executed = console.step_until_next_frame()

            if debug_mode:
                print(
                    f"frame={console.ppu.frame} "
                    f"pc=${cpu.pc:04X} "
                    f"instructions={executed}"
                )
    except KeyboardInterrupt:
        print("

Stopped by user.")

if __name__ == "__main__":
    main()

手动命令

uv run python core_validator.py

预期的手动行为

此命令不会自行退出。它会持续逐帧执行直到开发者按下 Ctrl+C。这是预期行为,因为 core_validator.py 是手动长时间运行的 ROM 执行工具,而非自动化测试。

为什么本测试不调用 main()

core_validator.py 是一个手动无限循环。自动化测试必须保持有限执行,且不得要求本地商业 ROM 文件。这些测试仅检查结构。

为什么只检查 debug_mode 是否存在:debug_mode 是手动控制旋钮。后续教程步骤可能根据教学内容将其设为 True 或 False。测试不应冻结该值。

超出本步骤范围

  • pygame 显示
  • 键盘映射
  • 提交的 ROM 测试固件
  • 断言 core_validator.py 产生正确的游戏运行结果
  • 从 pytest 调用 main()

运行本课

uv run pytest tests/chapter_08_manual_main/test_294_manual_core_validator_rom_loop.py -v