⚠️ Clean-Room / Educativo

Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.

Análisis Forense de Trazado de Ejecución

Fecha: 2025-12-18 Step ID: 0042 Estado: Draft

Resumen

Se creó una herramienta de trazado forense (tools/debug_trace.py) para analizar la ejecución del emulador sin interfaz gráfica. El análisis de las primeras instrucciones reveló que el juego ejecuta DI (0xF3) en la dirección 0x0150, deshabilitando el IME (Interrupt Master Enable) al inicio. El juego nunca ejecuta EI (0xFB) para volver a habilitar las interrupciones, lo que explica por qué el juego se queda en un bucle infinito esperando V-Blank.

Se detectó un bucle de espera (delay loop) entre las direcciones 0x1383-0x1389 que decrementa el registro DE desde 0x06D6 (1750) hasta que sea cero. El juego está esperando un evento (probablemente V-Blank) que nunca ocurre porque IME está deshabilitado, incluso si IE estuviera configurado correctamente.

Concepto de Hardware

En la Game Boy, las interrupciones requieren dos niveles de habilitación:

  1. IME (Interrupt Master Enable): Control global de interrupciones, controlado por las instrucciones DI (0xF3) y EI (0xFB). Si IME = False, ninguna interrupción se procesará, independientemente de IE.
  2. IE (Interrupt Enable, 0xFFFF): Registro de memoria que controla qué tipos de interrupciones están habilitadas (V-Blank, LCD STAT, Timer, Serial, Joypad).

Para que una interrupción se procese, ambos deben estar habilitados: IME = True Y el bit correspondiente en IE = 1. Si IME está deshabilitado, incluso si IE está configurado correctamente, las interrupciones no se procesarán.

Los juegos típicamente ejecutan DI al inicio para deshabilitar interrupciones durante la inicialización, y luego ejecutan EI para volver a habilitarlas. Si el juego nunca ejecuta EI, las interrupciones permanecen deshabilitadas y el juego puede quedar atascado en bucles de espera.

Fuente: Pan Docs - Interrupts, Interrupt Master Enable (IME), Interrupt Enable Register (IE)

Implementación

Se creó la herramienta tools/debug_trace.py que:

  • Carga una ROM usando la clase Viboy
  • Intercepta todas las escrituras en memoria usando un wrapper TraceMMU
  • Ejecuta un número configurable de instrucciones (por defecto 50,000)
  • Registra cada instrucción con: PC, opcode, registros, ciclos y escrituras en I/O
  • Detecta bucles infinitos analizando patrones repetidos en el historial de PC
  • Genera un reporte con escrituras críticas (IE, LCDC, IF, STAT) y las primeras/últimas 50 instrucciones
  • Muestra las primeras instrucciones para analizar la secuencia de inicialización del juego

Componentes creados/modificados

  • tools/debug_trace.py: Script principal de trazado forense (actualizado para mostrar primeras instrucciones)
  • TraceMMU: Clase wrapper que intercepta escrituras en memoria

Decisiones de diseño

Se optó por crear un wrapper de MMU en lugar de modificar el código del emulador para mantener la herramienta de trazado separada del código de producción. Esto permite activar/desactivar el trazado sin afectar el emulador principal.

La detección de bucles se implementa analizando patrones repetidos en el historial de PC. Se detectan tanto bucles exactos (misma secuencia repetida) como bucles cortos con oscilación entre pocas direcciones (típico de saltos condicionales).

Archivos Afectados

  • tools/debug_trace.py - Herramienta de trazado forense (nuevo, actualizado)

Tests y Verificación

Se ejecutó el script de trazado con tetris_dx.gbc (ROM aportada por el usuario, no distribuida):

  • Comando ejecutado (análisis inicial): python tools/debug_trace.py tetris_dx.gbc --max-instructions 5000
  • Comando ejecutado (análisis extendido): python tools/debug_trace.py tetris_dx.gbc --max-instructions 100000
  • Entorno: Windows 10, Python 3.13.5
  • Resultado: Ejecución exitosa, 100,000 instrucciones trazadas
  • Qué valida:
    • Que el juego ejecuta DI (0xF3) en la instrucción #3 (PC: 0x0150)
    • Que el juego NUNCA ejecuta EI (0xFB) en 100,000 instrucciones
    • Que el juego nunca escribe en IE (0xFFFF) durante las 100,000 instrucciones
    • Que existe un bucle de espera que persiste después de 100,000 instrucciones
    • Que el juego escribe en I/O (370 escrituras detectadas, principalmente en 0xFF00 - P1)

Hallazgos del análisis - Secuencia de Inicio

Análisis de las primeras instrucciones reveló la secuencia de inicialización:

PC: 0x0100 | NOP (0x00)              - Inicio del código del cartucho
PC: 0x0101 | JP nn (0xC3)            - Salto a 0x0150
PC: 0x0150 | DI (0xF3)               - ⚠️ DESHABILITA INTERRUPCIONES (IME = False)
PC: 0x0151 | LD (0xFF00+C), A (0xE0) - Escribe en 0xFF04 (DIV - Timer Divider)
PC: 0x0153 | LD (0xFF00+C), A (0xE0) - Otra escritura en I/O
PC: 0x0155 | LD BC, d16 (0x01)       - Carga BC con 0x0004
PC: 0x0158 | CALL nn (0xCD)         - Llama a función en 0x1380
PC: 0x1380 | LD DE, d16 (0x11)      - Carga DE con 0x06D6 (1750)
PC: 0x1383-0x1389 | Bucle de espera - Decrementa DE hasta 0

Hallazgos del análisis - Verificación Extendida (100,000 instrucciones)

Análisis extendido con 100,000 instrucciones confirmó los hallazgos iniciales:

EJECUCIONES DE DI/EI (Interrupt Master Enable):
  Instruccion #3 | PC: 0x0150 | DI (0xF3) | IME antes: Deshabilitado

RESUMEN:
  - DI ejecutado: 1 vez (instrucción #3, PC: 0x0150)
  - EI ejecutado: 0 veces (NUNCA en 100,000 instrucciones)
  - Escrituras en IE (0xFFFF): 0
  - Escrituras en I/O: 370 (principalmente 0xFF00 - P1 Joypad)
  - Estado final: Bucle persistente entre 0x12DD-0x12EC

Hallazgo crítico confirmado: El juego ejecuta DI (0xF3) una sola vez en la instrucción #3 (PC: 0x0150), deshabilitando IME al inicio. El juego NUNCA ejecuta EI (0xFB) en 100,000 instrucciones para volver a habilitar las interrupciones. Esto significa que incluso si IE estuviera configurado correctamente, las interrupciones no se procesarían porque IME está permanentemente en False.

Observación adicional: Después de 100,000 instrucciones, el juego sigue en un bucle, pero ahora entre las direcciones 0x12DD-0x12EC (diferente al bucle inicial 0x1383-0x1389). El juego escribe en 0xFF00 (P1 - Joypad) durante el bucle, lo que sugiere que está esperando entrada del usuario o algún evento que nunca ocurre.

Hallazgos del análisis - Bucle de Espera

BUCLE DETECTADO:
PC oscila entre: 0x1383-0x1389
Patrón:
  0x1383: NOP (0x00)
  0x1384: NOP (0x00)
  0x1385: NOP (0x00)
  0x1386: DEC DE (0x1B) - Decrementa DE
  0x1387: LD A, D (0x7A) - Carga D en A
  0x1388: OR E (0xB3) - OR entre A y E
  0x1389: JR NZ, -5 (0x20) - Salto relativo si no es cero

Análisis del bucle: El juego está en un delay loop que decrementa DE desde 0x06D6 (1750) hasta que sea 0. Cuando DE llega a 0, el salto condicional (JR NZ) no se ejecuta y el bucle debería terminar. Sin embargo, el juego probablemente espera que algo más ocurra (como una interrupción V-Blank) antes de continuar, pero como IME está deshabilitado, las interrupciones nunca se procesan.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • IME vs IE: Las interrupciones requieren dos niveles de habilitación: IME (control global, DI/EI) e IE (registro de memoria). Si IME está deshabilitado, ninguna interrupción se procesará, incluso si IE está configurado correctamente.
  • DI/EI: Los juegos típicamente ejecutan DI al inicio para deshabilitar interrupciones durante la inicialización, y luego ejecutan EI para volver a habilitarlas. Si el juego nunca ejecuta EI, las interrupciones permanecen deshabilitadas.
  • Delay Loops: Los juegos usan bucles de espera (delay loops) para pausar la ejecución durante un tiempo determinado. Estos bucles típicamente decrementan un registro hasta que sea 0.
  • Bucle infinito: Si un juego espera una interrupción que nunca ocurre (porque IME está deshabilitado), puede quedar atascado en un bucle infinito esperando un evento que nunca llegará.

Lo que Falta Confirmar

  • Por qué el juego no ejecuta EI: Confirmado que el juego nunca ejecuta EI en 100,000 instrucciones. No está claro si:
    • El juego nunca llega a la parte del código que ejecuta EI (se queda atascado antes)
    • Hay un bug en la implementación de DI/EI que impide que IME se habilite correctamente
    • El juego espera que IME esté habilitado por defecto (bug en inicialización)
  • Qué ocurre después del delay loop: El análisis de 100,000 instrucciones muestra que el juego entra en diferentes bucles (0x1383-0x1389 inicialmente, luego 0x12DD-0x12EC). El juego nunca sale completamente de los bucles de espera.
  • Estado inicial de IME: El análisis muestra que IME está "Deshabilitado" antes de ejecutar DI en la instrucción #3, lo que sugiere que IME se inicializa en False (correcto). Sin embargo, necesito verificar la implementación para confirmar.
  • Implementación de DI/EI: Necesito verificar que la implementación de DI/EI en la CPU funcione correctamente y que IME se actualice como se espera cuando se ejecutan estas instrucciones.

Hipótesis y Suposiciones

Hipótesis principal (CONFIRMADA): El juego ejecuta DI al inicio para deshabilitar interrupciones durante la inicialización, pero nunca ejecuta EI para volver a habilitarlas (verificado en 100,000 instrucciones). Esto causa que el juego quede atascado en un bucle de espera esperando V-Blank, que nunca se procesa porque IME está permanentemente deshabilitado.

Hipótesis secundaria: Es posible que:

  • El juego tenga un bug y nunca llegue al código que ejecuta EI
  • Haya un bug en la implementación de DI/EI que impida que IME se habilite correctamente
  • El juego espere que IME esté habilitado por defecto (bug en inicialización del emulador)

Suposición no verificada: No puedo verificar si el juego debería ejecutar EI sin analizar el código de la ROM (lo cual no es posible sin herramientas de desensamblado). Sin embargo, el hecho de que el juego nunca ejecute EI en 100,000 instrucciones sugiere fuertemente que hay un problema en la lógica de inicialización o en la implementación del emulador.

Próximos Pasos

  • [ ] Verificar la implementación de DI/EI en la CPU para asegurar que IME se actualiza correctamente
  • [ ] Trazar más instrucciones (100,000+) para ver si el juego eventualmente ejecuta EI
  • [ ] Analizar el estado inicial de IME y otros registros críticos al inicio de la ejecución
  • [ ] Verificar si hay algún problema con el manejo de interrupciones que impida que el juego las habilite
  • [ ] Considerar forzar IME = True temporalmente para ver si el juego continúa (si IE está configurado)
  • [ ] Verificar si el juego espera que IME esté habilitado por defecto al inicio (bug en inicialización)