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

EI Watchdog

Date:2025-12-23 StepID:0248 State: draft

Summary

Analysis of the Timeline Logger (Step 0247) revealed that the game is trying to use DMA and writing the `FD` sentinel to HRAM, but the GPS constantly shows `IME:0` (interrupts disabled). The hypothesis is that the game relies on an interrupt routine (V-Blank) to copy data from HRAM/ROM to WRAM, but since IME is disabled, the interrupt never fires and the main loop waits forever.

This Step instruments the `EI` (Enable Interrupts, opcode 0xFB) instruction to detect if the game attempts to enable interrupts at any time. If the `[EI]` log never appears, it means that the game has decided not to use interrupts yet, which would explain the deadlock.

Hardware Concept

EI (Enable Interrupts, Opcode 0xFB): Instruction that enables the Interrupt Master Enable (IME) with a delay of 1 instruction. On real hardware, when `EI` is executed, the IME is not activated immediately, but rather after the next instruction is executed. This allows the statement following `EI` to be executed without interruption, which is critical for patterns like:

EI
RETI ; This instruction is executed without interruptions
      ; After RETI, IME is activated and pending interrupts are checked

IME (Interrupt Master Enable): Global flag that controls whether the CPU can process interrupts. If IME is disabled (`IME:0`), the CPU ignores all interrupts, even if they are enabled in the IE register (Interrupt Enable, 0xFFFF) and there are pending signals in IF (Interrupt Flag, 0xFF0F).

The Deadlock Problem by IME: Many Game Boy games use V-Blank interrupts to synchronize critical operations such as data copies to VRAM or WRAM. If the game waits for an interrupt that never happens (because IME is disabled), it can get stuck in an infinite loop waiting for an event that will never arrive.

Fountain: Pan Docs - CPU Instruction Set (EI), Interrupt Master Enable (IME)

Implementation

Added temporary instrumentation to the `0xFB` (EI) case of `CPU.cpp` to log whenever the game tries to enable interrupts. The log shows the Program Counter (PC) where the `EI` instruction was executed, allowing you to determine if the game attempts to enable interrupts and at what point in the code.

Modified components

  • src/core/cpp/CPU.cpp: Added log `[EI]` in case 0xFB with current PC. Re-added `#include` temporarily for diagnosis.

Design decisions

Re-added `#include` temporarily (although it was removed in Step 0243 for performance) because this specific diagnostic requires immediate visibility in the console. Once the problem is identified, this instrumentation will be removed to restore performance.

The log is printed directly with `printf` instead of using a more complex logging system because:

  • It is temporary diagnostic instrumentation
  • Needs to be immediately visible in the console
  • It should not interfere with the normal flow of execution

Affected Files

  • src/core/cpp/CPU.cpp- Added log in case 0xFB (EI) and re-added #include <cstdio> temporarily (Step 0248)

Tests and Verification

To verify this instrumentation:

  1. Recompile the C++ extension: .\rebuild_cpp.ps1eitherpython setup.py build_ext --inplace
  2. Run the emulator: python main.py roms/tetris.gb
  3. Observe the console:
    • If it appears[EI] Interrupts Enabled on PC:XXXX!: The game tries to enable interrupts. Note the PC and check if it occurs before or after the wait loop.
    • If it does NOT appear[EI]: The game never runs `EI`, confirming that interrupts remain disabled and explaining the deadlock.

C++ Compiled Module Validation: Instrumentation is in native C++ code, so it requires recompilation to take effect.

Sources consulted

Educational Integrity

What I Understand Now

  • EI is 1 instruction late: The IME is not activated immediately after `EI`, but after executing the next instruction. This is real hardware behavior.
  • IME is the global control: Even if IE (Interrupt Enable) is set correctly, if IME is disabled, the CPU ignores all interrupts.
  • Deadlock by IME: If a game waits for an interrupt that never happens (because IME is disabled), it can get stuck in an infinite loop.

What remains to be confirmed

  • Does the game run EI?: We need to check if the game tries to enable interruptions at any time. If you never run `EI`, the IME will remain disabled and interrupts will never be processed.
  • When should EI be executed?: If the game executes `EI`, we need to determine whether it occurs before or after the wait loop. If it happens later, the loop will never be broken.
  • Is IE logging configured?: Although GPS already reports IE, we need to check if the game writes to IE to enable V-Blank or Timer interrupts.

Hypotheses and Assumptions

Main Hypothesis: The game relies on an interrupt routine (probably V-Blank) to copy data from HRAM/ROM to WRAM. Since IME is disabled, the interrupt never fires, the copy never happens, and the main loop waits forever.

Assumption: If the game never runs `EI`, it means that it is designed to run uninterrupted in this initialization phase, or that there is a bug in the code flow that prevents `EI` from running.

Next Steps

  • [ ] Run the emulator and see if the log appears[EI]
  • [ ] If appears[EI]: Analyze which PC it occurs on and if it is before or after the wait loop
  • [ ] If NOT displayed[EI]: Confirm that the game never enables interruptions and look for alternatives (manual copy? another mechanism?)
  • [ ] Check the value of the IE register in the GPS to confirm if interrupts are enabled at the hardware level
  • [ ] Once the problem is identified, remove temporary instrumentation to restore performance