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
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ónsrc/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
- Pan Docs: LCD Timing
- Pan Docs: LCDC Register (LY)
- Cython Documentation: Compilation
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