This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
DMG Tiledata Zero + CGB Present White
Summary
This step implements a minimal fix for the blank screen issue in DMG mode (tetris.gb) and diagnoses the white window issue in CGB mode (tetris_dx.gbc). The main issue identified: the game was not trying to write to tiledata when `VIBOY_SIM_BOOT_LOGO=0` due to incorrect post-boot state. Implemented `init_post_boot_dmg_state()` which sets the correct state according to Pan Docs (LCDC=0x00 OFF initially). Result: The game now attempts to write to tiledata (6144 attempts at frame 180), but all writes are zero, suggesting that the game is in an initialization state where it has not yet decompressed the graphics data.
Hardware Concept
According to Pan Docs - Power Up Sequence, after the Boot ROM terminates, the hardware is left in a known state:
- LCDC (0xFF40): LCD OFF (0x00) initially. The game must be activated manually.
- STAT (0xFF41): 0x00 by default
- BGP (0xFF47): 0xFC (shades 3,2,1,0) - standard DMG palette
- IF (0xFF0F): 0xE1 (VBlank, Timer, Serial flags set)
- IE (0xFFFF): 0x00 (no interrupts initially enabled)
Identified Problem: Our emulator was initializing LCDC=0x91 (LCD ON) when `VIBOY_SIM_BOOT_LOGO=0`, which may have caused the game to not correctly execute its initialization routine. The game expects LCDC to be OFF initially and activates it manually during its initialization routine.
Reference:Pan Docs - "Power Up Sequence", "LCDC Register (0xFF40)"
Implementation
Phase C: Fix Minimum DMG - Case 2
C1: Implementation of init_post_boot_dmg_state()
Implemented `init_post_boot_dmg_state()` method in MMU.cpp that sets the correct DMG post-boot state according to Pan Docs:
void MMU::init_post_boot_dmg_state() {
// Known state after DMG ROM boot
// Source: Pan Docs - Power Up Sequence
memory_[0xFF40] = 0x00; // LCDC: LCD OFF
memory_[0xFF41] = 0x00; // STAT
memory_[0xFF42] = 0x00; //SCY
memory_[0xFF43] = 0x00; // SCX
memory_[0xFF47] = 0xFC; //BGP
memory_[0xFF48] = 0x00; // OBP0
memory_[0xFF49] = 0x00; // OBP1
memory_[0xFF0F] = 0xE1; // IF
memory_[0xFFFF] = 0x00; //IE
//...other records...
}
C2: Modification of initialize_io_registers()
Changed `initialize_io_registers()` to call `init_post_boot_dmg_state()` when it is DMG and `VIBOY_SIM_BOOT_LOGO=0`:
void MMU::initialize_io_registers() {
bool is_cgb = (hardware_mode_ == HardwareMode::CGB);
// Step 0491: If it is DMG and VIBOY_SIM_BOOT_LOGO=0, use correct post-boot status
const char* env_boot_logo = std::getenv("VIBOY_SIM_BOOT_LOGO");
bool sim_boot_logo = (env_boot_logo && std::string(env_boot_logo) == "1");
if (!is_cgb && !sim_boot_logo) {
// DMG mode without boot logo: use post-boot state according to Pan Docs
init_post_boot_dmg_state();
return;
}
// ...rest of the code...
}
C3: Marking vram_write_stats_ as mutable
Marked `vram_write_stats_` as `mutable` in MMU.hpp to allow updating from const methods when necessary (although it is ultimately not used in read()).
Tests and Verification
Execution Command
export PYTHONPATH=/media/fabini/8CD1-4C30/ViboyColor:$PYTHONPATH
export VIBOY_SIM_BOOT_LOGO=0
export VIBOY_DEBUG_DMG_TILE_FETCH=1
export VIBOY_DEBUG_VRAM_WRITES=1
export VIBOY_DEBUG_PRESENT_TRACE=1
export VIBOY_DUMP_RGB_FRAME=180
export VIBOY_DUMP_RGB_PATH=/tmp/viboy_tetris_gb_rgb_f####.ppm
python3 tools/rom_smoke_0442.py roms/tetris.gb --frames 240 > /tmp/viboy_0491_tetris_fix.log 2>&1
Results - Frame 180
| Metrics | Before the Fix | After the Fix | Change |
|---|---|---|---|
| TiledataAttemptsB0 | 0 | 6144 | ✅ +6144 |
| TiledataNonZeroB0 | 0 | 0 | ⚠️ No change |
| TilemapAttemptsB0 | 0 | 3072 | ✅ +3072 |
| TilemapNonZeroB0 | 0 | 1024 | ✅ +1024 |
Validación de Módulo Compilado C++
PYTHONPATH=/media/fabini/8CD1-4C30/ViboyColor:$PYTHONPATH python3 -c "from viboy_core import PyMMU; mmu = PyMMU(); print('✅ Module working correctly')"
Result:✅ Module compiled correctly and works without errors.
Modified Files
src/core/cpp/MMU.hpp- Added methodinit_post_boot_dmg_state()and markedvram_write_stats_asmutablesrc/core/cpp/MMU.cpp- Implementedinit_post_boot_dmg_state()and modifiedinitialize_io_registers()docs/reports/reporte_step0491.md- Complete report with comparative metrics
Findings and Conclusions
Significant Progress
The post-boot state fix allowed the game to try to write to tiledata and tilemap. Before the fix, there were no write attempts (TiledataAttemptsB0=0). After the fix, there are 6144 write attempts to tiledata and 3072 attempts to tilemap.
Persistent Problem
All writes to tiledata are zero (TiledataNonZeroB0=0), which suggests that the game is in an initialization state where it has not yet decompressed the graphics data. The tilemap has data (1024 non-zero bytes), but tiledata is completely empty.
Next Steps
- Investigate why the game is writing only zeros to tiledata (is it waiting for some condition before unzipping?)
- Execute
rom_smokefor CGB (tetris_dx.gbc) and parseFB_PRESENT_SRCto understand why it is white even thoughFB_RGBhas a signal - Check the actual contents of VRAM after writes to confirm if data is being written but not read correctly
References
- Pan Docs - Power Up Sequence
- Pan Docs - LCDC Register (0xFF40)
- Pan Docs - VRAM Banking (CGB)
- Step 0490: VRAMWriteStats Expansion
- Step 0489: FB_PRESENT_SRC Capture
- Full report (docs/reports/reporte_step0491.md)