This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
ROM Protection & Interrupt Trace
Summary
This Step implements two critical integrity improvements:ROM protectionandinterrupt trace. Analysis of Step 0251 revealed that the game was writing to the ROM range (`0x0000-0x7FFF`), which could corrupt the game code at runtime. Additionally, the mystery of constant `IME:0` requires instrumentation to detect who disables interrupts (is it `DI`? or a firing interrupt?).
Hardware Concept
ROM protection
On a real Game Boy, the cartridge ROM (`0x0000-0x7FFF`) is physicallyread only. try to write in this range it does not modify the data in the ROM, but is sent to theMBC (Memory Bank Controller)of the cartridge to control the switching of memory banks.
"ROM ONLY" cartridges (Type 0x00): Cartridges without MBC (such as Tetris) do not have memory banks. In these cases, writes to the ROM range are simply silently ignored. The hardware does not generate errors, but it does not modify the memory.
The Problem in Our Emulator: If our MMU allows writing directly to `memory_[addr]` for addresses in the ROM range, we are allowing the game to beself mutilation. If the game overwrites its own code with values like `0xFD`, any future execution will read corrupted instructions, causing erratic jumps, crashes, and strange behavior.
Fountain: Pan Docs - "Memory Map", "Cartridge Types"
Outage Tracking
The Game Boy's interrupt system has two critical states:
- IME (Interrupt Master Enable): Global flag that enables/disables all interrupts.
- IF (Interrupt Flag): Register indicating which interrupts are pending.
- IE (Interrupt Enable): Register indicating which interrupts are enabled.
Who deactivates IME?There are two main ways:
- `DI` instruction (0xF3): Disable IME immediately. It is typically used at the beginning of critical routines.
- Interrupt Processing: When an interrupt is triggered, the hardware automatically disables IME to avoid nested interruptions. The ISR (Interrupt Service Routine) can reactivate IME with `EI` if needed.
The Mystery of Step 0251: The GPS showed a constant `IME:0`, even though we saw a `[EI]`. we need to know who turns off interrupts to understand why the game does not respond to V-Blank interrupts.
Fountain: Pan Docs - "Interrupts", "DI", "EI"
Implementation
1. ROM protection in MMU
Added a check to the `MMU::write()` method that prevents writes to the ROM range (`0x0000-0x7FFF`). Protection is placedafterof all special cases (DIV, TIMA, TMA, TAC, P1) butbeforeof direct writing to `memory_`.
// --- Step 0252: ROM PROTECTION ---
if (addr< 0x8000) {
// ROM es de solo lectura: NO escribir en memory_
// Los logs de SENTINEL y DMA ya se registraron arriba si aplicaban,
// pero no modificamos la memoria para evitar corrupción.
return;
}
// -----------------------------------------
Design Decisions:
- The `SENTINEL` and `DMA` logs are kept for diagnostic purposes, but the memory is not modified.
- We do not generate errors or warnings: the real hardware simply ignores these writes silently.
- For cartridges with MBC, this logic should be extended to handle changing banks, but for now is sufficient for "ROM ONLY" cartridges.
2. CPU Interrupt Tracing
Two instrumentation points were added:
- In `case 0xF3` (DI): Log when the `DI` instruction is executed.
- In `handle_interrupts()`: Log when an interrupt is triggered and IME is disabled.
// In case 0xF3 (DI):
printf("[DI] Interrupts Disabled on PC:%04X!\n", regs_->pc);
// In handle_interrupts():
printf("[INT] Interrupt triggered! Type: %02X. Jumping to Vector. (IME disabled)\n", pending);
Log format:
[DI]: Indicates that the `DI` instruction was executed at the specified PC address.[INT]: Indicates that an interrupt was triggered. The `Type` value is the set interrupt bit (0x01=V-Blank, 0x02=LCD STAT, 0x04=Timer, 0x08=Serial, 0x10=Joypad).
Affected Files
src/core/cpp/MMU.cpp- Added ROM protection in the methodwrite()(lines ~399-408).src/core/cpp/CPU.cpp- Added tracking logs incase 0xF3(DI) andhandle_interrupts().
Tests and Verification
To verify the implementation, you must run the emulator with Tetris and analyze the logs:
- Recompile:
.\rebuild_cpp.ps1 - Run with redirect:
python main.py roms/tetris.gb > debug_252.log 2>&1 - Analyze logs:
- ROM: Did the log disappear?
Write SENTINEL [0065]? (Or at least, are you sure it doesn't corrupt memory?) - IME: Search the log:
- Do you see
[DI]after the[EI]? - Do you see
[INT] Interrupt triggered!?
- Do you see
- ROM: Did the log disappear?
Expected Validation:
- The logs of
Write SENTINEL [0065]They may still appear (for diagnosis), but the ROM should not be corrupted. - If we see
[DI]after[EI], we will know that the game is explicitly disabling interrupts. - If we see
[INT], we will know that interrupts are firing, but IME is automatically disabled.
C++ Compiled Module Validation: Changes are to compiled C++ code, so recompilation is required before trying.
Sources consulted
- Bread Docs:Memory Map
- Bread Docs:Cartridge Types
- Bread Docs:Interrupts
- Bread Docs:DI (Disable Interrupts)
- Bread Docs:EI (Enable Interrupts)
Educational Integrity
What I Understand Now
- ROM is read only: On real hardware, writing to ROM does not modify the data, but is sent to the MBC. For "ROM ONLY" cartridges, these writes are simply ignored.
- Memory corruption: If we allow the game to write in its own code, any future reading can return corrupted data, causing erratic jumps and unpredictable behavior.
- IME is automatically disabled: When an interrupt is triggered, the hardware disables IME to prevent nested interruptions. The ISR can reactivate IME with `EI` if needed.
What remains to be confirmed
- Is the game using DI?: The logs of
[DI]They will tell us if the game is disabling interruptions explicitly afterEI. - Are outages skyrocketing?: The logs of
[INT]They will tell us if the interruptions are firing, but IME is automatically disabled. - Does ROM protection solve the problem?: If ROM corruption was the cause of the erratic behavior, flashing the ROM should significantly improve the stability of the emulator.
Hypotheses and Assumptions
Main Hypothesis: ROM corruption (writing `0xFD` to `0x0065`) was causing the game to read instructions corrupted, causing erratic jumps and the strange behavior we observed. Protecting the ROM should prevent this corruption and improve stability.
Assumption about IME: We assume that the game is disabling IME after activating it with `EI`, or that the Interrupts are being fired but IME is automatically disabled. The logs will tell us what is the real case.
Next Steps
- [ ] Run the emulator with Tetris and analyze the ROM protection logs.
- [ ] Check if the logs
[DI]and[INT]reveal who deactivates IME. - [ ] If ROM protection solves the problem, consider implementing MBC management for cartridges with memory banks.
- [ ] If IME is automatically deactivated by interruptions, verify that the ISR reactivates IME correctly.