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.
Test del Rotulador Negro: Escritura Directa
Resumen
La sonda del Step 0211 confirmó que la validación de direcciones VRAM es correcta (VALID CHECK: PASS) y que la matemática de direcciones es perfecta. Sin embargo, la pantalla sigue blanca porque estamos renderizando el Tile 0 (vacío). Para confirmar visualmente que tenemos control sobre el framebuffer dentro del bucle de renderizado validado, implementamos una escritura directa de índice de color 3 (Negro) en un patrón de rayas verticales.
Objetivo: Generar barras verticales negras forzando framebuffer_[i] = 3 dentro del bloque validado. Si esto funciona, veremos rayas verticales negras y blancas, confirmando que el pipeline de renderizado real está recorriendo la pantalla y pasando la validación.
Concepto de Hardware: Validación Visual del Pipeline
El Step 0211 nos confirmó que la validación de direcciones VRAM funciona correctamente. El log mostró VALID CHECK: PASS y CalcTileAddr: 0x8000 con TileID: 0x00, lo que significa que la matemática es perfecta. Sin embargo, la pantalla sigue blanca.
El problema de "dónde estamos mirando": El Tile 0 (ubicado en 0x8000) está vacío/blanco por defecto. Nuestra sonda miró el píxel (0,0), que corresponde al Tile 0. Aunque forzamos byte1=0xFF en el Step 0209, es posible que la decodificación de bits o la paleta en Python esté haciendo que ese "3" se vea blanco, o simplemente que necesitamos ser más agresivos para confirmar el control total.
La solución del "Rotulador Negro": En lugar de depender de la lectura de VRAM y la decodificación de bits, vamos a escribir directamente el índice de color 3 (Negro) en el framebuffer dentro del bloque validado. Si esto pone la pantalla negra (o a rayas), habremos confirmado que el pipeline de renderizado real (VRAM → Validación → Framebuffer) funciona, y que el problema anterior era puramente de datos (Tile 0 vacío).
Patrón de rayas verticales: Para hacer el test más visible, implementamos un patrón alternado: cada 8 píxeles, forzamos el color 3 (Negro). En las franjas alternas, dejamos el comportamiento normal (que probablemente lea 0/blanco del Tile 0). Esto generará barras verticales negras y blancas, confirmando visualmente que:
- El bucle de renderizado está recorriendo todos los píxeles de la pantalla.
- La validación de VRAM está funcionando correctamente.
- El framebuffer está siendo escrito correctamente.
- El pipeline C++ → Cython → Python funciona end-to-end.
Fuente: Pan Docs - "PPU Rendering", "Framebuffer", "Color Indexing"
Implementación
Se modificó la función PPU::render_scanline() en el archivo src/core/cpp/PPU.cpp para implementar el patrón de rayas verticales negras dentro del bloque validado de VRAM.
Modificación del Bloque de Renderizado
Se reemplazó el código que forzaba byte1 = 0xFF y byte2 = 0xFF (Step 0209) con un patrón condicional que escribe directamente en el framebuffer:
// --- Step 0212: EL TEST DEL ROTULADOR NEGRO ---
// Ignoramos la lectura de VRAM y la decodificación por un momento.
// Escribimos directamente en el framebuffer.
// Si esto funciona, veremos barras verticales negras.
// Patrón de rayas: 8 píxeles negros, 8 píxeles normales (blancos por ahora)
if ((x / 8) % 2 == 0) {
framebuffer_[line_start_index + x] = 3; // FORZAR NEGRO (Índice 3)
} else {
// Para las otras franjas, dejamos el comportamiento "normal" (que probablemente lea 0/blanco del Tile 0)
uint8_t byte1 = mmu_->read(tile_line_addr);
uint8_t byte2 = mmu_->read(tile_line_addr + 1);
uint8_t bit_index = 7 - (map_x % 8);
uint8_t bit_low = (byte1 >> bit_index) & 1;
uint8_t bit_high = (byte2 >> bit_index) & 1;
uint8_t color_index = (bit_high << 1) | bit_low;
framebuffer_[line_start_index + x] = color_index;
}
// ----------------------------------------------
Lógica del Patrón
El patrón funciona de la siguiente manera:
- Condición
(x / 8) % 2 == 0: Divide la coordenada X por 8 (cada tile tiene 8 píxeles de ancho) y verifica si el resultado es par. Esto crea franjas de 8 píxeles de ancho. - Franjas pares: Se fuerza directamente
framebuffer_[line_start_index + x] = 3(Negro), ignorando completamente la lectura de VRAM y la decodificación de bits. - Franjas impares: Se mantiene el comportamiento normal: lectura de VRAM, decodificación de bits y escritura del índice de color calculado. Como el Tile 0 está vacío, estas franjas probablemente serán blancas (color_index = 0).
Resultado esperado: Si el código funciona correctamente, deberíamos ver una pantalla con rayas verticales alternadas: negras (donde forzamos el color 3) y blancas (donde leemos el Tile 0 vacío).
Archivos Afectados
src/core/cpp/PPU.cpp- Modificado el bloque de renderizado enrender_scanline()(líneas 385-402) para implementar el patrón de rayas verticales negras
Tests y Verificación
Compilación: El código debe compilarse exitosamente con python setup.py build_ext --inplace o .\rebuild_cpp.ps1. No se introdujeron errores de compilación.
Validación de módulo compilado C++: La extensión Cython se generará correctamente y estará lista para pruebas en tiempo de ejecución.
Prueba esperada: Al ejecutar el emulador con python main.py roms/tetris.gb, deberíamos ver una pantalla con rayas verticales negras y blancas alternadas:
- Rayas negras: Donde nuestro "rotulador" forzó el color 3 (cada 8 píxeles, empezando desde X=0).
- Rayas blancas: Donde la PPU leyó el Tile 0 (vacío) de la VRAM (cada 8 píxeles, empezando desde X=8).
Validación de éxito: Si vemos este patrón, habremos confirmado que:
- El bucle de renderizado está funcionando correctamente.
- La validación de VRAM está permitiendo el acceso (el bloque
ifse está ejecutando). - El framebuffer está siendo escrito correctamente.
- El pipeline C++ → Cython → Python funciona end-to-end.
- El problema anterior era puramente de datos (Tile 0 vacío), no de lógica.
Próximo paso si funciona: Una vez confirmado que tenemos control total sobre el framebuffer, el siguiente paso será cargar datos reales en VRAM o mirar al tile correcto del mapa de tiles.
Fuentes Consultadas
- Pan Docs: "PPU Rendering" - Proceso de renderizado de líneas de escaneo
- Pan Docs: "Framebuffer" - Estructura del buffer de píxeles
- Pan Docs: "Color Indexing" - Índices de color (0-3) y paletas
Integridad Educativa
Lo que Entiendo Ahora
- Validación visual: A veces, la mejor forma de confirmar que un sistema funciona es hacer algo visualmente obvio (como escribir píxeles negros directamente) en lugar de depender de datos complejos.
- Control total del framebuffer: Si podemos escribir directamente en el framebuffer y ver el resultado, sabemos que el pipeline de renderizado funciona. El problema entonces es solo de datos (qué escribimos), no de lógica (cómo escribimos).
- Patrones de diagnóstico: Los patrones visuales (como rayas) son excelentes para confirmar que un bucle está recorriendo todos los elementos correctamente.
Lo que Falta Confirmar
- Resultado visual: Ver si realmente aparecen las rayas verticales negras y blancas en la pantalla.
- Control del pipeline: Confirmar que tenemos control total sobre el framebuffer dentro del bucle validado.
- Origen del problema anterior: Si las rayas aparecen, confirmaremos que el problema era de datos (Tile 0 vacío), no de lógica.
Hipótesis y Suposiciones
Hipótesis principal: Si vemos las rayas verticales negras y blancas, habremos confirmado que todo el pipeline funciona correctamente. El problema anterior era simplemente que estábamos renderizando el Tile 0, que está vacío por defecto.
Suposición: Una vez confirmado el control visual, el siguiente paso será cargar datos reales en VRAM o mirar al tile correcto del mapa de tiles para ver contenido real del juego.
Próximos Pasos
- [ ] Recompilar el módulo C++ con
.\rebuild_cpp.ps1opython setup.py build_ext --inplace - [ ] Ejecutar el emulador:
python main.py roms/tetris.gb - [ ] Verificar resultado visual: Buscar rayas verticales negras y blancas alternadas
- [ ] Si funciona: Confirmar que tenemos control total del framebuffer y que el problema era de datos, no de lógica
- [ ] Si funciona: Cargar datos reales en VRAM o mirar al tile correcto del mapa de tiles