This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
V-Blank Handler Audit
Summary
This Step implements the "Deep Handler Audit" Operation to investigate the execution flow from the V-Blank interrupt vector (0x0040). Analysis of Step 0280 confirmed that interrupts are enabled correctly (IE=0x0D, IME=1) and that the CPU jumps to vector 0x0040 constantly. However, the game is still stuck in the polling loop because the V-Blank handler does not appear to be updating the progress flag at 0xD732.
Implemented a sniper that identifies the jump destination address (JP) in vector 0x0040 and an Execution Sniper that captures the instructions within the V-Blank routine until a RETI (0xD9) is found. This will allow you to identify what the game code does once it enters the interrupt and why it doesn't communicate with the main loop.
Hardware Concept
The Game Boy uses fixed interrupt vectors in low memory (Bank 0) to handle hardware events. When an enabled interrupt occurs, the hardware automatically performs a PUSH PC, disables the IME and jumps to a specific address:
- 0x0040:V-Blank (Vertical Blanking)
- 0x0048:LCD STAT
- 0x0050:Timer
- 0x0058:Serial
- 0x0060:joypad
Because these vectors only have 8 bytes of space before the next vector, standard developer practice is to place an instructionJP nn(3 bytes) that jumps to a longer routine located elsewhere in the ROM. The interrupt routine (ISR) must end withRETI(Return and Enable Interrupts), which recovers the PC from the battery and reactivates the interrupt master (IME).
Fountain:Pan Docs - "Interrupts": "When an interrupt is serviced, the PC is pushed onto the stack and the CPU jumps to the interrupt vector. The IME flag is reset... The ISR usually finishes with a RETI instruction."
Implementation
Two tracking mechanisms were added inCPU.cpp:
1. Jump Destination Trace at 0x0040
When the CPU reaches the V-Blank vector, we read the next two bytes into memory (Little-Endian) to identify which address the V-Blank vector jumps to.JP nn.
// Location: CPU::step()
if (original_pc == 0x0040) {
uint16_t jump_target = mmu_->read(0x0041) | (static_cast(mmu_->read(0x0042))<< 8);
printf("[VBLANK-TRACE] Vector 0x0040: JP 0x%04X detectado. Iniciando rastreo del handler...\n", jump_target);
}
2. Sniper Run Handler
A Sniper was implemented that captures the instructions executed within the handler. The trace is activated when entering 0x0040 and stops when it is found
the instructionRETI(0xD9) or a limit of 100 instructions is reached.
// Location: CPU::step()
static bool in_vblank_handler = false;
static int handler_step_count = 0;
if (original_pc == 0x0040) {
in_vblank_handler = true;
handler_step_count = 0;
}
if (in_vblank_handler && handler_step_count< 100) {
uint8_t op = mmu_->read(original_pc);
printf("[HANDLER-EXEC] PC:0x%04X OP:0x%02X | A:0x%02X HL:0x%04X | IME:%d\n",
original_pc, op, regs_->a, regs_->get_hl(), ime_ ? 1 : 0);
handler_step_count++;
if (op == 0xD9) {
printf("[HANDLER-EXIT] RETI detected on PC:0x%04X. End of handler trace.\n", original_pc);
in_vblank_handler = false;
}
}
Affected Files
src/core/cpp/CPU.cpp- Implementation of Jump Destination Tracker and V-Blank Handler Execution Sniper
Tests and Verification
Verification will be performed by running Pokémon Red and analyzing the flow of instructions within the handler.
Command executed:
python main.py roms/pkmn.gb > debug_step_0281.log 2>&1
Validation:If the log shows the message[VBLANK-TRACE]followed by multiple[HANDLER-EXEC], we will have captured the handler flow.
We will look for memory writing instructions (such asRHP (HL), AeitherLD (nnnn), A) that affect address 0xD732.
Compiled C++ module validation:
python setup.py build_ext --inplace
Sources consulted
- Bread Docs:https://gbdev.io/pandocs/- "Interrupts" section
- Bread Docs:https://gbdev.io/pandocs/- "CPU Instruction Set" section
Educational Integrity
What I Understand Now
- Interruption Vectors:They are fixed addresses where the CPU jumps when an event occurs. Due to their limited size, they often contain jumps to more complex routines.
- V-Blank Handler:It is responsible for making the graphical and game logic updates that must be synchronized with the end of the screen drawing.
- RETI:It is the critical instruction that closes the interruption cycle, recovering the normal flow and reactivating the ability to receive new interruptions.
Next Steps
- [ ] Recompile the C++ core and run Pokémon Red
- [ ] Analyze the log
[HANDLER-EXEC]to identify where the V-Blank jumps - [ ] Check if the handler tries to modify 0xD732
- [ ] If the handler jumps to an empty or corrupted zone, investigate why ROM bank 0 is not accessible