CPU Flow Diagnostics and Writes to VRAM

Summary

Complete instrumentation is implemented to diagnose whyVRAM remains empty (0/6144) after 120 secondsinpkmn.gb, even though the rendering works (correct checkerboard) and the Joypad works (polling confirmed in previous Steps).

The diagnosis reveals that theCPU DOES write to VRAM(10,000+ writes), but it is running a routinemass deletion(PC:0x36E3 writes zeros) followed by partial tile loading (18.08% non-zero). The game then enters apolling loop in Bank 28(PCs 0x614D-0x6153), expecting conditions that the emulator does not provide.

Context and Objective

Problem to Solve

After Steps 0380-0381, we confirm that:

  • ✅ Rendering works (checkerboard visible)
  • ✅ Joypad works (24,803 writes to P1, row reading correct)
  • VRAM remains empty(0/6144 non-zero bytes after 120 seconds)

Hypotheses to verify:

  • H1 (CPU/stream): The CPU is stuck in a loop/wait and never loads tiles.
  • H2 (VRAM locked): The CPU tries to load tiles but the writes to0x8000–0x9FFFare blocked incorrectly.

Step Objective

Determine which hypothesis (H1 or H2) is real by implementing:

  1. Writes to VRAM (0x8000-0x9FFF)
  2. PC sampler and loop detection
  3. Interrupt status (IE/IF/IME)

Hardware Concept

Access to VRAM by PPU Modes (Pan Docs)

According toPan Docs - VRAM Access:

  • VRAM (0x8000-0x9FFF): Area where tiles (Tile Data) and tilemaps (Tile Maps) are loaded.
  • Access restriction: In real hardware, the CPUcan'taccess VRAM duringPPU Mode 3(pixel transfer). In other modes (Mode 0 = H-Blank, Mode 1 = V-Blank, Mode 2 = OAM Search), the CPU can access.
  • LCDC (0xFF40): Controls the status of the LCD. Bit 7=1 activates the LCD.

Role of VBlank and Timer (Pan Docs)

According toPan Docs - Interrupts:

  • VBlank (IE/IF bit 0): Interrupt generated at the beginning of the V-Blank period (LY=144). Many games load tiles during V-Blank to avoid visual artifacts.
  • Timer (IE/IF bit 2): Interruption generated by the Timer when TIMA overflows.
  • IME (Interrupt Master Enable): Global flag that enables/disables interrupt processing.
  • YeahIE/IME/IF are poorly handled, the game can hang around waiting for an interruption that never comes, which blocks progression (including loading tiles).

Implementation

Task 1: Instrumentation of Writes to VRAM (MMU)

It was modifiedsrc/core/cpp/MMU.cppto add:

  • Global counters: vram_write_total_step382_andvram_write_nonzero_step382_.
  • Limited logs: Only the first 50 scripts with complete information:
    • Current PC (debug_current_pc)
    • Address and value (addr, value)
    • PPU, LY, LCDC mode (if PPU is connected)
    • If writing would be locked in Mode 3 (Blocked:YES/NO)
  • Summaries every 1000 writes: Total, non-zero, ratio.

Task 2: PC Sampler and Loop Detection (CPU)

It was modifiedsrc/core/cpp/CPU.cppto add:

  • Sampling every 10,000 instructions: PC, Bank, IME, IE, IF, HALT (first 50 samples only).
  • Loop Detection: If the same PC is repeated in multiple consecutive samples, logge ar alert.
  • HALT Transitions: Log when the CPU enters/exits the HALT state.

Task 3: Interrupt Verification (IE/IF/IME)

Existing instrumentation was reused[IE-WRITE-TRACE](already present in MMU.cpp) to verify:

  • Writes to IE (0xFFFF)
  • What interrupts are enabled
  • IME status (reported by PC sampler)

Tests and Verification

Compilation

cd /media/fabini/8CD1-4C30/ViboyColor
python3 setup.py build_ext --inplace > build_log_step0382.txt 2>&1
# Exit code: 0 (success)

Controlled Execution (30 seconds)

cd /media/fabini/8CD1-4C30/ViboyColor
timeout 30 python3 main.py roms/pkmn.gb > logs/step0382_cpu_vram_probe.log 2>&1
# Exit code: 124 (successful timeout)

Evidence Extraction

# Escrituras a VRAM
grep -E "\[MMU-VRAM-(WRITE|BLOCK|WRITE-SUMMARY)\]" logs/step0382_cpu_vram_probe.log | head -n 120

# PC sampler / bucles
grep -E "\[CPU-(SAMPLE|LOOP-DETECT|HALT)\]" logs/step0382_cpu_vram_probe.log | head -n 120

# Interrupt status
grep -E "\[IE-WRITE-TRACE\]" logs/step0382_cpu_vram_probe.log | head -n 60

# Errores
grep -i "error\|exception\|traceback" logs/step0382_cpu_vram_probe.log | head -n 60
# (No errors found)

Results - Writes to VRAM

[MMU-VRAM-WRITE] #1 | PC:0x36E3 | Addr:0x8000 | Val:0x00 | Mode:0 | LY:83 | LCDC:0x81 | Blocked:NO
[MMU-VRAM-WRITE] #2 | PC:0x36E3 | Addr:0x8001 | Val:0x00 | Mode:0 | LY:83 | LCDC:0x81 | Blocked:NO
...
[MMU-VRAM-WRITE] #50 | PC:0x36E3 | Addr:0x8031 | Val:0x00 | Mode:2 | LY:88 | LCDC:0x81 | Blocked:NO
[MMU-VRAM-WRITE-SUMMARY] Total:1000 | NonZero:0 | Ratio:0.00%
...
[MMU-VRAM-WRITE-SUMMARY] Total:8000 | NonZero:0 | Ratio:0.00%
[MMU-VRAM-WRITE-SUMMARY] Total:9000 | NonZero:808 | Ratio:8.98%
[MMU-VRAM-WRITE-SUMMARY] Total:10000 | NonZero:1808 | Ratio:18.08%

Results - PC Sampler

[CPU-SAMPLE] #1 | Instrs:10000 | PC:0x1F80 | Bank:1 | IME:0 | IE:0x00 | IF:0x01 | HALT:0
[CPU-SAMPLE] #2 | Instrs:20000 | PC:0x1F85 | Bank:1 | IME:0 | IE:0x00 | IF:0x01 | HALT:0
...
[CPU-SAMPLE] #11 | Instrs:110000 | PC:0x1CFA | Bank:1 | IME:0 | IE:0x0D | IF:0x00 | HALT:0
[CPU-SAMPLE] #12 | Instrs:120000 | PC:0x6151 | Bank:28 | IME:1 | IE:0x0D | IF:0x00 | HALT:0
[CPU-SAMPLE] #13 | Instrs:130000 | PC:0x614F | Bank:28 | IME:1 | IE:0x0D | IF:0x00 | HALT:0
...
[CPU-SAMPLE] #50 | Instrs:500000 | PC:0x6153 | Bank:28 | IME:1 | IE:0x0D | IF:0x00 | HALT:0

Results - Disruptions

[IE-WRITE-TRACE] PC:0x1FAE Bank:1 | 0x00 -> 0x0D
[IE-WRITE-TRACE] Interrupts Enabled: V-Blank Timer Serial 
[IE-WRITE-TRACE] ⚠️ V-BLANK INTERRUPT ENABLED on PC:0x1FAE

C++ Compiled Module Validation

✅ All logs come from compiled C++ code (tags[MMU-VRAM-WRITE], [CPU-SAMPLE]).

Analysis of Results

✅ Confirmation: CPU YES Write to VRAM

  • 10,000+ writes to VRAMdetected in 30 seconds.
  • PC:0x36E3(cleaning routine known from previous Steps).
  • PPU mode: Some writes are marked asBlocked:YESin Mode 3, but most are in Mode 0/2 (not blocked).
  • CRITICAL: The first 8,000 writes areall zeros (Val:0x00).
  • As of write #9000:808 non-zero(8.98%).
  • Final total:1,808/10,000 non-zero(18.08%).

✅ Confirmation: CPU Running Normally

  • There is NO infinite loop: PC advances through multiple locations.
  • Bank change detected: Bank 1 → Bank 28 about 120,000 instructions.
  • Polling in range0x614D-0x6153(Bank 28) - normal waiting behavior.
  • IME=1, IE=0x0D(V-Blank+Timer+Serial enabled).
  • NOT in HALT.

🔍 Conclusion

The problem is acombination of H1 and initialization behavior:

  1. The CPU DOES write to VRAM (discards pure H2 - not a complete lock).
  2. But it's running a routinemass deletion (PC:0x36E3write zeros).
  3. Then loadsome useful(18.08%), but insufficient or in the wrong location.
  4. The game comes intostandby/polling mode(Bank 28, PCs0x614D-0x6153), waiting for conditions that the emulator does not provide.

The root causeis that the game is waiting for something (probably loading data from ROM or specific event) but our emulator is not providing the right conditions for it to progress.

Next step(Step 0383): Investigate what the game is waiting for in Bank 28 (PCs0x614D-0x6153). Possible causes:

  • Specific interrupt wait (Timer, Serial)
  • Waiting for ROM reading (additional banks)
  • Hardware status wait (PPU, Timer)
  • Bug in opcodes related to conditional jumps or HALT

Modified Files

  • src/core/cpp/MMU.hpp- Counters declaration and functionget_vram_write_stats()
  • src/core/cpp/MMU.cpp- Instrumentation of writes to VRAM (Step 0382)
  • src/core/cpp/CPU.hpp- Member statement for PC sampler
  • src/core/cpp/CPU.cpp- PC sampler and loop detection

References