⚠️ 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.

Auditoría Extendida de Interrupciones y DMA

Fecha: 2025-12-25 Step ID: 0286 Estado: VERIFIED

Resumen

Extensión de la instrumentación de diagnóstico del emulador para capturar el flujo completo de ejecución de handlers de interrupciones y monitorear operaciones críticas de DMA y VRAM. Aumento del límite del Sniper del Handler a 500 instrucciones, detección de RET (0xC9) además de RETI (0xD9), implementación de monitor específico para disparo de OAM DMA ([DMA-TRIGGER]) y monitor temporal sin filtros para VRAM ([VRAM-TOTAL]).

Concepto de Hardware

Handlers de Interrupciones: Cuando una interrupción se dispara en la Game Boy, la CPU salta automáticamente a un vector de interrupción específico (ej: 0x0040 para V-Blank). El código en ese vector (el "handler") debe ejecutarse completamente y terminar con una instrucción RETI (Return and Enable Interrupts, 0xD9) que restaura el estado anterior y rehabilita las interrupciones. Algunos handlers mal implementados pueden usar RET (0xC9) en lugar de RETI, lo que no habilita las interrupciones y puede causar problemas.

DMA (Direct Memory Access): El registro DMA (0xFF46) permite transferir 160 bytes desde cualquier dirección de memoria a OAM (Object Attribute Memory, 0xFE00-0xFE9F) en un solo ciclo. Cuando se escribe un valor en 0xFF46, el hardware copia automáticamente 160 bytes desde la dirección (valor << 8) a OAM. Esta operación es crítica para cargar sprites en el juego.

VRAM (Video RAM): La región 0x8000-0x9FFF contiene los datos de tiles y mapas de tiles que la PPU lee para renderizar. Monitorear todas las escrituras en VRAM (sin filtros) permite detectar cualquier actividad sospechosa que pueda estar causando problemas de renderizado.

Fuente: Pan Docs - "Interrupts", "DMA Transfer", "VRAM (Video RAM)"

Implementación

Se realizaron cuatro mejoras principales en la instrumentación de diagnóstico:

1. Aumento del Límite del Sniper del Handler

El límite del Sniper del Handler ([HANDLER-EXEC]) se aumentó de 100 a 500 instrucciones para capturar el flujo completo de ejecución del handler hasta el retorno. Esto es necesario porque algunos handlers pueden ser más largos de lo esperado y necesitamos ver todo el flujo de ejecución.

Código modificado en CPU.cpp:

// Rastrear instrucciones dentro del handler
// --- Step 0286: Aumentado límite a 500 instrucciones para capturar flujo completo ---
if (in_vblank_handler && handler_step_count < 500) {
    // ... código de rastreo ...
}

2. Detección de RET (0xC9) además de RETI (0xD9)

Se añadió detección de la instrucción RET (0xC9) además de RETI (0xD9) para identificar handlers que terminan sin habilitar IME. Esto es importante porque algunos handlers mal implementados pueden usar RET en lugar de RETI, lo que causa que las interrupciones no se rehabilen correctamente.

Código modificado en CPU.cpp:

// --- Step 0286: Detección de RET (0xC9) además de RETI (0xD9) ---
// Algunos handlers pueden terminar con RET sin habilitar IME, lo cual es un bug
// pero debemos detectarlo para entender el flujo completo del handler
if (op == 0xD9) {
    printf("[HANDLER-EXIT] RETI detectado en PC:0x%04X. Fin del rastreo del handler.\n", original_pc);
    in_vblank_handler = false;
} else if (op == 0xC9) {
    printf("[HANDLER-EXIT] RET detectado en PC:0x%04X (SIN habilitar IME). Fin del rastreo del handler.\n", original_pc);
    in_vblank_handler = false;
}

3. Monitor de Disparo de OAM DMA ([DMA-TRIGGER])

Se implementó un monitor específico que detecta cuando se activa el DMA para transferir datos a OAM. El monitor reporta la dirección fuente, el rango de direcciones que se copiarán y el PC donde se activó el DMA.

Código añadido en MMU.cpp:

// --- Step 0286: Monitor de Disparo de OAM DMA ([DMA-TRIGGER]) ---
// Detecta cuando se activa el DMA para transferir datos a OAM (0xFE00-0xFE9F)
// El DMA copia 160 bytes desde la dirección (value << 8) a OAM
// Fuente: Pan Docs - "DMA Transfer": Escritura en 0xFF46 inicia transferencia
printf("[DMA-TRIGGER] DMA activado: Source=0x%02X00 (0x%04X-0x%04X) -> OAM (0xFE00-0xFE9F) | PC:0x%04X\n",
       value, static_cast<uint16_t>(value) << 8, (static_cast<uint16_t>(value) << 8) + 159, debug_current_pc);

4. Monitor Temporal Sin Filtros para VRAM ([VRAM-TOTAL])

Se implementó un monitor temporal que captura TODAS las escrituras en VRAM sin filtros para detectar cualquier actividad sospechosa. Este monitor complementa el monitor [VRAM-VIBE] existente que filtra valores comunes de inicialización.

Código añadido en MMU.cpp:

// --- Step 0286: Monitor Temporal Sin Filtros para VRAM ([VRAM-TOTAL]) ---
// Captura TODAS las escrituras en VRAM sin filtros para detectar cualquier actividad sospechosa.
// Este monitor es temporal y se usa para diagnóstico cuando hay problemas con la carga de gráficos.
// Fuente: Pan Docs - "VRAM (Video RAM)": 0x8000-0x9FFF contiene Tile Data y Tile Maps
static int vram_total_count = 0;
if (vram_total_count < 500) {  // Límite alto para capturar actividad completa
    printf("[VRAM-TOTAL] Write %04X=%02X PC:%04X (Bank:%d)\n", 
           addr, value, debug_current_pc, current_rom_bank_);
    vram_total_count++;
}

Decisiones de Diseño

  • Límite de 500 instrucciones: Se eligió 500 como un balance entre capturar handlers largos y evitar saturación de logs. Si un handler necesita más de 500 instrucciones, probablemente hay un problema más grave.
  • Detección de RET: Se decidió detectar RET además de RETI para identificar bugs potenciales en el código del juego, aunque técnicamente RET no debería usarse en handlers de interrupciones.
  • Monitor temporal de VRAM: Se implementó como monitor temporal (con límite de 500 reportes) para evitar saturación de logs. Una vez que se identifique el problema, este monitor puede desactivarse o reducirse.

Archivos Afectados

  • src/core/cpp/CPU.cpp - Aumento del límite del Sniper del Handler y detección de RET
  • src/core/cpp/MMU.cpp - Implementación de monitores [DMA-TRIGGER] y [VRAM-TOTAL]

Tests y Verificación

La instrumentación se validó mediante compilación y ejecución del emulador. Los monitores generan logs que pueden analizarse para diagnosticar problemas de interrupciones, DMA y VRAM.

Validación de módulo compilado C++: Los cambios se compilaron correctamente sin errores de sintaxis o warnings. La instrumentación se ejecuta en tiempo de ejecución y genera logs cuando se activan las condiciones monitoreadas.

Comando de compilación:

python setup.py build_ext --inplace

Resultado: Compilación exitosa sin errores.

Fuentes Consultadas

  • Pan Docs: Interrupts - Vectores de interrupción y comportamiento de RETI
  • Pan Docs: DMA Transfer - Operación del registro DMA (0xFF46)
  • Pan Docs: VRAM (Video RAM) - Región de memoria 0x8000-0x9FFF

Integridad Educativa

Lo que Entiendo Ahora

  • Handlers de Interrupciones: Los handlers deben terminar con RETI para restaurar el estado y rehabilitar interrupciones. RET no es suficiente porque no habilita IME.
  • DMA: El registro DMA permite transferir 160 bytes a OAM en un solo ciclo. Es crítico para cargar sprites en el juego.
  • Instrumentación: Los monitores de diagnóstico deben ejecutarse antes de cualquier early return para capturar todas las ejecuciones, incluso cuando hay interrupciones.

Lo que Falta Confirmar

  • Comportamiento real de handlers largos: ¿Es común que los handlers tengan más de 100 instrucciones? El límite de 500 debería capturar la mayoría de los casos.
  • Frecuencia de DMA: ¿Con qué frecuencia se activa el DMA en juegos reales? El monitor [DMA-TRIGGER] nos ayudará a entender esto.
  • Patrones de escritura en VRAM: El monitor [VRAM-TOTAL] nos ayudará a identificar patrones sospechosos de escritura que puedan estar causando problemas de renderizado.

Hipótesis y Suposiciones

Asumimos que un límite de 500 instrucciones es suficiente para capturar la mayoría de los handlers de interrupciones. Si encontramos handlers más largos, podemos aumentar el límite o investigar por qué son tan largos.

Asumimos que detectar RET en handlers es útil para identificar bugs, aunque técnicamente RET no debería usarse en handlers de interrupciones. Si encontramos muchos casos de RET, puede indicar un problema más profundo en la emulación de interrupciones.

Próximos Pasos

  • [ ] Analizar logs generados por los nuevos monitores para identificar patrones de comportamiento
  • [ ] Verificar que los handlers de interrupciones terminen correctamente con RETI
  • [ ] Confirmar que el DMA se activa en los momentos esperados durante la ejecución del juego
  • [ ] Revisar los patrones de escritura en VRAM para identificar posibles problemas de carga de gráficos
  • [ ] Ajustar límites de monitores si es necesario basándose en los datos recopilados