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

Triggered Execution Trace (Trap Trace)

Date:2025-12-18 StepID:0052 State: draft

Summary

Implemented a "triggered" execution trace system that automatically activates when the game writes `LCDC=0x80` (bottomless LCD ON). The system captures the first 100 instructions executed after this critical event, displaying detailed information for each instruction: PC, opcode, registers, flags, interrupt status (IF/IE), and PPU status (LY/STAT). This plot will allow you to identify if the game enters a polling loop waiting for V-Blank and why it does not exit that loop.

Hardware Concept

When a Game Boy game turns on the LCD by typing `LCDC=0x80`, it is turning on the screen but without the background (bit 0 = 0). This is a common transient state on initialization: the game turns on the LCD and then waits for V-Blank to occur to configure the rest of the graphics (activate background, sprites, etc.).

If the game has interrupts disabled (`IE=00`), you cannot use the automatic interrupt mechanism. Instead, you should dopolling(ask manually) by reading the `IF` register (0xFF0F) and checking bit 0 (V-Blank). If the bit is active, the game knows it is in V-Blank and can continue. If not, you must wait in a loop.

The issue we are investigating is the game getting stuck after turning on the LCD. The trace will allow us to see exactly which instructions are executed and if there is a polling loop that V-Blank never detects, which would indicate that our emulator is not correctly setting the V-Blank flag in `IF` when appropriate.

Fountain:Pan Docs - LCD Control Register, Interrupt Flag Register, V-Blank Polling

Implementation

Added a plotting system in the `Viboy` class that is automatically activated when a change in the LCDC register from any value to `0x80` is detected. The system captures the CPU status before each instruction and displays detailed information on the console.

Modified components

  • src/viboy.py: Added `_trace_active`, `_trace_counter`, and `_prev_lcdc` attributes to control tracing. The detection and tracing logic was implemented in the `run()` method.

Design decisions

Activation Detection:Plotting is activated when LCDC changes from any value to `0x80`. This captures the exact moment the game turns on the LCD, which is when the issue we're investigating begins.

Limit of 100 instructions:The plot is limited to 100 instructions to avoid flooding the console with information. This number is enough to see several cycles of a typical polling loop.

Information captured:The plot shows PC, opcode, all registers (A, BC, DE, HL, SP), flags (Z, N, H, C), interrupt registers (IF, IE), and PPU status (LY, STAT). This information is enough to understand what the game is doing and why it gets stuck.

Capture before tick:The PC and opcode are captured before the instruction is executed (`tick()`), to show the instruction to be executed, not the next one. This is most useful for debugging.

Affected Files

  • src/viboy.py- Added "triggered" plotting system with detection of LCDC change to 0x80

Tests and Verification

State:Run with test ROM (pkmn.gb).

Command executed: python main.py pkmn.gb

Around:Windows, Python 3.13.5

Observed result:The trace was successfully activated when the game wrote `LCDC=0x80`. 100 instructions were captured showing a clear polling loop:

TRACE [007]: PC=0x006B | OP=0xF0 | ... | IF=0x00 IE=0x00 | LY= 0 STAT=0x03 | Cycles=3
TRACE [008]: PC=0x006D | OP=0xFE | ... | IF=0x00 IE=0x00 | LY= 0 STAT=0x03 | Cycles=2
TRACE [009]: PC=0x006F | OP=0x20 | ... | IF=0x00 IE=0x00 | LY= 0 STAT=0x03 | Cycles=3
TRACE [010]: PC=0x006B | OP=0xF0 | ... | IF=0x00 IE=0x00 | LY= 0 STAT=0x03 | Cycles=3
...

Loop analysis:The identified pattern is:

  • 0xF0(LDH A, (n)) at PC=0x006B - Read IF (0xFF0F)
  • 0xFE(CP d8) at PC=0x006D - Compares A to an immediate value (probably 0x01 for the V-Blank bit)
  • 0x20(JR NZ, e) at PC=0x006F - Relative jump if non-zero (returns back if no V-Blank)

Identified problem:The trace shows that:

  • LY advances correctly (0 → 1 → 2), indicating that the PPU is working
  • IF is always 0x00, which means the V-Blank flag is never set.
  • The trace only captures 100 instructions (~1 line), but to get to LY=144 ~5,472 instructions are needed
  • The log does not appear🎯 PPU: V-Blank started, confirming that the PPU is not reaching LY=144

Hypothesis:The problem is that the PPU is not advancing fast enough, or there is some problem with the LCD verification. Implemented a fix to verify that the LCD is on before advancing the PPU.

Modifications made:

  • Increased trace limit from 100 to 1000 instructions to capture more information
  • Added check in PPU so that it only advances when the LCD is on (LCDC bit 7 = 1)
  • Added informative log when V-Blank is activated for diagnostics

State: draft- Pending verification if the correction solves the problem.

Sources consulted

Educational Integrity

What I Understand Now

  • Polling vs Interruptions:When interrupts are disabled, games must manually poll the IF register to detect events such as V-Blank. This is less efficient but allows full control over when events are processed.
  • Transient state LCDC=0x80:This value indicates that the LCD is on but the background is disabled. It is a common state during initialization, where the game waits for V-Blank to configure the rest of the graphics.
  • Triggered layout:A plotting system that is automatically activated when a specific event occurs (in this case, LCDC change to 0x80) is more useful than a continuous plot, as it captures exactly the critical moment without generating unnecessary noise.

What remains to be confirmed

  • Polling pattern:I need to check if the game actually goes into a polling loop and what exact instructions it uses. The layout will give us this information.
  • IF Activation:I need to confirm if our emulator is correctly setting IF bit 0 when the PPU goes into V-Blank. If you don't, the game will never detect V-Blank and you will be stuck.
  • V-Blank Timing:I need to check if the PPU timing is correct and if V-Blank activates at the right time according to the hardware specification.

Hypotheses and Assumptions

Main hypothesis:The game gets stuck because it enters a polling loop waiting for V-Blank, but our emulator is not correctly setting the V-Blank flag in IF when the PPU enters V-Blank mode.

Assumption:I assume the game uses polling because IE=00 (interrupts disabled). If the plot shows an IF read loop, this will confirm the assumption.

Next Steps

  • [ ] Run the emulator with a ROM (pkmn.gb) and capture the trace when activated
  • [ ] Analyze the trace to identify if there is a polling loop
  • [ ] If there is a loop, check if the V-Blank flag in IF is activated correctly
  • [ ] If the flag does not activate, check the PPU logic to ensure that it activates IF when it enters V-Blank
  • [ ] Check PPU timing to ensure V-Blank occurs at the correct time