This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Blank Screen Triage - Framebuffer vs Palettes vs Blit
Summary
Step 0488 implements complete instrumentation to diagnose the emulator blank screen issue. Framebuffer statistics (FrameBufferStats) and palettes (PaletteStats) are added to rom_smoke snapshots, framebuffer dump to PPM is implemented for visual evidence outside of SDL, and a unit test is created that validates that the PPU can produce color diversity when VRAM has data. The results show that the problem is different depending on the mode: in DMG (tetris.gb) the framebuffer is completely white (bug in fetch/decode), while in CGB (tetris_dx.gbc) the framebuffer DOES have diversity but the window is still white (bug in blit/presentation).
Hardware Concept
The PPU (Picture Processing Unit) of the Game Boy renders frames of 160x144 pixels. The rendering process includes:
- Tiles Fetch: Reading tile data from VRAM (0x8000-0x97FF)
- Decode 2bpp: 2bpp data conversion to color indices (0-3)
- Palette application: Mapping of indices to shades (DMG: BGP) or RGB colors (CGB: CGB palettes)
- Write to framebuffer: Writing pixels to the display buffer
- Blit/Presentation: Transfer framebuffer to SDL texture for display
The blank screen issue can occur in any of these steps. To diagnose it, we need:
- FrameBufferStats: Check if the PPU is generating a framebuffer with more than 1 color/pattern
- PaletteStats: Check if the paddles are correctly configured
- PPM Dump: Visual evidence outside of SDL to rule out blit/presentation issues
Reference: Pan Docs - LCD Control Register (FF40 - LCDC), Background Palette (FF47 - BGP), CGB Palettes (FF68-FF6B).
Implementation
5 phases of instrumentation are implemented to diagnose the blank screen problem:
Phase A: FrameBufferStats
Structure is addedFrameBufferStatstoPPU.hppwith:
fb_crc32: Framebuffer hash to detect changesfb_unique_colors: Number of unique indexes (0-3) presentfb_nonwhite_count: Pixels with index != 0fb_nonblack_count: Pixels with index != 3fb_top4_colors: The 4 most frequent indicesfb_top4_colors_count: Count of each indexfb_changed_since_last: Indicates if the framebuffer changed since the last frame
The functioncompute_framebuffer_stats()runs afterswap_framebuffers()and is crawlingVIBOY_DEBUG_FB_STATS=1. It is exposed to Python via the Cython wrapper.
Phase B: PaletteStats
Added pallet statistics collection inrom_smoke_0442.py:
- CGB vs DMG mode detection
- DMG records: BGP, OBP0, OBP1 and derived index→shade mapping
- CGB Registries: BGPI, BGPD, OBPI, OBPD
- Non-white input counters on CGB palettes
getters are added inMMUto access CGB palette data (get_cgb_bg_palette_data(), get_cgb_obj_palette_data()).
Phase C: Dump PPM
It is implemented_dump_framebuffer_to_ppm()in Python that:
- Read the index framebuffer from the PPU
- Convert indices to RGB using BGP (DMG) or CGB palettes
- Writes a PPM file (Netpbm P6 format) to the specified path
- Crawled by
VIBOY_DUMP_FB_FRAMEandVIBOY_DUMP_FB_PATH
Phase D: Unit Test
It is createdtest_ppu_framebuffer_diversity_0488.pythat:
- Create a tile checkerboard in VRAM (alternates indices 0 and 3)
- Configure tilemap and activate LCD/BG
- Executes entire frames explicitly waiting for
is_frame_ready() - Verify that
fb_unique_colors >= 2andfb_nonwhite_count > 0
Result: ✅ The test passes, confirming that the PPU can produce diversity when VRAM has data.
Phase E: rom_smoke Execution and Report
It runsrom_smoke_0442.pywith:
VIBOY_SIM_BOOT_LOGO=0(clean baseline)VIBOY_DEBUG_FB_STATS=1(activate statistics)VIBOY_DUMP_FB_FRAME=180(dump at frame 180)- ROMs:
tetris.gb(DMG) andtetris_dx.gbc(CGB)
Report is generated indocs/reports/reporte_step0488.mdwith snapshot tables, PPM analysis, and decision tree.
Affected Files
src/core/cpp/PPU.hpp- StructureFrameBufferStatsand methodget_framebuffer_stats()src/core/cpp/PPU.cpp- Implementation ofcompute_framebuffer_stats()and call afterswap_framebuffers()src/core/cython/ppu.pxd- Cython statementFrameBufferStatssrc/core/cython/ppu.pyx- Python wrapperget_framebuffer_stats()src/memory/mmu.py- Gettersget_cgb_bg_palette_data()andget_cgb_obj_palette_data()tools/rom_smoke_0442.py- Integration of FrameBufferStats and PaletteStats in snapshots, feature_dump_framebuffer_to_ppm()tests/test_ppu_framebuffer_diversity_0488.py- Unit test that validates framebuffer diversitydocs/reports/reporte_step0488.md- Complete report with analysis and decision tree
Tests and Verification
Unit test: test_ppu_framebuffer_diversity_0488.py::test_ppu_produces_multiple_colors_when_vram_has_pattern
def test_ppu_produces_multiple_colors_when_vram_has_pattern(self):
"""Test that verifies that PPU produces >1 color when VRAM has a pattern."""
# Create tile checkerboard in VRAM
# Configure tilemap and activate LCD/BG
# Execute full frames waiting for is_frame_ready()
# Check fb_unique_colors >= 2 and fb_nonwhite_count > 0
assert fb_stats['fb_unique_colors'] >= 2
assert fb_stats['fb_nonwhite_count'] > 0
Result: ✅ HAPPENS(1 passed in 0.05s)
C++ Compiled Module Validation: The test validates that the PPU compiled in C++ can produce color diversity when VRAM contains a known pattern.
Running rom_smoke:
- tetris.gb (DMG): 240 frames executed, PPM generated at frame 180
- tetris_dx.gbc (CGB): 240 frames executed, PPM generated at frame 180
Sources consulted
- Bread Docs:LCD Control Register (FF40 - LCDC)
- Bread Docs:Background Palette (FF47 - BGP)
- Bread Docs:CGB Palettes (FF68-FF6B)
- Bread Docs:PPU Rendering Pipeline
Educational Integrity
What I Understand Now
- PPU Rendering Pipeline: The complete process from fetching tiles to rendering, and where each step can go wrong.
- FrameBufferStats: How to measure framebuffer diversity without relying on SDL visualization.
- PaletteStats: How to verify that the palettes are set correctly in both modes (DMG and CGB).
- Dump PPM: How to obtain visual evidence outside the presentation system to isolate blit/presentation problems.
What remains to be confirmed
- Bug in fetch/decode DMG: Why the framebuffer is completely white in DMG mode even though the tilemap has data.
- Bug in blit/CGB presentation: Why SDL window shows white screen when framebuffer has diversity (4 unique colors).
- Tiles loading timing: If there are VRAM access restrictions that prevent the correct loading of tiles into real ROMs.
Hypotheses and Assumptions
Main hypothesis: The problem is different depending on the mode:
- DMG: PPU is not rendering correctly (empty framebuffer). Possible causes: bug in fetch/decode, VRAM access restrictions, or incorrect initialization.
- CGB: PPU IS rendering (framebuffer has diversity), but blit/presentation fails. Possible causes: incorrect SDL texture format, incorrectly configured pitch/stride, or whiteout after rendering.
Next Steps
Step 0489 - Mode Specific Diagnostics:
- For tetris.gb (DMG):
- Instrument
render_scanline()to check fetch/decode in DMG mode - Compare with tetris_dx.gbc which DOES work (CGB mode)
- Check VRAM access restrictions during PPU modes in DMG
- Instrument
- For tetris_dx.gbc (CGB):
- Instrument before and after the blit (source buffer hash vs texture uploaded buffer hash)
- Check SDL texture format (RGBA vs BGRA)
- Check framebuffer pitch/stride
- Verify that the framebuffer is not being whitened after rendering