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

Immediate Boot Monitor

Date:2025-12-18 StepID:0065 State: Verified

Summary

An aggressive emulator boot monitoring system was implemented to detect "silent deadlocks." The system prints the status of the CPU (PC, SP) in the first 20 steps of the main loop and adds protection against opcodes that return 0 cycles, avoiding cause the emulator to freeze in a mathematical infinite loop.

Hardware Concept

On a real Game Boy, each CPU instruction consumes a set number of cycles. machine (M-Cycles). The system clock advances continuously, and each component (CPU, PPU, Timer) consumes cycles to advance its internal state.

If an instruction returned 0 cycles (which should not happen on real hardware), the emulator timer would never advance, causing the main loop to hang stuck on the same instruction infinitely. This is a "silent deadlock": the program It doesn't crash, but it doesn't progress either.

The boot diagnostic allows you to identify:

  • Stagnant PC:If the Program Counter does not change between steps, the CPU is executing the same instruction repeatedly (possible infinite loop in game code or bug in the emulator).
  • Lock on initialization:If the emulator gets stuck before completing the first second, the problem is in the boot phase (possibly waiting for an event which never happens).
  • Opcode with 0 cycles:If an instruction that returns 0 cycles is detected, There is a bug in the opcode implementation that causes mathematical deadlock.

Fountain:Pan Docs - CPU Instruction Set, Timing, System Clock

Implementation

Two diagnostic mechanisms were added insrc/viboy.py:

1. Boot Monitor

At the start of the main looprun(), a counter was addeddebug_step_counterwhich prints the state of the CPU (PC and SP) in the first 20 steps of the loop. This allows verify that the CPU is executing instructions and advancing the Program Counter.

#DEBUG: Aggressive Boot Monitor
debug_step_counter = 0

while True:
    # DEBUG: Boot Monitor - print first 20 steps
    if debug_step_counter< 20:
        pc = self._cpu.registers.get_pc()
        sp = self._cpu.registers.get_sp()
        print(f"🚀 BOOT STEP {debug_step_counter}: PC={pc:04X} | SP={sp:04X}", flush=True)
    debug_step_counter += 1

2. Protection against Zero Cycles

In the methodtick(), a check was added that detects if the CPU returns 0 cycles. If this occurs, at least 1 cycle is forced to prevent the timer from freeze. This protection applies both in normal execution and in HALT state.

# Execute a normal instruction
cycles = self._cpu.step()

# CRITICAL: Infinite loop protection
if cycles == 0:
    pc = self._cpu.registers.get_pc()
    print(f"🚨 ALERT: CPU returned 0 cycles on PC={pc:04X}!", flush=True)
    cycles = 1 # Force forward to avoid hanging

Design decisions

  • Direct print instead of logger:It is usedprint()withflush=Trueto ensure immediate visibility, even if the level of logging is in WARNING or ERROR.
  • 20 step limit:The first 20 steps are enough to detect Immediate blockages without saturating the output.
  • Force 1 minimum cycle:If 0 cycles are detected, 1 cycle is forced to avoid deadlock, but keep the warning so the developer knows there is a bug in the opcode.

Affected Files

  • src/viboy.py- Added boot monitor and zero cycle protection

Tests and Verification

This change is purely diagnostic and does not require unit tests. Validation is done running the emulator and observing the boot monitor output.

Command executed: python main.py pkmn.gb --verbose

Around:Windows, Python 3.13.5

Result:✅ PASSED - Boot monitor worked correctly

Observations:

  • ✅ The CPU is executing instructions normally (PC advances: 0x0100 → 0x0101 → 0x0150 → ... → 0x1F68)
  • ✅ No "0 cycle" alerts appeared (no problematic opcodes detected)
  • ✅ Emulator ran without silent deadlock (user manually stopped it with Ctrl+C after 4211 cycles)
  • ⚠️ The heartbeat did not appear because the emulator stopped before completing 60 frames (only 4211 cycles executed, not enough to generate frames)

Interpretation:The original problem (not seeing heartbeat) is not a silent deadlock. The CPU is working correctly. The next step is to verify if the PPU is generating frames correctly, since theframe_countonly increases whenppu.is_frame_ready()returnstrue.

Next diagnosis needed:Add PPU status monitor (LY, mode, generated frames) to determine if the problem is that the PPU is not reaching V-Blank or if there is another problem.

Sources consulted

Note: 0 cycle protection is a security measure of the emulator. In hardware In reality, all instructions consume at least 1 M-Cycle.

Educational Integrity

What I Understand Now

  • Silent Deadlock:A program can get stuck without crashing, especially if the timer does not advance (cycles == 0).
  • Boot Diagnosis:Print CPU status on first steps allows you to quickly identify where the blockage occurs.
  • Defensive protection:Although on real hardware it shouldn't happen, it is It is useful to add checks that detect anomalous conditions and avoid deadlocks.

What remains to be confirmed

  • Actual behavior:I need to run the emulator with a ROM to check what the boot monitor shows and if it detects the deadlock problem.
  • Problematic opcode:If the 0 cycle alert appears, I need Identify which opcode is causing the problem and fix it.

Hypotheses and Assumptions

Main hypothesis:Emulator is getting stuck in main loop before being able to complete a second of execution. The boot monitor will allow you to identify if the problem is:

  • A PC that does not advance (infinite loop in the game code or bug in the emulator)
  • An opcode that returns 0 cycles (implementation bug)
  • A crash in pygame.event.pump() or graphical initialization

Next Steps

  • [x] Run the emulator with a ROM and observe the boot monitor output ✅
  • [x] Verify that the CPU is running correctly ✅
  • [ ] Add PPU status monitor (LY, mode, generated frames) to diagnose why the heartbeat does not appear
  • [ ] Check if the PPU is reaching V-Blank (line 144) and marking frames as ready
  • [ ] If the PPU is not generating frames, investigate the timing or status of the LCD (LCDC bit 7)