This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Instrumentation and Visual Unlock Fix
Summary
Critical fix to emulator instrumentation to ensure that diagnostic monitors run correctly, even when there are interruptions that cause early returns. Moved the Handler's Sniper block ([HANDLER-EXEC]) to the start of CPU::step() and implemented a liberal VRAM write monitor ([VRAM-VIBE]) to detect real graphics loads. Verifying that writes in the range 0x8000-0x9FFF are successful in memory so they can be read by the PPU.
Hardware Concept
In the actual Game Boy hardware, VRAM (Video RAM) is a special memory region located in the range 0x8000-0x9FFF that contains the tile data and tile maps that the PPU (Picture Processing Unit) reads to render the screen. VRAM is accessible by both the CPU (for writing graphics data) and the PPU (for reading data during rendering).
VRAM Access Restrictions:On real hardware, VRAM is only accessible by the CPU during certain PPU modes (specifically, during V-Blank and H-Blank). However, many modern emulators relax this restriction to simplify implementation, allowing writes to VRAM at any time. This emulator follows this more permissive approach.
Real Data Detection:When a game loads graphics into VRAM, it typically writes values other than 0x00 (blank) and 0x7F (a common initialization value). A "liberal" monitor filters out these common values and only reports writes that are likely to contain real graph data.
Fountain:Pan Docs - "VRAM (Video RAM)": 0x8000-0x9FFF contains Tile Data (0x8000-0x97FF) and Tile Maps (0x9800-0x9FFF).
Implementation
Three main changes were made to improve instrumentation and ensure correct writing to VRAM:
1. Handler Sniper Block Movement
The [HANDLER-EXEC] instrumentation block that tracks the execution of the V-Blank handler was moved from the end of CPU::step() (after the opcode switch) to the beginning of the method, just after capturing the original PC. This ensures that the monitor runs even when there are interruptions that cause early returns before reaching the switch.
Motivation:The original code was located after the opcode switch, which meant that if an interrupt caused an early return (line 518-520), the monitor never ran. By moving it to the start, we ensure that it runs on every call to step(), regardless of whether there are interruptions.
2. Liberal VRAM Write Monitor ([VRAM-VIBE])
Implemented a new monitor in MMU::write() that detects writes to VRAM (0x8000-0x9FFF) by filtering out common initialization values (0x00 and 0x7F). The monitor reports up to 200 writes that likely contain real graphics data.
Characteristics:
- Filters values 0x00 (blank) and 0x7F (common initialization)
- Reports distinct values that are likely real chart data
- Limit of 200 reports to avoid log saturation
- Includes PC information, ROM bank and written address/value
3. Verification of Writes in VRAM
Added an explicit check to the code to ensure that writes in the range 0x8000-0x9FFF succeed in memory. Although the direct write to memory_[addr] is done at the end of the function, an explicit comment and check was added to ensure that the PPU can read the written data.
Modified components
- CPU.cpp: Moved the [HANDLER-EXEC] block to the start of step(), after capturing original_pc and before handle_interrupts(). Removed duplicate block from end of method.
- MMU.cpp: Implemented the [VRAM-VIBE] monitor in MMU::write() that detects writes to VRAM with values other than 0x00 and 0x7F. Added explicit verification that VRAM writes are successful.
Design decisions
Liberal Monitor:A "liberal" approach was chosen for the VRAM monitor because the values 0x00 and 0x7F are very common in memory initialization and clearing, but do not represent actual graphics data. Filtering these values reduces noise in the logs and allows you to focus on writes that are likely to contain real graph data.
Handler Monitor Location:Moved to the start of step() to ensure it runs every loop, even when there are interruptions. This is critical for diagnosis because the V-Blank handler is an essential part of the rendering flow.
Affected Files
src/core/cpp/CPU.cpp- Move the [HANDLER-EXEC] block to the start of step()src/core/cpp/MMU.cpp- Implementation of the [VRAM-VIBE] monitor and verification of writes to VRAM
Tests and Verification
The implementation was verified by:
- Successful build:The C++ code compiled without errors using
python setup.py build_ext --inplace - Syntax validation:No linter errors found in modified files
- Compiled C++ module validation:The Cython extension has been compiled successfully and is ready to use
Note:Functional tests will be performed on subsequent runs of the emulator to verify that the monitors correctly capture handler activity and VRAM writes.
Sources consulted
- Bread Docs:VRAM (Video RAM)- Range 0x8000-0x9FFF contains Tile Data and Tile Maps
- Bread Docs:Interrupt Vectors- Vector 0x0040 is the V-Blank handler
Educational Integrity
What I Understand Now
- Early Returns in Instrumentation:Early returns (such as those caused by interrupts) can cause instrumentation code placed after the return to never be executed. It is critical to place diagnostic monitors before any possible early return.
- Common Values Filtering:In instrumentation, it is useful to filter out common seed values (such as 0x00 and 0x7F) to reduce noise in the logs and focus on data that is likely to be meaningful.
- VRAM Access:Although real hardware has VRAM access restrictions based on PPU mode, many emulators allow writes at any time to simplify implementation.
What remains to be confirmed
- Monitor Effectiveness [VRAM-VIBE]:We need to verify in real runs whether the monitor correctly captures graphics loads and whether common value filtering is effective.
- Impact on Performance:Although the monitor has a limit of 200 reports, we need to verify that it does not significantly affect the performance of the emulator.
Hypotheses and Assumptions
Assumption on Initialization Values:We assume that the values 0x00 and 0x7F are common in initialization and memory clearing, but this is based on empirical observations. If we find that other values are more common, we can adjust the filter.
Next Steps
- [ ] Run the emulator with a test ROM to verify that the [HANDLER-EXEC] and [VRAM-VIBE] monitors correctly capture the activity
- [ ] Analyze generated logs to identify patterns in VRAM writes
- [ ] Verify that VRAM writes are correctly reflected in the PPU rendering
- [ ] Adjust monitor filter [VRAM-VIBE] if necessary based on observed data