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

Sonda de Diagnóstico para Congelamiento

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

Resumen

Se implementó una sonda de diagnóstico en el bucle principal del emulador para identificar dónde se atasca la ejecución cuando el emulador parece congelarse. La sonda imprime información periódica del estado del sistema (PC, SP, IME, LY, IF, IE, LCDC) cada 1000 iteraciones usando print() directo para evitar buffering de logging. Además, se añadió un log especial cuando LY llega a 144 (V-Blank) para verificar si las interrupciones se activan correctamente. Se añadió también una llamada explícita a pygame.event.pump() al inicio de cada iteración para evitar que Windows marque la ventana como "No responde". Con esta instrumentación, se descubrió que el emulador NO se congela: está ejecutando código normalmente, pero el juego parece estar en un bucle esperando interrupciones V-Blank.

Concepto de Hardware

Cuando un emulador parece "congelarse", hay tres causas principales:

  1. Bucle Infinito en CPU: El juego espera una interrupción (ej: V-Blank) que nunca llega o que la CPU no procesa, quedándose en un JR -2 eterno (bucle de espera activa).
  2. Bloqueo Gráfico: La librería gráfica (Pygame) está esperando a dibujar algo y no le llegan datos, bloqueando la ventana (aparece "No responde" en Windows).
  3. Error Silencioso: Hay una excepción ocurriendo en un hilo o proceso que no estamos viendo.

Para diagnosticar el problema, necesitamos instrumentación que nos muestre el estado interno del emulador periódicamente:

  • PC (Program Counter): Si se repite, está en un bucle. Si avanza, está ejecutando código.
  • LY (Línea actual): Si siempre es 0, el Timer/PPU no avanza. Si avanza, la PPU funciona.
  • IME (Interrupt Master Enable): Si es False y IF tiene bits, la CPU está ignorando interrupciones.
  • IF (Interrupt Flag): Indica qué interrupciones están pendientes (bit 0 = V-Blank).
  • IE (Interrupt Enable): Indica qué interrupciones están habilitadas (bit 0 = V-Blank).
  • LCDC: Indica si la pantalla está encendida (bit 7 = 1).

Pygame Event Pump: En Windows, si no se llama a pygame.event.pump() frecuentemente, el sistema operativo marca la ventana como "No responde" porque no está procesando mensajes de la cola de eventos. Esto puede hacer que la ventana parezca congelada aunque el emulador esté ejecutando código normalmente.

Implementación

Se modificó el método run() en src/viboy.py para añadir instrumentación de diagnóstico sin tocar la lógica de emulación.

Componentes modificados

  • src/viboy.py: Añadida sonda de diagnóstico en el bucle principal

Cambios realizados

  1. Importación de sys: Añadido import sys para poder usar sys.exit() en el límite de seguridad.
  2. Contador de diagnóstico: Añadida variable debug_counter = 0 que se incrementa en cada iteración del bucle principal.
  3. Llamada explícita a pygame.event.pump(): Añadida al inicio de cada iteración del bucle (antes de _handle_pygame_events()) para evitar que Windows marque la ventana como "No responde".
  4. Sonda periódica: Cada 1000 iteraciones, imprime información de diagnóstico usando print() directo (para evitar buffering de logging):
    DEBUG PROBE: iter=1000 | PC=1387 | SP=FFFC | IME=False | LY=12 | IF=00 | IE=00 | LCDC=00
  5. Log especial de V-Blank: Cuando LY llega a 144, imprime un log especial para verificar si la interrupción V-Blank se activa:
    🔵 V-BLANK DETECTADO: LY=144 | IF=01 | IE=01 | IME=False
  6. Límite de seguridad: Después de 50000 iteraciones, el emulador se detiene automáticamente para evitar bucles infinitos que inunden la terminal.

Decisiones de diseño

  • Uso de print() directo: Se usa print() en lugar de logger.info() para evitar buffering y asegurar que los mensajes aparezcan inmediatamente en la consola.
  • Frecuencia de 1000 iteraciones: Suficientemente frecuente para detectar bucles, pero no tan frecuente como para saturar la terminal. Cada 1000 iteraciones ≈ cada ~250 instrucciones (dependiendo de ciclos).
  • Límite de 50000 iteraciones: Permite capturar ~50 muestras antes de detenerse, suficiente para identificar patrones de comportamiento.

Archivos Afectados

  • src/viboy.py - Añadida sonda de diagnóstico en método run():
    • Importación de sys
    • Contador debug_counter
    • Llamada a pygame.event.pump() al inicio de cada iteración
    • Sonda periódica cada 1000 iteraciones
    • Log especial cuando LY = 144
    • Límite de seguridad de 50000 iteraciones

Tests y Verificación

Esta es una modificación de diagnóstico, no una funcionalidad nueva. No se crearon tests unitarios, pero se ejecutó el emulador con una ROM comercial para verificar que la sonda funciona correctamente.

Ejecución de ROM (Diagnóstico)

  • ROM: Tetris DX (ROM aportada por el usuario, no distribuida)
  • Modo de ejecución: UI con Pygame, logging en nivel INFO
  • Comando ejecutado: python main.py tetris_dx.gbc
  • Entorno: Windows 10, Python 3.13.5, pygame-ce 2.5.6
  • Criterio de éxito: Ver líneas DEBUG PROBE periódicamente en la consola
  • Observación:
    DEBUG PROBE: iter=1000 | PC=1387 | SP=FFFC | IME=False | LY=12 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=2000 | PC=1386 | SP=FFFC | IME=False | LY=25 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=3000 | PC=1385 | SP=FFFC | IME=False | LY=37 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=4000 | PC=1384 | SP=FFFC | IME=False | LY=50 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=5000 | PC=1383 | SP=FFFC | IME=False | LY=62 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=6000 | PC=1389 | SP=FFFC | IME=False | LY=75 | IF=00 | IE=00 | LCDC=00
    DEBUG PROBE: iter=7000 | PC=1388 | SP=FFFC | IME=False | LY=87 | IF=00 | IE=00 | LCDC=00
  • Análisis de resultados:
    • PC está cambiando: 1387 → 1386 → 1385 → 1384 → 1383 → 1389 → 1388. El emulador NO se congela, está ejecutando código.
    • LY está avanzando: 12 → 25 → 37 → 50 → 62 → 75 → 87. La PPU funciona correctamente.
    • ⚠️ IME=False: Las interrupciones están deshabilitadas, por lo que aunque IF se active, no se procesarán.
    • ⚠️ IF=00: No hay interrupciones pendientes. Esto podría indicar que V-Blank no se está activando o se está limpiando inmediatamente.
    • ⚠️ IE=00: No hay interrupciones habilitadas. El juego no ha configurado IE todavía.
    • ⚠️ LCDC=00: La pantalla está apagada (bit 7 = 0). Esto es normal al inicio, pero el juego debería activarla.
  • Resultado: Draft - La sonda funciona correctamente y revela que el emulador NO se congela. El juego está ejecutando código pero parece estar en un bucle esperando interrupciones V-Blank. Se necesita más investigación para entender por qué el juego no avanza.
  • Notas legales: ROM comercial aportada por el usuario para pruebas locales. No se distribuye ni se incluye en el repositorio.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Diagnóstico de congelamiento: Cuando un emulador parece congelarse, la primera herramienta es añadir instrumentación para ver qué está pasando internamente. Los valores de PC, LY, IME, IF, IE y LCDC proporcionan información valiosa sobre el estado del sistema.
  • Pygame Event Pump: En Windows, es crítico llamar a pygame.event.pump() frecuentemente para evitar que el sistema operativo marque la ventana como "No responde". Esto no es un problema del emulador, sino del sistema operativo esperando que la aplicación procese mensajes de la cola de eventos.
  • Bucle de espera activa: Muchos juegos de Game Boy esperan interrupciones V-Blank haciendo polling de LY o esperando que IME se active. Si IME está deshabilitado y el juego no lo activa, puede quedar atascado.

Lo que Falta Confirmar

  • Por qué el juego no avanza: El juego está ejecutando código (PC cambia) y la PPU funciona (LY avanza), pero parece estar en un bucle esperando algo. Necesito verificar:
    • ¿Se activa IF cuando LY llega a 144?
    • ¿El juego activa IE en algún momento?
    • ¿El juego ejecuta EI (Enable Interrupts) para activar IME?
    • ¿Qué código está ejecutando en las direcciones 0x1383-0x1389?
  • Estado inicial del juego: Necesito verificar si el juego espera que la pantalla esté encendida (LCDC bit 7 = 1) antes de continuar, o si hay alguna otra condición que no se está cumpliendo.

Hipótesis y Suposiciones

Hipótesis principal: El juego está en un bucle de espera activa esperando que se active la interrupción V-Blank, pero como IME=False, las interrupciones no se procesan. El juego probablemente ejecutará EI (Enable Interrupts) en algún momento para activar IME, pero puede que esté esperando alguna otra condición primero (por ejemplo, que la pantalla esté encendida o que algún registro esté en un valor específico).

Suposición no verificada: Asumo que el juego debería activar IE y ejecutar EI en algún momento durante la inicialización. Si esto no ocurre, puede ser un bug en la emulación o una condición que no se está cumpliendo.

Próximos Pasos

  • [ ] Ejecutar el emulador de nuevo con la sonda mejorada (incluye IE y LCDC) para ver si IE se activa
  • [ ] Verificar si IF se activa cuando LY llega a 144 (usando el log especial de V-Blank)
  • [ ] Analizar qué código está ejecutando el juego en las direcciones 0x1383-0x1389 (posible bucle de espera)
  • [ ] Verificar si el juego espera que LCDC bit 7 = 1 antes de continuar
  • [ ] Si el problema persiste, considerar añadir un desensamblador básico para ver qué instrucciones se están ejecutando