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

Analysis: Post-Initialization Loop CPU Trace

Date:2025-12-20 StepID:0153 State: 🔍 DRAFT

Summary

After correcting the Zero (Z) flag bug in the `DEC B` instruction (Step 0152), the emulator was run with the Tetris ROM to capture and analyze the new CPU trace. The goal was to verify that the initialization loop ended correctly and to find out what instructions the game executes after exiting the loop. Analysis confirmed that the loop terminates correctly when `B` reaches `0x00`, but revealed that there are multiple loops nested in the initialization routine.

Hardware Concept

The initialization routine of a Game Boy game typically performs multiple sequential tasks:

  1. Memory cleaning:Clears regions of RAM (WRAM, VRAM, OAM) by writing specific values ​​(usually `0x00` or `0xFF`).
  2. Hardware configuration:Initializes I/O registers (LCDC, BGP, etc.).
  3. Graphics loading:Copy tool data from ROM to VRAM.
  4. Palette configuration:Set the color palettes.

Each of these tasks may require multiple nested loops. For example, clearing VRAM (8KB) ​​requires writing 8192 bytes, which is typically done with a loop that decrements a counter to zero. If there are multiple memory regions to clean, there will be multiple nested loops.

Nested loops on Game Boy:It is common to use different registers for each loop (e.g. `B` for the inner loop, `C` for the outer loop). When the inner loop ends (when `B` reaches `0x00`), the code can continue with another task or enter another loop.

Trace Analysis

The emulator was run with the Tetris ROM (`roms/tetris.gb`) and the trace of the first 200 instructions was captured (increased from 150 to capture more information).

Analysis Results

1. Bug Fix Confirmation

The trace confirmed that the fix in Step 0152 works correctly:

  • The loop `LDD (HL), A -> DEC B -> JR NZ` is executed correctly.
  • When `B` reaches `0x00`, the `Z` flag is set correctly (`Z: 1`).
  • The `JR NZ` instruction does not jump when `Z=1`, and the loop ends.
  • The PC remains at `0x0297` after exiting the loop.

2. Discovery of Nested Loops

Analysis revealed that there are multiple nested loops in the initialization routine:

[CPU TRACE 8] PC: 0x0293 | Opcode: 0x32 (LDD (HL), A)
[CPU TRACE 9] PC: 0x0294 | Opcode: 0x05 (DEC B)
  [DEBUG DEC B] B before: 0x00, Z before: 1
  [DEBUG DEC B] B after: 0xFF, Z after: 0
[CPU TRACE 10] PC: 0x0295 | Opcode: 0x20 (JR NZ, e)
  [DEBUG JR NZ] B: 0xFF, Z: 0, offset: 0xFC
  [DEBUG JR NZ] JUMPING to PC: 0x0293
...
[CPU TRACE 1224] PC: 0x0295 | Opcode: 0x20 (JR NZ, e)
  [DEBUG JR NZ] B: 0x00, Z: 1, offset: 0xFC
  [DEBUG JR NZ] NOT JUMPING, continuing on PC: 0x0297
  [DEBUG DEC B] B before: 0x00, Z before: 0
  [DEBUG DEC B] B after: 0xFF, Z after: 0
  [DEBUG JR NZ] B: 0xFF, Z: 0, offset: 0xFC
  [DEBUG JR NZ] JUMPING to PC: 0x0293

Critical observation:Immediately after the loop ends at `PC:0x0297`, another `DEC B` appears and restarts the loop. This suggests that:

  • There is an outer loop that controls multiple iterations of the inner loop.
  • The code at `0x0297` probably contains another `DEC` instruction (possibly `DEC C` or `DEC E`) that decrements the outer loop counter.
  • After decrementing the outer counter, the code resets `B` and re-enters the inner loop.

3. Insufficient Trace Limit

The 200 instruction limit is still not enough to see what happens after all the loops finish. The trace shows the loop restarting multiple times, but does not capture the moment when all loops terminate and the code continues to the next initialization phase.

Modifications Made

  • src/core/cpp/CPU.cpp: Increased `DEBUG_INSTRUCTION_LIMIT` from 150 to 200 to capture more instructions.

Affected Files

  • src/core/cpp/CPU.cpp- Increased trace limit from 150 to 200 instructions

Tests and Verification

This step is for analysis, not implementation. The verification was carried out by:

  1. Running the emulator: python main.py roms/tetris.gb > temp_trace2.log 2>&1
  2. Log analysis:Search for patterns in the trace to identify loop behavior.
  3. Fix confirmation:Verification that the loop terminates correctly when `B=0x00` and `Z=1`.

Result:The loop ends correctly, but there are multiple nested loops that require further analysis.

Sources consulted

  • Bread Docs:Game Boy Pan Docs- General architecture reference
  • Execution trace analysis: Logs captured from the emulator running Tetris

Educational Integrity

What I Understand Now

  • Nested loops in initialization:Game Boy games use nested loops to clear multiple memory regions. Each loop has its own counter (register), and when the inner loop ends, the code can reset the counter and re-enter, or continue with another task.
  • Fix verification:The Z flag fix works correctly. The loop ends when `B=0x00` and `Z=1`, confirming that the fix in Step 0152 is effective.
  • Trace limits:To analyze complex routines with multiple nested loops, it may be necessary to increase the trace limit or use more sophisticated logging techniques (e.g., conditional logging that only logs when exiting loops).

What remains to be confirmed

  • Complete loop structure:We don't know how many nested loops there are or what registers are used for each loop. We need to see more instructions or use smarter logging.
  • Next initialization phase:We don't know what happens after all the loops end. What instructions does the game execute next? Do you configure I/O logs? Does it copy graphics to VRAM?
  • Opcodes not implemented:There may be unimplemented opcodes that block execution after the loops terminate.

Hypotheses and Assumptions

Hypothesis:The code at `0x0297` probably contains a `DEC` instruction that decrements the outer loop counter (possibly `DEC C` or `DEC E`), followed by an instruction that resets `B` and re-enters the inner loop. This would explain why we see another `DEC B` immediately after exiting the loop.

Assumption:We assume that the nested loop structure is standard for memory clearing routines on the Game Boy. This is consistent with common patterns in initialization code.

Next Steps

  • [ ] Increase the trace limit further (ex: 500-1000 instructions) to capture the moment when all loops terminate.
  • [ ] Implement conditional logging that only logs when exiting loops or when the PC changes to an address outside the loop range.
  • [ ] Analyze the extended trace to identify which opcodes are executed after all loops terminate.
  • [ ] Identify any unimplemented opcode that may be blocking execution.
  • [ ] Document the complete structure of the initialization loops once the full trace is captured.