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.
Cerrar Ambigüedad PPU vs Paletas vs Presentación
Resumen
Step 0489 implementa instrumentación en tres puntos críticos del pipeline de renderizado para cerrar la ambigüedad sobre dónde ocurre el problema de pantalla en blanco: FB_INDEX (buffer de índices generado por PPU), FB_RGB (buffer RGB después de mapear paletas), y FB_PRESENT_SRC (buffer exacto pasado a SDL/ventana). Adicionalmente, se añaden instrumentaciones para tracking de writes a paletas CGB y contadores de lecturas de tile data DMG. Los resultados revelan que el PPU no está leyendo tile data (TileBytesTotal=0), lo que explica por qué el framebuffer está completamente en blanco.
Concepto de Hardware
El pipeline de renderizado del Game Boy tiene múltiples etapas donde puede ocurrir un problema de pantalla en blanco:
- FB_INDEX: El PPU genera un buffer de 160x144 píxeles con índices de color (0-3) basado en datos de tiles de VRAM
- FB_RGB: Los índices se mapean a colores RGB usando paletas (DMG: BGP, CGB: paletas CGB)
- FB_PRESENT_SRC: El buffer RGB se transfiere a la textura SDL para presentación en pantalla
Para diagnosticar dónde ocurre el problema, necesitamos capturar estadísticas (CRC32, conteos de píxeles no-blancos) en cada uno de estos tres puntos. Si FB_INDEX está en blanco pero FB_RGB no, el problema está en el mapeo de paletas. Si FB_RGB tiene datos pero FB_PRESENT está en blanco, el problema está en el blit/presentación. Si los tres están en blanco, el problema está en el PPU (fetch/decode de tiles).
Referencia: Pan Docs - LCD Control Register (FF40 - LCDC), Background Palette (FF47 - BGP), CGB Palettes (FF68-FF6B), Tile Data (0x8000-0x97FF).
Implementación
Se implementan 5 fases de instrumentación para cerrar la ambigüedad:
Fase A: ThreeBufferStats
Se añade la estructura ThreeBufferStats a PPU.hpp con estadísticas para los tres buffers:
- FB_INDEX:
idx_crc32,idx_unique,idx_nonzero - FB_RGB:
rgb_crc32,rgb_unique_colors_approx,rgb_nonwhite_count - FB_PRESENT_SRC:
present_crc32,present_nonwhite_count,present_fmt,present_pitch,present_w,present_h
La función compute_three_buffer_stats() se ejecuta cuando LY=144 y frame_ready_ es true. Para FB_RGB, se maneja tanto modo CGB (usa buffer RGB directamente) como DMG (convierte índices a RGB usando BGP). FB_PRESENT_SRC se captura desde Python en renderer.py justo antes de pygame.display.flip().
Gate: VIBOY_DEBUG_PRESENT_TRACE=1
Fase B: Dump PPM RGB
Se implementa _dump_rgb_framebuffer_to_ppm() en rom_smoke_0442.py que:
- Lee el framebuffer de índices desde el PPU
- Convierte índices a RGB usando BGP (DMG) o paletas CGB
- Escribe un archivo PPM (Netpbm P6) con el framebuffer RGB
Gate: VIBOY_DUMP_RGB_FRAME y VIBOY_DUMP_RGB_PATH
Fase C: CGB Palette Write Stats
Se añade la estructura CGBPaletteWriteStats a MMU.hpp para rastrear writes a paletas CGB:
bgpd_write_count,last_bgpd_write_pc,last_bgpd_value,last_bgpiobpd_write_count,last_obpd_write_pc,last_obpd_value,last_obpi
El tracking se realiza en MMU::write() cuando se escriben registros 0xFF68-0xFF6B.
Gate: VIBOY_DEBUG_CGB_PALETTE_WRITES=1
Fase D: DMG Tile Fetch Stats
Se añade la estructura DMGTileFetchStats a PPU.hpp para rastrear lecturas de tile data:
tile_bytes_read_total_count: Total de bytes de tile data leídostile_bytes_read_nonzero_count: Bytes no-cero leídos
El tracking se realiza en PPU::decode_tile_line() cuando se leen bytes de VRAM para decodificar tiles.
Gate: VIBOY_DEBUG_DMG_TILE_FETCH=1
Fase E: Ejecución y Reporte
Se ejecuta rom_smoke_0442.py con todas las flags activas y ROM tetris.gb (DMG). Se genera reporte completo en docs/reports/reporte_step0489.md.
Archivos Afectados
src/core/cpp/PPU.hpp- EstructurasThreeBufferStatsyDMGTileFetchStatssrc/core/cpp/PPU.cpp- Métodoscompute_three_buffer_stats()y tracking endecode_tile_line()src/core/cython/ppu.pxd/ppu.pyx- Exposición de estructuras y métodos a Pythonsrc/core/cpp/MMU.hpp- EstructuraCGBPaletteWriteStatssrc/core/cpp/MMU.cpp- Tracking de writes a paletas CGBsrc/core/cython/mmu.pxd/mmu.pyx- Exposición deCGBPaletteWriteStatsa Pythonsrc/gpu/renderer.py- Captura deFB_PRESENT_SRCdesde Pygame Surfacetools/rom_smoke_0442.py- Integración de todas las estadísticas y función_dump_rgb_framebuffer_to_ppm()docs/reports/reporte_step0489.md- Reporte completo con análisis de resultados
Tests y Verificación
Se ejecutó rom_smoke_0442.py con ROM tetris.gb (DMG) y flags:
VIBOY_DEBUG_PRESENT_TRACE=1
VIBOY_DEBUG_CGB_PALETTE_WRITES=1
VIBOY_DEBUG_DMG_TILE_FETCH=1
VIBOY_DUMP_RGB_FRAME=5
VIBOY_DUMP_RGB_PATH=docs/reports/dumps/tetris_frame_####.ppm
Resultados de snapshots:
- Frame 0:
ThreeBufferStats=IdxCRC32=0x00000000 IdxUnique=1 IdxNonZero=0 | RgbCRC32=0x00000000 RgbUnique=1 RgbNonWhite=2304 | PresentCRC32=0x00000000 PresentNonWhite=0 - Frame 1: Similar a Frame 0
- Frame 2:
RgbCRC32=0x4F0D7000peroRgbNonWhite=0(inconsistencia detectada) - CGBPaletteWriteStats: Sin writes (esperado para DMG)
- DMGTileFetchStats:
TileBytesTotal=0 TileBytesNonZero=0⚠️ CRÍTICO
Dump PPM generado: docs/reports/dumps/tetris_frame_0005.ppm (68KB)
Compilación: ✅ Sin errores, test_build.py pasa correctamente
Fuentes Consultadas
- Pan Docs: LCD Control Register (FF40 - LCDC)
- Pan Docs: Background Palette (FF47 - BGP)
- Pan Docs: CGB Palettes (FF68-FF6B)
- Pan Docs: Tile Data (0x8000-0x97FF)
Integridad Educativa
Lo que Entiendo Ahora
- Pipeline de renderizado: El proceso de renderizado tiene múltiples etapas (fetch → decode → palette → present) y cada una puede ser el origen de un problema de pantalla en blanco.
- Instrumentación de tres buffers: Capturar estadísticas en FB_INDEX, FB_RGB y FB_PRESENT permite identificar exactamente dónde ocurre el problema.
- Tracking de tile fetch: Si el PPU no está leyendo tile data, el framebuffer estará en blanco independientemente de las paletas o la presentación.
Lo que Falta Confirmar
- Por qué decode_tile_line() no se ejecuta: El tracking muestra TileBytesTotal=0, lo que indica que
decode_tile_line()no se está llamando o las lecturas de VRAM están bloqueadas. - Locks de VRAM durante modo 3: Necesito verificar si hay locks en
MMU.cppque bloqueen lecturas de VRAM durante el modo Pixel Transfer. - Inconsistencia en conversión RGB: Frame 2 muestra RgbCRC32≠0 pero RgbNonWhite=0, lo que sugiere un bug en el muestreo o la conversión.
Hipótesis y Suposiciones
Hipótesis principal: El PPU no está leyendo tile data porque VRAM está vacía (confirmado: 0/6144 bytes no-cero) o porque hay locks que bloquean las lecturas durante el modo 3. Esto explica por qué el framebuffer está completamente en blanco.
Próximos Pasos
- [ ] Investigar por qué
decode_tile_line()no se ejecuta o las lecturas de VRAM están bloqueadas - [ ] Revisar locks de VRAM en
MMU.cppdurante modo 3 (Pixel Transfer) - [ ] Corregir inconsistencia en conversión RGB (muestreo cada 10 píxeles puede estar causando el problema)
- [ ] Verificar timing: el PPU podría intentar renderizar antes de que VRAM esté lista
- [ ] Ejecutar con ROM CGB (
tetris_dx.gbc) para comparar comportamiento