⚠️ 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.

Operation IE Hunter: Tracking IE Registry and Interrupts

Date:2025-12-25 StepID:0274 State: draft

Summary

This Step implements "Operation IE Hunter" to track who and when modifies the registry Interrupt Enable Address (IE, Address0xFFFF). The analysis of Step 0273 revealed thatIE = 0x00(all interrupts disabled) whileIF = 0x01(V-Blank pending), which prevents the game from processing interrupts and causes a deadlock in waiting loops.

Instrumentation was added at three critical points: (1) capturing each write in0xFFFFinMMU.cpp, (2) trace the execution flow after the cleanup loop ends of VRAM (PC:36E3), and (3) logging of instructionsEI(Enable Interrupts) andD.I.(Disable Interrupts) onCPU.cpp. The objective is to identify the exact moment in which the IE logging is disabled and what code causes it.

Hardware Concept

The Game Boy's interrupt system hastwo levels of controlthat they must be active simultaneously for an interrupt to be processed:

1. IME (Interrupt Master Enable) - Internal CPU Flag

The IME is an internal flag of the CPU that is controlled by the instructionsEI(0xFB) andD.I.(0xF3). This flag determines whether the CPU can process interrupts in the next instruction cycle. The instructionEIhas a special behavior: activates the IMEafterto execute the next instruction, allowing that instruction to be executed without interruptions. This is critical for routines that need to execute an atomic sequence.

2. IE (Interrupt Enable Register) - Register Mapped to 0xFFFF

The IE register is a hardware register mapped to the address0xFFFFthat controls what types of interrupts are enabled. Each bit corresponds to a type of interrupt:

  • Bit 0:V-Blank Interrupt
  • Bit 1:LCD STAT Interrupt
  • Bit 2:Timer Interrupt
  • Bit 3:Serial Interrupt
  • Bit 4:Joypad Interrupt

If a bit is in0, that type of interrupt is disabled, even if the hardware requests the interrupt (activating the corresponding bit in IF).

Condition for Processing an Interrupt

For an interrupt to be processed,three simultaneous conditions:

  1. IME = 1:The internal CPU flag must be active.
  2. IE[bit] = 1:The corresponding bit in IE must be active.
  3. IF[bit] = 1:The corresponding bit in IF must be active (request pending).

If any of these conditions fail, the interrupt is not processed, even though the hardware is still requesting it. In the case of Pokémon Red, the analysis of Step 0273 revealed thatIE = 0x00(all bits disabled) whileIF = 0x01(V-Blank pending), which means that the game is waiting for an interrupt that can never be processed because IE is turned off.

Fountain:Pan Docs - Interrupts Section. The IE register is read/write and is can be modified at any time by means of an instructionLD (0xFFFF), Aor similar.

Implementation

Three complementary instrumentation systems were implemented to track the status of the Interrupts and execution flow:

1. Write Trace in IE (MMU.cpp)

Added an instrumentation block in the methodMMU::write()that detects each writing in the address0xFFFF(IE record). The log includes:

  • The new value written in IE
  • The PC from which the write was executed
  • The current ROM bank

This allows you to identify exactly what code IE is modifying and when it occurs.

// --- Step 0274: IE-WRITE - Interrupt Enable Register Trace ---
if (addr == 0xFFFF) {
    printf("[IE-WRITE] New value: 0x%02X from PC: 0x%04X (Bank:%d)\n",
           value, debug_current_pc, current_rom_bank_);
}

2. VRAM Post-Clean Trace (CPU.cpp)

Added a "trail" system that activates when the PC exits the cleaning loop of VRAM in0x36E3. The system assumes that the loop is 6 bytes (3 bytes of instructions + 3 bytes of conditional jump), so the output should be inPC:36E9.

When it is detected that the PC reaches0x36E9, scan mode is activated and the next 100 instructions with the complete CPU status (registers, opcodes, IE, IF). This allows you to see which path the game takes after clearing the VRAM and if there is code that disables IE at that time.

// --- Step 0274: VRAM Post-Cleaning Monitoring ---
static bool tracing_after_vram_clear = false;
static int trace_count = 0;

if (regs_->pc == 0x36E9 && !tracing_after_vram_clear) {
    printf("[VRAM-CLEAR-EXIT] Cleanup loop has ended. Starting Trail...\n");
    tracing_after_vram_clear = true;
}

if (tracing_after_vram_clear && trace_count< 100) {
    printf("[TRAIL] PC:%04X OP:%02X AF:%04X BC:%04X DE:%04X HL:%04X IE:%02X IF:%02X\n",
           regs_->pc, mmu_->read(regs_->pc),
           regs_->get_af(), regs_->get_bc(), regs_->get_de(), regs_->get_hl(),
           mmu_->read(0xFFFF), mmu_->read(0xFF0F));
    trace_count++;
}

3. EI/DI Instruction Monitor (CPU.cpp)

Added logging in the cases of the instructionsEI(0xFB) andD.I.(0xF3) to track when the code tries to activate or deactivate the IME. This complements the IE trace, since both conditions (IME and IE) must be active to process interrupts.

case 0xF3://DI (Disable Interrupts)
    printf("[CPU] DI (Disable Interrupts) on PC:0x%04X\n", (regs_->pc - 1) & 0xFFFF);
    ime_ = false;
    ime_scheduled_ = false;
    cycles_ += 1;
    return 1;

case 0xFB://EI (Enable Interrupts)
    printf("[CPU] EI (Enable Interrupts) on PC:0x%04X\n", (regs_->pc - 1) & 0xFFFF);
    ime_scheduled_ = true;
    cycles_ += 1;
    return 1;

Design Decisions

  • 100 trace limit:Post-cleanup trace is limited to 100 instructions to avoid log saturation. If the problem occurs later, the limit can be increased.
  • PC:36E9 as trigger:The cleanup loop is assumed to be 6 bytes based on in the analysis of Step 0273. If the trigger does not activate, the address can be adjusted.
  • Unlimited logging for IE-WRITE:Writes in IE are critical and relatively infrequent, so the number of logs is not limited to avoid losing information.

Affected Files

  • src/core/cpp/MMU.cpp- Added write trace in IE (0xFFFF)
  • src/core/cpp/CPU.cpp- Added VRAM post-cleaning trace and EI/DI logging

Tests and Verification

The instrumentation is validated by running the emulator with Pokémon Red and analyzing the logs. generated. The logs should show:

  • [IE-WRITE]:Every time 0xFFFF is written, especially if 0x00 is written
  • [VRAM-CLEAR-EXIT]:When the cleanup loop ends
  • [TRAIL]:The next 100 instructions after exiting the loop
  • [CPU] DI/EI:Every time a DI or EI instruction is executed

Test command:

python main.py roms/pkmn.gb

Expected validation:The logs should reveal if there is code you write0x00in0xFFFFafter VRAM clearing, or if the IME is disabled by an instructionD.I.without it being reactivated later.

Compiled C++ module validation:The build should complete without errors and the logs should appear during the emulator execution.

Sources consulted

  • Bread Docs:Interrupts Section- Explanation of the interruption system, IME, IE and IF
  • Bread Docs:CPU Instruction Set- Behavior of EI and DI instructions
  • Analysis of Step 0273: Identification of the problem of IE=0x00 and IF=0x01

Educational Integrity

What I Understand Now

  • Two-level interruption system:The IME (internal flag) and the IE (mapped register) are independent and both must be active to process interrupts. This allows fine control of the interrupt system.
  • EI behavior:The EI instruction activates the IME after executing the next instruction, allowing atomic sequences. This is critical for routines that They need to execute code without interruptions.
  • Deadlock by IE=0:If the IE register is at 0x00, no interrupts will be can process, even if the hardware requests interrupts (IF) and the IME is active. This can cause infinite loops if the game waits for an interrupt that is never processed.

What remains to be confirmed

  • Exact moment of disabling:We need to identify what code it writes 0x00 to 0xFFFF and when it occurs. The [IE-WRITE] logs should reveal this.
  • Post-cleaning flow:The [TRAIL] trace should show which path the game takes after clearing the VRAM and if there is code that disables IE at that time.
  • IME status:[CPU] DI/EI logs should show if IME is disabled and it doesn't reactivate, or if the problem is just the IE log.

Hypotheses and Assumptions

Main hypothesis:The game disables IE (writes 0x00 to 0xFFFF) for or after VRAM cleaning, and never wakes it up before entering the wait loop. This causes the game to wait for an interrupt that can never be processed.

Assumption about PC:36E9:We assume that the cleanup loop is 6 bytes based on in the analysis of Step 0273. If the trigger does not activate, it may be that the loop has a length different or the PC jumps to another address.

Next Steps

  • [ ] Run the emulator with Pokémon Red and analyze the generated logs
  • [ ] Identify the exact moment when IE is disabled (look for [IE-WRITE] with value 0x00)
  • [ ] Analyze the [TRAIL] to see what code is executed after VRAM cleaning
  • [ ] Check if there is a DI instruction that deactivates the IME without reactivating it
  • [ ] If the culprit code is identified, implement correction or adjustment in the emulator