Step 0482: Desbloquear Ruta FF92 (Mario) + Detectar Wait-Loop Real (Tetris DX) + Eliminar Estado Estático
Resumen
Este step implementa tres objetivos principales:
- Fase 0: Higiene - Eliminar estado estático compartido entre tests
- Fase A: Mario - Instrumentación para análisis de control flow (Branch Decision Counters, Last Compare/BIT Tracking, LCDC Disable Tracking)
- Fase B: Tetris DX - Detector dinámico de wait-loops y histograma de opcodes desconocidos
Implementación
Fase 0: Higiene - Eliminar Estado Estático
Convertidos contadores estáticos a miembros de instancia para aislar tests:
- MMU:
ie_write_count_,last_ie_written_,last_ie_write_pc_,last_ie_write_timestamp_,last_ie_read_value_,ie_read_count_,key1_write_count_,joyp_write_count_,io_read_counts_,lcdc_disable_events_ - CPU:
ei_count_,di_count_
Fase A: Mario - Instrumentación para Análisis
A1) Branch Decision Counters
Gate: VIBOY_DEBUG_BRANCH=1
Implementado en CPU.cpp/CPU.hpp:
branch_decisions_: map de PC → BranchDecision (taken_count, not_taken_count, last_target, last_taken, last_flags)last_cond_jump_pc_,last_target_,last_taken_,last_flags_- Getters expuestos en Cython
A2) Last Compare/BIT Tracking
Gate: VIBOY_DEBUG_BRANCH=1
Implementado en CPU.cpp/CPU.hpp:
- Last Compare:
last_cmp_pc_,last_cmp_a_,last_cmp_imm_,last_cmp_result_flags_ - Last BIT:
last_bit_pc_,last_bit_n_,last_bit_value_ - Tracking en instrucciones
CP n(0xFE) yBIT n, r(CB opcodes) - Getters expuestos en Cython
A3) LCDC Disable Tracking
Implementado en MMU.cpp/MMU.hpp y PPU.cpp/PPU.hpp:
lcdc_disable_events_: contador de eventos (1→0)last_lcdc_write_pc_,last_lcdc_write_value_PPU::handle_lcd_disable(): resetea LY a 0, STAT mode a HBlank- Getters expuestos en Cython
A4) Test Clean-Room LCDC Disable
Archivo: tests/test_lcdc_disable_resets_ly_0482.py
Resultado: ✅ PASA
Verifica que cuando LCDC bit7 pasa de 1→0:
- LY se resetea a 0 y se estabiliza
- STAT mode se establece en HBlank (mode 0)
lcdc_disable_events == 1
Fase B: Tetris DX - Detectar Wait-Loop Real
B1) Dynamic Wait-Loop Detector
Archivo: tools/rom_smoke_0442.py
Función detect_dynamic_wait_loop():
- Analiza I/O reads program en hotspot
- Identifica I/O read dominante
- Correlaciona con
last_cmp/last_bitpara determinar condición de espera - Retorna:
waits_on_addr,mask,cmp,bit,io_reads_distribution
B2) Unknown Opcode Histogram
Archivo: tools/rom_smoke_0442.py
Función get_unknown_opcode_histogram():
- Cuenta opcodes desconocidos (DB) en disasm window
- Retorna top 10 opcodes ordenados por frecuencia
- Permite priorizar implementación de opcodes en hotspot
Integración en Snapshots
Añadidos a snapshots de rom_smoke_0442.py:
BranchInfo: información de branch counters (si VIBOY_DEBUG_BRANCH=1)LastCmp: último CP ejecutado (PC, A, Imm)LastBit: último BIT ejecutado (PC, Bit, Val)LCDC_DisableEvents: contador de eventos de disableDynamicWaitLoop: resultado del detector dinámicoUnknownOpcodes: top 5 opcodes desconocidos en hotspot
Concepto de Hardware
LCDC (LCD Control Register - 0xFF40)
El registro LCDC controla el estado del LCD. El bit 7 (LCDC.7) activa/desactiva el LCD. Cuando el LCD se apaga (bit 7 pasa de 1→0), según Pan Docs:
- LY (Line Y) se resetea a 0
- STAT mode se establece en un estado estable (Mode 0 = HBlank)
- No debe quedar frame pending infinito
Este comportamiento es crítico para muchos juegos que apagan el LCD durante transiciones de pantalla o inicialización.
Branch Decision Tracking
El tracking de decisiones de branch permite analizar el flujo de control del programa:
- Taken/Not-Taken Counts: Cuántas veces un salto condicional fue tomado vs no tomado
- Last Target/Flags: Último target y flags al momento del salto
- Útil para identificar loops y condiciones que bloquean progreso
Last Compare/BIT Tracking
El tracking de últimas comparaciones y test de bits permite correlacionar:
- CP (Compare): Último valor comparado (A vs immediate)
- BIT: Último bit testeado (número de bit y valor)
- Con I/O reads para identificar condiciones de espera en wait-loops
Tests y Verificación
Test LCDC Disable
Comando:
pytest tests/test_lcdc_disable_resets_ly_0482.py -v
Resultado:
1 passed in 0.25s
Código del Test (fragmento clave):
def test_lcdc_disable_resets_ly(self):
mmu = PyMMU()
ppu = PyPPU(mmu)
mmu.set_ppu(ppu)
# Encender LCD
mmu.write(IO_LCDC, 0x91)
ppu.step(4560) # 10 scanlines
assert ppu.get_ly() == 10
# Apagar LCD
mmu.write(IO_LCDC, 0x11)
ppu.step(1000)
# Verificar LY reseteado
assert ppu.get_ly() == 0
assert ppu.get_mode() == 0 # HBlank
assert mmu.get_lcdc_disable_events() == 1
Validación: Test valida módulo compilado C++ (PPU::handle_lcd_disable)
Ejecución rom_smoke
ROMs ejecutadas:
mario.gbc- 240 framestetris_dx.gbc- 240 framestetris.gb- 240 frames
Logs:
/tmp/mario_smoke_0482.log/tmp/tetris_dx_smoke_0482.log/tmp/tetris_smoke_0482.log
Archivos Afectados
src/core/cpp/MMU.hpp/MMU.cppsrc/core/cpp/CPU.hpp/CPU.cppsrc/core/cpp/PPU.hpp/PPU.cppsrc/core/cython/mmu.pxd/mmu.pyxsrc/core/cython/cpu.pxd/cpu.pyxtests/test_lcdc_disable_resets_ly_0482.py(nuevo)tools/rom_smoke_0442.py
Referencias
- Pan Docs - LCD Control Register (FF40 - LCDC)
- Pan Docs - CPU Instruction Set (CP, BIT)
- Pan Docs - LCD Status Register (STAT)