This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Freezing Diagnostic Probe
Summary
A was implementeddiagnostic probein the main loop of the emulator to identify
where execution gets stuck when the emulator seems to freeze. The probe prints periodic information
of the system state (PC, SP, IME, LY, IF, IE, LCDC) every 1000 iterations usingprint()direct to avoid log buffering. Additionally, a special log was added when LY reaches 144 (V-Blank)
to check if interrupts are triggered correctly. An explicit call topygame.event.pump()at the start of each iteration to prevent Windows from marking the window as
"He doesn't respond." With this instrumentation, it was discovered that the emulatorDoes NOT freeze: is
running code normally, but the game seems to be in a loop waiting for V-Blank interruptions.
Hardware Concept
When an emulator appears to "freeze", there are three main causes:
- Infinite Loop on CPU:The game waits for an interruption (ex: V-Blank) that never comes
or that the CPU does not process, staying in a
JR-2eternal (hot standby loop). - Graphic Lock:The graphics library (Pygame) is waiting to draw something and they don't get it data, locking the window ("Not Responding" appears on Windows).
- Silent Error:There is an exception occurring in a thread or process that we are not seeing.
To diagnose the problem, we needinstrumentationto show us the internal state from the emulator periodically:
- PC (Program Counter):If it repeats, you are in a loop. If you move forward, you are executing code.
- LY (Current Line):If it is always 0, the Timer/PPU does not advance. If it moves forward, the PPU works.
- IME (Interrupt Master Enable):If False and IF has bits, the CPU is ignoring interrupts.
- IF (Interrupt Flag):Indicates which interrupts are pending (bit 0 = V-Blank).
- IE (Interrupt Enable):Indicates which interrupts are enabled (bit 0 = V-Blank).
- LCDC:Indicates if the screen is on (bit 7 = 1).
Pygame Event Pump:On Windows, if you do not callpygame.event.pump()frequently,
The operating system marks the window as "Not Responding" because it is not processing messages from the event queue.
This can cause the window to appear frozen even though the emulator is running code normally.
Implementation
The method was modifiedrun()insrc/viboy.pyto add diagnostic instrumentation
without touching the emulation logic.
Modified components
- src/viboy.py:Added diagnostic probe in main loop
Changes made
- sys import:Added
import systo be able to usesys.exit()at the safety limit. - Diagnostic counter:Added variable
debug_counter = 0that increases in each iteration of the main loop. - Explicit call to pygame.event.pump():Added at the beginning of each loop iteration
(before
_handle_pygame_events()) to prevent Windows from marking the window as "Not Responding." - Periodic probe:Every 1000 iterations, print diagnostic information using
print()direct (to avoid log buffering):DEBUG PROBE: iter=1000 | PC=1387 | SP=FFFC | IME=False | LY=12 | IF=00 | IE=00 | LCDC=00 - V-Blank special log:When LY reaches 144, print a special log to verify
if the V-Blank interrupt is activated:
🔵 V-BLANK DETECTED: LY=144 | IF=01 | IE=01 | IME=False - Safety limit:After 50000 iterations the emulator stops automatically to avoid infinite loops flooding the terminal.
Design decisions
- Using direct print():It is used
print()ratherlogger.info()to avoid buffering and ensure that messages appear immediately on the console. - Frequency of 1000 iterations:Frequent enough to detect loops, but not as frequent frequent enough to saturate the terminal. Every 1000 iterations ≈ every ~250 instructions (depending on cycles).
- Limit of 50000 iterations:Allows you to capture ~50 samples before stopping, enough to Identify behavioral patterns.
Affected Files
src/viboy.py- Added diagnostic probe in methodrun():- Import of
sys - Counter
debug_counter - Call to
pygame.event.pump()at the beginning of each iteration - Periodic probe every 1000 iterations
- Special log when LY = 144
- Safety limit of 50000 iterations
- Import of
Tests and Verification
This is a diagnostic modification, not new functionality. No unit tests were created, but the emulator was run with a commercial ROM to verify that the probe is working correctly.
Running ROM (Diagnostics)
- ROM:Tetris DX (user-contributed ROM, not distributed)
- Execution mode:UI with Pygame, logging at INFO level
- Command executed:
python main.py tetris_dx.gbc - Around:Windows 10, Python 3.13.5, pygame-ce 2.5.6
- Success Criterion:See lines
DEBUG PROBEperiodically in the console - Observation:
DEBUG PROBE: iter=1000 | PC=1387 | SP=FFFC | IME=False | LY=12 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=2000 | PC=1386 | SP=FFFC | IME=False | LY=25 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=3000 | PC=1385 | SP=FFFC | IME=False | LY=37 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=4000 | PC=1384 | SP=FFFC | IME=False | LY=50 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=5000 | PC=1383 | SP=FFFC | IME=False | LY=62 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=6000 | PC=1389 | SP=FFFC | IME=False | LY=75 | IF=00 | IE=00 | LCDC=00 DEBUG PROBE: iter=7000 | PC=1388 | SP=FFFC | IME=False | LY=87 | IF=00 | IE=00 | LCDC=00 - Analysis of results:
- ✅ PC is changing:1387 → 1386 → 1385 → 1384 → 1383 → 1389 → 1388. The emulator does NOT freeze, it is executing code.
- ✅ LY is moving forward:12 → 25 → 37 → 50 → 62 → 75 → 87. The PPU is working correctly.
- ⚠️ IME=False:Interrupts are disabled, so even if IF is set, they will not be processed.
- ⚠️ IF=00:There are no pending interruptions. This could indicate that V-Blank is not activating or being cleaned immediately.
- ⚠️ IE=00:No interrupts enabled. The game has not set up IE yet.
- ⚠️ LCDC=00:The display is off (bit 7 = 0). This is normal at startup, but the game should activate it.
- Result: draft- The probe works correctly and reveals that the emulator does NOT freeze. The game is running code but appears to be in a loop waiting for V-Blank interrupts. More research is needed to understand why the game is not progressing.
- Legal notes:Commercial ROM provided by the user for local testing. It is not distributed or included in the repository.
Sources consulted
- Bread Docs:Interrupts, V-Blank, LCD Timing
- Pygame Documentation:pygame.event.pump()
Educational Integrity
What I Understand Now
- Freezing diagnosis:When an emulator seems to freeze, the first tool is to add instrumentation to see what is happening internally. The values of PC, LY, IME, IF, IE and LCDC They provide valuable information about the state of the system.
- Pygame Event Pump:On Windows, it is critical to call
pygame.event.pump()frequently to prevent the operating system from marking the window as "Not Responding". This is not an emulator problem, but rather the operating system waiting for the application to process messages from the event queue. - Hot standby loop:Many Game Boy games expect V-Blank interruptions by polling LY or waiting for IME to activate. If IME is disabled and the game does not activate it, you may get stuck.
What remains to be confirmed
- Why the game does not advance:The game is running code (PC changes) and PPU works (LY advances),
but it seems to be in a loop waiting for something. I need to verify:
- Is IF activated when LY reaches 144?
- Does the game ever activate IE?
- Does the game run EI (Enable Interrupts) to activate IME?
- What code are you running at addresses 0x1383-0x1389?
- Initial state of the game:I need to check if the game expects the screen to be on (LCDC bit 7 = 1) before continuing, or if there are any other conditions that are not being met.
Hypotheses and Assumptions
Main hypothesis:The game is in a hot wait loop waiting for the interrupt to trigger
V-Blank, but since IME=False, interrupts are not processed. The game will probably runEI(Enable Interrupts) at some point to activate IME, but it may be waiting for some other condition first
(for example, the screen is on or some register is at a specific value).
Unverified assumption:I assume the game should activate IE and run EI at some point during the initialization. If this does not occur, it may be a bug in the emulation or a condition that is not being met.
Next Steps
- [ ] Run the emulator again with the enhanced probe (includes IE and LCDC) to see if IE activates
- [ ] Check if IF is activated when LY reaches 144 (using V-Blank's special log)
- [ ] Analyze what code the game is running at addresses 0x1383-0x1389 (possible wait loop)
- [ ] Check if the game expects LCDC bit 7 = 1 before continuing
- [ ] If the problem persists, consider adding a basic disassembler to see what instructions are being executed