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

Green Stripe Research and Visual Diagnosis

Date:2025-12-25 StepID:0299 State: VERIFIED

Summary

Implemented 4 visual diagnostic monitors to investigate why the emulator displays green vertical stripes instead of graphics. The monitors capture the actual content of the framebuffer, the tile IDs of the tilemap, the tile data read from VRAM, and applying the palette during centerline rendering (LY=72).

Aim: Identify the root cause of the green stripe pattern by analyzing real data that are being processed during rendering.

Hardware Concept

Visual Patterns on Game Boy

Vertical stripes on the Game Boy usually appear when:

  • Tilemap with repeated values: The tilemap contains the same tile ID repeated in columns, generating a vertical pattern
  • Empty tiles with visible palette: The tiles are empty (0x00) but the palette generates visible colors (green)
  • Incorrect address calculation: The calculation of tile addresses is incorrect, generating repetitive readings of the same tile
  • Scroll generating pattern: Scroll (SCX/SCY) is generating a repeating pattern in the calculated directions

Fountain: Pan Docs - "Tile Data", "Background", "Palette"

Framebuffer and Palette

The framebuffer containscolor indices (0-3), not RGB colors directly. The BGP (Background Palette) maps these indices to final colors:

  • Index 0 → Color 0 (White by default)
  • Index 1 → Color 1 (Light gray by default)
  • Index 2 → Color 2 (Default Dark Gray)
  • Index 3 → Color 3 (Black by default)

The Python renderer applies a debug palette that maps these indices to RGB. If the framebuffer contains repetitive indices, stripes of the corresponding color will be seen.

Fountain: Pan Docs - "Background Palette (BGP)", "LCD Display"

Tiles Rendering

Each tile has 8x8 pixels. Each pixel is encoded with 2 bits (4 possible colors: 0, 1, 2, 3). Two consecutive bytes in VRAM represent a line of 8 pixels:

  • Byte 1: Low bit of each pixel (LSB)
  • Byte 2: High bit of each pixel (MSB)

If a tile is empty (0x00 in both bytes), all pixels will be color_index 0, which with the palette standard is mapped to white. However, if the palette is configured differently, it could generate green colors.

Fountain: Pan Docs - "Tile Data", "Tile Format"

Implementation

Visual Diagnostic Monitors

4 visual diagnostic monitors were implemented insrc/core/cpp/PPU.cppinside the functionrender_scanline(). All monitors are activated during centerline rendering (LY=72) and they capture data from the first 32 pixels/tiles to avoid saturating the logs.

1. Real Framebuffer Monitor ([FRAMEBUFFER-DUMP])

Captures the actual contents of the framebuffer after the centerline is rendered. Shows color indices (0-3) that were written to the framebuffer for the first 32 pixels.

// At the end of line 72 rendering
static int framebuffer_dump_count = 0;
if (ly_ == 72 && framebuffer_dump_count< 3) {
    printf("[FRAMEBUFFER-DUMP] Frame %d, LY:72 | First 32 pixels (indices 0-31):\n", 
           framebuffer_dump_count + 1);
    for(int x_dump = 0; x_dump < 32; x_dump++) {
        printf("%02X ", framebuffer_[line_start_index + x_dump]);
        if ((x_dump + 1) % 16 == 0) printf("\n");
    }
    printf("\n");
    framebuffer_dump_count++;
}

2. Real Tilemap Monitor ([TILEMAP-DUMP-VISUAL])

Captures the actual tile IDs being read from the tilemap during centerline rendering. Shows the first 32 tile IDs read from the tilemap.

// Before the render loop, when ly_ == 72
static int tilemap_dump_count = 0;
if (ly_ == 72 && tilemap_dump_count< 3) {
    printf("[TILEMAP-DUMP-VISUAL] Frame %d, LY:72 | First 32 tile IDs:\n", 
           tilemap_dump_count + 1);
    for(int x_dump = 0; x_dump < 32; x_dump++) {
        uint8_t map_x_dump = (x_dump + scx) & 0xFF;
        uint8_t map_y_dump = (ly_ + scy) & 0xFF;
        uint16_t tile_map_addr_dump = tile_map_base + (map_y_dump / 8) * 32 + (map_x_dump / 8);
        uint8_t tile_id_dump = mmu_->read(tile_map_addr_dump);
        printf("%02X ", tile_id_dump);
        if ((x_dump + 1) % 16 == 0) printf("\n");
    }
    printf("\n");
    tilemap_dump_count++;
}

3. Real Tiles Data Monitor ([TILEDATA-DUMP-VISUAL])

Captures the actual tile data being read from VRAM during rendering. Show the bytes of the first 4 tiles (every 8 pixels) read from VRAM.

// Inside the render loop, when reading from VRAM
static int tiledata_dump_count = 0;
if (ly_ == 72 && x< 32 && tiledata_dump_count < 3) {
    if (x % 8 == 0) {  // Cada 8 píxeles (un tile completo)
        uint8_t tile_index = x / 8;
        if (tile_index < 4) {
            printf("[TILEDATA-DUMP-VISUAL] Frame %d | Tile %d (ID:%02X) | Addr:%04X | Line:%d | Bytes: %02X %02X\n",
                   tiledata_dump_count + 1, tile_index, tile_id, tile_line_addr, line_in_tile, byte1, byte2);
        }
    }
}
if (ly_ == 72 && x == 31) tiledata_dump_count++;

4. Applied Palette Monitor ([PALETTE-DUMP-VISUAL])

Capture the BGP palette application to see what final colors are generated. Show the mapping color_index → final_color for the first 32 pixels.

// Inside the render loop, after applying the palette
static int palette_dump_count = 0;
if (ly_ == 72 && x< 32 && palette_dump_count < 3) {
    if (x == 0) {
        printf("[PALETTE-DUMP-VISUAL] Frame %d, LY:72 | BGP:0x%02X | First 32 pixels (ColorIndex ->FinalColor):\n",
               palette_dump_count + 1, bgp);
    }
    printf("(%d->%d) ", color_index, final_color);
    if ((x + 1) % 16 == 0) printf("\n");
}
if (ly_ == 72 && x == 31) palette_dump_count++;

Design Decisions

  • Center line (LY=72): The center line was chosen because it is representative of the visible content and avoids edges that could have special behaviors
  • First 32 pixels/tiles: It is limited to 32 so as not to saturate the logs, but it is enough to identify repetitive patterns
  • 3 frame limit: Each monitor is only activated in the first 3 frames to avoid log saturation
  • Hexadecimal format: Data is displayed in hexadecimal for easy pattern identification

Affected Files

  • src/core/cpp/PPU.cpp- Added 4 visual diagnostic monitors inrender_scanline()
  • ANALYSIS_GREEN_STRIPS_STEP_0299.md- Analysis document (new, template to complete after execution)
  • docs/bitacora/entries/2025-12-25__0299__investigacion-rayas-verdes-diagnostico-visual.html- HTML log entry
  • docs/bitacora/index.html- Updated with entry 0299
  • REPORT_PHASE_2.md- Updated with Step 0299

Tests and Verification

The monitors will be validated by running the emulator and analyzing the generated logs:

  • Compilation: Monitors compiled without errors with C++17
  • Execution: Pending running the emulator to capture logs
  • Analysis: Pending analysis of logs to identify patterns

Command to run and capture logs:

python main.py --rom roms/pokemon_red.gb > logs/diagnostico_rayas_0299.log 2>&1

Log analysis: The logs will be analyzed using the documentANALYSIS_GREEN_STRIPS_STEP_0299.mdto identify the root cause of the green stripe pattern.

Sources consulted

Educational Integrity

What I Understand Now

  • Visual patterns: Vertical stripes can originate from different points in the rendering pipeline (tilemap, tiles, palette)
  • Framebuffer: Contains color indices, not RGB colors directly. The palette maps these indices to final colors
  • Visual diagnosis: Capturing data at multiple points in the pipeline allows you to identify where a visual pattern originates
  • Log limits: It is critical to limit the number of logs to avoid saturating the context and maintain performance

What remains to be confirmed

  • royal pattern: Which color index generates dark green and which generates light green?
  • Pattern Origin: If it comes from the tilemap, the tiles, or the palette
  • Root cause: Why the repetitive pattern is generated

Hypotheses and Assumptions

4 hypotheses have been formulated about the origin of the green stripes:

  1. Hypothesis A: Tilemap with repeated values
  2. Hypothesis B: Empty tiles with green palette
  3. Hypothesis C: Incorrect address calculation
  4. Hypothesis D: Scroll generating pattern

The implemented monitors will allow confirming or rejecting these hypotheses after running the emulator and analyze the logs.

Next Steps

  • [ ] Run the emulator and capture logs from the 4 monitors
  • [ ] Analyze logs to identify patterns in framebuffer, tilemap, tiles and palette
  • [ ] Confirm or reject the 4 hypotheses about the origin of the green stripes
  • [ ] Identify the root cause of the pattern
  • [ ] Implement correction based on findings (if applicable)
  • [ ] Complete the documentANALYSIS_GREEN_STRIPS_STEP_0299.mdwith the results