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

Interceptor de Transferencia DMA/HRAM

Fecha: 2025-12-22 Step ID: 0245 Estado: Draft

Resumen

El Centinela (Step 0244) confirmó que el juego escribe el marcador 0xFD en HRAM (0xFF8D), pero luego lo busca desesperadamente en WRAM, causando un bucle infinito. Falta el eslabón perdido: ¿Quién mueve los datos de HRAM a WRAM? Se implementa un interceptor de transferencia que monitorea escrituras en el registro DMA (0xFF46) y lecturas en HRAM (0xFF8D) para determinar si el juego intenta usar DMA o una rutina de copia manual.

Concepto de Hardware

DMA (Direct Memory Access): El Game Boy tiene un registro DMA (0xFF46) que permite copiar 160 bytes de datos desde cualquier dirección de memoria a la OAM (Object Attribute Memory, 0xFE00-0xFE9F). Cuando el juego escribe un byte en 0xFF46, el hardware inicia automáticamente una transferencia desde la dirección (valor × 0x100) a OAM. Por ejemplo, escribir 0xFE en 0xFF46 inicia una transferencia desde 0xFE00 a OAM.

Transferencias Manuales de Memoria: Además de DMA, los programas pueden usar instrucciones de copia manual como LDI (Load Increment) o LDD (Load Decrement) para mover datos entre áreas de memoria. Estas instrucciones copian un byte desde la dirección apuntada por HL a la dirección apuntada por DE, incrementando o decrementando ambos punteros.

El Problema del Eslabón Perdido: El Step 0244 confirmó que:

  • El juego escribe 0xFD en HRAM (0xFF8D).
  • El juego busca 0xFD en WRAM (0xCxxx).
  • El marcador nunca aparece en WRAM, causando un bucle infinito.

Esto sugiere que hay una transferencia de datos que debería ocurrir entre la escritura en HRAM y la búsqueda en WRAM, pero que no está funcionando. Las posibilidades son:

  • Opción A: El juego intenta usar DMA para copiar datos, pero nuestra implementación de DMA no está funcionando o no está copiando a la dirección correcta.
  • Opción B: El juego usa una rutina de copia manual (LDI/LDD) que lee desde HRAM y escribe en WRAM, pero la lectura o escritura falla silenciosamente.
  • Opción C: El juego escribió en HRAM pero nunca intentó copiar los datos (problema anterior en la lógica de inicialización).

El interceptor de transferencia instrumenta dos puntos críticos:

  • Registro DMA (0xFF46): Detecta si el juego intenta activar una transferencia DMA.
  • HRAM (0xFF8D): Detecta si alguien intenta leer el marcador desde HRAM (lo cual sería necesario para copiarlo a WRAM).

Fuente: Pan Docs - "DMA Transfer", "OAM DMA", "High RAM (HRAM)", "Memory Map"

Implementación

Se añaden dos bloques de instrumentación en la MMU: uno en MMU::read para detectar lecturas en HRAM (0xFF8D) y otro en MMU::write para detectar escrituras en el registro DMA (0xFF46). Además, se crea un script de análisis automático para procesar los logs y generar un resumen estructurado.

Componentes modificados

  • src/core/cpp/MMU.cpp: Añadidos bloques de instrumentación en MMU::read (HRAM) y MMU::write (DMA).
  • tools/analizar_dma_0245.py: Script de análisis automático para procesar logs y generar resumen.

Código añadido en MMU::read

// --- Step 0245: HRAM READ WATCHDOG ---
// El Centinela (Step 0244) confirmó que el juego escribe 0xFD en HRAM (0xFF8D),
// pero luego lo busca en WRAM. Necesitamos saber si alguien intenta leer 0xFF8D
// para copiar esos datos a WRAM. Si nadie lee 0xFF8D, nadie puede copiar el marcador.
// Fuente: Pan Docs - "High RAM (HRAM)", "Memory Map"
if (addr == 0xFF8D) {
    printf("[HRAM] ¡Lectura detectada en FF8D! PC podría estar copiando datos.\n");
}
// -----------------------------------------

Código añadido en MMU::write

// --- Step 0245: INTERCEPTOR DMA ---
// El Centinela (Step 0244) confirmó que el juego escribe 0xFD en HRAM (0xFF8D),
// pero luego lo busca en WRAM. Necesitamos saber si el juego intenta activar
// una transferencia DMA para mover datos de HRAM a WRAM.
// El registro DMA (0xFF46) se escribe con el byte alto de la dirección fuente
// (ej: escribir 0xFE inicia una transferencia desde 0xFE00).
// Fuente: Pan Docs - "DMA Transfer", "OAM DMA"
if (addr == 0xFF46) {
    printf("[DMA] ¡Escritura en Registro DMA (FF46)! Valor: %02X (Source: %04X00)\n", value, value);
}
// -----------------------------------------

Script de Análisis Automático

Se crea el script tools/analizar_dma_0245.py que:

  • Analiza los logs del emulador buscando eventos DMA y HRAM.
  • Genera estadísticas de eventos (total, distribución, correlación).
  • Proporciona hipótesis y conclusiones basadas en los hallazgos.

Uso del script:

# 1. Ejecutar el emulador y redirigir la salida a un log
python main.py roms/tetris.gb > dma_check.log 2>&1

# 2. Analizar el log y generar el resumen
python tools/analizar_dma_0245.py dma_check.log > RESUMEN_DMA_0245.txt

Decisiones de diseño

  • Instrumentación selectiva: Solo se instrumentan las direcciones críticas (0xFF46 y 0xFF8D) para minimizar el overhead y mantener la velocidad del emulador.
  • Formato de mensajes: Los mensajes usan prefijos [DMA] y [HRAM] para facilitar su búsqueda en los logs.
  • Análisis automático: El script de análisis procesa los logs de forma estructurada, permitiendo identificar patrones y correlaciones sin revisar manualmente miles de líneas.

Archivos Afectados

  • src/core/cpp/MMU.cpp - Añadidos bloques de instrumentación en MMU::read (HRAM) y MMU::write (DMA).
  • tools/analizar_dma_0245.py - Script de análisis automático para procesar logs y generar resumen.
  • docs/bitacora/entries/2025-12-22__0245__interceptor-dma-hram.html - Entrada de bitácora.
  • docs/bitacora/index.html - Actualizado con nueva entrada.
  • INFORME_FASE_2.md - Actualizado con Step 0245.

Tests y Verificación

La verificación se realiza mediante ejecución del emulador y análisis de logs:

  1. Recompilación: Recompilar la extensión C++ con la nueva instrumentación:
    python setup.py build_ext --inplace
  2. Ejecución con logging: Ejecutar el emulador durante 10 segundos y redirigir la salida:
    python main.py roms/tetris.gb > dma_check.log 2>&1
  3. Análisis automático: Procesar el log con el script de análisis:
    python tools/analizar_dma_0245.py dma_check.log > RESUMEN_DMA_0245.txt
  4. Interpretación de resultados:
    • Si se detectan eventos DMA: El juego SÍ intenta usar DMA, pero la transferencia falla.
    • Si se detectan lecturas HRAM: El juego SÍ intenta leer el marcador, pero la copia falla.
    • Si NO se detecta nada: El juego escribió en HRAM pero nunca intentó transferir los datos.

Validación de módulo compilado C++: La instrumentación está implementada directamente en C++, por lo que requiere recompilación de la extensión. Los mensajes de log se generan en tiempo de ejecución y se capturan en la salida estándar.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • DMA en Game Boy: El registro DMA (0xFF46) permite copiar 160 bytes desde cualquier dirección a OAM. Sin embargo, el hardware real solo copia a OAM, no a otras áreas de memoria. Si el juego intenta usar DMA para copiar de HRAM a WRAM, esto no funcionará en hardware real (a menos que haya una rutina de copia manual que lea desde OAM después de la transferencia DMA).
  • Transferencias Manuales: Las instrucciones LDI y LDD permiten copiar datos byte a byte entre áreas de memoria. Estas instrucciones son más lentas que DMA pero más flexibles.
  • El Eslabón Perdido: Si el juego escribe en HRAM y busca en WRAM, debe haber una transferencia intermedia. Si no detectamos DMA ni lecturas HRAM, es posible que el juego use una rutina de copia que no estamos instrumentando, o que haya un problema anterior que impide que el juego llegue a la rutina de copia.

Lo que Falta Confirmar

  • ¿El juego usa DMA?: Necesitamos ejecutar el emulador y analizar los logs para confirmar si el juego intenta activar DMA.
  • ¿El juego lee HRAM?: Necesitamos confirmar si alguien intenta leer 0xFF8D para copiar el marcador.
  • ¿Hay una rutina de copia manual?: Si no detectamos DMA ni lecturas HRAM, es posible que el juego use una rutina de copia que no estamos instrumentando (por ejemplo, una rutina que lee desde una dirección diferente o que usa un método de copia que no detectamos).

Hipótesis y Suposiciones

Suposición principal: Asumimos que si el juego intenta transferir datos de HRAM a WRAM, lo hará mediante DMA o mediante una rutina de copia manual que lea desde HRAM. Sin embargo, es posible que el juego use un método diferente (por ejemplo, una rutina que lee desde una dirección diferente o que usa un método de copia que no detectamos).

Limitación de la instrumentación: Solo instrumentamos dos puntos críticos (DMA y HRAM). Si el juego usa una rutina de copia que no pasa por estos puntos, no la detectaremos. En ese caso, necesitaríamos instrumentar más áreas o usar un enfoque diferente (por ejemplo, un rastreador de escrituras en WRAM que detecte cuando se escribe 0xFD en WRAM).

Próximos Pasos

  • [ ] Recompilar la extensión C++: python setup.py build_ext --inplace
  • [ ] Ejecutar el emulador durante 10 segundos: python main.py roms/tetris.gb > dma_check.log 2>&1
  • [ ] Analizar el log: python tools/analizar_dma_0245.py dma_check.log > RESUMEN_DMA_0245.txt
  • [ ] Si se detectan eventos DMA: Investigar por qué la transferencia DMA falla (verificar implementación de DMA, dirección de destino, etc.).
  • [ ] Si se detectan lecturas HRAM: Investigar por qué la copia manual falla (verificar instrucciones LDI/LDD, redirección de Echo RAM, etc.).
  • [ ] Si NO se detecta nada: Instrumentar más áreas (por ejemplo, rastreador de escrituras en WRAM) o investigar la lógica de inicialización del juego para encontrar dónde se supone que debería ocurrir la transferencia.