This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Debug: Trace from PC=0x0100 to Capture Hidden Loop
Summary
The `LY=0` deadlock persists, but there are no warnings for unimplemented opcodes, indicating that the CPU is in an infinite loop of valid instructions. The trace fired at `PC=0x0300` does not fire because the PC is stuck before. The plotting system is modified to activate from the start of the execution (`PC=0x0100`) and capture the infinite loop in action.
Hardware Concept
In a Game Boy emulator, when the CPU is stuck in an infinite loop, the scan line counter (LY) cannot advance because the CPU is not generating enough cycles for the PPU to progress. This is a classicchicken and egg deadlock:
- The game awaits:The game code is waiting for the PPU to do something (e.g. enter V-Blank, change the STAT state).
- The PPU cannot advance:The PPU cannot do anything because `LY` does not advance.
- LY does not advance:`LY` does not advance because the CPU is stuck and not giving it enough cycles.
If the CPU is executing valid instructions (without unknown opcode warnings), but the system is not advancing, the only logical explanation is that the CPU is executing a small loop of code over and over again, so fast that the cycle accumulator in `viboy.py` never reaches the 456 cycles needed to advance a single line of the PPU.
To identify this loop, we need to see what the CPU is doing in the first place. The "triggered" trace at `PC=0x0300` is useless if the PC never gets there. Therefore, we move the trigger to the beginning of the program (`PC=0x0100`), which is where the ROM execution begins after boot.
Implementation
The tracing system in `CPU.cpp` is modified to activate from the beginning of the execution and capture a greater number of instructions to identify the loop pattern.
Modifications in CPU.cpp
Plotting constants are changed:
- DEBUG_TRIGGER_PC:From `0x0300` to `0x0100` (program start).
- DEBUG_INSTRUCTION_LIMIT:From `100` to `200` (for further instructions and to capture the loop pattern).
Modified Code
// Static variables for diagnostic logging with system "triggered"
// We change the trigger at the beginning of the program to see the hidden loop
static const uint16_t DEBUG_TRIGGER_PC = 0x0100; // Trace activation address (program start)
static bool debug_trace_activated = false; // Activation flag
static int debug_instruction_counter = 0; // Post-activation counter
// We increase the limit a little to see the loop pattern
static const int DEBUG_INSTRUCTION_LIMIT = 200; // Post-activation limit (increased to capture loops)
Compilation and Execution Process
To apply the changes:
- Recompile the C++ module:Run `.\rebuild_cpp.ps1` or `python setup.py build_ext --inplace`.
- Run the emulator:`python main.py roms/tetris.gb` (or any ROM).
- Analyze the trace:The console will display the first 200 instructions from the start of the program.
Expected Analysis
At the end of the trace, we should see a repeating pattern of 2 or 3 opcodes/PCs repeating over and over. This will be our infinite loop. A typical example would be:
...
[CPU TRACE 195] PC: 0x02B4 | Opcode: 0xFE (CP d8)
[CPU TRACE 196] PC: 0x02B6 | Opcode: 0x28 (JR Z, e)
[CPU TRACE 197] PC: 0x02B4 | Opcode: 0xFE (CP d8)
[CPU TRACE 198] PC: 0x02B6 | Opcode: 0x28 (JR Z, e)
[CPU TRACE 199] PC: 0x02B4 | Opcode: 0xFE (CP d8)
This pattern will tell us exactly what the game is waiting for. You're probably doing a `CP` (Compare) against a hardware register (like `STAT`) and waiting for it to change value, but since the PPU is stopped, that value never changes.
Modified Files
src/core/cpp/CPU.cpp- Modification of layout constants (lines 8-11).
Next Steps
- Recompile and run:Apply the changes and run the emulator to obtain the complete trace.
- Identify the loop:Analyze the trace to find the repeating pattern at the end.
- Determine the cause:Understand what hardware register the game is waiting for and why it doesn't change.
- Implement the solution:Correct the identified problem (it may be an unimplemented hardware register, an interrupt flag, or a synchronization problem).
References
- Bread Docs:Section on scan line counter (LY) and CPU-PPU synchronization.
- GBEDG:Documentation on the STAT register and interrupt flags.