Step 0477: Por Qué CGB se Queda con IME/IE en 0 - Prueba de Causa Raíz
Resumen Ejecutivo
Step 0476 mostró que las ROMs CGB tienen IME=0 e IE=0x00, pero eso puede ser causa o síntoma. Step 0477 determina con evidencia por qué el juego no llega a habilitarlos: arregla el disassembler para mostrar I/O real (no DB 0xE0/0xF0), añade timeline de IME/IE/EI/DI con PC y timestamps, y aplica clasificación automática para identificar la causa raíz.
Resultado: ✅ Disassembler corregido (LDH, LD (FF00+C), LD (a16), CB prefix). ✅ Tracking de transiciones IME/EI/DI implementado. ✅ Tracking de writes IE/IF con timestamps. ✅ Tests clean-room pasando (5/5). ✅ Clasificador automático implementado (Caso A/B/C/D). ✅ Métricas añadidas a snapshots de rom_smoke.
Concepto de Hardware
EI Delayed Enable (Pan Docs)
La instrucción EI (Enable Interrupts) habilita IME (Interrupt Master Enable) con un retraso de 1 instrucción. Esto significa que:
- Cuando se ejecuta
EI, IME NO se activa inmediatamente - IME se activa DESPUÉS de ejecutar la siguiente instrucción
- Esto permite que la instrucción siguiente a
EIse ejecute sin interrupciones
Fuente: Pan Docs - EI instruction: "The interrupt master enable flag is set one instruction after EI is executed."
Clasificación de Causa Raíz
Step 0477 implementa un clasificador automático que identifica 4 casos posibles:
- Caso A: EI nunca se ejecuta (el juego está atascado antes de habilitar interrupciones)
- Caso B: EI se ejecuta pero IME no sube (bug EI delayed enable)
- Caso C: IME sube pero IE=0 (juego no habilita IE o no lo necesita)
- Caso D: IME+IE OK pero no hay service (revisar generación de requests)
Implementación
Fase A: Arreglar el Disassembler
El disassembler en rom_smoke_0442.py mostraba DB 0xE0/0xF0 para instrucciones LDH, ocultando el I/O real. Se añadieron:
LDH (FF00+n),A(0xE0)LDH A,(FF00+n)(0xF0)LD (FF00+C),A(0xE2)LD A,(FF00+C)(0xF2)LD (a16),A(0xEA)LD A,(a16)(0xFA)- Prefijo CB (0xCB) - consume 2 bytes
También se implementó disasm_window() para desensamblar una ventana alrededor del PC y evitar empezar en mitad de instrucción.
Fase B: Tracking de Transiciones IME/EI/DI
Se añadieron miembros privados en CPU para trackear:
ime_set_events_count_- Contador de veces que IME se activalast_ime_set_pc_- PC donde IME se activó por última vezlast_ime_set_timestamp_- Timestamp de la última activaciónlast_ei_pc_- PC de la última ejecución de EIlast_di_pc_- PC de la última ejecución de DI
En MMU se añadieron timestamps para writes a IE/IF:
last_if_write_timestamp_- Timestamp del último write a IF- Getter
get_last_ie_write_timestamp()- Timestamp del último write a IE
Fase C: Tests Clean-Room
Se crearon dos tests para validar la implementación:
test_ei_delayed_enable_0477.py- Verifica que EI habilita IME con retraso de 1 instrucción (3 tests, todos pasando)test_di_cancels_pending_ei_0477.py- Verifica que DI cancela un EI pendiente (2 tests, todos pasando)
Fase D: Métricas y Clasificador en rom_smoke
Se añadieron métricas al snapshot de rom_smoke:
IME_SetEvents- Contador de activaciones de IMEIME_SetPC- PC donde IME se activóIME_SetTS- Timestamp de activaciónEI_PC- PC de última ejecución de EIDI_PC- PC de última ejecución de DIEI_Pending- Estado de EI pending (delayed enable)IEWriteTS- Timestamp del último write a IEIF_WriteTS- Timestamp del último write a IF
Se implementó el clasificador automático _classify_ime_ie_state() que identifica automáticamente el caso A/B/C/D basado en las métricas.
Archivos Afectados
tools/rom_smoke_0442.py- Disassembler corregido, métricas añadidas, clasificador implementadosrc/core/cpp/CPU.hpp- Miembros privados para tracking IME/EI/DIsrc/core/cpp/CPU.cpp- Implementación de tracking y getterssrc/core/cpp/MMU.hpp- Timestamp para IF writessrc/core/cpp/MMU.cpp- Implementación de timestamp trackingsrc/core/cython/cpu.pxd- Declaraciones de getterssrc/core/cython/cpu.pyx- Wrappers Python de getterssrc/core/cython/mmu.pxd- Declaraciones de getters de timestampsrc/core/cython/mmu.pyx- Wrappers Python de getters de timestamptests/test_ei_delayed_enable_0477.py- Tests de EI delayed enable (nuevo)tests/test_di_cancels_pending_ei_0477.py- Tests de DI cancela pending EI (nuevo)
Tests y Verificación
Tests Clean-Room
Comando ejecutado: pytest -q tests/test_ei_delayed_enable_0477.py tests/test_di_cancels_pending_ei_0477.py
Resultado: 5 passed in 0.49s
Test: EI Delayed Enable Básico
def test_ei_delayed_enable_basic(self):
# Estado inicial: IME=0
assert cpu.get_ime() == 0
assert cpu.get_ei_pending() == False
# Ejecutar EI
cpu.step() # EI
assert cpu.get_ime() == 0 # NO se activa inmediatamente
assert cpu.get_ei_pending() == True
# Ejecutar NOP (siguiente instrucción)
cpu.step() # NOP
assert cpu.get_ime() == 1 # Se activa después de NOP
assert cpu.get_ei_pending() == False
assert cpu.get_ime_set_events_count() == 1
Validación Nativa: Validación de módulo compilado C++ con tracking de IME/EI/DI.