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.
Step 0256: Paleta de Debug (High Contrast)
Resumen
Este Step implementa una paleta de debug de alto contraste en el renderizador de Python (`src/gpu/renderer.py`) que ignora completamente los registros de paleta del hardware (BGP, OBP0, OBP1) y mapea directamente los índices de color (0-3) del framebuffer de la PPU a colores fijos de alto contraste. El objetivo es revelar cualquier píxel que la PPU esté generando, incluso si los registros de paleta están en `0x00` (todo blanco) o si la MMU no está sirviendo correctamente los valores de paleta al frontend.
Si después de esta modificación vemos formas negras/grises moviéndose en la pantalla (como el logo de GAME FREAK o la intro de Gengar vs Nidorino en Pokémon Red), sabremos que el núcleo C++ funciona correctamente y el problema está únicamente en la lectura/escritura de los registros de paleta (`FF47-FF49`) en la MMU.
Concepto de Hardware
En la Game Boy, los registros de paleta controlan cómo se traducen los índices de color (0-3) generados por la PPU a colores RGB visibles en pantalla. El framebuffer de la PPU contiene índices de color (0, 1, 2, 3), no colores RGB directamente. Estos índices deben pasar por una paleta para convertirse en colores visibles.
Registros de Paleta:
- BGP (0xFF47): Paleta del Background. Cada par de bits (0-1, 2-3, 4-5, 6-7) mapea un índice de color (0-3) a un tono de gris (0-3). Si BGP es `0x00`, todos los índices se mapean al color 0 (blanco), haciendo que incluso píxeles negros (índice 3) se rendericen como blancos.
- OBP0 (0xFF48): Paleta de Sprites (canal 0). Similar a BGP, pero el color 0 es siempre transparente en sprites.
- OBP1 (0xFF49): Paleta de Sprites (canal 1). Similar a OBP0.
Problema Crítico: Si los registros de paleta están en `0x00` o si la MMU no está sirviendo correctamente estos valores, todos los píxeles se renderizarán como blancos, incluso si la PPU está generando correctamente los índices de color. Esto hace que sea imposible distinguir entre un problema de renderizado (PPU no genera píxeles) y un problema de paleta (PPU genera píxeles pero se renderizan como blancos).
Solución de Debug: Al forzar una paleta fija de alto contraste que mapea directamente los índices 0-3 a colores visibles (Blanco, Gris Claro, Gris Oscuro, Negro), podemos "ver" cualquier píxel que la PPU esté generando, independientemente del estado de los registros de paleta. Si vemos formas negras/grises, sabemos que la PPU funciona; si seguimos viendo todo blanco, el problema está en la PPU misma.
Fuente: Pan Docs - Palette Registers (BGP, OBP0, OBP1)
Implementación
Se modificó el método `render_frame()` en `src/gpu/renderer.py` para forzar una paleta de debug de alto contraste en dos lugares:
1. Renderizado con PPU C++ (líneas 444-515)
Se reemplazó la lógica de lectura y decodificación de BGP con un mapeo directo de índices a colores:
# --- Step 0256: DEBUG PALETTE FORCE (HIGH CONTRAST) ---
# Ignoramos BGP/OBP del hardware para ver los índices crudos de la PPU.
# Esto nos confirmará si la PPU está dibujando sprites/fondo.
# Paleta fija de alto contraste: 0=Blanco, 1=Gris Claro, 2=Gris Oscuro, 3=Negro
debug_palette_map = {
0: (224, 248, 208), # 00: White/Greenish (Color 0)
1: (136, 192, 112), # 01: Light Gray (Color 1)
2: (52, 104, 86), # 10: Dark Gray (Color 2)
3: (8, 24, 32) # 11: Black (Color 3)
}
# Mapeo directo: índice del framebuffer -> color RGB
palette = [
debug_palette_map[0],
debug_palette_map[1],
debug_palette_map[2],
debug_palette_map[3]
]
# ----------------------------------------
2. Renderizado con método Python (líneas 525-832)
Se aplicó la misma paleta de debug al método Python que calcula tiles desde VRAM, reemplazando la decodificación de BGP.
3. Renderizado de Sprites (líneas 873-1027)
Se modificó el método `render_sprites()` para usar la misma paleta de debug, ignorando OBP0 y OBP1:
# --- Step 0256: DEBUG PALETTE FORCE (HIGH CONTRAST) ---
# Ignoramos OBP0/OBP1 del hardware para ver los índices crudos de los sprites.
debug_palette_map = {
0: (224, 248, 208), # 00: White/Greenish (Color 0 - transparente en sprites)
1: (136, 192, 112), # 01: Light Gray (Color 1)
2: (52, 104, 86), # 10: Dark Gray (Color 2)
3: (8, 24, 32) # 11: Black (Color 3)
}
palette0 = [debug_palette_map[0], debug_palette_map[1],
debug_palette_map[2], debug_palette_map[3]]
palette1 = [debug_palette_map[0], debug_palette_map[1],
debug_palette_map[2], debug_palette_map[3]]
# ----------------------------------------
Decisiones de diseño
- Paleta de Alto Contraste: Se eligieron colores con suficiente contraste para que cualquier píxel con índice > 0 sea claramente visible, incluso en fondos claros.
- Mapeo Directo: Se evita cualquier decodificación de BGP/OBP para eliminar posibles puntos de fallo. Si el framebuffer tiene índice 3, se renderiza como negro directamente.
- Consistencia Visual: Se usa la misma paleta para fondo y sprites para facilitar la comparación visual.
- No Requiere Recompilación: Esta modificación es puramente en Python, por lo que no requiere recompilar C++. Esto permite iterar rápidamente durante el debugging.
Archivos Afectados
src/gpu/renderer.py- Modificado `render_frame()` y `render_sprites()` para forzar paleta de debug de alto contraste (Step 0256).
Tests y Verificación
Validación Manual:
- Ejecutar:
python main.py roms/pkmn.gb(o cualquier ROM con sprites). - Observar la pantalla:
- Si vemos formas negras/grises moviéndose (logo de GAME FREAK, intro de Gengar vs Nidorino): ✅ ÉXITO - La PPU funciona correctamente, el problema está en los registros de paleta.
- Si seguimos viendo todo blanco/verde: ❌ PROBLEMA - La PPU no está generando píxeles o el framebuffer no se está leyendo correctamente.
- Interpretación de Resultados:
- Si vemos formas: El núcleo C++ es perfecto. El problema está en la lectura/escritura de BGP/OBP0/OBP1 en la MMU o en el mapeo de paletas.
- Si no vemos formas: El problema está en la PPU (no genera píxeles) o en la transferencia del framebuffer desde C++ a Python.
Nota: Este Step no requiere tests unitarios porque es una modificación temporal de debug. Una vez identificado el problema, se restaurará la lógica normal de paletas.
Fuentes Consultadas
- Pan Docs: Palette Registers (BGP, OBP0, OBP1)
- Pan Docs: LCD PPU - Background and Sprite Rendering
Integridad Educativa
Lo que Entiendo Ahora
- Paletas como Traducción: Los registros de paleta actúan como una "tabla de traducción" que convierte índices de color (0-3) en colores RGB visibles. Si esta traducción falla (BGP=0x00), todos los píxeles se renderizan como blancos, incluso si la PPU genera correctamente los índices.
- Debugging por Eliminación: Al forzar una paleta fija, eliminamos la variable "paleta" del problema. Si después de esto vemos formas, sabemos que el problema está en la paleta; si no vemos formas, el problema está en la PPU misma.
- Framebuffer como Índices: El framebuffer de la PPU contiene índices de color (0-3), no colores RGB. Estos índices deben pasar por una paleta para convertirse en colores visibles.
Lo que Falta Confirmar
- Resultado Visual: ¿Veremos formas negras/grises en Pokémon Red después de esta modificación? Esto confirmará si el problema está en la paleta o en la PPU.
- Estado de los Registros: Si vemos formas, necesitaremos verificar por qué BGP/OBP0/OBP1 están en 0x00 o por qué la MMU no los está sirviendo correctamente.
Hipótesis y Suposiciones
Hipótesis Principal: El juego (Pokémon Red) tiene los registros de paleta configurados a `0x00` (todo blanco) o la MMU no está guardando/sirviendo correctamente estos valores. Si forzamos una paleta de alto contraste y vemos formas, confirmaremos esta hipótesis.
Suposición: El framebuffer de la PPU C++ contiene índices válidos (0-3) y se está transfiriendo correctamente a Python. Si esta suposición es incorrecta, no veremos formas incluso con la paleta forzada.
Próximos Pasos
- [ ] Ejecutar
python main.py roms/pkmn.gby observar la pantalla. - [ ] Si vemos formas negras/grises:
- Verificar por qué BGP/OBP0/OBP1 están en 0x00 o por qué la MMU no los está sirviendo correctamente.
- Corregir la lectura/escritura de los registros de paleta en la MMU.
- Restaurar la lógica normal de paletas y validar que los colores se muestran correctamente.
- [ ] Si no vemos formas:
- Verificar que el framebuffer de la PPU C++ contiene índices válidos (0-3).
- Verificar que el framebuffer se está transfiriendo correctamente desde C++ a Python.
- Investigar por qué la PPU no está generando píxeles o por qué el framebuffer está vacío.