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

Diagnóstico: Bucle de Espera de V-Blank y Escrituras en VRAM

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

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
  • 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 emulador
  • src/memory/mmu.py - Añadido método get_vram_write_count() y mensaje informativo de diagnóstico activo
  • src/gpu/ppu.py - Activado logging temporal cuando se genera V-Blank
  • main.py - Configurado encoding UTF-8 para Windows y nivel de logging a INFO
  • docs/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
También se debe verificar si la PPU llega a V-Blank después de esperar más tiempo (20-30 segundos) para confirmar si el problema es solo de velocidad o si hay un problema más fundamental.

Fuentes Consultadas

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.