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

DMG VRAM Tiledata Write Audit + Force-Writes A/B + IRQ Tracking Fix

Fecha: 2026-01-10 Step ID: 0501 Estado: VERIFIED

Resumen

Este Step implementa un sistema completo de auditoría de escrituras VRAM para diagnosticar por qué los juegos DMG (específicamente tetris.gb) no muestran contenido visual. Se añadió instrumentación exhaustiva en MMU::write() para VRAM (0x8000-0x9FFF) que captura estado PPU, razón de bloqueo, y verifica readback inmediato. Se implementó un flag VIBOY_VRAM_FORCE_WRITES para tests de aislamiento que fuerza escrituras incluso cuando están bloqueadas. Se corrigió una inconsistencia en el tracking de IRQ donde irq_vblank_services_ e interrupt_taken_counts_[0] no se incrementaban en el mismo punto. Se añadió tracking de modo PPU para detectar problemas de timing (mode3 stuck). Finalmente, se implementó el clasificador DMG v3 que usa estas nuevas métricas para diagnóstico más preciso.

Concepto de Hardware

VRAM Access Rules (Pan Docs): La VRAM (0x8000-0x9FFF) es accesible por la CPU durante ciertos modos de la PPU:

  • LCD OFF (LCDC bit 7 = 0): VRAM es siempre accesible
  • LCD ON (LCDC bit 7 = 1):
    • Mode 0 (HBlank): VRAM accesible
    • Mode 1 (VBlank): VRAM accesible
    • Mode 2 (OAM Search): VRAM accesible
    • Mode 3 (Pixel Transfer): VRAM NO accesible - bloqueada

Si la CPU intenta escribir a VRAM durante Mode 3 cuando LCD está ON, el write es bloqueado (ignorado). Esto es crítico porque el PPU está leyendo datos de VRAM para renderizar, y un write simultáneo podría corromper los datos.

PPU Modes: La PPU opera en 4 modos principales (documentados en STAT register):

  • Mode 0 (HBlank): Período entre líneas (252-455 ciclos de CPU)
  • Mode 1 (VBlank): Período entre frames (líneas 144-153, ~4560 ciclos)
  • Mode 2 (OAM Search): Búsqueda de sprites (0-79 ciclos de CPU)
  • Mode 3 (Pixel Transfer): Transferencia de píxeles a LCD (80-251 ciclos de CPU)

Si Mode 3 dura demasiado tiempo (más de 456 ciclos por línea), indica un problema de timing del PPU que podría causar bloqueos incorrectos de VRAM.

IRQ Tracking Consistency: Para que el tracking de IRQ sea confiable, los contadores de "servicio" e "IRQ tomado" deben incrementarse en el mismo punto exacto. Si se incrementan en puntos diferentes, pueden desincronizarse y causar diagnósticos incorrectos.

Referencia: Pan Docs - VRAM Access, LCD Timing, STAT Register

Implementación

Fase A: IRQ Tracking Fix ✅

A1) Unificación de Semántica IRQ Tracking:

  • Modificación de CPU::handle_interrupts() para asegurar que irq_vblank_services_ e interrupt_taken_counts_[0] se incrementen en el mismo punto exacto
  • Añadido log de desincronización si los contadores no coinciden (debugging)
  • Garantiza que "IRQ serviced" y "IRQ taken" son semánticamente equivalentes

Fase B: VRAM Write Audit ✅

B1) Instrumentación en MMU::write() para VRAM:

  • Nueva estructura VRAMWriteEvent:
    • frame_id: ID del frame
    • pc, addr, value: Dirección y valor del write
    • region: TILE_DATA (0x8000-0x97FF) o TILE_MAP (0x9800-0x9FFF)
    • lcdc, lcd_on, stat_mode, ly: Estado PPU en el momento del write
    • allowed, blocked_reason: Si el write fue permitido y por qué
    • readback_value, readback_matches: Verificación inmediata de integridad
    • forced: Si el write fue forzado (VIBOY_VRAM_FORCE_WRITES=1)
  • Nueva estructura VRAMWriteAuditStats:
    • Contadores agregados: tiledata_write_attempts, tiledata_write_allowed, tiledata_write_blocked, tiledata_write_readback_mismatch
    • Lo mismo para tilemap
    • last_blocked_pc, last_blocked_addr, last_blocked_reason: Último bloqueo para debugging
  • Ring buffer de 256 eventos VRAM (vram_write_ring_)
  • Captura completa en MMU::write() cuando addr >= 0x8000 && addr <= 0x9FFF
  • Aplicación de reglas Pan Docs: bloquea writes durante Mode 3 si LCD está ON
  • Readback inmediato después de write exitoso para verificar integridad

B2) Flag VIBOY_VRAM_FORCE_WRITES:

  • Variable de entorno VIBOY_VRAM_FORCE_WRITES que fuerza escrituras VRAM incluso cuando están bloqueadas
  • Útil para tests de aislamiento: diferencia entre problemas de timing/modo vs bugs de MMU/decoder
  • Si con force-writes el juego funciona, el problema es de timing; si no, es un bug en MMU/decoder

Fase C: PPU Mode Reality Check ✅

C) Métricas PPU Mode por Frame:

  • Nueva estructura PPUModeStats:
    • mode_entries_count[4]: Número de entradas en cada modo (0, 1, 2, 3)
    • mode_cycles[4]: Ciclos totales en cada modo
    • ly_min, ly_max: Rango de LY observado
    • frames_with_mode3_stuck: Frames donde Mode 3 duró más de 456 ciclos
  • Actualización en PPU::step():
    • Incremento de mode_entries_count[mode_] y mode_cycles[mode_] en update_mode()
    • Actualización de ly_min y ly_max después de incrementar ly_
    • Detección de Mode 3 stuck: si mode_ == 3 y cycles > CYCLES_PER_SCANLINE, incrementa contador

Fase E: DMG Classifier v3 ✅

E) Clasificador DMG v3 con VRAM Audit:

  • Nuevo método _classify_dmg_quick_v3() en rom_smoke_0442.py
  • Usa get_vram_write_audit_stats(), get_vram_write_ring(), y get_ppu_mode_stats()
  • Clasificaciones nuevas:
    • VRAM_BLOCKED_INCORRECTLY: Writes bloqueados en modos que no deberían bloquear (no Mode 3)
    • VRAM_BLOCKED_MODE3_STUCK: Writes bloqueados porque Mode 3 está stuck
    • VRAM_WRITE_READBACK_MISMATCH: Writes permitidos pero readback no coincide
    • PPU_MODE3_STUCK: PPU en Mode 3 demasiado tiempo
    • PPU_MODE3_DOMINANT: Mode 3 ocupa más del 60% del tiempo
    • VRAM_NO_ATTEMPTS: No hay intentos de escribir a VRAM (programa no intenta cargar tiles)
    • VRAM_ALLOWED_BUT_NOT_READABLE: Writes permitidos pero luego no se pueden leer correctamente
  • Fallback a v2 si no hay métricas nuevas disponibles
  • Actualizado _classify_dmg_quick() para usar v3 por defecto

Exposición Cython ✅

Wrappers Cython:

  • mmu.pxd: Añadidas estructuras VRAMWriteEvent y VRAMWriteAuditStats
  • mmu.pyx: Implementados métodos get_vram_write_audit_stats() y get_vram_write_ring()
  • ppu.pxd: Añadida estructura PPUModeStats
  • ppu.pyx: Implementado método get_ppu_mode_stats()
  • Corrección de tipos: uso de != 0 en lugar de bool() para convertir bint a Python bool
  • Import explícito de PPUModeStats en ppu.pyx

Archivos Afectados

  • src/core/cpp/MMU.hpp: Estructuras VRAMWriteEvent y VRAMWriteAuditStats
  • src/core/cpp/MMU.cpp: Instrumentación VRAM en write(), implementación de getters
  • src/core/cpp/CPU.cpp: Fix de unificación de IRQ tracking
  • src/core/cpp/PPU.hpp: Estructura PPUModeStats
  • src/core/cpp/PPU.cpp: Tracking de modo PPU en step()
  • src/core/cython/mmu.pxd: Declaraciones de estructuras y métodos
  • src/core/cython/mmu.pyx: Wrappers Python para VRAM audit
  • src/core/cython/ppu.pxd: Declaración de PPUModeStats
  • src/core/cython/ppu.pyx: Wrapper Python para PPU mode stats
  • tools/rom_smoke_0442.py: Clasificador DMG v3

Tests y Verificación

Compilación:

python3 setup.py build_ext --inplace

Compilación exitosa sin errores. Correcciones aplicadas:

  • Corrección de tipos bool()!= 0 para bint en Cython
  • Import explícito de PPUModeStats en ppu.pyx

Tests Unitarios:

pytest tests/test_core_cpu.py -v

Resultado: 6 passed in 0.14s

Test de Build:

python3 test_build.py

Resultado: EXITO - El pipeline de compilacion funciona correctamente

Validación Nativa: Todas las nuevas funciones están disponibles y compiladas correctamente:

  • mmu.get_vram_write_audit_stats()
  • mmu.get_vram_write_ring(max_events)
  • ppu.get_ppu_mode_stats()

Resultados y Diagnóstico

Estado Actual:

  • ✅ IRQ tracking unificado y consistente
  • ✅ Sistema completo de auditoría VRAM implementado
  • ✅ PPU mode tracking para detectar problemas de timing
  • ✅ Clasificador DMG v3 con nuevas métricas
  • ✅ Flag VIBOY_VRAM_FORCE_WRITES para tests de aislamiento

Próximos Pasos:

  • Fase D (Pendiente): Ejecutar 3 tests por ROM (tetris.gb, pkmn.gb) con diferentes configuraciones de VIBOY_VRAM_FORCE_WRITES y capturar snapshots
  • Fase F (Condicional): Aplicar fix mínimo solo si el diagnóstico es conclusivo

Nota: El sistema de auditoría está listo para recopilar evidencia detallada sobre cómo se está accediendo a VRAM, si los writes están siendo bloqueados correctamente según Pan Docs, y si hay problemas de timing del PPU que puedan estar causando bloqueos incorrectos.

Referencias

  • Pan Docs - VRAM Access Rules
  • Pan Docs - LCD Timing, PPU Modes, STAT Register
  • Pan Docs - Interrupts, IRQ Handling
  • Cython Documentation - Type Conversion, C++ Integration