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

Debug: Triggered CPU Trace Implementation

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

Summary

Analysis of the 2000-instruction trace (Step 0156) showed that the fixed-length trace method is inefficient in overcoming long ROM initialization routines. A "triggered" tracing system was implemented that is automatically activated when the Program Counter (PC) exceeds a specific address (0x0300), thus avoiding recording thousands of initialization loop instructions and allowing the critical code that is executed later to be directly captured.

Hardware Concept

Game Boy ROM boot routines can run thousands of instructions in memory cleanup loops before reaching the main game code. The layout of all these instructions is inefficient and generates massive logs that make analysis difficult.

A more efficient strategy is to useconditional plottingeithershot trace- Instead of logging the first N instructions from startup, the system waits until the Program Counter reaches a specific address (the "trigger") and only then starts logging. This allows:

  • Avoid noise:Thousands of initialization loop instructions are not recorded.
  • Targeted approach:Critical code that is executed after initialization is directly captured.
  • Efficiency:The size of the logs and analysis time are drastically reduced.

Based on the previous analysis, we know that Tetris initialization loops end around the address0x0297-0x0298. Set the trigger to0x0300guarantees that we will capture the code that is executed immediately after all the loops have finished.

Fountain:Standard debugging technique in emulation - Conditional Breakpoints / Triggered Tracing

Implementation

The fixed length plotting system was replaced with a triggered plotting system based on the Program Counter. The system now includes:

  • Activation address (trigger): DEBUG_TRIGGER_PC = 0x0300
  • Activation flag: debug_trace_activatedwhich is activated when the PC exceeds the trigger
  • Post-activation counter:Log up to 100 instructions after activation

Modified components

  • CPU.cpp:Complete replacement of system-triggered plotting logic.

Changes made

Updated static variables:

// Static variables for diagnostic logging with system "triggered"
static const uint16_t DEBUG_TRIGGER_PC = 0x0300; // Trace activation address
static bool debug_trace_activated = false;      // Activation flag
static int debug_instruction_counter = 0;       // Post-activation counter
static const int DEBUG_INSTRUCTION_LIMIT = 100; // Post-activation limit (reduced because it is now directed)

Updated builder:

CPU::CPU(MMU* mmu, CoreRegisters* registers)
    : mmu_(mmu), regs_(registers), cycles_(0), ime_(false), halted_(false), ime_scheduled_(false) {
    //...
    // Reset counter and debug flag when creating new instance
    debug_trace_activated = false;
    debug_instruction_counter = 0;
}

Plotting logic in step():

// --- NEW TRIGGERED PLOT LOGIC ---
// 1. Check if we should activate the trace when the PC exceeds the trigger
if (!debug_trace_activated && current_pc >= DEBUG_TRIGGER_PC) {
    printf("--- [CPU TRACE TRIGGERED at PC: 0x%04X] ---\n", current_pc);
    debug_trace_activated = true;
}

// 2. If the trace is active, record up to the limit
if (debug_trace_activated && debug_instruction_counter< DEBUG_INSTRUCTION_LIMIT) {
    printf("[CPU TRACE %d] PC: 0x%04X | Opcode: 0x%02X\n",
           debug_instruction_counter, current_pc, opcode);
    debug_instruction_counter++;
}
// --- FIN DE LA NUEVA LÓGICA DE TRAZADO DISPARADO ---

Design decisions

  • Trigger address (0x0300):Chosen based on previous analysis which showed that loops end around 0x0297-0x0298. 0x0300 provides a safety margin.
  • Reduced limit (100 instructions):Since the trace is now targeted and captures critical code, 100 instructions should be enough to identify the next missing opcode.
  • Static flag:A static flag is used to maintain state between calls tostep(), allowing the system to activate only once and then record the following instructions.

Affected Files

  • src/core/cpp/CPU.cpp- Complete replacement of the plotting system with PC-based triggered logic.

Tests and Verification

Verification will be done by running the emulator with a test ROM:

  • Compile command: .\rebuild_cpp.ps1
  • Execute command: python main.py roms/tetris.gb
  • Expected result:
    • The console should remain silent during initialization loops.
    • When the PC reaches 0x0300, you should see:--- [CPU TRACE TRIGGERED at PC: 0x0300] ---
    • The 100 critical instructions that are executed next should then appear.
  • Validation:The new trace should be radically different from the previous one, without showing any instructions from the 0x0293-0x0295 loop, and directly capturing the code of the next initialization phase.

Note: Full validation requires running the emulator and parsing the console output. This step documents the implementation of the triggered tracing system.

Sources consulted

  • Standard Debugging Technique: Conditional Breakpoints / Triggered Tracing
  • Previous trace analysis (Steps 0155 and 0156) that identified the location of the initialization loops

Note: This is a standard debugging technique in emulation, not specific to Game Boy hardware.

Educational Integrity

What I Understand Now

  • Conditional plotting:Tracing all instructions from the beginning is inefficient when there are long initialization routines. Conditional tracing allows you to focus on critical code.
  • Debugging strategy:Instead of using "brute force" (increasing the limit indefinitely), it is better to use a targeted strategy that captures only what is relevant.
  • Analysis of previous traces:Analysis of the traces above allowed us to identify where the initialization loops end, allowing us to choose an appropriate trigger direction.

What remains to be confirmed

  • Trigger effectiveness:We need to verify that 0x0300 is an appropriate address and that it captures critical code without losing important information.
  • Limit of 100 instructions:We need to confirm that 100 instructions are enough to identify the next missing opcode.
  • Analysis of the new trace:Once the emulator is run, we need to analyze the new trace to identify the next opcode that we should implement.

Hypotheses and Assumptions

Main hypothesis:The code that runs after 0x0300 will contain the following missing opcode that we need to implement for the game to continue. This hypothesis is based on previous analysis that showed that the initialization loops end around 0x0297-0x0298.

Next Steps

  • [ ] Recompile the C++ module with.\rebuild_cpp.ps1
  • [ ] Run the emulator withpython main.py roms/tetris.gb
  • [ ] Parse the new directed trace to identify the next missing opcode
  • [ ] Implement the identified opcode if necessary
  • [ ] Verify that the game can continue beyond initialization