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

Debug Palette: The Red Test

Date:2025-12-22 StepID:0216 State: DRAFT

Summary

The analysis of the data from Step 0215 isconclusive. We have isolated the problem with surgical precision:

  1. C++ (PPU):Generate pixels with value3(Correct, it's black).
  2. Cython (Bridge):Transfer the value3intact to Python (Correct).
  3. Python (BGP):The record has the value0xE4(Correct, standard palette).
  4. Screen:SampleWHITE.

The Logical Deduction:

If the renderer input is3and the BGP registry0xE4says that index 3 should be mapped to Color 3... soyour definition of "Color 3" inrenderer.pyit's WHITE.

Solution:We correct the definition of colors inrenderer.pyand, to be 100% sure, we make theColor 3 is REDtemporarily. If we see red streaks, we will have won the war against the white screen.

Hardware Concept: The Game Boy Color Palette

The original Game Boy uses a palette of 4 shades of gray/green that map to color indices (0-3). The BGP record (Background Palette, 0xFF47) defines how these indices are mapped to actual colors.

Game Boy Standard Palette:

  • Color 0:White/Light Green (224, 248, 208) - Lightest
  • Color 1:Light gray (136, 192, 112) - Light gray
  • Color 2:Dark gray (52, 104, 86) - Dark gray
  • Color 3:Black/Dark Green (8, 24, 32) - Darkest

The investment problem:

If the definition of colors in the Python code is inverted or poorly defined, index 3 (which should be black) will be rendered as white. This is exactly what is happening: the pipeline is working correctly (C++ sends 3, Python receives 3, BGP is 0xE4), but the screen is white.

The Red Test:

To visually confirm that we have control over the final mapping, we temporarily force index 3 to render asRED(255, 0, 0). If we see red stripes on the screen, it means that:

  1. The data pipeline works correctly.
  2. The renderer is applying the palette correctly.
  3. The problem was simply the definition of base colors.

Fountain:Pan Docs - Background Palette Register (BGP), LCD Control Register.

Implementation

The following modifications were implemented insrc/gpu/renderer.py:

1. Explicit Definition of Colors in__init__

Added an explicit definition of Game Boy base colors and a mapped palette:

# --- FIX STEP 0216: Explicit Definition of Colors ---
# Original Game Boy: 0=Lighter, 3=Darker
# Game Boy standard palette (original green/yellow)
self.COLORS = [
    (224, 248, 208), #0: White/Light Green (White)
    (136, 192, 112), #1: Light Gray
    (52, 104, 86), #2: Dark Gray
    (8, 24, 32) #3: Black/Dark Green (Black)
]
# Current palette mapped (index -> RGB)
self.palette = list(self.COLORS)

# Flag for debug log (one time only)
self.debug_palette_printed = False
# ----------------------------------------

2. BGP Palette Decoding Fix

Changed BGP palette decoding to use explicit colors and added visual debugging with red:

# Decode BGP palette (each pair of bits represents a color 0-3)
# --- FIX STEP 0216: Use explicit COLORS and visual debug ---
palette = [None] * 4
for i in range(4):
    color_idx = (bgp >> (i * 2)) & 0x03
    palette[i] = self.COLORS[color_idx]
    
    # --- VISUAL DEBUG STEP 0216 ---
    # If the final index is 3 (Black), we force it to RED
    # to visually confirm that we are painting what we want.
    if color_idx == 3:
        palette[i] = (255, 0, 0) # STRONG RED
    #--------------------------

# Diagnostic log (only once if BGP changes or if it is the first frame)
if not self.debug_palette_printed:
    print(f"\n--- [RENDERER PALETTE DEBUG] ---")
    print(f"BGP Raw: 0x{bgp:02X}")
    print(f"Mapping:")
    for i in range(4):
        base_color_idx = (bgp >> (i * 2)) & 0x03
        print(f" Index {i} -> BaseColor {base_color_idx} -> RGB {palette[i]}")
    print(f"--------------------------------\n")
    self.debug_palette_printed = True
# ----------------------------------------

This modification was applied in two places:

  1. In the method that uses the C++ framebuffer (lines ~467-472).
  2. In the original Python method (lines ~593-598).

Tests and Verification

Command executed:

python main.py roms/tetris.gb

Expected result:

  • Must seeRED and white vertical stripeson the screen.
  • The debug log should show the palette mapping with index 3 mapped to RED.

Validation:

If we see red, it means that:

  1. The data pipeline works correctly (C++ → Cython → Python).
  2. The renderer is applying the palette correctly.
  3. The problem was simply the definition of base colors.

Next steps after confirming red:

  1. Remove the red hack (if color_idx == 3: ...).
  2. Remove the stripes hack in C++ (Step 0212).
  3. Remove the hackbyte=0xFFin C++ (Step 0209).
  4. And we will see Tetris!

Affected Files

  • src/gpu/renderer.py- Correction of color definition and visual debug with red

References

  • Pan Docs - Background Palette Register (BGP)
  • Pan Docs - LCD Control Register
  • Step 0215 - Palette Correction: Why is Black White?
  • Step 0212 - Black Marker Test
  • Step 0209 - Radical Diagnosis: Force Color Black