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
Summary
The analysis of the data from Step 0215 isconclusive. We have isolated the problem with surgical precision:
- C++ (PPU):Generate pixels with value
3(Correct, it's black). - Cython (Bridge):Transfer the value
3intact to Python (Correct). - Python (BGP):The record has the value
0xE4(Correct, standard palette). - 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:
- The data pipeline works correctly.
- The renderer is applying the palette correctly.
- 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:
- In the method that uses the C++ framebuffer (lines ~467-472).
- 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:
- The data pipeline works correctly (C++ → Cython → Python).
- The renderer is applying the palette correctly.
- The problem was simply the definition of base colors.
Next steps after confirming red:
- Remove the red hack (
if color_idx == 3: ...). - Remove the stripes hack in C++ (Step 0212).
- Remove the hack
byte=0xFFin C++ (Step 0209). - 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