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

Identificar Bloqueo DMG y Demostrar Señal CGB

Fecha: 2025-01-06 Step ID: 0493 Estado: VERIFIED

Resumen

Este step implementa diagnóstico avanzado para identificar el bloqueo post-clear en modo DMG (tetris.gb) y demostrar si hay señal en modo CGB (tetris_dx.gbc) después de que aparezcan writes no-cero a tiledata. Se reforzó la sección AfterClear con IME/IE/IF/HALT/VBlank/LCDC/STAT/LY, se implementó disasm focal del hotspot top1, y se añadió un clasificador automático del bloqueo. Para CGB, se implementaron dumps sincronizados de FB_INDEX, FB_RGB y FB_PRESENT_SRC en el mismo frame. Resultado clave DMG: Loop en PC 0x036C esperando condición que nunca se cumple (WAIT_LOOP_IRQ_ENABLED). Resultado clave CGB: Caso 1 confirmado - idx_nonzero>0 pero rgb_nonwhite==0, indicando problema en paletas/mapeo CGB.

Concepto de Hardware

Bloqueo Post-Clear: Después de que un juego completa el clear VRAM inicial (escribiendo 6144 bytes de ceros a tiledata), típicamente carga los datos gráficos reales y luego espera alguna condición (VBlank, Timer, Joypad, etc.) antes de continuar. Si el emulador no implementa correctamente alguna de estas condiciones, el juego puede quedar bloqueado en un loop infinito esperando que algo cambie.

PC Hotspots: Direcciones de memoria (Program Counter) que se ejecutan con mucha más frecuencia que otras. Un hotspot dominante (ej: 0x036C ejecutado 498,286 veces) indica que el código está atascado en un loop esperando una condición.

IO Reads Dominantes: Registros I/O que se leen masivamente durante el bloqueo. Por ejemplo, si IF (Interrupt Flag, 0xFF0F) se lee 151M+ veces, el juego está esperando que algún flag de interrupción cambie.

Three-Buffer Pipeline (CGB): El pipeline de renderizado CGB tiene tres buffers:

  • FB_INDEX: Framebuffer de índices de color (0-3 para DMG, 0-31 para CGB)
  • FB_RGB: Framebuffer RGB888 convertido desde índices usando paletas CGB
  • FB_PRESENT_SRC: Buffer final que se presenta a la pantalla (después de cualquier procesamiento adicional)

Si idx_nonzero>0 pero rgb_nonwhite==0, el problema está en la conversión índice→RGB (paletas CGB no configuradas o mapeo incorrecto).

Referencia: Pan Docs - "Interrupts", "CGB Palettes", "LCD Status Register"

Implementación

Fase A: DMG - Identificación del Bloqueo

A1: Sección AfterClear Reforzada

Se reforzó la sección AfterClear en rom_smoke_0442.py para capturar más información cuando se detecta que el clear VRAM está completo:

  • Estado CPU: IME, IE, IF, HALTED
  • Estado PPU: LCDC, STAT, LY
  • Estadísticas VBlank: VBlankReq, VBlankServ
  • PC hotspots top 3 con contadores
  • IO reads top 3 con contadores

A2: Disasm Focal del Hotspot

Se implementó disasm automático del hotspot top1 usando la función existente disasm_window():

  • Desensambla 10-20 instrucciones alrededor del PC hotspot
  • Detecta automáticamente branches/loops y desensambla el destino
  • Marca el PC actual con "<-- HOTSPOT" para fácil identificación

A3: Clasificador Automático

Se implementó la función _classify_dmg_blockage() que analiza el snapshot AfterClear y clasifica el bloqueo en una de estas categorías:

  • WAIT_LOOP_VBLANK_STAT: Esperando VBlank/STAT/LY
  • WAIT_LOOP_TIMER: Esperando Timer (DIV/TIMA/TAC)
  • WAIT_LOOP_JOYPAD: Esperando Joypad
  • WAIT_LOOP_IRQ_DISABLED/ENABLED: Esperando interrupción
  • HALTED: CPU en HALT
  • UNKNOWN: No clasificable automáticamente

Fase B: CGB - Demostración de Señal

B1: Dumps Sincronizados

Se implementó la función _dump_synchronized_buffers() que genera dumps de los tres buffers en el mismo frame:

  • FB_INDEX: Dump usando BGP para conversión DMG (gateado por VIBOY_DUMP_IDX_PATH)
  • FB_RGB: Dump directo del framebuffer RGB888 desde PPU (gateado por VIBOY_DUMP_RGB_PATH)
  • FB_PRESENT_SRC: Se genera en renderer.py cuando se llama render_frame() (no disponible en modo headless)

Todos los dumps se generan en el frame especificado por VIBOY_DUMP_RGB_FRAME para garantizar sincronización.

B2: Métricas Mínimas en Snapshot

Se aseguró que el snapshot incluya todas las métricas necesarias para clasificar el problema CGB:

  • ThreeBufferStats: idx_nonzero, rgb_nonwhite, present_nonwhite, CRCs
  • CGBPaletteWriteStats: BGPD/OBPD write counts y últimos valores (gateado por VIBOY_DEBUG_CGB_PALETTE_WRITES)
  • VRAM_Regions: Tiledata y tilemap nonzero por regiones/bancos

Archivos Afectados

  • tools/rom_smoke_0442.py - Reforzada sección AfterClear, implementada función _classify_dmg_blockage(), implementada función _dump_synchronized_buffers()
  • docs/reports/reporte_step0493.md - Reporte completo con evidencia DMG y CGB

Tests y Verificación

Comando DMG ejecutado:

export VIBOY_SIM_BOOT_LOGO=0
export VIBOY_POST_BOOT_DMG_PROFILE=B
export VIBOY_DEBUG_VRAM_WRITES=1
export VIBOY_DEBUG_DMG_TILE_FETCH=1
export VIBOY_DEBUG_PRESENT_TRACE=1
timeout 600 python3 tools/rom_smoke_0442.py roms/tetris.gb --frames 3000

Resultado: Ejecutado hasta frame 2563 (timeout 120s). Loop identificado en PC 0x036C con clasificación WAIT_LOOP_IRQ_ENABLED.

Comando CGB ejecutado:

export VIBOY_SIM_BOOT_LOGO=0
export VIBOY_DEBUG_VRAM_WRITES=1
export VIBOY_DEBUG_PRESENT_TRACE=1
export VIBOY_DUMP_RGB_FRAME=600
export VIBOY_DUMP_RGB_PATH=/tmp/viboy_tetris_dx_rgb_f####.ppm
export VIBOY_DUMP_IDX_PATH=/tmp/viboy_tetris_dx_idx_f####.ppm
timeout 300 python3 tools/rom_smoke_0442.py roms/tetris_dx.gbc --frames 1200

Resultado: Ejecutado hasta frame 1200 exitosamente. ThreeBufferStats muestra idx_nonzero=22910 pero rgb_nonwhite=0, confirmando Caso 1 (problema en paletas/mapeo CGB).

Dumps generados:

  • /tmp/viboy_tetris_dx_idx_f600.ppm (FB_INDEX, 68KB) ✅
  • /tmp/viboy_tetris_dx_rgb_f600.ppm (FB_RGB, 68KB) ✅

Validación de módulo compilado C++: ✅ Compilación exitosa. Módulo C++ compilado correctamente. Todas las funciones expuestas a Python funcionan correctamente.

Fuentes Consultadas

  • Pan Docs: "Interrupts", "CGB Palettes", "LCD Status Register"
  • Pan Docs: "VRAM", "Power Up Sequence"

Integridad Educativa

Lo que Entiendo Ahora

  • Bloqueo Post-Clear: Los juegos pueden quedar bloqueados después del clear VRAM si esperan una condición que el emulador no implementa correctamente. El análisis de PC hotspots e IO reads dominantes permite identificar qué condición espera el juego.
  • Clasificación Automática: Es posible clasificar automáticamente el tipo de bloqueo analizando los IO reads dominantes y el estado de IME/IE/IF. Esto acelera el diagnóstico y permite proponer fixes mínimos específicos.
  • Three-Buffer Pipeline CGB: El pipeline de renderizado CGB tiene tres etapas (índices, RGB, present). Si una etapa falla, las siguientes también fallan. El análisis de ThreeBufferStats permite identificar exactamente en qué etapa está el problema.
  • Caso 1 CGB: Si idx_nonzero>0 pero rgb_nonwhite==0, el problema está en la conversión índice→RGB, típicamente porque las paletas CGB no están configuradas o el mapeo está incorrecto.

Lo que Falta Confirmar

  • DMG: Por qué el loop en 0x036C no progresa a pesar de que VBlank se sirve correctamente. Necesita análisis más profundo del disasm y posiblemente instrumentación adicional del servicio de interrupciones.
  • CGB: Verificar que las paletas CGB se están escribiendo correctamente (activar VIBOY_DEBUG_CGB_PALETTE_WRITES=1) y revisar la función convert_framebuffer_to_rgb() para asegurar que lee las paletas correctamente.
  • Clear VRAM Detection: Por qué el clear VRAM no se detectó en DMG (ClearDoneFrame=0) a pesar de que hay 6144 intentos de write. Posible bug en la lógica de detección o los writes no fueron todos en el mismo frame.

Hipótesis y Suposiciones

DMG: Asumimos que el loop en 0x036C espera un flag específico de IF que no se está activando o se limpia incorrectamente. Esto necesita verificación con instrumentación adicional.

CGB: Asumimos que el problema está en las paletas CGB no configuradas, pero esto necesita verificación activando VIBOY_DEBUG_CGB_PALETTE_WRITES=1 y revisando los logs.

Próximos Pasos

  • [ ] DMG: Investigar por qué el loop en 0x036C no progresa a pesar de que VBlank se sirve correctamente
  • [ ] DMG: Verificar que el servicio de interrupciones limpia IF correctamente
  • [ ] CGB: Activar VIBOY_DEBUG_CGB_PALETTE_WRITES=1 y verificar que las paletas se están escribiendo
  • [ ] CGB: Revisar convert_framebuffer_to_rgb() para asegurar que lee paletas CGB correctamente
  • [ ] Ambos: Verificar que la detección de clear VRAM funciona correctamente cuando los writes no son todos en el mismo frame