This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Investigation of Why VRAM Is Not Filled with Tiles
Summary
Detailed diagnostic code was implemented to investigate why VRAM is not filled with tiles while games are running. Added logs to verify all writes to VRAM, VRAM periodic status, write timing (LCD off vs. on), and tile deletion detection. The results show that the games are writing ONLY ZEROS (0x00) to VRAM during execution, which explains why VRAM is never filled with tiles. All writes occur when the LCD is on, which is unusual since games typically write tiles when the LCD is off.
Hardware Concept
VRAM Access:VRAM (0x8000-0x97FF) contains tile and tilemap data. Games write tiles to VRAM during initialization or when the LCD is off. Access to VRAM is restricted when the LCD is on (only during VBLANK or when the LCD is off). On real hardware, writing to VRAM when the LCD is on can cause data corruption or be ignored.
LCD Timing:Games often write tiles when the LCD is off to avoid conflicts with rendering. Writing to VRAM when the LCD is on can cause problems or be ignored. The LCD can be turned off/on using the LCDC register (bit 7). When the LCD is off, the PPU stops and LY stays at 0, allowing the game to safely modify VRAM.
Tiles Writing:Each tile is 16 bytes (8x8 pixels, 2 bytes per line). Tiles are written in sequences of 16 consecutive bytes. Games can delete tiles by writing 0x00 to VRAM. If a game writes only 0x00 to VRAM, it is not loading actual tiles, it is just clearing VRAM.
Full Tiles Detection:A complete tile has 16 consecutive bytes with non-zero data. If a game writes tiles, we should see 16-byte sequences with valid data. If we only see 0x00 writes, the game is clearing VRAM, not loading tiles.
Implementation
4 main diagnostic tasks were implemented according to the Step 0352 plan:
1. Detailed Verification of Writes to VRAM (MMU.cpp)
Added code inMMU::write()to log all writes to VRAM (0x8000-0x97FF). The code counts total writes, non-zero writes, and detects sequences of 16 consecutive bytes (full tiles). The first 100 writes are logged in detail with address, value, PC, and statistics.
// --- Step 0352: Detailed Verification of Writes to VRAM ---
static int vram_write_detailed_count = 0;
static int vram_write_total_count = 0;
static int vram_write_non_zero_count = 0;
static int vram_write_tile_sequences = 0;
if (addr >= 0x8000 && addr< 0x9800) {
vram_write_total_count++;
if (value != 0x00) {
vram_write_non_zero_count++;
}
// Loggear primeras 100 escrituras detalladamente
// Detectar secuencias de 16 bytes (tiles completos)
// Loggear estadísticas cada 1000 escrituras
}
// -------------------------------------------
2. Periodic VRAM Status Check (PPU.cpp)
Added code inPPU::step()to check the VRAM status every 100 frames. The code counts non-zero bytes in VRAM, checks for full tiles (16 consecutive bytes with non-zero data), and generates warnings if VRAM is empty after many frames.
// --- Step 0352: Periodic Verification of VRAM Status ---
if (frame_counter_ % 100 == 0 && vram_state_check_count< 50) {
// Contar bytes no-cero en VRAM
int non_zero_bytes = 0;
int complete_tiles = 0;
for (uint16_t addr = 0x8000; addr < 0x9800; addr += 16) {
// Verificar si el tile tiene datos
// Si tiene al menos 8 bytes no-cero, considerarlo completo
}
printf("[PPU-VRAM-STATE-PERIODIC] Frame %llu | Non-zero bytes: %d/6144 ...\n", ...);
}
// -------------------------------------------
3. Write Timing Verification (LCD Off vs On)
Added methodPPU::is_lcd_on()to check the LCD status, and added code inMMU::write()to log the status of the LCD when writing to VRAM. This allows you to identify whether writes occur when the LCD is on or off.
// --- Step 0352: Verification of Timing of Writes to VRAM ---
bool lcd_is_on = false;
if (ppu_ != nullptr) {
lcd_is_on = ppu_->is_lcd_on();
}
printf("[MMU-VRAM-WRITE-LCD-TIMING] Write #%d | Addr=0x%04X | Value=0x%02X | "
"LCD=%s | PC=0x%04X\n", ...);
if (lcd_is_on && value != 0x00) {
printf("[MMU-VRAM-WRITE-LCD-TIMING] ⚠️ WARNING: Writing to VRAM when LCD is on!\n");
}
// -------------------------------------------
4. Tiles Deletion Detection
Added code inMMU::write()to detect when tiles are deleted (writing 0x00 after non-zero data was written). The code maintains a map of the last values written to each address and detects when 0x00 is written after a non-zero value.
// --- Step 0352: Tiles Deletion Detection ---
static std::map<uint16_t, uint8_t> vram_last_value;
static int vram_erase_count = 0;
if (vram_last_value.find(addr) != vram_last_value.end()) {
uint8_t last_value = vram_last_value[addr];
if (last_value != 0x00 && value == 0x00) {
//Detect deletion
vram_erase_count++;
printf("[MMU-VRAM-ERASE] Erase #%d | Addr=0x%04X | Last value=0x%02X ...\n", ...);
}
}
vram_last_value[addr] = value;
// -------------------------------------------
Created/Modified Components
src/core/cpp/MMU.cpp: Added diagnostic code for VRAM writes, LCD timing, and erase detectionsrc/core/cpp/PPU.cpp: Agregado código de verificación periódica del estado de VRAMsrc/core/cpp/PPU.hpp: Added methodis_lcd_on()to check the LCD status
Affected Files
src/core/cpp/MMU.cpp- Added diagnostic code for VRAM writes, LCD timing, and erase detectionsrc/core/cpp/PPU.cpp- Added periodic VRAM status check code and methodis_lcd_on()src/core/cpp/PPU.hpp- Added public methodis_lcd_on()
Tests and Verification
Tests were run with the 5 ROMs in parallel for ~2.5 minutes each:
- Tested ROMs:pkmn.gb, tetris.gb, mario.gbc, pkmn-amarillo.gb, Oro.gbc
- Command:
timeout 150 python3 main.py roms/[ROM].gb 2>&1 | tee logs/test_[ROM]_step0352.log - Log analysis:commands were used
grepto extract specific information without cluttering the context
Main Findings
- Writes to VRAM:Thousands of writes detected, butALL are 0x00(Non-zero writes=0, Non-zero ratio=0.00%)
- VRAM Status:It always has only 40 non-zero bytes (0.65%), below the threshold of 200
- Write timing:All writes occur when LCD=ON and all are 0x00
- Deleting tiles:No erases are detected (Total erases=0), which means that tiles are not being written first and then deleted
Log Example
[MMU-VRAM-WRITE-STATS] Total writes=1000 | Non-zero writes=0 | Tile sequences=3 | Non-zero ratio=0.00%
[MMU-VRAM-WRITE-STATS] Total writes=2000 | Non-zero writes=0 | Tile sequences=3 | Non-zero ratio=0.00%
[PPU-VRAM-STATE-PERIODIC] Frame 0 | Non-zero bytes: 40/6144 (0.65%) | Complete tiles: 3/384 (0.78%) | Empty: YES
[MMU-VRAM-WRITE-LCD-TIMING] Write #1 | Addr=0x8000 | Value=0x00 | LCD=ON | PC=0x1679
[MMU-VRAM-ERASE-STATS] Total writes=1000 | Total erases=0 | Erase ratio=0.00%
Conclusion
The games are writing ONLY ZEROS (0x00) to VRAM during execution.No real tiles are being written. This explains why VRAM is never filled with tiles. The problem is not in the detection or generation of the framebuffer, but rather that the games are not loading tiles into VRAM during execution.
Sources consulted
- Bread Docs:Memory Map, VRAM, LCD Timing
- Bread Docs:LCD Control Register (LCDC)
- Bread Docs:Tile Data, Tile Map
Educational Integrity
What I Understand Now
- Writes to VRAM:Games write thousands of times to VRAM during execution, but all writes are 0x00 (zeroes). This means that games are clearing VRAM, not loading tiles.
- LCD Timing:All writes occur when the LCD is on, which is unusual. Normally games write tiles when the LCD is off to avoid conflicts.
- VRAM Status:VRAM always has only 40 non-zero bytes (0.65%), which is very little. This suggests that the games are not loading tiles during execution, or that the tiles are loaded very early and then deleted.
What remains to be confirmed
- Why do games write only zeros?We need to investigate if games expect VRAM to be empty at startup, or if there is something wrong with the initialization timing.
- When are the tiles loaded?Tiles may load very early (during initialization) and then be deleted, or they may not load at all if there is a problem with the game initialization.
- Are there restrictions on access to VRAM?On real hardware, writing to VRAM when the LCD is on can cause problems. We need to check if our emulator is honoring these restrictions correctly.
Hypotheses and Assumptions
Main hypothesis: Los juegos están limpiando VRAM al inicio de la ejecución, pero no están cargando tiles después. Esto podría deberse a que:
- Games expect VRAM to be empty at startup and load tiles later (after the 2.5 minute test)
- There is some problem with the game initialization that prevents tiles from loading
- Tiles are loaded from somewhere else (not from VRAM directly) or loaded differently
Next Steps
- [ ] Investigate why games write only zeros to VRAM
- [ ] Check if tiles are loaded very early (during initialization) and then deleted
- [ ] Investigate if there are VRAM access restrictions that we are not respecting
- [ ] Check if games load tiles from another place or in a different way
- [ ] Implement correction based on findings (Step 0353)