Step 0479: Cerrar Wait Loop - Identificar I/O Exacto y Arreglar Solo Eso
Resumen Ejecutivo
Step 0478 identificó que las ROMs CGB están atascadas en wait loops, pero no se identificó el I/O exacto que están esperando. Step 0479 implementa diagnóstico automático para identificar el I/O exacto del loop (LY, STAT, IF, CGB I/O) y aplica fixes mínimos solo si hay evidencia concluyente.
Resultado: ✅ Implementado `parse_loop_io_pattern()` para detectar automáticamente el I/O esperado. ✅ Añadida instrumentación gated por I/O esperado y específica LY/STAT/IF en MMU. ✅ Implementados tests clean-room (2/2 pasando). ✅ Ejecutado rom_smoke con baseline limpio y generado reporte. ✅ I/O identificado: mario.gbc espera `0xFF92` (I/O CGB no estándar), tetris_dx.gbc espera `0xFF00` (JOYP).
Concepto de Hardware
CPU Wait Loops en Game Boy
Los wait loops son patrones comunes en el código de Game Boy donde la CPU espera a que un registro I/O cambie a un valor específico. El patrón típico es:
loop:
LDH A,(IO_REG) ; Leer registro I/O
AND 0xXX ; Aplicar máscara (opcional)
CP 0xYY ; Comparar con valor esperado (opcional)
JR NZ, loop ; Saltar si no coincide
Fuente: Pan Docs - I/O Registers
I/O Registers Comunes en Wait Loops
- LY (0xFF44): LCD Y-Coordinate - Los juegos esperan que LY alcance un valor específico (ej: LY >= 0x90 para VBlank)
- STAT (0xFF41): LCD Status - Los juegos esperan que el modo STAT cambie (ej: modo VBlank = 0x01)
- IF (0xFF0F): Interrupt Flag - Los juegos esperan que un bit de IF se active (ej: IF bit0 = VBlank)
- JOYP (0xFF00): Joypad Input - Los juegos esperan que el joypad cambie (raro en wait loops de inicialización)
I/O CGB No Estándar (0xFF92, etc.)
Algunos juegos CGB usan registros I/O no documentados en Pan Docs estándar. Estos pueden ser registros específicos del hardware CGB o registros custom usados por el juego.
Fuente: Game Boy Color Technical Documentation
Implementación
Fase A: Extraer Condición Exacta del Loop (Sin Tocar Core)
Se implementó la función parse_loop_io_pattern() en tools/rom_smoke_0442.py que analiza el disasm window alrededor del PC hotspot para identificar automáticamente:
waits_on: Dirección I/O esperada (0xFF44, 0xFF41, 0xFF0F, etc.)mask: Máscara AND aplicada (0x01, 0x03, etc.)cmp: Valor comparado (si hay CP)pattern: Tipo de espera (LY_GE, STAT_MODE, IF_BIT, CGB_IO, UNKNOWN)
Estos campos se añadieron al snapshot output para cada ROM CGB en frame 180.
Fase B: Instrumentación Gated y Específica
Se añadió instrumentación en MMU.cpp/hpp para rastrear:
- Instrumentación gated por I/O esperado: Contador de reads desde programa del I/O esperado (controlado por
VIBOY_DEBUG_IO) - Instrumentación específica LY/STAT/IF: Contadores de cambios por frame para LY, STAT mode, e IF bit0
Los nuevos métodos expuestos en Cython:
set_waits_on_addr(uint16_t addr): Configura el I/O esperado para instrumentación gatedget_ly_changes_this_frame(): Contador de cambios de LY por frameget_stat_mode_changes_count(): Contador de cambios de modo STAT por frameget_if_bit0_set_count_this_frame(): Contador de veces que IF bit0 se pone a 1 por frame
Fase D: Tests Clean-Room
Se implementaron dos tests clean-room para validar el comportamiento básico antes de aplicar fixes:
test_ly_stat_progress_realistic_0479.py: Verifica que LY progresa correctamente durante la ejecucióntest_if_set_by_ppu_vblank_0479.py: Verifica que IF bit0 se pone a 1 cuando el PPU entra en VBlank
Ambos tests pasan ✅.
Fase Run: Ejecución y Reporte
Se ejecutó rom_smoke con baseline limpio (VIBOY_SIM_BOOT_LOGO=0, VIBOY_DEBUG_IO=1) para las ROMs CGB:
mario.gbc- 240 framestetris_dx.gbc- 240 frames
Se generó el reporte /tmp/reporte_step0479.md con análisis detallado de cada ROM.
Resultados
mario.gbc (Frame 180)
- PC_hotspot1:
0x12A0 - LoopWaitsOn:
0xFF92(I/O CGB no estándar) - Pattern:
UNKNOWN - LY_ReadMax:
145✅ (LY progresa correctamente) - STAT_LastRead:
0x00⚠️ (STAT no se está leyendo o está en 0) - IE:
0x00❌ - IF:
0xE3(bits activos)
Análisis: El loop está esperando 0xFF92 (I/O CGB no estándar), no un I/O estándar. LY progresa correctamente, pero el loop no se desbloquea porque 0xFF92 no está implementado o no cambia.
tetris_dx.gbc (Frame 180)
- PC_hotspot1:
0x1383 - LoopWaitsOn:
0xFF00(JOYP) - Pattern:
CGB_IO - Mask:
0x03 - Cmp:
0x03 - LY_ReadMax:
145✅ (LY progresa correctamente) - STAT_LastRead:
0x00⚠️ - IE:
0x00❌ - IF:
0xE1(bits activos)
Análisis: El loop está esperando JOYP con condición específica (mask=0x03, cmp=0x03). LY progresa correctamente, pero el loop no se desbloquea porque la condición de JOYP no se cumple.
Hallazgos Clave
- LY progresa correctamente: Ambas ROMs muestran
LY_ReadMax=145, lo que indica que LY SÍ está cambiando. - STAT no se lee correctamente:
STAT_LastRead=0x00en ambas ROMs es sospechoso. Puede indicar que STAT no se está leyendo durante la ejecución o que hay un problema con la lectura de STAT desde la PPU. - IE sigue en 0x00: Ambas ROMs tienen
IE=0x00, lo que confirma el hallazgo de Step 0478. - I/O esperado diferente: mario.gbc espera
0xFF92(I/O CGB no estándar), tetris_dx.gbc espera0xFF00(JOYP).
Archivos Afectados
tools/rom_smoke_0442.py- Añadida funciónparse_loop_io_pattern()y campos al snapshotsrc/core/cpp/MMU.hpp- Añadidos miembros privados para instrumentaciónsrc/core/cpp/MMU.cpp- Implementada instrumentación gated y específicasrc/core/cython/mmu.pxd- Añadidas declaraciones de nuevos métodossrc/core/cython/mmu.pyx- Exposición de nuevos métodos a Pythontests/test_ly_stat_progress_realistic_0479.py- Test clean-room para LY/STATtests/test_if_set_by_ppu_vblank_0479.py- Test clean-room para IF bit0
Tests y Verificación
Tests unitarios: pytest con 2 tests nuevos pasando:
- ✅
test_ly_stat_progress_realistic_0479.py::test_ly_stat_progress_realistic- Verifica que LY progresa correctamente - ✅
test_if_set_by_ppu_vblank_0479.py::test_if_set_by_ppu_vblank- Verifica que IF bit0 se pone a 1 en VBlank
Ejecución rom_smoke: Ejecutado con baseline limpio para mario.gbc y tetris_dx.gbc. Reporte generado en /tmp/reporte_step0479.md.
Fuentes Consultadas
- Pan Docs: I/O Registers (LY, STAT, IF, JOYP)
- Game Boy Color Technical Documentation: I/O Registers no estándar
Integridad Educativa
Lo que Entiendo Ahora
- Wait Loops: Los juegos Game Boy usan wait loops esperando que registros I/O cambien a valores específicos. El patrón típico es LDH A,(IO) → AND mask → CP val → JR NZ, loop.
- Diagnóstico Automático: Se puede analizar el disasm window alrededor del hotspot para identificar automáticamente el I/O esperado y la condición del loop.
- Instrumentación Gated: Se puede instrumentar solo el I/O esperado para evitar overhead en el bucle principal, activando la instrumentación solo cuando
VIBOY_DEBUG_IOestá habilitado.
Lo que Falta Confirmar
- I/O CGB 0xFF92: Este registro no está documentado en Pan Docs estándar. Requiere investigación adicional para determinar si es un registro CGB específico o un registro custom usado por mario.gbc.
- STAT LastRead=0x00: Por qué STAT se lee como 0x00 cuando debería tener valores válidos. Puede ser un problema con la lectura de STAT desde la PPU o con la inicialización del LCD.
- JOYP Condition: Por qué tetris_dx.gbc espera JOYP con mask=0x03 y cmp=0x03. Requiere verificar los defaults de JOYP y la semántica de read/write.