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.
Diagnóstico de VRAM y Tilemap
Resumen
Implementación de tres monitores de diagnóstico adicionales para verificar qué lee la PPU de VRAM y qué contiene el tilemap. El Step 0288 identificó que VRAM está vacía (solo ceros), por lo que estos monitores permitirán confirmar si el problema está en la lectura de la PPU o en la carga de datos. Se implementaron los monitores [VRAM-READ], [TILEMAP-INSPECT] y [TILEDATA-INSPECT].
Concepto de Hardware
Lectura de VRAM por la PPU: Durante el renderizado de cada scanline, la PPU lee datos de VRAM para construir la imagen. Primero lee el Tile Map (0x9800-0x9FFF o 0x9C00-0x9FFF según LCDC bit 6) para obtener el Tile ID que debe renderizar en cada posición. Luego lee el Tile Data (0x8000-0x97FF) usando el Tile ID para obtener los bytes de píxeles. Si VRAM está vacía (solo ceros), la PPU leerá tiles vacíos y renderizará una pantalla en blanco o con un solo color.
Tile Map y Tile IDs: El Tile Map es una tabla de 32x32 tiles que especifica qué tile debe renderizarse en cada posición de la pantalla. Cada byte del Tile Map contiene un Tile ID (0-255). El Tile ID se usa para calcular la dirección base del tile en Tile Data: si el modo de direccionamiento es signed (LCDC bit 4 = 0), Tile ID 0 está en 0x9000 y se usa aritmética con signo; si es unsigned (bit 4 = 1), Tile ID 0 está en 0x8000 y se multiplica directamente por 16.
Tile Data y Formato 2bpp: Cada tile ocupa 16 bytes (2 bytes por línea, 8 líneas). Cada línea se compone de 2 bytes: el byte bajo contiene los bits bajos de cada píxel (bit 7 = píxel 0, bit 6 = píxel 1, etc.), y el byte alto contiene los bits altos. El color del píxel se calcula como (bit_alto << 1) | bit_bajo, resultando en un índice de color (0-3). Si ambos bytes son 0x00, el tile está vacío (todos los píxeles son color 0).
Fuente: Pan Docs - "Video RAM (VRAM)", "Tile Data", "Tile Map", "LCD Control Register (LCDC)"
Implementación
1. Monitor [VRAM-READ] en MMU.cpp
Se implementó un monitor en la función MMU::read() que captura todas las lecturas de VRAM (0x8000-0x9FFF). El monitor reporta la dirección leída, el valor obtenido, el PC que originó la lectura y el banco ROM actual. Tiene un límite de 100 lecturas para evitar saturación de logs.
// --- Step 0289: Monitor de Lecturas de VRAM ([VRAM-READ]) ---
if (addr >= 0x8000 && addr <= 0x9FFF) {
uint8_t vram_value = memory_[addr];
static int vram_read_count = 0;
if (vram_read_count < 100) {
printf("[VRAM-READ] Read %04X -> %02X (PC:0x%04X Bank:%d)\n",
addr, vram_value, debug_current_pc, current_rom_bank_);
vram_read_count++;
}
return vram_value;
}
2. Inspector [TILEMAP-INSPECT] en PPU.cpp
Se mejoró el inspector de Tile Map existente (del Step 0263) para ejecutarse al inicio de cada frame (LY=0) en lugar de solo una vez. El inspector imprime los primeros 32 bytes del tilemap (primera fila completa), calcula un checksum del tilemap completo (1024 bytes) y reporta la configuración de LCDC. Solo se ejecuta en los primeros 5 frames para no saturar los logs.
// --- Step 0289: Inspector de Tilemap ([TILEMAP-INSPECT]) ---
static int frame_count = 0;
if (ly_ == 0) {
frame_count++;
if (frame_count <= 5) {
printf("[TILEMAP-INSPECT] Frame %d | LCDC: %02X | BG Map Base: %04X | BG Data Base: %04X\n",
frame_count, lcdc, tile_map_base, tile_data_base);
// Imprimir las primeras 32 bytes (primera fila completa del tilemap)
printf("[TILEMAP-INSPECT] First 32 bytes (row 0) of Map at %04X:\n", tile_map_base);
for(int i=0; i<32; i++) {
printf("%02X ", mmu_->read(tile_map_base + i));
if ((i + 1) % 16 == 0) printf("\n");
}
printf("\n");
// Calcular checksum del tilemap para detectar cambios
uint16_t checksum = 0;
for(int i=0; i<1024; i++) {
checksum += mmu_->read(tile_map_base + i);
}
printf("[TILEMAP-INSPECT] Tilemap checksum (first 1024 bytes): 0x%04X\n", checksum);
}
}
3. Inspector [TILEDATA-INSPECT] en PPU.cpp
Se implementó un inspector en el bucle de renderizado que verifica si los tiles contienen datos válidos cuando se leen. El inspector se ejecuta solo en el centro de la pantalla (LY=72, X=80) y en los primeros 3 frames para no saturar los logs. Si detecta que un tile contiene solo ceros (byte1 == 0x00 && byte2 == 0x00), emite un warning.
// --- Step 0289: Inspector de Tile Data ([TILEDATA-INSPECT]) ---
static int tiledata_inspect_count = 0;
if (ly_ == 72 && x == 80 && tiledata_inspect_count < 3) {
printf("[TILEDATA-INSPECT] LY:72 X:80 | TileID:%02X | TileAddr:%04X | Byte1:%02X Byte2:%02X\n",
tile_id, tile_addr, byte1, byte2);
if (byte1 == 0x00 && byte2 == 0x00) {
printf("[TILEDATA-INSPECT] WARNING: Tile %02X contains only zeros (empty tile)\n", tile_id);
}
tiledata_inspect_count++;
}
Objetivos de los Monitores
- [VRAM-READ]: Verificar qué direcciones lee la PPU de VRAM y qué valores obtiene. Esto permite confirmar si la PPU está leyendo solo ceros o datos válidos.
- [TILEMAP-INSPECT]: Verificar qué Tile IDs contiene el tilemap. Si todos los Tile IDs son 0x00, significa que el tilemap está vacío o apunta solo a tiles vacíos.
- [TILEDATA-INSPECT]: Verificar si los tiles referenciados por el tilemap contienen datos válidos. Si los tiles están vacíos (solo ceros), confirmará que el problema está en la carga de datos de tiles.
Archivos Afectados
src/core/cpp/MMU.cpp- Implementación del monitor [VRAM-READ]src/core/cpp/PPU.cpp- Implementación de los inspectores [TILEMAP-INSPECT] y [TILEDATA-INSPECT]
Tests y Verificación
Los monitores se activarán durante la ejecución del emulador y generarán logs con los prefijos correspondientes. Para verificar que funcionan correctamente:
- Comando ejecutado:
python setup.py build_ext --inplace - Resultado: Módulo C++ compilado sin errores
- Comando ejecutado:
python main.py roms/pkmn.gb > debug_step_0289.log 2>&1 - Resultado esperado: Los logs deberían contener:
- Hasta 100 líneas con prefijo
[VRAM-READ] - Hasta 5 bloques con prefijo
[TILEMAP-INSPECT](uno por cada uno de los primeros 5 frames) - Hasta 3 líneas con prefijo
[TILEDATA-INSPECT](una por cada uno de los primeros 3 frames en el centro de la pantalla)
- Hasta 100 líneas con prefijo
Validación: Los monitores se ejecutarán durante el renderizado y proporcionarán información valiosa sobre qué lee la PPU de VRAM y qué contiene el tilemap. Esto permitirá confirmar si el problema está en la lectura de la PPU o en la carga de datos.
Fuentes Consultadas
- Pan Docs: Video RAM (VRAM)
- Pan Docs: Tile Data
- Pan Docs: Tile Map
- Pan Docs: LCD Control Register (LCDC)
Integridad Educativa
Lo que Entiendo Ahora
- Lectura de VRAM por la PPU: La PPU lee VRAM durante el renderizado para obtener Tile IDs del Tile Map y luego los datos de píxeles de Tile Data. Si VRAM está vacía, la PPU leerá tiles vacíos y renderizará una pantalla en blanco.
- Tile Map y Tile IDs: El Tile Map es una tabla que especifica qué tile renderizar en cada posición. Si todos los Tile IDs son 0x00, el tilemap está vacío o apunta solo a tiles vacíos.
- Formato 2bpp: Cada tile ocupa 16 bytes (2 bytes por línea). Si ambos bytes de una línea son 0x00, todos los píxeles de esa línea son color 0 (blanco/verde).
Lo que Falta Confirmar
- ¿Qué lee la PPU de VRAM? Los monitores implementados permitirán verificar si la PPU está leyendo solo ceros o datos válidos.
- ¿Qué contiene el tilemap? El inspector de tilemap permitirá verificar si el tilemap está vacío o contiene Tile IDs válidos.
- ¿Los tiles tienen datos válidos? El inspector de tile data permitirá verificar si los tiles referenciados por el tilemap contienen datos válidos.
Hipótesis y Suposiciones
Hipótesis: Basado en el análisis del Step 0288, esperamos que los monitores confirmen que:
- La PPU está leyendo solo ceros de VRAM (confirmando que VRAM está vacía)
- El tilemap contiene Tile IDs válidos (posiblemente todos 0x00 o apuntando a tiles vacíos)
- Los tiles referenciados están vacíos (solo ceros), confirmando que el problema está en la carga de datos
Próximos Pasos
- [ ] Ejecutar el emulador con Pokémon Red y analizar los logs generados por los nuevos monitores
- [ ] Verificar si [VRAM-READ] confirma que la PPU está leyendo solo ceros
- [ ] Verificar si [TILEMAP-INSPECT] muestra que el tilemap está vacío o contiene Tile IDs válidos
- [ ] Verificar si [TILEDATA-INSPECT] confirma que los tiles están vacíos
- [ ] Step 0290: Implementar monitores de LCDC y paleta ([LCDC-CHANGE], [PALETTE-APPLY])
- [ ] Step 0291: Aplicar correcciones basadas en todos los hallazgos