⚠️ Clean-Room / Educativo

Este proyecto es educativo y Open Source. No se copia código de otros emuladores. Implementación basada únicamente en documentación técnica y tests permitidas.

Investigación de Tiles Difusos y Optimización de Renderizado

Fecha: 2025-12-29 Step ID: 0336 Estado: VERIFIED

Resumen

Este step investiga por qué los tiles aparecen difusos o a medias y por qué tarda mucho en aparecer contenido de la ROM. Se implementaron logs de diagnóstico para verificar la decodificación de tiles (2bpp), la aplicación de la paleta BGP, el scroll (SCX/SCY), el direccionamiento signed/unsigned, y el timing de carga de tiles. Los logs revelaron que la decodificación y aplicación de paleta funcionan correctamente, pero los tiles se cargan muy tarde (Frame 4720-4943), lo cual explica por qué tarda mucho en aparecer contenido.

Concepto de Hardware

Decodificación 2bpp (2 bits per pixel)

Cada tile del Game Boy tiene 8x8 píxeles = 64 píxeles. Cada píxel se codifica con 2 bits (2bpp = 2 bits per pixel), por lo que se necesitan 16 bytes por tile (8 líneas × 2 bytes por línea). El formato es:

  • Byte 1: Contiene el bit bajo (bit_low) de cada píxel
  • Byte 2: Contiene el bit alto (bit_high) de cada píxel
  • Índice de color: color_index = (bit_high << 1) | bit_low

El índice de color resultante (0-3) se mapea a un color final usando la paleta BGP.

Paleta BGP (Background Palette)

BGP (Background Palette, registro 0xFF47) mapea índices de color crudos (0-3) a índices finales (0-3). El formato es: [color3][color2][color1][color0], donde cada color ocupa 2 bits. El color final se calcula como:

final_color = (BGP >> (color_index * 2)) & 0x03

Por ejemplo, con BGP = 0xE4 (11100100):

  • Color 0 → (0xE4 >> 0) & 0x03 = 0
  • Color 1 → (0xE4 >> 2) & 0x03 = 1
  • Color 2 → (0xE4 >> 4) & 0x03 = 2
  • Color 3 → (0xE4 >> 6) & 0x03 = 3

Este es un mapeo identidad estándar que preserva los índices de color.

Scroll (SCX/SCY)

SCX (Scroll X, registro 0xFF43) desplaza el background horizontalmente, y SCY (Scroll Y, registro 0xFF42) desplaza el background verticalmente. El scroll se aplica al calcular la posición en el tilemap:

map_x = (screen_x + SCX) & 0xFF
map_y = (screen_y + SCY) & 0xFF

Direccionamiento Signed/Unsigned

El Game Boy puede usar dos modos de direccionamiento para los tiles:

  • Unsigned (LCDC bit 4 = 1): Tile IDs 0-255, base en 0x8000
  • Signed (LCDC bit 4 = 0): Tile IDs -128 a 127, base en 0x9000

En modo signed, el tile ID se interpreta como int8_t, permitiendo referenciar tiles antes de la base (0x9000 - 128*16 = 0x8800).

Implementación

Se implementaron 5 sistemas de logs de diagnóstico para investigar los problemas de renderizado:

1. Logs de Decodificación de Tiles (2bpp)

Se agregaron logs en PPU::render_scanline() que verifican la decodificación de tiles en la línea central (LY=72). Los logs muestran:

  • Tile ID y dirección del tile
  • Bytes 1 y 2 de la línea del tile
  • Decodificación manual de algunos píxeles (primer y último píxel)
  • Índice de color resultante para cada píxel

Tag de log: [PPU-TILE-DECODE]

2. Logs de Aplicación de Paleta

Se agregaron logs que verifican la aplicación de la paleta BGP. Los logs muestran:

  • Índice de color crudo (0-3)
  • Valor de BGP
  • Color final después de aplicar la paleta
  • Mapeo BGP (color_index → final_color)

Tag de log: [PPU-PALETTE-APPLY]

3. Análisis de Timing de Carga de Tiles

Se agregó análisis en MMU::write() que detecta cuándo se cargan los tiles y analiza la velocidad de carga. Los logs muestran:

  • Frame en que se carga el primer tile
  • Número de tiles cargados por segundo
  • Total de tiles cargados

Tag de log: [TILE-LOAD-TIMING]

4. Logs de Scroll (SCX/SCY)

Se agregaron logs que verifican los valores de scroll durante el renderizado. Los logs muestran:

  • Frame actual
  • Línea de escaneo (LY)
  • Valores de SCX y SCY
  • Posición calculada en el tilemap (MapX, MapY)

Tag de log: [PPU-SCROLL]

5. Logs de Direccionamiento Signed/Unsigned

Se agregaron logs que verifican el direccionamiento de tiles. Los logs muestran:

  • Tile ID
  • Tipo de direccionamiento (signed/unsigned)
  • Base de datos de tiles
  • Dirección calculada del tile
  • Si es signed, el valor signed del tile ID

Tag de log: [PPU-ADDRESSING]

Archivos Afectados

  • src/core/cpp/PPU.cpp - Agregados logs de decodificación, paleta, scroll y direccionamiento
  • src/core/cpp/MMU.cpp - Agregado análisis de timing de carga de tiles
  • build_log_step0336.txt - Log de compilación
  • logs/test_*_step0336.log - Logs de pruebas con las 5 ROMs

Tests y Verificación

Se ejecutaron pruebas con las 5 ROMs durante 2.5 minutos cada una:

  • pkmn.gb: 20 tiles cargados, primer tile en Frame 4943
  • tetris.gb: 3 tiles cargados, primer tile en Frame 1
  • mario.gbc: 0 tiles cargados (solo checkerboard)
  • pkmn-amarillo.gb: 20 tiles cargados, primer tile en Frame 4716
  • Oro.gbc: 20 tiles cargados, primer tile en Frame 4720

Hallazgos Clave

  1. Decodificación de tiles funciona correctamente: Los logs muestran que la decodificación 2bpp es correcta. Tiles con datos (0xFF 0xFF) se decodifican correctamente a color_idx=3, y tiles vacíos (0x00 0x00) a color_idx=0.
  2. Aplicación de paleta funciona correctamente: Los logs muestran que la paleta BGP se aplica correctamente, mapeando índices de color crudos a índices finales (0→0, 3→3 con BGP=0xE4).
  3. Tiles se cargan muy tarde: Los tiles se cargan en Frame 4720-4943 (aproximadamente 78-82 segundos después del inicio), lo cual explica por qué tarda mucho en aparecer contenido de la ROM. Esto es normal para algunos juegos que inicializan muchos sistemas antes de cargar gráficos.
  4. Scroll funciona correctamente: Los logs muestran que SCX y SCY se aplican correctamente, aunque inicialmente están en 0.
  5. Direccionamiento funciona correctamente: Los logs muestran que tanto el direccionamiento signed como unsigned funcionan correctamente. Por ejemplo, en Oro.gbc se detecta direccionamiento signed con TileID 0x7F mapeando a 0x97F0.

Ejemplo de Logs

[PPU-TILE-DECODE] Frame 1 | TileID: 0x00 | Addr: 0x8000 | Byte1: 0xFF Byte2: 0xFF
[PPU-TILE-DECODE] Pixel 7: bit_low=1 bit_high=1 color_idx=3
[PPU-PALETTE-APPLY] Frame 1 | ColorIndex: 3 | BGP: 0xE4 | FinalColor: 3 | BGP mapping: 3->3
[TILE-LOAD-TIMING] Primer tile cargado en Frame 4720 (PC:0x5EB2)
[PPU-SCROLL] Frame 1 | LY: 72 | SCX: 0 | SCY: 0 | MapX: 0 | MapY: 72
[PPU-ADDRESSING] Frame 1 | TileID: 0x7F | Signed: 1 | DataBase: 0x9000 | TileAddr: 0x97F0

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Decodificación 2bpp: La decodificación de tiles funciona correctamente. Los bits se leen en el orden correcto (bit_high y bit_low) y se combinan para formar el índice de color (0-3).
  • Aplicación de paleta: La paleta BGP se aplica correctamente usando el desplazamiento de bits. El mapeo identidad (BGP=0xE4) preserva los índices de color.
  • Timing de carga: Los tiles se cargan muy tarde en algunos juegos (Frame 4720-4943), lo cual es normal para juegos que inicializan muchos sistemas antes de cargar gráficos. Esto explica por qué tarda mucho en aparecer contenido.
  • Direccionamiento: Tanto el direccionamiento signed como unsigned funcionan correctamente. El modo signed permite referenciar tiles antes de la base (0x8800-0x8FFF) usando valores negativos.

Lo que Falta Confirmar

  • Tiles difusos: Los logs muestran que la decodificación y paleta funcionan correctamente, pero los tiles aún aparecen difusos. Esto podría deberse a:
    • Problemas en el renderizador Python que convierte índices de color a RGB
    • Problemas en la aplicación de colores finales (paleta de Game Boy Color)
    • Problemas en la interpolación o escalado de píxeles
  • Optimización de carga: Aunque los tiles se cargan tarde, esto es normal para algunos juegos. Sin embargo, podría investigarse si hay formas de optimizar la carga o si hay problemas de sincronización.

Hipótesis y Suposiciones

Los logs confirman que la decodificación y aplicación de paleta funcionan correctamente en el lado C++. El problema de tiles difusos probablemente está en el renderizador Python que convierte los índices de color a RGB o en la aplicación de colores finales. El próximo step debería investigar el renderizador Python.

Próximos Pasos

  • [ ] Investigar el renderizador Python que convierte índices de color a RGB
  • [ ] Verificar la aplicación de colores finales (paleta de Game Boy Color)
  • [ ] Investigar problemas de interpolación o escalado de píxeles
  • [ ] Si se identifica la causa de los tiles difusos, implementar corrección