This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Debug: Instrument Default Case to Capture Unknown Opcodes
Summary
The case was modifieddefaultin the methodCPU::step()to implement a "fail-fast" strategy that terminates execution immediately when an unimplemented opcode is encountered, instead of returning 0 cycles and causing a silent deadlock. This allows you to quickly identify which opcodes are missing to be implemented by displaying a fatal error message with the exact opcode and PC where the problem occurs.
Hardware Concept: Fail-Fast Debugging
In emulator development, it is standard practice to have the kernel fail loudly and early when it encounters an unexpected condition, such as an unknown opcode. Instead of allowing the emulator to continue in an undefined state (like our deadlockLY=0), we force it to stop immediately, showing us the exact cause of the problem.
The "fail-fast" strategy dramatically speeds up the debugging cycle because:
- Immediate Identification:The program terminates at the exact moment it encounters the problem, not after executing thousands of instructions in a corrupted state.
- Precise Information:Reports the exact opcode and memory address (PC) where the failure occurs, allowing direct and efficient investigation.
- Avoid Undefined States:It prevents the emulator from entering infinite loops or corrupted states that are difficult to debug retrospectively.
In our specific case, the deadlock ofLY=0persisted becausecpu.step()was returning 0 cycles repeatedly when encountering unimplemented opcode. When switching to fail-fast, the emulator will immediately stop and show us which opcode is missing, allowing us to deploy it and continue emulation.
Implementation
The case was modifieddefaultin the methodCPU::step()insrc/core/cpp/CPU.cppso that, instead of printing a warning and returning 0 cycles, it prints a fatal message and ends the execution withexit(1).
Modified components
src/core/cpp/CPU.cpp: Modified the casedefaultof the opcode switch to implement fail-fast.
Changes made
The previous code only printed a warning and returned 0 cycles:
default:
printf("[CPU WARN] Opcode not implemented: 0x%02X on PC: 0x%04X. Returning 0 cycles.\n", opcode, current_pc);
return 0;
The new code implements fail-fast:
default:
// --- Instrumentation of Step 0168 ---
// Fail-fast: If we find an unknown opcode, we report it
// and we finish the execution immediately. This is much better than
// return 0 cycles and cause a silent deadlock.
printf("[CPU FATAL] Unimplemented opcode: 0x%02X at PC: 0x%04X\n", opcode, current_pc);
exit(1); // Terminate the program with an error code.
return 0; // Will not be reached, but maintains compiler logic.
Design decisions
It was decided to useexit(1)instead of throwing a C++ exception because:
- Simplicity:In this phase of development, we want immediate and visible failure, not complex exception handling.
- Clarity:The error message is printed directly to the console, making debugging easier.
- Efficiency:There is no exception handling overhead in critical execution code.
In the future, when the kernel is more complete, we might consider changing this to a more sophisticated logging system or exceptions, but to identify missing opcodes during development, fail-fast is the optimal strategy.
Affected Files
src/core/cpp/CPU.cpp- Modified the casedefaultto implement fail-fast
Tests and Verification
This mod does not require specific unit tests because it is a debugging tool that will be activated when running the emulator with a real ROM. Verification is done by running the emulator:
python main.py roms/tetris.gb
Expected result:The emulator should terminate immediately (without displaying the heartbeat loop) and display a fatal error message in the console in the format:
[CPU FATAL] Unimplemented opcode: 0xXX at PC: 0xXXXX
This message will identify exactly which opcode is missing and what memory address it is at, allowing for a quick and accurate fix.
Compiled C++ module validation:The module should recompile successfully. The filecstdlibIt was already included in the file, so no additional includes are needed.
Sources consulted
- Software Development Practice: "Fail-Fast" Strategy for Early Debugging
- Pan Docs: CPU Instruction Set - Reference for implementing missing opcodes once identified
Educational Integrity
What I Understand Now
- Fail-Fast in Emulation:It is better to make the emulator fail immediately with accurate information than to allow it to continue in an undefined state, especially during development.
- Silent Deadlock:Returning 0 cycles when an unknown opcode is encountered can cause the emulated time to not advance, creating a deadlock that is difficult to debug. Fail-fast avoids this problem completely.
- Debug Instrumentation:Adding specific debugging code (such as fatal error messages) at critical points in the code helps identify problems quickly.
What remains to be confirmed
- What specific opcode is causing the deadlock - this will be determined when the emulator is run with the new instrumentation.
- If there are multiple missing opcodes or just one that blocks early execution.
Hypotheses and Assumptions
Main hypothesis:There is at least one unimplemented opcode that runs during ROM initialization (before reachingPC=0x0300), causingcpu.step()return 0 cycles and the emulator goes into deadlock.
Assumption:Once the missing opcode is identified, implementing it should allow the emulation to proceed to the next missing opcode (if it exists) or until initialization completes and rendering begins.
Next Steps
- [ ] Recompile the C++ module with the new instrumentation
- [ ] Run the emulator with a ROM to identify the missing opcode
- [ ] Implement the identified opcode according to Pan Docs
- [ ] Repeat the process until the emulation progresses correctly
- [ ] Once the emulator advances, consider moving from fail-fast to a more sophisticated logging system for production