⚠️ 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.

Visual Diagnostic: LCD Off

Date:2025-12-18 StepID:0048 State: draft

Summary

The renderer was instrumented with avisual diagnosisto distinguish between two critical scenarios: (1) LCD off (LCDC bit 7 = 0) and (2) LCD on but drawing white (vane/VRAM issue). The diagnostic changes the background color when the LCD is off from white todark bluefor facilitate visual identification of the problem. The test result confirms that the emulator showsblue screen, which means the LCD is permanently off and the game has not arrived at the point of turning it on. This indicates a problem oflogic/CPU(probably interruptions or timer) which prevents the game from progressing beyond initialization.

Hardware Concept

The recordLCDC (LCD Control, 0xFF40)controls the status of the Game Boy's LCD. Hebit 7is the "LCD Enable": when it is at 0, the LCD is off and the screen shows white (or in our case, blue for diagnostics). When set to 1, the LCD is on and the PPU can render graphics.

During the initialization of a game, the following typically occurs:

  1. The game disables interrupts (DI) and clears memory
  2. The game loads tiles and sets the tilemap in VRAM
  3. The game configures the BGP palette (Background Palette)
  4. The game activates the LCD by writing LCDC with bit 7 = 1
  5. Game waits for V-Blank or STAT polling to sync

If the gamenever activates the LCD(LCDC bit 7 stays at 0), means the game is stuck at some point before initialization. The most common causes are:

  • Interrupts don't work:The game waits for V-Blank but the interrupt never fires
  • Timer not working:The game waits for a time interval but the timer does not advance
  • Log polling fails:The game reads LY or STAT but always returns the same value
  • Infinite loop:The game enters a waiting loop from which it never comes out

Fountain:Pan Docs - LCD Control Register (LCDC), LCD Timing

Implementation

The method was modifiedrender_frame()insrc/gpu/renderer.pyto add visual diagnosis:

Background Color Change

When the LCD is off (LCDC bit 7 = 0), the renderer now fills the buffer withdark blue (0, 0, 128)instead of white. This allows you to visually distinguish between:

  • BLUE screen:LCD off → Logic/CPU problem
  • WHITE screen:LCD on but drawing white → Paddle/VRAM problem

Diagnostic Log

Added a critical DEBUG log just before rendering the Background when the LCD is on:

logger.debug(
    f"RENDER: LCD ON | BGP={self.mmu.read_byte(IO_BGP):02X} | "
    f"SCX={self.mmu.read_byte(IO_SCX)} | "
    f"VRAM_CHECK={self.mmu.read_byte(0x8000):02X}"
)

This log shows the status of the paddle (BGP), the horizontal scroll (SCX) and the first byte of VRAM for quick diagnosis.

Log when LCD is Off

When the LCD is off, it records:

logger.debug("RENDER: LCD OFF (Blue Screen) - LCDC bit 7=0")

Modified components

  • src/gpu/renderer.py: Modified methodrender_frame()for visual diagnosis

Affected Files

  • src/gpu/renderer.py- Modified methodrender_frame()for visual diagnosis (blue color when LCD off, DEBUG logs)

Tests and Verification

Execution mode:Manual, running the emulator with real ROMs

Tested ROM:tetris_dx.gbc (user-contributed ROM, not distributed)

Command executed: python main.py tetris_dx.gbc

Around:Windows 10, Python 3.10+

Success Criterion:Visually distinguish whether the problem is LCD off (blue screen) or LCD on by drawing white (white screen)

Observation:The screen is displayed completelydark bluewith the flashing red square (heartbeat) visible in the upper left corner. No color change to white is observed during execution. The logs repeatedly show:

RENDER: LCD OFF (Blue Screen) - LCDC bit 7=0

This confirms that the LCDC register has bit 7 permanently set to 0, meaning the game never activates the LCD.

Result: draft- Diagnosis completed. The problem is identified:LCD offbecause the game does not advance beyond initialization. Additional investigation is required into why the game does not activate the LCD (possibly interrupts, timer or register polling).

Legal notes:The tetris_dx.gbc ROM is provided by the user for local testing. It is not distributed or linked in the repository.

Sources consulted

Educational Integrity

What I Understand Now

  • Visual diagnosis:Changing the background color when the LCD is off allows you to quickly distinguish between two different problems: LCD off (blue) vs LCD on drawing white (white).
  • LCDC bit 7:This bit controls whether the LCD is on or off. If the game never writes 1 to this bit, the screen will remain off.
  • Game initialization:Games typically activate the LCD after loading tiles and configuring the palette. If the LCD stays off, it means the game is stuck before reaching that point.

What remains to be confirmed

  • Root cause of crash:It is not confirmed why the game does not activate the LCD. The hypotheses are: (1) interrupts do not work, (2) timer does not work, (3) register polling fails, (4) infinite loop. Additional analysis of the game code is required to determine the exact cause.
  • IME status:It has not been checked whether IME (Interrupt Master Enable) is enabled or disabled during execution. If disabled, interrupts will not be processed even if they are fired.
  • IF/IE Status:It has not been verified whether the interrupt flags (IF) and the interrupt enable register (IE) are configured correctly.

Hypotheses and Assumptions

Main hypothesis:The game is waiting for a V-Blank interrupt or a change to the LY/STAT register that never happens, causing the game to stay in an infinite waiting loop. This hypothesis is based on previous analysis (Step 0042) which showed that the game runs DI (0xF3) at startup and never runs EI (0xFB) to re-enable interrupts.

Assumption:If IME is disabled, the game could be manually polling IF to detect V-Blank, but if IF is never activated (because the PPU is not generating interrupts correctly), the game will wait forever.

Next Steps

  • [ ] Check IME status during execution (is it enabled or disabled?)
  • [ ] Check if IF is updated when V-Blank occurs (regardless of IME)
  • [ ] Check if the Timer is working correctly and generating interruptions
  • [ ] Analyze the game code at the point where it gets stuck to understand what it is waiting for
  • [ ] Implement more detailed logging of interrupt status (IME, IF, IE) during execution