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.
Activación de Renderizado Real (Background C++)
Resumen
El enlace de video C++ → Python funciona correctamente (se confirmó con la pantalla gris y el punto rojo a 60 FPS). Se eliminó el código de diagnóstico (pantalla gris y píxel rojo) del constructor de la PPU C++ y se activó el renderizado real del Background. El framebuffer ahora se inicializa a blanco (0xFFFFFFFF) y el método `render_scanline()` renderiza los tiles del juego real desde VRAM cuando la PPU entra en H-Blank (Mode 0).
Concepto de Hardware
Renderizado Scanline en Game Boy: La PPU renderiza la pantalla línea por línea (scanline rendering). Cada línea visible (0-143) pasa por 4 modos:
- Mode 2 (OAM Search): 0-79 ciclos - La PPU busca sprites que intersectan con la línea actual
- Mode 3 (Pixel Transfer): 80-251 ciclos - La PPU transfiere datos de tiles desde VRAM
- Mode 0 (H-Blank): 252-455 ciclos - La PPU renderiza la línea completa en el framebuffer
- Mode 1 (V-Blank): Líneas 144-153 - Período de retrazo vertical, no se renderiza
Renderizado de Background: Durante el H-Blank (Mode 0), la PPU lee el tilemap desde VRAM (0x9800-0x9BFF o 0x9C00-0x9FFF), decodifica los tiles desde Tile Data (0x8000-0x8FFF o 0x8800-0x97FF), aplica el scroll (SCX/SCY) y la paleta BGP, y escribe los 160 píxeles de la línea actual en el framebuffer.
Inicialización del Framebuffer: El framebuffer debe inicializarse a un color por defecto (blanco) antes de que la PPU comience a renderizar. Si se deja sin inicializar o se inicializa a un color de diagnóstico (gris), los juegos mostrarán colores incorrectos o pantallas de prueba en lugar del contenido real.
Fuente: Pan Docs - LCD Timing, Background, Tile Data, 2bpp Format
Implementación
Se realizaron los siguientes cambios en `src/core/cpp/PPU.cpp`:
1. Limpieza del Código de Diagnóstico
Se eliminó el código de diagnóstico que pintaba la pantalla de gris y un píxel rojo:
- Constructor: Cambió la inicialización del framebuffer de gris (`0xFF808080`) a blanco (`0xFFFFFFFF`)
- Constructor: Eliminó la línea que pintaba el píxel rojo de diagnóstico (`framebuffer_[0] = 0xFFFF0000`)
2. Verificación de Lógica de Renderizado
Se verificó que la lógica de renderizado funciona correctamente:
- Método `step()`: Llama a `render_scanline()` cuando la PPU entra en Mode 0 (H-Blank) y la línea es visible (LY < 144)
- Método `render_scanline()`: Renderiza Background, Window y Sprites en el orden correcto
- Método `render_bg()`: Lee VRAM, aplica scroll (SCX/SCY), decodifica tiles 2bpp y aplica paleta BGP
Componentes modificados
src/core/cpp/PPU.cpp- Constructor: Inicialización del framebuffer a blanco, eliminación de código de diagnóstico
Decisiones de diseño
Color de inicialización: Se eligió blanco (0xFFFFFFFF) como color por defecto porque es el color 0 de la paleta BGP estándar (0xE4), que corresponde a "transparente" o "blanco" en la mayoría de juegos. Si un tile no se renderiza correctamente, aparecerá blanco en lugar de gris, lo que facilita la depuración.
Sin cambios en la lógica de renderizado: La lógica de renderizado ya estaba implementada correctamente en pasos anteriores. Solo se necesitaba eliminar el código de diagnóstico para activar el renderizado real.
Archivos Afectados
src/core/cpp/PPU.cpp- Limpieza de código de diagnóstico, inicialización del framebuffer a blanco
Tests y Verificación
Se ejecutaron los tests de renderizado para asegurar que la limpieza del código de diagnóstico no rompió la funcionalidad:
$ python -m pytest tests/test_core_ppu_rendering.py -v
============================= test session starts =============================
platform win32 -- Python 3.13.5, pytest-9.0.2, pluggy-1.6.0
collected 4 items
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_bg_rendering_simple_tile PASSED [ 25%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_bg_rendering_scroll PASSED [ 50%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_window_rendering PASSED [ 75%]
tests/test_core_ppu_rendering.py::TestCorePPURendering::test_framebuffer_memoryview PASSED [100%]
============================== 4 passed in 0.12s ==============================
Validación de módulo compilado C++: Todos los tests pasaron, confirmando que:
- El renderizado de Background funciona correctamente
- El scroll (SCX/SCY) se aplica correctamente
- La Window se renderiza encima del Background
- El framebuffer se expone correctamente como memoryview (Zero-Copy)
Test clave: `test_bg_rendering_simple_tile` verifica que un tile negro se renderiza correctamente en el framebuffer cuando se configura en VRAM y tilemap. Este test confirma que el renderizado real funciona.
Fuentes Consultadas
- Pan Docs: LCD Timing, Background, Tile Data
- Pan Docs: 2bpp Format, Tile Decoding
Integridad Educativa
Lo que Entiendo Ahora
- Renderizado Scanline: La PPU renderiza línea por línea durante el H-Blank (Mode 0), no frame por frame. Esto es crítico para la sincronización con la CPU.
- Inicialización del Framebuffer: El framebuffer debe inicializarse a un color por defecto (blanco) antes de que la PPU comience a renderizar. Los colores de diagnóstico (gris, rojo) deben eliminarse antes de activar el renderizado real.
- Orden de Renderizado: Background → Window → Sprites. Este orden determina qué capa se dibuja encima de otra.
Lo que Falta Confirmar
- Renderizado de Sprites: Los sprites se renderizan pero la lógica de prioridad puede necesitar ajustes. Verificar con juegos reales.
- Window: La Window puede tener problemas de timing o posicionamiento en algunos juegos. Verificar con Tetris y otros juegos.
Hipótesis y Suposiciones
Color de inicialización: Se asume que blanco (0xFFFFFFFF) es un color seguro para inicializar el framebuffer. Si un juego muestra colores incorrectos, puede ser necesario inicializar a otro color o verificar la paleta BGP.
Próximos Pasos
- [ ] Probar con ROMs reales (Tetris, Mario) para verificar que el renderizado funciona correctamente
- [ ] Verificar que los sprites se renderizan correctamente (prioridad, transparencia, flip)
- [ ] Optimizar el renderizado si es necesario (cacheo de tiles, optimización de bucles)
- [ ] Implementar soporte para Game Boy Color (paletas CGB, VRAM Banks)