This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Diagnostics: V-Blank Wait Loop and VRAM Writes
Summary
After confirming that the game is not writing to VRAM (VRAM writes=0), a system was added Periodic diagnostic that shows the full status of the emulator every 5 seconds. The diagnosis revealed that the game is in a small loop (PC oscillates between 0x006B, 0x006D, 0x006F) running very few instructions (~60 per second), which prevents the PPU from advancing enough to reach V-Blank (LY=144). The game is waiting for V-Blank before copying graphics to VRAM, but it never arrives because the PPU moves too slowly.
Hardware Concept
On the Game Boy, the PPU (Pixel Processing Unit) advances synchronized with the CPU. Each instruction of the CPU consumes clock cycles (M-Cycles), which are converted to T-Cycles (clock cycles of the system) to advance the PPU. The PPU requires 456 T-Cycles to complete a scan line, and 144 visible lines + 10 V-Blank lines = 154 lines per full frame.
Critical dependency:If the CPU executes too few instructions, the PPU advances too fast. slow. If the PPU does not reach V-Blank (LY=144), the game that V-Blank is waiting for never leaves the waiting loop. This creates a vicious cycle: the game expects V-Blank, but V-Blank doesn't happen. because the game executes very few instructions.
STAT Polling:Many games poll the STAT register (0xFF41) waiting for Allow the PPU to enter V-Blank mode (bit 0-1 = 01) before copying graphics to VRAM. If the PPU never reaches V-Blank, the game is stuck in the waiting loop.
Source: Pan Docs - LCD Timing, V-Blank, STAT Register, System Clock
Implementation
Added a periodic diagnostic system in the main execution loop that shows full emulator state information every 5 seconds, regardless of whether there are frames ready or not. This allows diagnosing timing and synchronization problems.
Modified components
- Viboy (src/viboy.py):
- Added executed instruction counter
- Added periodic diagnostic every 5 seconds showing:
- Number of instructions executed
- Number of VRAM writes detected
- Current PC (Program Counter)
- CPU HALT Status
- LCDC (LCD Control Register)
- STAT (LCD Status Register)
- LY (PPU Current Line)
- IF (Interrupt Flag Register)
- IE (Interrupt Enable Register)
- Modified the heartbeat to include the VRAM write counter
- MMU (src/memory/mmu.py):
- Added method
get_vram_write_count()to get the write counter in VRAM - Added informational message upon initialization indicating that VRAM diagnostic is active
- Added method
- PPU (src/gpu/ppu.py):
- Temporary logging enabled when V-Blank (LY=144) is generated for diagnostics
- main.py:
- Configured UTF-8 encoding for Windows to allow displaying emojis in console
- Changed logging level to INFO temporarily for diagnostics
Design decisions
- Frame-independent periodic diagnosis:The diagnosis is run every 5 seconds based on real time, not frames. This allows problems to be diagnosed even when there are no frames ready or when the game is frozen.
- Complete status information:Diagnostic shows all logs (PC, LCDC, STAT, LY, IF, IE) to fully understand what the emulator at all times.
- V-Blank Logging:Logging was temporarily enabled when the PPU generates V-Blank to check if the PPU is advancing correctly.
Affected Files
src/viboy.py- Added periodic diagnostics every 5 seconds with complete information on the emulator statussrc/memory/mmu.py- Added methodget_vram_write_count()and active diagnostic information messagesrc/gpu/ppu.py- Enabled temporary logging when V-Blank is generatedmain.py- Set UTF-8 encoding for Windows and logging level to INFOdocs/bitacora/entries/2025-12-18__0062__diagnosis-loop-waiting-vblank.html(new)docs/bitacora/index.html(modified, added entry 0062)docs/bitacora/entries/2025-12-18__0061__unlocking-vram-diagnosis-writings.html(modified, updated "Next" link)COMPLETE_REPORT.md(modified, added entry 0062)
Tests and Verification
State: In diagnosis
Command executed:
python main.py pkmn.gb
Around:Windows 10, Python 3.13.5
Test ROM:Pokémon Red (user-contributed ROM, not distributed)
Observed result:
- Periodic diagnosis shows:
INFO: 📊 Diagnostics: 304 instructions, VRAM writes=0, PC=0x006F, HALTED=False, LCDC=0x80, STAT=0x00, LY=6, IF=0x00, IE=0x00 INFO: 📊 Diagnostics: 605 instructions, VRAM writes=0, PC=0x006B, HALTED=False, LCDC=0x80, STAT=0x00, LY=13, IF=0x00, IE=0x00 INFO: 📊 Diagnostics: 906 instructions, VRAM writes=0, PC=0x006D, HALTED=False, LCDC=0x80, STAT=0x00, LY=20, IF=0x00, IE=0x00 INFO: 📊 Diagnostics: 1,208 instructions, VRAM writes=0, PC=0x006B, HALTED=False, LCDC=0x80, STAT=0x00, LY=27, IF=0x00, IE=0x00
Interpretation of the result:
- The game IS running code:The PC switches between 0x006B, 0x006D, 0x006F, indicating that it is in a small but active loop.
- The game executes very few instructions:Only ~60 instructions per second (304 instructions in 5 seconds). A real Game Boy executes millions of instructions per second, so this is extremely slow.
- The PPU advances very slowly:LY only reaches 6, 13, 20, 27... in 5 seconds. For reaching V-Blank (LY=144) would take much longer.
- The game is NOT writing to VRAM:VRAM writes=0 in all diagnostics.
- The game is waiting for V-Blank:The PC oscillates in a very low range (0x006B-0x006F), suggesting a waiting loop. You are probably polling STAT hoping that the PPU enters V-Blank mode.
- No interrupts enabled:IE=0x00 (no interrupts enabled) and IF=0x00 (no interrupts pending).
- LCDC=0x80:LCD is on (bit 7=1), but BG Display is off (bit 0=0).
- STAT=0x00:The PPU is in mode 0 (H-Blank), not V-Blank.
What is valid:This diagnosis confirms that:
- ✅ The game is running code (not completely frozen)
- ✅ The game is NOT writing to VRAM (VRAM writes=0)
- ⚠️ The game executes very few instructions (~60 per second, extremely slow)
- ⚠️ PPU advances very slowly (LY only reaches 27 in 5 seconds, needs to reach 144 for V-Blank)
- ⚠️ The game is in a loop waiting for V-Blank which never happens because the PPU moves too slowly
Next step:Investigate why the game executes so few instructions. Possible causes:
- The game is in a very slow loop (many instructions per iteration)
- There is a problem with the timing that makes the emulator go very slow
- The game is waiting for some event that never happens
- There is a problem with the PPU synchronization that causes it to move very slowly
Sources consulted
- Bread Docs:LCD Timing
- Bread Docs:LCD Status Register (STAT)
- Bread Docs:V-Blank
- Bread Docs:System Clock
Educational Integrity
What I Understand Now
- Critical dependency between CPU and PPU:The PPU advances synchronized with the CPU. If the CPU executes too few instructions, the PPU advances very slowly. If the PPU does not reach V-Blank, the game you're waiting for V-Blank never leaves the waiting loop. This creates a vicious circle.
- STAT Polling:Many games poll the STAT register waiting for Allow the PPU to enter V-Blank mode before copying graphics to VRAM. If the PPU never reaches V-Blank, the game gets stuck in the waiting loop.
- Systematic diagnosis:Add complete emulator status information (PC, LCDC, STAT, LY, IF, IE) allows you to diagnose timing and synchronization problems of systematically. Without this information, it is impossible to understand why the game is not progressing.
- Execution speed:A real Game Boy executes millions of instructions per second. If the emulator executes only ~60 instructions per second, there is a serious problem timing or synchronization.
What remains to be confirmed
- Why does the game execute so few instructions?I need to investigate if
the problem is:
- A very slow loop in the game code
- A problem with the emulator timing
- A problem with PPU synchronization
- The game is waiting for some event that never happens
- Does the PPU reach V-Blank if we wait longer?Yes after 20-30 seconds the PPU reaches LY=144 and V-Blank is generated, then the problem is only speed. If never arrives, there is a more fundamental problem with timing.
- Does the game go out of loop when V-Blank occurs?If the PPU reaches V-Blank and the game exits the loop, then the problem is solved. If it doesn't come out, there is another problem (maybe the game is waiting for something else besides V-Blank).
Hypotheses and Assumptions
Main hypothesis:The game is in a loop waiting for V-Blank, but the PPU It moves too slowly because the game executes too few instructions. This creates a circle vicious: the game expects V-Blank, but V-Blank does not occur because the game executes too few instructions.
Assumption:I assume the problem is speed/timing, not a fundamental bug in implementation. If the game ran at normal speed, the PPU would advance normally and would reach V-Blank, allowing the game to exit the loop and copy graphics to VRAM.
Next Steps
- [ ] Run the emulator and wait 20-30 seconds to check if the PPU reaches V-Blank (LY=144) and whether the V-Blank interrupt is generated ("🎯 PPU: V-Blank started" message).
- [ ] If the PPU reaches V-Blank but the game does not exit the loop, investigate what else is waiting the game (perhaps it is waiting for IE to be enabled or the interrupt to be processed).
- [ ] If the PPU never reaches V-Blank, investigate why the game executes so few instructions:
- Check if there is a problem with the emulator timing
- Check if there is a problem with PPU synchronization
- Check if the game is in a very slow loop
- Check if the game is waiting for some event that never happens
- [ ] Once the speed/timing problem is resolved, check if the game starts writing in VRAM after exiting the wait loop.