⚠️ 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 Visual: LCD Apagado

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

Resumen

Se instrumentó el renderer con un diagnóstico visual para distinguir entre dos escenarios críticos: (1) LCD apagado (LCDC bit 7 = 0) y (2) LCD encendido pero dibujando blanco (problema de paleta/VRAM). El diagnóstico cambia el color de fondo cuando el LCD está apagado de blanco a azul oscuro para facilitar la identificación visual del problema. El resultado del test confirma que el emulador muestra pantalla azul, lo que significa que el LCD está permanentemente apagado y el juego no ha llegado al punto de encenderlo. Esto indica un problema de lógica/CPU (probablemente interrupciones o timer) que impide que el juego avance más allá de la inicialización.

Concepto de Hardware

El registro LCDC (LCD Control, 0xFF40) controla el estado del LCD de la Game Boy. El bit 7 es el "LCD Enable": cuando está en 0, el LCD está apagado y la pantalla muestra blanco (o en nuestro caso, azul para diagnóstico). Cuando está en 1, el LCD está encendido y la PPU puede renderizar gráficos.

Durante la inicialización de un juego, típicamente ocurre lo siguiente:

  1. El juego desactiva interrupciones (DI) y limpia memoria
  2. El juego carga tiles y configura el tilemap en VRAM
  3. El juego configura la paleta BGP (Background Palette)
  4. El juego activa el LCD escribiendo LCDC con bit 7 = 1
  5. El juego espera V-Blank o hace polling de STAT para sincronizar

Si el juego nunca activa el LCD (LCDC bit 7 permanece en 0), significa que el juego está atascado en algún punto anterior de la inicialización. Las causas más comunes son:

  • Interrupciones no funcionan: El juego espera V-Blank pero la interrupción nunca se dispara
  • Timer no funciona: El juego espera un intervalo de tiempo pero el timer no avanza
  • Polling de registros falla: El juego lee LY o STAT pero siempre devuelve el mismo valor
  • Bucle infinito: El juego entra en un bucle de espera del que nunca sale

Fuente: Pan Docs - LCD Control Register (LCDC), LCD Timing

Implementación

Se modificó el método render_frame() en src/gpu/renderer.py para añadir diagnóstico visual:

Cambio de Color de Fondo

Cuando el LCD está apagado (LCDC bit 7 = 0), el renderer ahora rellena el buffer con azul oscuro (0, 0, 128) en lugar de blanco. Esto permite distinguir visualmente entre:

  • Pantalla AZUL: LCD apagado → Problema de lógica/CPU
  • Pantalla BLANCA: LCD encendido pero dibujando blanco → Problema de paleta/VRAM

Log de Diagnóstico

Se añadió un log de DEBUG crítico justo antes de renderizar el Background cuando el LCD está encendido:

logger.debug(
    f"RENDER: LCD ON | BGP={self.mmu.read_byte(IO_BGP):02X} | "
    f"SCX={self.mmu.read_byte(IO_SCX)} | "
    f"VRAM_CHECK={self.mmu.read_byte(0x8000):02X}"
)

Este log muestra el estado de la paleta (BGP), el scroll horizontal (SCX) y el primer byte de VRAM para diagnóstico rápido.

Log cuando LCD está Apagado

Cuando el LCD está apagado, se registra:

logger.debug("RENDER: LCD OFF (Pantalla Azul) - LCDC bit 7=0")

Componentes modificados

  • src/gpu/renderer.py: Modificado método render_frame() para diagnóstico visual

Archivos Afectados

  • src/gpu/renderer.py - Modificado método render_frame() para diagnóstico visual (color azul cuando LCD apagado, logs de DEBUG)

Tests y Verificación

Modo de ejecución: Manual, ejecutando el emulador con ROMs reales

ROM probada: tetris_dx.gbc (ROM aportada por el usuario, no distribuida)

Comando ejecutado: python main.py tetris_dx.gbc

Entorno: Windows 10, Python 3.10+

Criterio de éxito: Distinguir visualmente si el problema es LCD apagado (pantalla azul) o LCD encendido dibujando blanco (pantalla blanca)

Observación: La pantalla se muestra completamente azul oscuro con el cuadrado rojo parpadeante (heartbeat) visible en la esquina superior izquierda. No se observa ningún cambio de color a blanco durante la ejecución. Los logs muestran repetidamente:

RENDER: LCD OFF (Pantalla Azul) - LCDC bit 7=0

Esto confirma que el registro LCDC tiene el bit 7 permanentemente en 0, lo que significa que el juego nunca activa el LCD.

Resultado: Draft - Diagnóstico completado. El problema está identificado: LCD apagado debido a que el juego no avanza más allá de la inicialización. Se requiere investigación adicional sobre por qué el juego no activa el LCD (posiblemente interrupciones, timer o polling de registros).

Notas legales: La ROM tetris_dx.gbc es aportada por el usuario para pruebas locales. No se distribuye ni se enlaza en el repositorio.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Diagnóstico visual: Cambiar el color de fondo cuando el LCD está apagado permite distinguir rápidamente entre dos problemas diferentes: LCD apagado (azul) vs LCD encendido dibujando blanco (blanco).
  • LCDC bit 7: Este bit controla si el LCD está encendido o apagado. Si el juego nunca escribe 1 en este bit, la pantalla permanecerá apagada.
  • Inicialización del juego: Los juegos típicamente activan el LCD después de cargar tiles y configurar la paleta. Si el LCD permanece apagado, significa que el juego está atascado antes de llegar a ese punto.

Lo que Falta Confirmar

  • Causa raíz del bloqueo: No está confirmado por qué el juego no activa el LCD. Las hipótesis son: (1) interrupciones no funcionan, (2) timer no funciona, (3) polling de registros falla, (4) bucle infinito. Se requiere análisis adicional del código del juego para determinar la causa exacta.
  • Estado de IME: No se ha verificado si IME (Interrupt Master Enable) está habilitado o deshabilitado durante la ejecución. Si está deshabilitado, las interrupciones no se procesarán aunque se disparen.
  • Estado de IF/IE: No se ha verificado si los flags de interrupción (IF) y el registro de habilitación de interrupciones (IE) están configurados correctamente.

Hipótesis y Suposiciones

Hipótesis principal: El juego está esperando una interrupción V-Blank o un cambio en el registro LY/STAT que nunca ocurre, causando que el juego se quede en un bucle de espera infinito. Esta hipótesis se basa en el análisis previo (Step 0042) que mostró que el juego ejecuta DI (0xF3) al inicio y nunca ejecuta EI (0xFB) para volver a habilitar interrupciones.

Suposición: Si IME está deshabilitado, el juego podría estar haciendo polling manual de IF para detectar V-Blank, pero si IF nunca se activa (porque la PPU no está generando interrupciones correctamente), el juego se quedará esperando eternamente.

Próximos Pasos

  • [ ] Verificar estado de IME durante la ejecución (¿está habilitado o deshabilitado?)
  • [ ] Verificar si IF se actualiza cuando ocurre V-Blank (independientemente de IME)
  • [ ] Verificar si el Timer está funcionando correctamente y generando interrupciones
  • [ ] Analizar el código del juego en el punto donde se queda atascado para entender qué está esperando
  • [ ] Implementar logging más detallado del estado de interrupciones (IME, IF, IE) durante la ejecución