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: Bucle de Espera de V-Blank y Escrituras en VRAM
Resumen
Tras confirmar que el juego no está escribiendo en VRAM (VRAM writes=0), se añadió un sistema de diagnóstico periódico que muestra el estado completo del emulador cada 5 segundos. El diagnóstico reveló que el juego está en un bucle pequeño (PC oscila entre 0x006B, 0x006D, 0x006F) ejecutando muy pocas instrucciones (~60 por segundo), lo que impide que la PPU avance lo suficiente para llegar a V-Blank (LY=144). El juego está esperando V-Blank antes de copiar gráficos a VRAM, pero nunca llega porque la PPU avanza demasiado lento.
Concepto de Hardware
En la Game Boy, la PPU (Pixel Processing Unit) avanza sincronizada con la CPU. Cada instrucción de la CPU consume ciclos de reloj (M-Cycles), que se convierten a T-Cycles (ciclos de reloj del sistema) para avanzar la PPU. La PPU necesita 456 T-Cycles para completar una línea de escaneo, y 144 líneas visibles + 10 líneas de V-Blank = 154 líneas por frame completo.
Dependencia crítica: Si la CPU ejecuta muy pocas instrucciones, la PPU avanza muy lento. Si la PPU no llega a V-Blank (LY=144), el juego que está esperando V-Blank nunca sale del bucle de espera. Esto crea un círculo vicioso: el juego espera V-Blank, pero V-Blank no ocurre porque el juego ejecuta muy pocas instrucciones.
Polling de STAT: Muchos juegos hacen polling del registro STAT (0xFF41) esperando que la PPU entre en modo V-Blank (bit 0-1 = 01) antes de copiar gráficos a VRAM. Si la PPU nunca llega a V-Blank, el juego queda atrapado en el bucle de espera.
Fuente: Pan Docs - LCD Timing, V-Blank, STAT Register, System Clock
Implementación
Se añadió un sistema de diagnóstico periódico en el bucle principal de ejecución que muestra información completa del estado del emulador cada 5 segundos, independientemente de si hay frames listos o no. Esto permite diagnosticar problemas de timing y sincronización.
Componentes modificados
- Viboy (src/viboy.py):
- Añadido contador de instrucciones ejecutadas
- Añadido diagnóstico periódico cada 5 segundos que muestra:
- Número de instrucciones ejecutadas
- Número de escrituras en VRAM detectadas
- PC (Program Counter) actual
- Estado de HALT de la CPU
- LCDC (LCD Control Register)
- STAT (LCD Status Register)
- LY (Línea actual de la PPU)
- IF (Interrupt Flag Register)
- IE (Interrupt Enable Register)
- Modificado el heartbeat para incluir el contador de escrituras en VRAM
- MMU (src/memory/mmu.py):
- Añadido método
get_vram_write_count()para obtener el contador de escrituras en VRAM - Añadido mensaje informativo al inicializar indicando que el diagnóstico VRAM está activo
- Añadido método
- PPU (src/gpu/ppu.py):
- Activado logging temporal cuando se genera V-Blank (LY=144) para diagnóstico
- main.py:
- Configurado encoding UTF-8 para Windows para permitir mostrar emojis en consola
- Cambiado nivel de logging a INFO temporalmente para diagnóstico
Decisiones de diseño
- Diagnóstico periódico independiente de frames: El diagnóstico se ejecuta cada 5 segundos basado en tiempo real, no en frames. Esto permite diagnosticar problemas incluso cuando no hay frames listos o cuando el juego está congelado.
- Información completa del estado: El diagnóstico muestra todos los registros críticos (PC, LCDC, STAT, LY, IF, IE) para entender completamente qué está haciendo el emulador en cada momento.
- Logging de V-Blank: Se activó temporalmente el logging cuando la PPU genera V-Blank para verificar si la PPU está avanzando correctamente.
Archivos Afectados
src/viboy.py- Añadido diagnóstico periódico cada 5 segundos con información completa del estado del emuladorsrc/memory/mmu.py- Añadido métodoget_vram_write_count()y mensaje informativo de diagnóstico activosrc/gpu/ppu.py- Activado logging temporal cuando se genera V-Blankmain.py- Configurado encoding UTF-8 para Windows y nivel de logging a INFOdocs/bitacora/entries/2025-12-18__0062__diagnostico-bucle-espera-vblank.html(nuevo)docs/bitacora/index.html(modificado, añadida entrada 0062)docs/bitacora/entries/2025-12-18__0061__desbloqueo-vram-diagnostico-escrituras.html(modificado, actualizado enlace "Siguiente")INFORME_COMPLETO.md(modificado, añadida entrada 0062)
Tests y Verificación
Estado: En diagnóstico
Comando ejecutado:
python main.py pkmn.gb
Entorno: Windows 10, Python 3.13.5
ROM de prueba: Pokémon Red (ROM aportada por el usuario, no distribuida)
Resultado observado:
- Diagnóstico periódico muestra:
INFO: 📊 Diagnóstico: 304 instrucciones, VRAM writes=0, PC=0x006F, HALTED=False, LCDC=0x80, STAT=0x00, LY=6, IF=0x00, IE=0x00 INFO: 📊 Diagnóstico: 605 instrucciones, VRAM writes=0, PC=0x006B, HALTED=False, LCDC=0x80, STAT=0x00, LY=13, IF=0x00, IE=0x00 INFO: 📊 Diagnóstico: 906 instrucciones, VRAM writes=0, PC=0x006D, HALTED=False, LCDC=0x80, STAT=0x00, LY=20, IF=0x00, IE=0x00 INFO: 📊 Diagnóstico: 1,208 instrucciones, VRAM writes=0, PC=0x006B, HALTED=False, LCDC=0x80, STAT=0x00, LY=27, IF=0x00, IE=0x00
Interpretación del resultado:
- El juego SÍ está ejecutando código: El PC cambia entre 0x006B, 0x006D, 0x006F, lo que indica que está en un bucle pequeño pero activo.
- El juego ejecuta muy pocas instrucciones: Solo ~60 instrucciones por segundo (304 instrucciones en 5 segundos). Una Game Boy real ejecuta millones de instrucciones por segundo, así que esto es extremadamente lento.
- La PPU avanza muy lento: LY solo llega a 6, 13, 20, 27... en 5 segundos. Para llegar a V-Blank (LY=144) necesitaría mucho más tiempo.
- El juego NO está escribiendo en VRAM: VRAM writes=0 en todos los diagnósticos.
- El juego está esperando V-Blank: El PC oscila en un rango muy bajo (0x006B-0x006F), lo que sugiere un bucle de espera. Probablemente está haciendo polling de STAT esperando que la PPU entre en modo V-Blank.
- No hay interrupciones habilitadas: IE=0x00 (no hay interrupciones habilitadas) e IF=0x00 (no hay interrupciones pendientes).
- LCDC=0x80: El LCD está encendido (bit 7=1), pero el BG Display está apagado (bit 0=0).
- STAT=0x00: La PPU está en modo 0 (H-Blank), no en V-Blank.
Qué valida: Este diagnóstico confirma que:
- ✅ El juego está ejecutando código (no está congelado completamente)
- ✅ El juego NO está escribiendo en VRAM (VRAM writes=0)
- ⚠️ El juego ejecuta muy pocas instrucciones (~60 por segundo, extremadamente lento)
- ⚠️ La PPU avanza muy lento (LY solo llega a 27 en 5 segundos, necesita llegar a 144 para V-Blank)
- ⚠️ El juego está en un bucle esperando V-Blank que nunca ocurre porque la PPU avanza demasiado lento
Próximo paso: Investigar por qué el juego ejecuta tan pocas instrucciones. Posibles causas:
- El juego está en un bucle muy lento (muchas instrucciones por iteración)
- Hay un problema con el timing que hace que el emulador vaya muy lento
- El juego está esperando algún evento que nunca ocurre
- Hay un problema con la sincronización de la PPU que hace que avance muy lento
Fuentes Consultadas
- Pan Docs: LCD Timing
- Pan Docs: LCD Status Register (STAT)
- Pan Docs: V-Blank
- Pan Docs: System Clock
Integridad Educativa
Lo que Entiendo Ahora
- Dependencia crítica entre CPU y PPU: La PPU avanza sincronizada con la CPU. Si la CPU ejecuta muy pocas instrucciones, la PPU avanza muy lento. Si la PPU no llega a V-Blank, el juego que está esperando V-Blank nunca sale del bucle de espera. Esto crea un círculo vicioso.
- Polling de STAT: Muchos juegos hacen polling del registro STAT esperando que la PPU entre en modo V-Blank antes de copiar gráficos a VRAM. Si la PPU nunca llega a V-Blank, el juego queda atrapado en el bucle de espera.
- Diagnóstico sistemático: Añadir información completa del estado del emulador (PC, LCDC, STAT, LY, IF, IE) permite diagnosticar problemas de timing y sincronización de forma sistemática. Sin esta información, es imposible entender por qué el juego no avanza.
- Velocidad de ejecución: Una Game Boy real ejecuta millones de instrucciones por segundo. Si el emulador ejecuta solo ~60 instrucciones por segundo, hay un problema grave de timing o sincronización.
Lo que Falta Confirmar
- ¿Por qué el juego ejecuta tan pocas instrucciones? Necesito investigar si
el problema es:
- Un bucle muy lento en el código del juego
- Un problema con el timing del emulador
- Un problema con la sincronización de la PPU
- El juego está esperando algún evento que nunca ocurre
- ¿La PPU llega a V-Blank si esperamos más tiempo? Si después de 20-30 segundos la PPU llega a LY=144 y se genera V-Blank, entonces el problema es solo de velocidad. Si nunca llega, hay un problema más fundamental con la sincronización.
- ¿El juego sale del bucle cuando ocurre V-Blank? Si la PPU llega a V-Blank y el juego sale del bucle, entonces el problema está resuelto. Si no sale, hay otro problema (quizás el juego está esperando algo más además de V-Blank).
Hipótesis y Suposiciones
Hipótesis principal: El juego está en un bucle esperando V-Blank, pero la PPU avanza demasiado lento porque el juego ejecuta muy pocas instrucciones. Esto crea un círculo vicioso: el juego espera V-Blank, pero V-Blank no ocurre porque el juego ejecuta muy pocas instrucciones.
Suposición: Asumo que el problema es de velocidad/timing, no un bug fundamental en la implementación. Si el juego ejecutara a velocidad normal, la PPU avanzaría normalmente y llegaría a V-Blank, permitiendo que el juego salga del bucle y copie gráficos a VRAM.
Próximos Pasos
- [ ] Ejecutar el emulador y esperar 20-30 segundos para verificar si la PPU llega a V-Blank (LY=144) y si se genera la interrupción V-Blank (mensaje "🎯 PPU: V-Blank iniciado").
- [ ] Si la PPU llega a V-Blank pero el juego no sale del bucle, investigar qué más está esperando el juego (quizás está esperando que se habilite IE o que se procese la interrupción).
- [ ] Si la PPU nunca llega a V-Blank, investigar por qué el juego ejecuta tan pocas instrucciones:
- Verificar si hay un problema con el timing del emulador
- Verificar si hay un problema con la sincronización de la PPU
- Verificar si el juego está en un bucle muy lento
- Verificar si el juego está esperando algún evento que nunca ocurre
- [ ] Una vez resuelto el problema de velocidad/timing, verificar si el juego empieza a escribir en VRAM después de salir del bucle de espera.