This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Misaligned CPU: The Case of Opcode 0x08
Summary
Forensic analysis of the "Sniper" trace (Step 0228) revealed a critical timing error:
the opcode0x08 (LD (nn), SP) was not implemented. This caused the CPU
will interpret the next 2 address bytes as instructions, completely misaligning the stream
execution and executing "garbage" that corrupted the flags and game logic.
Hardware Concept
LD (nn), SPis an instruction3 bytesthat the Stack Pointer (SP) saves
at an absolute memory address specified directly in the code:
- Byte 1:Opcode
0x08 - Byte 2:Low address (LSB) in Little-Endian format
- Byte 3:Upper address (MSB) in Little-Endian format
The instruction writes SP to the specified address in Little-Endian format: low byte first
(SP & 0xFF) at addressnn, then the high byte (SP >> 8) in the addressnn + 1.
The Problem of Misalignment:
If this instruction is not implemented, the CPU treats it as a 1-byte instruction (probably
a NOP defaults or falls in the casedefault). Then when you try to run the following
instruction, reads bytes 2 and 3 of the previous instruction as if they were new opcodes. This causes:
- Misalignment:The PC does not advance correctly, jumping over data that should be addresses.
- Execution of "Trash":The data bytes are interpreted as instructions (ex:
0x2F=CPL,0x3F= CCF). - Flag Corruption:Instructions executed in error modify the flags unexpectedly.
- Broken Flow:The game completely loses control of the execution.
This type of error is especially difficult to detect because the game may continue to "run" for a long time, but with completely corrupt logic.
Implementation
We implement the opcode0x08in the methodstep()ofCPU.cpp,
following the Pan Docs specification.
Modification in CPU.cpp
We add the case0x08to the opcode switch, just afterNOPE(0x00):
// --- Step 0231: FIX MISALIGNMENT ---
// LD (nn), SP - Store the Stack Pointer at address nn
// This instruction is 3 bytes. If missing, the CPU executes the data
// of the address as instructions, corrupting the flow.
// Source: Pan Docs - LD (nn), SP: 5 M-Cycles
case 0x08: // LD (nn), SP
{
uint16_t addr = fetch_word(); // Consumes 2 more bytes (nn in Little-Endian)
// Write SP in Little Endian format (low byte first, high byte second)
mmu_->write(addr, regs_->sp & 0xFF); // Low byte
mmu_->write(addr + 1, (regs_->sp >> 8) & 0xFF); // High byte
cycles_ += 5; // LD (nn), SP consumes 5 M-Cycles
return 5;
}
// -------------------------------------
Design decisions
- Timing:5 M-Cycles according to Pan Docs (fetch opcode + fetch 2 bytes of address + 2 writes to memory).
- Little-Endian:The address is read in Little-Endian format (LSB first), and SP is also written in Little-Endian.
- Location:Placed just after
NOPEto maintain the logical order of opcodes.
Affected Files
src/core/cpp/CPU.cpp- Added case0x08in the method opcode switchstep()
Tests and Verification
To validate the implementation:
- Recompile:
.\rebuild_cpp.ps1eitherpython setup.py build_ext --inplace - Execute:
python main.py roms/tetris.gb - Analyze trace:Verify that "Sniper" no longer displays opcodes
0x2Fand0x3Frunning after0x08in the expected directions.
Expected Result:
- The CPU will no longer "eat" intermediate bytes after
0x08. - The logical flow of the game should be restored.
- High chance:Let the game start and see graphics!
- Medium chance:Let it move on to the next unimplemented (but already on the right track) opcode.
Compiled C++ module validation:The opcode runs directly in the C++ core, without Python overhead, guaranteeing precise timing of 5 M-Cycles.
Sources consulted
- Bread Docs:Game Boy Pan Docs- Opcode specification
LD (nn), SP(0x08) - Implementation based on general knowledge of LR35902 architecture and forensic analysis of execution traces.
Educational Integrity
What I Understand Now
- Instruction Misalignment:If a multi-byte instruction is not implemented, the CPU may interpret the data bytes as code, causing "garbage" execution.
- Forensic Trace Analysis:Comparing the sequence of executed opcodes to the expected sequence may reveal missing or poorly implemented opcodes.
- Little-Endian on Game Boy:Both addresses and 16-bit values are stored in Little-Endian (LSB first) format.
What remains to be confirmed
- Other Missing Opcodes:There may be more unimplemented opcodes that cause similar problems.
- Game Recovery:If this was the only problem, the game should boot correctly now.
Hypotheses and Assumptions
Main Hypothesis:The opcode0x08was the main cause of the misalignment.
With its implementation, the game should be able to properly progress through its initialization sequence.
If there are still problems, it's probably other missing opcodes or hardware synchronization issues.
Next Steps
- [ ] Run the emulator and verify that the misalignment has been corrected
- [ ] Analyze if the game progresses beyond the previous crash point
- [ ] Check if graphics appear or if the game turns on the screen
- [ ] If there are new errors, analyze the trace to identify the next missing opcode