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.
Diagnóstico DMA y OAM: La Estrella Perdida
Resumen
El emulador muestra el logo "GAME FREAK" estático en Pokémon Red, pero falta la animación de la estrella fugaz (Sprite) que debe bajar y transformar el texto. Si la estrella no aparece, la animación no empieza y el juego no pasa a la siguiente pantalla.
Se añadió instrumentación de diagnóstico para monitorear el DMA (Direct Memory Access) que copia datos de sprites a la OAM (Object Attribute Memory) y un heartbeat que muestra el estado de la OAM cada segundo. El objetivo es determinar si el problema está en la transferencia DMA, en la OAM corrupta/vacía, o en el renderizado de sprites.
Concepto de Hardware
La Game Boy usa DMA (Direct Memory Access) para transferir datos de sprites desde cualquier región de memoria (ROM, RAM, VRAM) a la OAM (Object Attribute Memory), que es una región especial de 160 bytes (0xFE00-0xFE9F) que almacena los atributos de hasta 40 sprites.
Cada sprite ocupa 4 bytes en OAM:
- Byte 0 (Y): Posición Y del sprite (0-255, pero típicamente 0-159)
- Byte 1 (X): Posición X del sprite (0-255, pero típicamente 0-167)
- Byte 2 (Tile): Índice del tile en VRAM (0-255)
- Byte 3 (Flags): Atributos (prioridad, flip X/Y, paleta, etc.)
Para iniciar una transferencia DMA, el juego escribe un valor XX en el registro 0xFF46. Esto copia inmediatamente 160 bytes desde la dirección XX00 hasta 0xFE00 (OAM). La transferencia es bloqueante y tarda aproximadamente 160 ciclos de máquina.
Si el DMA no funciona correctamente, los sprites no se copian a OAM y no aparecen en pantalla. Si la OAM está vacía (todo ceros), el renderizador no tiene datos de sprites para dibujar.
Fuente: Pan Docs - DMA Transfer, OAM (Object Attribute Memory)
Implementación
Se añadió logging detallado del DMA en src/memory/mmu.py y un heartbeat de diagnóstico de OAM en src/viboy.py.
Componentes modificados
- src/memory/mmu.py: Añadido logging detallado del DMA que muestra la dirección fuente, el primer byte de la fuente, y una muestra de los primeros 4 bytes de OAM después de la transferencia.
- src/viboy.py: Añadido heartbeat de tiempo real (cada segundo) que muestra el estado de la OAM: checksum, número de bytes no-cero, y atributos del primer sprite.
Logging de DMA
Cuando se escribe en el registro DMA (0xFF46), el sistema ahora registra:
- Dirección fuente (XX00) y el primer byte de la fuente (para verificar que hay datos)
- Dirección destino (0xFE00, inicio de OAM)
- Después de la transferencia, muestra los primeros 4 bytes de OAM decodificados como atributos de sprite (Y, X, Tile, Flags)
Esto permite detectar si:
- El juego intenta lanzar DMA pero la fuente está vacía (primer byte = 0x00 o 0xFF)
- El DMA se ejecuta pero los datos no llegan a OAM (OAM sigue vacía después de DMA)
- El DMA funciona correctamente pero los datos son incorrectos (atributos de sprite inválidos)
Heartbeat de OAM
Cada segundo, el sistema muestra:
- Checksum: Suma de los primeros 16 bytes de OAM (4 sprites). Si es 0, OAM está vacía.
- Non-zero bytes: Número de bytes no-cero en los primeros 16 bytes. Si es 0, no hay sprites.
- Primer sprite: Atributos decodificados del primer sprite (Y, X, Tile, Flags)
Esto permite detectar si:
- OAM está vacía (checksum = 0, non-zero = 0) → DMA no funciona o no se ejecuta
- OAM tiene datos pero el sprite está fuera de pantalla (Y > 159 o X > 167) → problema de coordenadas
- OAM tiene datos válidos → problema en el renderizador de sprites
Archivos Afectados
src/memory/mmu.py- Añadido logging detallado del DMA con validación de fuente y muestra de OAM después de transferenciasrc/viboy.py- Añadido heartbeat de tiempo real (cada segundo) que muestra el estado de la OAM
Tests y Verificación
Esta es una modificación de diagnóstico, no una implementación funcional nueva. No se añadieron tests unitarios, pero se validará mediante ejecución de ROMs.
Próxima validación: Ejecutar python main.py pkmn.gb y observar los logs para determinar:
- Si aparece el mensaje "💾 DMA START" → El juego intenta lanzar DMA
- Si "OAM SAMPLE" muestra checksum > 0 → Los sprites están en OAM
- Si "OAM SAMPLE" muestra checksum = 0 → OAM está vacía, DMA no funciona
- Si no aparece "DMA START" → El juego no intenta lanzar DMA (problema de lógica/CPU)
Estado: Pendiente de ejecución y análisis de logs.
Fuentes Consultadas
- Pan Docs: OAM (Object Attribute Memory)
- Pan Docs: DMA Transfer
Integridad Educativa
Lo que Entiendo Ahora
- DMA Transfer: El registro 0xFF46 permite copiar 160 bytes desde cualquier dirección XX00 a OAM (0xFE00). La transferencia es inmediata y bloqueante.
- OAM Structure: Cada sprite ocupa 4 bytes: Y, X, Tile, Flags. OAM puede almacenar hasta 40 sprites (160 bytes total).
- Diagnóstico: Si OAM está vacía, los sprites no se dibujan. Si DMA no se ejecuta, OAM permanece vacía.
Lo que Falta Confirmar
- Timing del DMA: ¿El DMA debe bloquear el acceso a OAM durante la transferencia? Actualmente no implementamos restricciones de acceso.
- Fuente del DMA: ¿Desde qué región de memoria copia Pokémon Red los sprites? (probablemente WRAM o HRAM)
- Renderizado de Sprites: Si OAM tiene datos válidos pero no se dibujan, el problema está en el renderizador (prioridad, paleta, modo 8x16, etc.)
Hipótesis y Suposiciones
Hipótesis principal: El DMA no está copiando los datos correctamente, o el juego no está intentando lanzar DMA. Si OAM está vacía, la estrella no existe y no se puede dibujar.
Suposición: Asumimos que el DMA se ejecuta inmediatamente cuando se escribe en 0xFF46, sin restricciones de timing. En hardware real, el DMA puede tener restricciones de acceso durante ciertos modos de PPU, pero por ahora no las implementamos.
Próximos Pasos
- [ ] Ejecutar
python main.py pkmn.gby analizar los logs de DMA y OAM - [ ] Si DMA no aparece: investigar por qué el juego no intenta lanzar DMA (problema de CPU/lógica)
- [ ] Si DMA aparece pero OAM está vacía: investigar por qué la transferencia no funciona
- [ ] Si OAM tiene datos pero no se dibujan: investigar el renderizador de sprites (prioridad, paleta, modo 8x16)
- [ ] Si OAM tiene datos válidos y se dibujan: el problema está resuelto, continuar con la siguiente animación