This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Validate VRAM RAW + Headless + Bank Mapping Diagnostics
Summary
RAW VRAM diagnostic validation, headless trust test and MBC mapping test tools.
It was confirmed that VRAM is stored in separate banks (vram_bank0_, vram_bank1_) at MMU,
was correctedread_raw()to read correctly from the VRAM banks, write→read_raw validation test was created,
headless trust test tool was created and MBC bank probe tool was created to validate deterministic mapping.Key finding:VRAM storage confirmed in MMU (not in PPU),read_raw()works properly,
and diagnostic tools are ready to validate MBC mapping and headless reliability.
Hardware Concept
On Game Boy Color (CGB), theVRAM (Video RAM)It is divided into two banks of 8KB each (total 16KB):
- VRAM Bank 0 (0x8000-0x9FFF):Tile Data and Tile Map base
- VRAM Bank 1 (0x8000-0x9FFF):Alternative Tile Data and Tile Map (CGB) Attributes
The recordVBK (0xFF4F)bit 0 selects which bank the CPU sees. The PPU can access both banks simultaneously during rendering. In DMG (Classic Game Boy) mode, only Bank 0 is used.
RAW Diagnosis:For reliable diagnosis, we need to read VRAM directly without PPU mode restrictions.read_raw()should read from the correct VRAM banks (vram_bank0_, vram_bank1_) when the address
is in the range 0x8000-0x9FFF, notmemory_[]which does not contain the contents of VRAM.
Fountain:Pan Docs - "CGB Registers", "VRAM Banks", "FF4F - VBK - VRAM Bank"
Implementation
Three phases of the plan were implemented:
Phase A: Check Where VRAM Really Is
The VRAM storage was located usinggrepabout C++ code:
- Find:VRAM is in
MMUasvram_bank0_andvram_bank1_(vectors of 0x2000 bytes each) - NOT in PPU:PPU does not have its own VRAM arrays, it only reads from MMU using
read_vram_bank() - Identified problem:
read_raw()read frommemory_[]which does not contain VRAM
Phase B: Correct read_raw() to Read from VRAM Banks
It was modifiedMMU::read_raw()insrc/core/cpp/MMU.cppto read from VRAM banks when the address
is in the range 0x8000-0x9FFF:
uint8_t MMU::read_raw(uint16_t addr) const {
// Step 0452: VRAM is in separate banks (vram_bank0_, vram_bank1_)
// For reliable diagnostics, read_raw() must read from VRAM banks
if (addr >= 0x8000 && addr<= 0x9FFF) {
uint16_t offset = addr - 0x8000;
if (vram_bank_ == 0 && offset < vram_bank0_.size()) {
return vram_bank0_[offset];
} else if (vram_bank_ == 1 && offset < vram_bank1_.size()) {
return vram_bank1_[offset];
}
return 0xFF;
}
if (addr >= MEMORY_SIZE) {
return 0xFF;
}
return memory_[addr];
}
Phase C: Create Validation Test write→read_raw
was createdtests/test_vram_raw_write_read_0452.pywith three tests:
- test_vram_write_read_raw_mmu:Validates that writing to VRAM and reading with
read_raw()works - test_vram_write_read_raw_range:Validate that
dump_raw_range()works correctly - test_vram_nonzero_sampling:Validates that we can detect non-zero bytes in VRAM using
read_raw()
Result:All tests pass correctly ✅
Phase D: Create Diagnostic Tools
Two tools were created:
- tools/test_headless_trust_0452.py:Headless trust test that validates that headless reports correctly with ROM clean-room
- tools/mbc_bank_probe_0452.py:MBC bank probe that tests deterministic MBC mapping by comparing read bytes with real ROM
Fix dump_raw_range() in Cython
It was correcteddump_raw_range()insrc/core/cython/mmu.pyxTo properly handle byte conversion:
def dump_raw_range(self, uint16_t start, uint16_t length):
#...buffer allocation code...
try:
self._mmu.dump_raw_range(start, length, buffer)
# Convert to Python bytes by creating a list and converting to bytes
result_list = []
for i in range(length):
result_list.append(buffer[i])
return bytes(result_list)
finally:
free(buffer)
Affected Files
src/core/cpp/MMU.cpp- Modifiedread_raw()to read from VRAM bankssrc/core/cython/mmu.pyx- Fixeddump_raw_range()for byte conversiontests/test_vram_raw_write_read_0452.py- Created write→read_raw validation testtools/test_headless_trust_0452.py- Created headless trust testtools/mbc_bank_probe_0452.py- Created MBC bank probe
Tests and Verification
Unit tests:
$ python3 -m pytest tests/test_vram_raw_write_read_0452.py -v
tests/test_vram_raw_write_read_0452.py::test_vram_write_read_raw_mmu PASSED
tests/test_vram_raw_write_read_0452.py::test_vram_write_read_raw_range PASSED
tests/test_vram_raw_write_read_0452.py::test_vram_nonzero_sampling PASSED
✅ 3 passed in 0.30s
Compiled C++ module validation:All tests pass, confirming thatread_raw()reads correctly from the VRAM banks and thatdump_raw_range()works correctly.
Test Code:
def test_vram_write_read_raw_mmu():
"""Validates that writing to VRAM and reading with read_raw works."""
mmu = PyMMU()
ppu = PyPPU(mmu)
mmu.set_ppu(ppu)
# Write non-zero pattern to 0x8000-0x8010
pattern = [0xAA, 0xBB, 0xCC, 0xDD]
for i, byte_val in enumerate(pattern):
mmu.write(0x8000 + i, byte_val)
# Read with read_raw (VRAM is in MMU)
for i, expected in enumerate(pattern):
current = mmu.read_raw(0x8000 + i)
assert current == expected, (
f"VRAM write→read_raw failed at 0x{0x8000+i:04X}: "
f"expected 0x{expected:02X}, obtained 0x{actual:02X}"
)
print("✅ MMU.write→read_raw works correctly")
Sources consulted
- Bread Docs:"CGB Registers", "VRAM Banks", "FF4F - VBK - VRAM Bank"
- Implementation based on analysis of existing C++ code (MMU.cpp, PPU.hpp)
Educational Integrity
What I Understand Now
- VRAM Storage:VRAM is in MMU as separate banks (
vram_bank0_,vram_bank1_), not inmemory_[]nor in PPU. This is critical for reliable diagnosis. - read_raw() vs read():
read_raw()should read directly from the VRAM banks when the address is at 0x8000-0x9FFF, not frommemory_[].read()It does dynamic ROM mapping but is not useful for RAW VRAM diagnostics. - Reliable Diagnosis:To validate that VRAM has data, we need
read_raw()that reads directly from the VRAM banks without PPU mode restrictions.
What remains to be confirmed
- Headless Trust Test:Validate that headless reports correctly with ROM clean-room (executed in Step 0453)
- MBC Bank Probe:Validate that the MBC mapping works correctly by comparing read bytes with real ROM (executed in Step 0453)
Hypotheses and Assumptions
It is assumed that the VRAM banks (vram_bank0_, vram_bank1_) are the source of truth for VRAM.
This was confirmed by C++ code analysis and write→read_raw validation tests.
Next Steps
- [x] Validate VRAM storage (MMU vs PPU)
- [x] Fix read_raw() to read from VRAM banks
- [x] Create test write→read_raw
- [x] Create headless trust test
- [x] Create MBC bank probe
- [ ] Run headless trust test and MBC bank probe (Step 0453)
- [ ] Analyze results and decide root cause (mapping vs CPU/IRQ vs PPU/palettes)