Step 0402 - Integración Frontend Boot ROM + Modo Stub Sin Archivo
1. Concepto de Hardware
Boot ROM y el Pipeline de Inicialización
La Boot ROM de la Game Boy es una ROM interna pequeña (256 bytes para DMG, 2304 bytes para CGB) que se ejecuta antes del código del cartucho. Su propósito es:
- Mostrar el logo de Nintendo: Animación scroll del logo (requisito de licencia)
- Validar el cartucho: Verificar checksum del logo en el header de la ROM
- Inicializar hardware: Configurar registros I/O (LCDC, BGP, paletas CGB, etc.)
- Transferir control: Escribir 0xFF50=1 para deshabilitar Boot ROM y saltar a 0x0100
Después de la Boot ROM, el estado del hardware es consistente y predecible. Los juegos dependen de este estado para su inicialización. Sin Boot ROM, los registros I/O quedan en valores indefinidos o incorrectos, causando problemas como BGP=0x00 (paleta inválida, pantalla blanca).
Soporte Opcional de Boot ROM en el Emulador
Para cumplir con Clean Room compliance, la Boot ROM NO se incluye en el repositorio. Sin embargo, el emulador ahora soporta:
- Boot ROM real: El usuario puede proveer su propia Boot ROM (extraída legalmente de hardware real)
- Modo stub: Configuración mínima post-boot sin ejecutar binario propietario (para validación)
- Modo skip-boot: Estado post-boot estándar (PC=0x0100, registros predefinidos)
Fuentes: Pan Docs - Power Up Sequence, Pan Docs - FF50 Register
2. Implementación
A. Frontend: CLI y Variables de Entorno
Modificaciones en main.py
Se añadieron dos flags CLI:
parser.add_argument(
"--bootrom",
type=str,
default=None,
help="Ruta al archivo Boot ROM (DMG: 256 bytes, CGB: 2304 bytes)",
)
parser.add_argument(
"--bootrom-stub",
action="store_true",
help="Activar modo stub de Boot ROM (sin binario, solo estado post-boot)",
)
Lógica de prioridad:
- Si
--bootrom PATHse especifica, cargar Boot ROM desde archivo - Si no, consultar variable de entorno
VIBOY_BOOTROM - Si
--bootrom-stubse especifica, activar modo stub - Si nada se especifica, usar modo skip-boot (comportamiento por defecto)
Modificaciones en src/viboy.py
El constructor de Viboy ahora acepta parámetros opcionales:
def __init__(
self,
rom_path: str | Path | None = None,
use_cpp_core: bool = True,
bootrom_bytes: bytes | None = None,
bootrom_stub: bool = False
) -> None:
# ...
self._bootrom_bytes = bootrom_bytes
self._bootrom_stub = bootrom_stub
En load_cartridge(), se aplica la Boot ROM o el stub:
if self._bootrom_bytes:
# Cargar Boot ROM real (provista por el usuario)
self._mmu.set_boot_rom(self._bootrom_bytes)
if self._mmu.is_boot_rom_enabled():
self._regs.pc = 0x0000 # Boot ROM activa: PC inicia en 0x0000
else:
self._initialize_post_boot_state()
elif self._bootrom_stub:
# Modo stub: activar stub sin binario propietario
self._mmu.enable_bootrom_stub(True, cgb_mode=False)
self._initialize_post_boot_state()
else:
# Modo skip-boot (por defecto)
self._initialize_post_boot_state()
B. Backend: Modo Stub en C++
Implementación en MMU.cpp
El método enable_bootrom_stub() aplica un estado post-boot mínimo:
void MMU::enable_bootrom_stub(bool enable, bool cgb_mode) {
if (!enable) {
boot_rom_enabled_ = false;
boot_rom_.clear();
return;
}
// Configurar registros I/O al estado post-boot (según Pan Docs)
memory_[0xFF40] = 0x91; // LCDC: LCD ON, BG ON, Tilemap 0x9800
memory_[0xFF47] = 0xFC; // BGP: Paleta estándar (00=blanco, 11=negro)
memory_[0xFF42] = 0x00; // SCY: Scroll Y = 0
memory_[0xFF43] = 0x00; // SCX: Scroll X = 0
memory_[0xFF48] = 0xFF; // OBP0: Paleta sprite 0
memory_[0xFF49] = 0xFF; // OBP1: Paleta sprite 1
memory_[0xFFFF] = 0x01; // IE: VBlank interrupt habilitado
// Escribir 0xFF50 = 1 para simular que la Boot ROM terminó
memory_[0xFF50] = 0x01;
boot_rom_enabled_ = false;
boot_rom_.clear();
}
Wrapper Cython (mmu.pyx)
def enable_bootrom_stub(self, bool enable, bool cgb_mode=False):
"""
Habilita el modo stub de Boot ROM (sin binario propietario).
El stub NO emula instrucciones reales del boot. Solo fuerza un conjunto mínimo
de estado post-boot documentado (Pan Docs) y marca boot_rom_enabled_=false
inmediatamente.
"""
if self._mmu == NULL:
raise MemoryError("La instancia de MMU en C++ no existe.")
self._mmu.enable_bootrom_stub(enable, cgb_mode)
3. Tests y Verificación
Comando de Compilación
python3 setup.py build_ext --inplace
Resultado: ✅ Compilación exitosa (sin errores)
Tests Controlados (30 segundos cada uno)
Test 1: Tetris DX (skip-boot, baseline)
timeout 30s python3 main.py roms/tetris_dx.gbc > logs/step0402_skip_tetris_dx.log 2>&1
Resultados:
- ✅ Sin regresiones
- Frame 720:
gameplay_state=YES - TileData: 23.0% (1416/6144 bytes)
- UniqueTiles: 256/256
- LCDC: cambió a 0x81 en frame 677
- BGP: cambió a 0xE4 en frame 711
Test 2: Zelda DX (skip-boot, baseline)
timeout 30s python3 main.py roms/Oro.gbc > logs/step0402_skip_zelda_dx.log 2>&1
Resultados:
- LCDC: 0xE3 desde frame 0 (cambió inmediatamente)
- BGP: 0x00 desde frame 0 (problema conocido: paleta inválida)
- TileData: 0% (nunca carga tiles)
- TileMap: 100% (2048/2048 bytes, pero todos 0x00)
- UniqueTiles: 1/256 (solo un tile ID)
gameplay_state=NO
Test 3: Zelda DX (stub)
timeout 30s python3 main.py --bootrom-stub roms/Oro.gbc > logs/step0402_stub_zelda_dx.log 2>&1
Resultados:
- ✅ Stub activado correctamente:
[BOOTROM-STUB] Activando modo stub (DMG) - Stub configuró: LCDC=0x91, BGP=0xFC, SCY=0, SCX=0, OBP0=0xFF, OBP1=0xFF, IE=0x01, FF50=0x01
- Pero: El juego sobrescribe BGP a 0x00 inmediatamente (frame 0)
- LCDC: cambiado a 0xE3 por el juego (frame 0)
- Resultados idénticos al skip-boot: TileData 0%, gameplay_state=NO
Análisis de Resultados
Tetris DX: Sin regresiones. El modo skip-boot (por defecto) sigue funcionando perfectamente.
Zelda DX: El stub se activó correctamente y configuró los registros I/O, pero el juego sobrescribe BGP a 0x00 inmediatamente. Esto confirma la hipótesis del Step 0400:
Zelda DX/Pokemon Red esperan que la Boot ROM real configure paletas CGB específicas, no solo los registros I/O básicos. El stub configura valores DMG básicos (BGP=0xFC), pero el código del juego asume que la Boot ROM ya configuró paletas CGB y las sobrescribe con valores específicos (que son 0x00 cuando no hay datos válidos).
Esto significa que el stub técnicamente funciona (aplica estado post-boot), pero no es suficiente para juegos que dependen de configuración CGB avanzada de la Boot ROM.
4. Conclusiones y Próximos Pasos
Logros del Step 0402
- ✅ Integración oficial de Boot ROM opcional en el frontend (CLI + env var)
- ✅ Modo stub implementado en C++ (sin binario propietario)
- ✅ Sin regresiones en ROMs funcionantes (Tetris DX)
- ✅ Wiring end-to-end validado (frontend → backend → MMU)
- ✅ Preparado para Boot ROM real cuando el usuario la provea
Limitaciones del Stub
El stub no emula la Boot ROM real. Solo configura un estado post-boot mínimo (DMG). Juegos que dependen de configuración CGB avanzada (paletas CGB, HDMA, etc.) no funcionarán con el stub.
Próximos Pasos
- Step 0403: Documentar cómo el usuario puede extraer Boot ROM legalmente desde hardware real
- Step 0404: Implementar emulación parcial de Boot ROM (scroll de logo, sin código propietario)
- Step 0405: Investigar configuración CGB específica que Zelda DX/Pokemon Red esperan
Lecciones Aprendidas
- Clean Room y pragmatismo: Podemos soportar Boot ROM sin violar clean room (usuario la provee)
- Stub vs Boot real: El stub es útil para validación, pero no reemplaza la Boot ROM real
- Dependencias del juego: Algunos juegos dependen profundamente de la Boot ROM (más de lo esperado)
5. Archivos Modificados
Frontend
main.py: Añadidos flags--bootromy--bootrom-stub, lectura de env varVIBOY_BOOTROMsrc/viboy.py: Constructor actualizado para recibirbootrom_bytesybootrom_stub, lógica de aplicación enload_cartridge()
Backend (C++)
src/core/cpp/MMU.hpp: Declaración deenable_bootrom_stub()src/core/cpp/MMU.cpp: Implementación deenable_bootrom_stub()
Wrapper Cython
src/core/cython/mmu.pxd: Declaración deenable_bootrom_stub()src/core/cython/mmu.pyx: Wrapper Python deenable_bootrom_stub()
Documentación
docs/bitacora/entries/2026-01-01__0402__bootrom-cli-env-y-stub.html: Esta entradadocs/bitacora/index.html: Actualizado con enlace a esta entradadocs/informe_fase_2/parte_00_steps_0370_0401.md: Actualizado con Step 0402
6. Referencias
- Pan Docs - Power Up Sequence
- Pan Docs - FF50 Register
- Pan Docs - Boot ROM
- Step 0400 - Análisis Comparativo: Tetris DX vs Zelda DX/Pokemon Red
- Step 0401 - Boot ROM opcional + Inicialización correcta I/O