298. 手动 main 错误报告

为 main_only_background.py 添加有用的模拟错误报告。

298 / 356 · tests/chapter_08_manual_main/test_298_manual_main_error_reporting.py

需要更新的文件

main_only_background.py

为什么需要这一步

main_only_background.py 使用 pygame 运行真实的手动 ROM 循环。当真实 ROM 执行遇到模拟器尚未实现的行为时,用户需要在 Python 回溯信息之前看到上下文。

如果没有上下文,错误可能只显示

ValueError: Unsupported CPU bus read: 4020

有了上下文,main_only_background.py 还应打印有用的模拟器状态:

Emulation Error:
    type=ValueError
    message=Unsupported CPU bus read: 4020
    pc=$812A
    ppu_frame=123
    ppu_scanline=241
    ppu_cycle=10

这不会替代回溯信息。原始异常仍应被重新抛出,以便开发者正常调试。

建议的实现示例

def print_emulation_error(error: Exception, console: Console) -> None:
    print("

Emulation Error:")

    print(f"    type={type(error).__name__}")
    print(f"    message={error}")
    print(f"    pc=${console.cpu.pc:04X}")
    print(f"    ppu_frame={console.ppu.frame}")
    print(f"    ppu_scanline={console.ppu.scanline}")
    print(f"    ppu_cycle={console.ppu.cycle}")


def main() -> None:
    ...

    pygame.init()
    try:
        window = pygame.display.set_mode(...)

        running = True
        while running:
            ...

            executed = console.step_until_next_frame()
            framebuffer = console.render_background_framebuffer()
            draw_framebuffer(window, framebuffer, SCALE)
            pygame.display.flip()

    except KeyboardInterrupt:
        print("

Stopped by user.")

    except Exception as error:
        print_emulation_error(error, console)
        raise
    finally:
        pygame.quit()

为什么要单独捕获 KeyboardInterrupt?Ctrl+C 是用户有意的停止操作,而非模拟器故障。它应打印友好的停止消息,且不应打印模拟错误报告。

为什么要重新抛出非预期异常?错误报告提供模拟器上下文,但回溯信息仍然重要。重新抛出可保持原始故障对调试可见。

超出本步骤范围

  • 更改 CPU 操作码诊断
  • 捕获并隐藏所有错误
  • 将日志写入文件
  • 从 pytest 调用 main()

运行本课

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