294. Bucle manual del validador del núcleo con ROM

Añadir un bucle de arranque de ROM manual en core_validator.py.

Lección 294 de 356 · tests/chapter_08_manual_main/test_294_manual_core_validator_rom_loop.py

Archivo a crear en la carpeta raíz

core_validator.py

Por qué existe este paso

El emulador ya tiene suficiente comportamiento de supervivencia al arranque para que resulte útil un punto de entrada manual para el desarrollador:

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

Este paso convierte core_validator.py en un lugar manual para probar una ROM local y mantener el emulador ejecutándose fotograma a fotograma sin pygame.

¿Por qué core_validator.py en lugar de main.py? main.py está reservado para el ejecutor visual de pygame. core_validator.py sigue siendo un pequeño ejecutor no visual, útil al depurar el núcleo del emulador sin una ventana ni un bucle de eventos de frontend.

Regla legal/de pruebas importante

El repositorio del tutorial no debe incluir archivos de ROM comerciales. Las pruebas automatizadas no deben requerir MarioBros.nes. Un desarrollador que quiera ejecutar Mario Bros. manualmente debe aportar su propia copia local legal:

MarioBros.nes

Hash de referencia usado durante el desarrollo del tutorial [Mario Bros. (World).nes]:

MD5 5d7bcc400a2fb5fa27346da345d3bb62  MarioBros.nes
SHA1 314b6e46e814f955b52ac954f67dab849582fe77

Este hash es solo una referencia manual. Las pruebas no deben requerir este archivo ni este hash exacto, porque los usuarios pueden tener volcados/revisiones legales distintos.

Ejemplo de implementación sugerido

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

Detenido por el usuario.")

if __name__ == "__main__":
    main()

Comando manual

uv run python core_validator.py

Comportamiento manual esperado

Este comando no termina por sí solo. Sigue avanzando fotogramas hasta que el desarrollador pulsa Ctrl+C. Eso es lo esperado, porque core_validator.py es una herramienta manual de ejecución de ROM de larga duración, no una prueba automatizada.

Por qué esta prueba no llama a main()

core_validator.py es un bucle manual infinito. Las pruebas automatizadas deben permanecer finitas y no deben requerir un archivo de ROM comercial local. Estas pruebas solo inspeccionan la estructura.

Por qué solo se comprueba la existencia de debug_mode: debug_mode es un control manual. Futuros pasos del tutorial podrán ponerla en True o False según lo que se esté enseñando. Las pruebas no deben fijar ese valor.

Fuera de alcance

  • Pantalla de pygame
  • Mapeo de teclado
  • Fixtures de ROM incluidas en el repositorio
  • Verificar que core_validator.py produce una jugabilidad correcta
  • Llamar a main() desde pytest

Ejecutar esta lección

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