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

Limpieza de Build y Verificación de Renderizado

Fecha: 2025-12-19 Step ID: 0120 Estado: Verified

Resumen

El usuario reportó que seguía viendo la pantalla gris con un punto rojo del paso 116, a pesar de haber aplicado los cambios del paso 119. El diagnóstico indicó que el módulo C++ no se había recompilado correctamente y Python estaba cargando una versión obsoleta del binario `viboy_core`. Se implementó una limpieza completa del build (eliminación de archivos compilados y caché) y se añadió diagnóstico de LY en el bucle principal para verificar que la PPU C++ está funcionando correctamente.

Concepto de Hardware

Problema de Caché de Compilación en Cython/C++: Cuando se modifica código C++ (`.cpp`/`.hpp`) o Cython (`.pyx`), Python puede seguir cargando el binario antiguo (`.pyd` en Windows, `.so` en Linux/Mac) si no se fuerza una recompilación completa. Esto ocurre porque:

  • Caché de Python: Python puede mantener el módulo compilado en memoria incluso después de cerrar el proceso
  • Archivos bloqueados: En Windows, los archivos `.pyd` pueden quedar bloqueados si hay procesos de Python ejecutándose
  • Dependencias de compilación: Cython solo recompila si detecta cambios en los archivos fuente, pero puede fallar si hay archivos intermedios corruptos

Registro LY (Línea Actual): El registro LY (Línea Y) de la PPU indica qué línea de la pantalla se está renderizando actualmente. Los valores van de 0 a 153:

  • 0-143: Líneas visibles (se renderizan en el framebuffer)
  • 144-153: V-Blank (período de retrazo vertical, no se renderiza)

Si LY se mueve (cambia de 0 a 153 y vuelve a 0), significa que la PPU está funcionando correctamente y avanzando las líneas. Si LY permanece en 0, puede indicar que la PPU no está recibiendo ciclos o que el LCD está deshabilitado.

Fuente: Pan Docs - LCD Timing, LY Register

Implementación

Se implementaron tres acciones principales:

1. Verificación del Código Fuente

Antes de recompilar, se verificó que el código fuente realmente había cambiado:

  • Constructor de PPU: Verificado que `std::fill` inicializa a blanco (`0xFFFFFFFF`), no gris (`0xFF808080`)
  • Método `step()`: Verificado que NO hay ninguna línea `framebuffer[0] = ...` (el punto rojo)
  • Método `step()`: Verificado que se llama a `render_scanline()` cuando `mode_ == MODE_0_HBLANK && ly_ < VISIBLE_LINES`

2. Limpieza y Recompilación Completa

Se ejecutaron los siguientes comandos en orden para eliminar cualquier rastro del binario viejo:

# 1. Limpiar build con setuptools
python setup.py clean --all

# 2. Eliminar carpeta build manualmente (si existe)
Remove-Item -Recurse -Force build

# 3. Eliminar archivos .pyd antiguos (si existen)
Remove-Item -Force viboy_core*.pyd

# 4. Recompilar desde cero
python setup.py build_ext --inplace

Nota sobre archivos bloqueados: En Windows, si el archivo `.pyd` está bloqueado por un proceso de Python en ejecución, será necesario cerrar todos los procesos de Python antes de poder eliminarlo. El nuevo binario se generará en `build/lib.win-amd64-cpython-313/` y se copiará al directorio raíz cuando el archivo antiguo se libere.

3. Diagnóstico de LY en el Bucle Principal

Se añadió logging de diagnóstico en `src/viboy.py` para verificar que la PPU C++ está funcionando:

# 6. Heartbeat: Diagnóstico de LY cada segundo (60 frames ≈ 1 segundo a 60 FPS)
if frame_count % 60 == 0 and self._ppu is not None:
    # Obtener LY de la PPU (compatible con Python y C++)
    ly_value = 0
    try:
        if self._use_cpp:
            # PPU C++: usar propiedad .ly (definida en ppu.pyx)
            ly_value = self._ppu.ly
        else:
            # PPU Python: usar método get_ly()
            ly_value = self._ppu.get_ly()
    except AttributeError:
        # Fallback si no existe el método/propiedad
        ly_value = 0
    
    # Logging de diagnóstico: Si LY se mueve (0-153), la PPU está viva
    logger.info(f"💓 Heartbeat ... LY_C++={ly_value} (PPU {'viva' if ly_value > 0 or frame_count > 60 else 'inicializando'})")

Este logging se muestra cada segundo (cada 60 frames) cuando se ejecuta con `--verbose`:

python main.py roms/tetris.gb --verbose

Archivos Afectados

  • src/viboy.py - Añadido logging de diagnóstico de LY en el bucle principal (método `run()`)
  • setup.py - No modificado, pero usado para limpieza y recompilación
  • src/core/cpp/PPU.cpp - Verificado (no modificado, ya estaba correcto desde el paso 119)

Tests y Verificación

Comando de verificación:

python main.py roms/tetris.gb --verbose

Resultado esperado:

  • Si la recompilación fue exitosa: La pantalla debe ser blanca (o negra) sin punto rojo. Si no se ve el juego, es que `render_scanline` no pinta bien, pero al menos estamos en la versión nueva.
  • Si se ve el juego: ¡Victoria! Era solo un problema de caché de compilación.
  • Logging de LY: Cada segundo, debe aparecer un mensaje como `💓 Heartbeat ... LY_C++=XX` donde XX cambia de 0 a 153. Si LY se mueve, la PPU está viva.

Validación de módulo compilado C++: El nuevo binario se genera en `build/lib.win-amd64-cpython-313/viboy_core.cp313-win_amd64.pyd` y se copia al directorio raíz cuando el archivo antiguo se libera.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Caché de compilación: Cython y Python pueden mantener binarios antiguos en memoria o en disco, causando que los cambios en código C++ no se reflejen hasta forzar una recompilación completa.
  • Diagnóstico de PPU: El registro LY es un indicador excelente de si la PPU está funcionando. Si LY se mueve (0-153), la PPU está viva y procesando líneas.
  • Archivos bloqueados en Windows: Los archivos `.pyd` pueden quedar bloqueados si hay procesos de Python ejecutándose, impidiendo su eliminación hasta que se cierren todos los procesos.

Lo que Falta Confirmar

  • Renderizado real: Si después de la recompilación la pantalla es blanca pero no se ve el juego, puede indicar que `render_scanline()` no está pintando correctamente los tiles desde VRAM.
  • Inicialización de registros: Verificar que los registros de la PPU (LCDC, BGP, SCX, SCY) se inicializan correctamente cuando se carga una ROM.

Hipótesis y Suposiciones

Suposición: El problema reportado (pantalla gris con punto rojo) era causado por un binario antiguo cargado en memoria o en disco. La recompilación completa debería resolver el problema si el código fuente está correcto.

Próximos Pasos

  • [ ] Verificar que la recompilación completa resuelve el problema de la pantalla gris
  • [ ] Si la pantalla es blanca pero no se ve el juego, investigar por qué `render_scanline()` no pinta correctamente
  • [ ] Verificar que el logging de LY muestra valores que cambian (0-153) cuando se ejecuta con `--verbose`
  • [ ] Si todo funciona, proceder con la implementación del Audio (APU) en la siguiente fase