⚠️ Clean-Room / Educational

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

Date:2026-01-03 StepID:0452 State: VERIFIED

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 inMMUasvram_bank0_andvram_bank1_(vectors of 0x2000 bytes each)
  • NOT in PPU:PPU does not have its own VRAM arrays, it only reads from MMU usingread_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 withread_raw()works
  • test_vram_write_read_raw_range:Validate thatdump_raw_range()works correctly
  • test_vram_nonzero_sampling:Validates that we can detect non-zero bytes in VRAM usingread_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 banks
  • src/core/cython/mmu.pyx- Fixeddump_raw_range()for byte conversion
  • tests/test_vram_raw_write_read_0452.py- Created write→read_raw validation test
  • tools/test_headless_trust_0452.py- Created headless trust test
  • tools/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

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 needread_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)