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

Mission in the Upper Zone: Game Debugging

Date:2025-12-22 StepID:0228 State: draft

Summary

The `LY=0` fix worked perfectly. The PPU now behaves like real hardware. The autopsy revealed that the CPU has escaped the BIOS and is running game code high in the ROM (PC: `0x2B15`), but it keeps the screen off (LCDC: `0x08`). To understand why the loading sequence has stopped, we reactivate the surgical tracing centered on the direction where the CPU now spends its time.

Hardware Concept

When a Game Boy game starts up, it typically follows this sequence:

  1. Startup Phase:The BIOS executes initialization code (low addresses: 0x0000-0x00FF).
  2. Transfer of Control:The game takes control and starts executing cartridge code (high addresses: 0x0100+).
  3. Game Initialization:The game turns off the screen (LCDC Bit 7 = 0), loads graphics into VRAM, configures palettes, and turns it back on.

The fact that the CPU is at `0x2B15` (ROM high zone) indicates that the game has passed the boot phase. However, if the game keeps the screen off and does not progress, you may be waiting for:

  • Timer (DIV):The DIV register (0xFF04) increments continuously. Some games wait for it to reach a specific value to sync.
  • Interruptions:The game may be waiting for an interrupt (V-Blank, Timer, etc.) that is not coming.
  • Joypad:Some games wait for user input before continuing.
  • Infinite Loop:If the code at `0x2B15` is an unconditional jump to itself (e.g. `JR -1`), the game is explicitly hung.

Surgical tracing will allow us to see exactly what instructions the CPU is executing and what registers it is consulting, which will reveal the cause of the blockage.

Implementation

We reactivate the "Sniper" in the CPU to trace instructions in the range `0x2B10-0x2B20`, where the CPU is spending its time according to Step 0227's autopsy.

Modification in CPU.cpp

We add a debug block in the `step()` method that is activated when the PC is in the target range:

// --- Step 0228: SNIPER AT 0x2B15 ---
if (regs_->pc >= 0x2B10 && regs_->pc<= 0x2B20) {
    uint8_t opcode_preview = mmu_->read(regs_->pc);
    uint8_t div_val = mmu_->read(0xFF04);
    
    printf("[SNIPER] PC:%04X | OP:%02X | AF:%04X | BC:%04X | DE:%04X | HL:%04X | DIV:%02X\n", 
           regs_->pc, opcode_preview, regs_->af, regs_->get_bc(), regs_->get_de(), regs_->get_hl(), div_val);
}
// ------------------------------------------

The plot prints:

  • PC:Program counter (current address)
  • OP:Opcode of the instruction to be executed
  • AF, BC, DE, HL:Status of main records
  • DIV:DIV (Timer) register value to check if it advances

Design decisions

We choose a small range (0x2B10-0x2B20) to minimize the impact on performance. The debug is only activated when the CPU is in that specific zone, so it does not affect the normal execution of the rest of the code. We also include the DIV record to verify if the Timer is advancing, which could be the cause of the crash.

Affected Files

  • src/core/cpp/CPU.cpp- Added surgical debug block in `step()` for range 0x2B10-0x2B20

Tests and Verification

To validate the implementation:

  1. Recompile: .\rebuild_cpp.ps1eitherpython setup.py build_ext --inplace
  2. Execute: python main.py roms/tetris.gb
  3. Parse output:Search lines with[SNIPER]on the console

What we are looking for:

  • If the code reads `0xFF04` (DIV) and compares, it is a Timer problem.
  • If the code reads `0xFF00` (Joypad), it is waiting for a button.
  • If it is an unconditional jump `JR -1`, it is an explicit hang (emulator Game Over).
  • If the opcode repeats constantly, it is an infinite loop.

Sources consulted

  • Bread Docs:Game Boy Pan Docs- Hardware specification
  • Implementation based on general knowledge of LR35902 architecture and emulator debugging techniques.

Educational Integrity

What I Understand Now

  • Startup Phase:The CPU executes BIOS code first, then transfers control to the cartridge.
  • High ROM Zone:Addresses 0x0100+ contain game code. If PC is at 0x2B15, the game has already taken control.
  • Lock on Initialization:Games may stop waiting for hardware (Timer, Interrupts, Joypad) that does not respond correctly.

What remains to be confirmed

  • Opcode at 0x2B15:What instruction the game is executing at that point.
  • Records consulted:Whether the game is reading DIV, Joypad or other hardware registers.
  • Execution Pattern:Whether it is an infinite loop or an active wait.

Hypotheses and Assumptions

Main Hypothesis:The game is waiting for the Timer (DIV) to advance, but the Timer is not incrementing correctly, or the game is waiting for an interrupt that is not being generated. The surgical layout will confirm or refute this hypothesis.

Next Steps

  • [ ] Run the emulator and analyze the Sniper output
  • [ ] Identify the opcode and execution pattern at 0x2B15
  • [ ] Check if the Timer (DIV) is advancing correctly
  • [ ] Determine if the blocking is by Timer, Interruptions or Joypad
  • [ ] Implement the corresponding fix according to the diagnosis