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
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 queirq_vblank_services_einterrupt_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 framepc,addr,value: Dirección y valor del writeregion: TILE_DATA (0x8000-0x97FF) o TILE_MAP (0x9800-0x9FFF)lcdc,lcd_on,stat_mode,ly: Estado PPU en el momento del writeallowed,blocked_reason: Si el write fue permitido y por quéreadback_value,readback_matches: Verificación inmediata de integridadforced: 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
- Contadores agregados:
- Ring buffer de 256 eventos VRAM (
vram_write_ring_) - Captura completa en
MMU::write()cuandoaddr >= 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_WRITESque 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 modoly_min,ly_max: Rango de LY observadoframes_with_mode3_stuck: Frames donde Mode 3 duró más de 456 ciclos
- Actualización en
PPU::step():- Incremento de
mode_entries_count[mode_]ymode_cycles[mode_]enupdate_mode() - Actualización de
ly_minyly_maxdespués de incrementarly_ - Detección de Mode 3 stuck: si
mode_ == 3ycycles > CYCLES_PER_SCANLINE, incrementa contador
- Incremento de
Fase E: DMG Classifier v3 ✅
E) Clasificador DMG v3 con VRAM Audit:
- Nuevo método
_classify_dmg_quick_v3()enrom_smoke_0442.py - Usa
get_vram_write_audit_stats(),get_vram_write_ring(), yget_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 estructurasVRAMWriteEventyVRAMWriteAuditStatsmmu.pyx: Implementados métodosget_vram_write_audit_stats()yget_vram_write_ring()ppu.pxd: Añadida estructuraPPUModeStatsppu.pyx: Implementado métodoget_ppu_mode_stats()- Corrección de tipos: uso de
!= 0en lugar debool()para convertirbinta Python bool - Import explícito de
PPUModeStatsenppu.pyx
Archivos Afectados
src/core/cpp/MMU.hpp: EstructurasVRAMWriteEventyVRAMWriteAuditStatssrc/core/cpp/MMU.cpp: Instrumentación VRAM enwrite(), implementación de getterssrc/core/cpp/CPU.cpp: Fix de unificación de IRQ trackingsrc/core/cpp/PPU.hpp: EstructuraPPUModeStatssrc/core/cpp/PPU.cpp: Tracking de modo PPU enstep()src/core/cython/mmu.pxd: Declaraciones de estructuras y métodossrc/core/cython/mmu.pyx: Wrappers Python para VRAM auditsrc/core/cython/ppu.pxd: Declaración dePPUModeStatssrc/core/cython/ppu.pyx: Wrapper Python para PPU mode statstools/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()→!= 0parabinten Cython - Import explícito de
PPUModeStatsenppu.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_WRITESy 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