⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

Doctor Viboy: Autonomous Diagnosis and Fix of HALT

Date:2025-12-18 StepID:0045 State: Verified

Summary

The tool was createdDoctor Viboy (tools/doctor_viboy.py), a parser self-contained lock that detects infinite loops, disassembles loop code, displays status complete the system and apply diagnostic heuristics. The Doctor identified that the emulator was left frozen because the CPU went into HALT state waiting for interruptions that never arrived, since during HALT only advanced 4 T-Cycles per tick, insufficient for the PPU to generate V-Blank interrupts. A fix was implemented insrc/viboy.pywhich advances multiple cycles during HALT (up to 114 M-Cycles = 456 T-Cycles = 1 PPU line) to keep the subsystems operating normally.

Hardware Concept

HALT on the Game Boy: The HALT instruction (opcode 0x76) puts the CPU into low power mode. The CPU stops executing instructions (the PC does not advance) until an interrupt occurs. However,system clock still runningduring HALT. This means that:

  • The PPU (Pixel Processing Unit) continues to advance lines and generate V-Blank interrupts
  • The Timer continues counting and may generate interruptions
  • Other subsystems continue to function normally

When the CPU is in HALT and there are interrupts pending (in IE and IF), the CPU wakes up automatically, even if IME (Interrupt Master Enable) is disabled. This allows games to do manual polling of IF after HALT.

Identified problem: In the above implementation, when the CPU was in HALT, the methodstep()returned only 1 M-Cycle (4 T-Cycles) per tick. The PPU needs 456 T-Cycles to complete one scan line, so it would take 114 ticks on HALT to advance a single line. This made the PPU would advance extremely slowly and never generate V-Blank interruptions, causing the game to freeze. frozen waiting for interruptions that never came.

Fountain: Pan Docs - HALT behavior, System Clock, Interrupts

Implementation

1. Viboy Doctor Tool

was createdtools/doctor_viboy.py, a standalone diagnostic tool that:

  • Run the emulator without a graphical interface: Load a ROM and run step-by-step instructions
  • Detect infinite loops: Maintains a history of the last 100 visited PC addresses and detects when the PC is stuck in a small memory range for many iterations (threshold: 5000 iterations)
  • Disassemble the loop: Implements a basic mini-disassembler that displays the loop instructions with their mnemonics (NOP, HALT, LD A, (nn), CP d8, JR, etc.)
  • Shows complete system status: CPU registers (AF, BC, DE, HL, PC, SP, Flags), IME, HALT status, and hardware registers (LCDC, STAT, LY, IF, IE, DIV, TIMA, TAC)
  • Apply diagnostic heuristics: Analyzes the loop instructions and hardware status to identify the cause:
    • If you read 0xFF44 (LY) and compare with 144: "Waiting for V-Blank"
    • If it reads 0xFF0F (IF): "Waiting for Polling"
    • If it reads 0xFF04 or 0xFF05: "Waiting for Timer"
    • If it reads 0xFF41 (STAT): "Waiting for LCD Mode"
    • If CPU is in HALT with no active interrupts: "CPU in HALT waiting for interrupt, but none are active"
  • Provides specific recommendations: Based on the diagnosis, it suggests what to check or what component may be failing

2. HALT Fix on Viboy

The method was modifiedtick()insrc/viboy.pyTo properly handle the HALT state:

  • HALT detection: If the CPU is in HALT, you enter a special loop
  • Multiple cycle advance: During HALT, multiple ticks (up to 114 M-Cycles = 456 T-Cycles = 1 full PPU line) are executed in a single call totick()
  • Automatic wake up: If the CPU wakes up (no longer in HALT) or there are interrupts pending, the loop is exited
  • Safety limit: A maximum limit (114 M-Cycles) is used to avoid infinite loops if something fails

This correctly simulates hardware behavior: during HALT, the system clock continues to run and the subsystems (PPU, Timer) continue to advance normally, allowing interruptions to be generated that wake up the CPU.

Components created/modified

  • tools/doctor_viboy.py(new): Standalone diagnostic tool with loop detector, basic disassembler, state analysis and diagnostic heuristics
  • src/viboy.py(modified): Method improvementtick()to advance multiple cycles during HALT, allowing the PPU and Timer to continue operating normally

Design decisions

Why 114 M-Cycles during HALT: 114 M-Cycles = 456 T-Cycles = exactly 1 complete line of PPU. This allows the PPU to advance at least one line for each call totick()during HALT, ensuring that progress is visible and that V-Blank interrupts are eventually generated.

Safety limit: The 114 M-Cycles limit prevents infinite loops if something fails. On real hardware, the clock keeps running indefinitely, but in the emulator we need a limit to prevent the program from get stuck if there is a bug.

Affected Files

  • tools/doctor_viboy.py- New standalone diagnostic tool (created)
  • src/viboy.py- Method improvementtick()to handle HALT correctly (modified)

Tests and Verification

Verification was done by running Doctor Viboy with real ROMs:

A) Execution of Doctor Viboy with tetris_dx.gbc

  • Command executed: python tools/doctor_viboy.py tetris_dx.gbc --max-instructions 1000000
  • Around: Windows 10, Python 3.13.5
  • Result: PASSED - 1,000,000 instructions executed without detecting infinite loops
  • How valid:
    • The emulator does not freeze in infinite loops
    • PC changes normally during execution
    • CPU can wake up from HALT when interrupts occur
  • Observation: The PC changed normally (0x12E1 → 0x01D2 → 0x0616 → 0x5055 → 0x0283 → 0x4528 → 0x41A7 → 0x41B3 → 0x02DF → 0x0283), confirming that the emulator progress correctly

B) Execution of Doctor Viboy with mario.gbc

  • Command executed: python tools/doctor_viboy.py mario.gbc --max-instructions 500000
  • Around: Windows 10, Python 3.13.5
  • Result: PASSED - 500,000 instructions executed without detecting infinite loops
  • How valid:
    • HALT fix works correctly with different ROMs
    • The emulator does not freeze with mario.gbc
  • Observation: The PC changed normally (0x12DF → 0x145F → 0x025B → 0x025A → 0x51F9), confirming that the emulator is working properly

C) Diagnosis of the original problem (before the fix)

  • Command executed: python tools/doctor_viboy.py tetris_dx.gbc
  • Result: INFINITE LOOP DETECTED at 0x0283 (NOP)
  • Diagnostic generated:
    • CPU on HALT waiting for interrupt, but none are active
    • V-Blank enabled but not active (IF bit 0 = 0)
    • LY = 119 (had not reached 144 to activate V-Blank)
  • Conclusion: Doctor Viboy correctly identified that the problem was that the CPU was in HALT waiting for interrupts that never came because the PPU was not advancing fast enough during HALT

Legal notes: The tetris_dx.gbc and mario.gbc ROMs are provided by the user for local testing. They are not distributed, they are not uploaded to the repository, and no downloads are linked.

Sources consulted

  • Bread Docs:https://gbdev.io/pandocs/- HALT behavior, System Clock, Interrupts
  • Pan Docs: LCD Timing, V-Blank Interrupt
  • Pan Docs: CPU Instruction Set (HALT opcode 0x76)

Educational Integrity

What I Understand Now

  • HALT and the system clock: When the CPU is in HALT, the system clock continues to run. The subsystems (PPU, Timer) should continue to advance normally. This is critical to generate interrupts that wake up the CPU.
  • Timing during HALT: On real hardware, during HALT the CPU consumes 1 cycle per tick but the The system clock continues to run at 4.19 MHz. The PPU and Timer continue to advance at their normal speed. In the emulator, we need to simulate this by advancing multiple cycles during HALT.
  • Diagnostic tools: Creating autonomous diagnostic tools is essential to identify complex problems such as infinite loops. Doctor Viboy can detect problems that would be difficult to find manually.

What remains to be confirmed

  • Timing accuracy during HALT: The limit of 114 M-Cycles is an approximation. In hardware Really, the clock keeps ticking indefinitely. Is this limit sufficient for all cases? It will be validated with more ROMs and tests.
  • Exact behavior of HALT with IME disabled: According to the documentation, HALT with IME=False may have special behaviors. It will be validated with more specific tests.

Hypotheses and Assumptions

114 M-Cycles limit during HALT: We assume to advance up to 114 M-Cycles (1 PPU line) per every call totick()during HALT is enough for the PPU and Timer to advance normally. This is based on the fact that 456 T-Cycles is the time of a complete PPU line, but is not completely verified with specific documentation. It will be empirically validated with real ROMs.

Next Steps

  • [ ] Test the emulator with more ROMs to validate that the HALT fix works in all cases
  • [ ] Improve Viboy Doctor to detect more loop patterns (ex: loops waiting for changes in specific registers)
  • [ ] Add more diagnostic heuristics to Doctor Viboy (e.g. detect DMA waits, STAT change waits)
  • [ ] Create specific unit tests to validate HALT behavior with different IME configurations and interrupts
  • [ ] Document the exact behavior of HALT according to the technical documentation