298. Manual main error reporting

Add useful emulation error reporting to main_only_background.py.

Lesson 298 of 356 · tests/chapter_08_manual_main/test_298_manual_main_error_reporting.py

File to update

main_only_background.py

Why this step exists

main_only_background.py runs a real manual ROM loop with pygame. When real ROM execution hits a missing emulator behavior, the user needs context before the Python traceback.

Without context, an error may only say

ValueError: Unsupported CPU bus read: 4020

With context, main_only_background.py should also print useful emulator state:

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

This does not replace the traceback. The original exception should still be re-raised so developers can debug normally.

Suggested implementation example

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()

Why catch KeyboardInterrupt separately? Ctrl+C is an intentional user stop, not an emulator failure. It should print a friendly stop message and should not print an emulation error report.

Why re-raise unexpected exceptions? The error report gives emulator context, but the traceback still matters. Re-raise keeps the original failure visible for debugging.

Out of scope

  • changing CPU opcode diagnostics
  • catching and hiding all errors
  • writing logs to files
  • calling main() from pytest

Run this lesson

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