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.
Fiabilidad Watch/Trace + Blindar Semántica JOYP
Resumen
Step 0487 implementa mejoras críticas de fiabilidad en los mecanismos de watch/trace y blindaje semántico para prevenir interpretaciones erróneas de registros I/O:
- Fase A (Mario - FF92 Watch Reliability): Implementación de "Single Source of Truth" con contadores cumulativos para FF92 writes/reads, tracking de resets no deseados, y test clean-room para validar fiabilidad.
- Fase B (Mario - FF92 → IE Chain): Ring-buffer trace en CPU para operaciones específicas (
LDH (0x92),A,LDH A,(0x92),LDH (0xFF),A) con evidencia irrefutable de la cadena FF92 → IE. - Fase C (Tetris DX - JOYP Semantics): Implementación de
JOYPSelectLabelenum y contadores cumulativos por tipo de selección (BUTTONS_SELECTED, DPAD_SELECTED, NONE_SELECTED) para blindar la semántica y prevenir malinterpretaciones. - Fase D (Autopress Edge-Triggered): Implementación opcional de autopress edge-triggered basado en eventos de selección JOYP.
Se generan reportes detallados en /tmp/reporte_step0487.md con evidencia irrefutable de los patrones detectados.
Concepto de Hardware
Single Source of Truth para Watch/Trace
Los mecanismos de watch/trace deben proporcionar una única fuente de verdad que sea:
- Cumulativa: Los contadores nunca se resetean durante la ejecución, permitiendo análisis a largo plazo.
- Completa: Captura todos los eventos relevantes sin pérdida de información.
- Consistente: Los valores reportados son siempre coherentes entre diferentes getters.
- Escribe un valor temporal en
HRAM[0xFF92]usandoLDH (0x92),A - Lee el valor de vuelta usando
LDH A,(0x92) - Escribe el valor final en
IE(0xFFFF) usandoLDH (0xFF),A - Bit 4 = 0, Bit 5 = 0: NONE_SELECTED (ningún grupo seleccionado, lectura devuelve 0xFF)
- Bit 4 = 0, Bit 5 = 1: DPAD_SELECTED (se lee el grupo de direcciones)
- Bit 4 = 1, Bit 5 = 0: BUTTONS_SELECTED (se lee el grupo de botones de acción)
- Bit 4 = 1, Bit 5 = 1: NONE_SELECTED (ambos bits activos = deselección)
Problema anterior: Los contadores de watch podían resetearse inesperadamente o no capturar todos los eventos, llevando a interpretaciones erróneas de los datos.
FF92 → IE Chain en Mario
Mario usa una secuencia específica para manipular el registro IE (Interrupt Enable):
Esta cadena requiere evidencia irrefutable para confirmar que se ejecuta correctamente y que no hay bugs
en el direccionamiento LDH cuando a8 >= 0x80.
JOYP Semantics - Blindaje contra Malinterpretaciones
El registro JOYP (0xFF00) tiene bits de selección (4-5) que determinan qué grupo de botones se lee:
Problema anterior: Interpretar valores crudos de JOYP (ej: "0x30 = buttons selected") sin considerar la semántica correcta de los bits de selección llevaba a conclusiones erróneas.
Solución: Usar un enum JOYPSelectLabel que etiqueta explícitamente el estado de selección,
eliminando ambigüedades y previniendo malinterpretaciones.
Fuente: Pan Docs - Joypad Input
Implementación
Fase A: FF92 Single Source of Truth
Se implementa la estructura HRAMFF92SingleSource en MMU.hpp con:
- Contadores cumulativos:
write_count_total,read_count_total - Tracking de último evento:
last_write_pc,last_write_val,last_read_pc,last_read_val - Detección de resets no deseados:
ff92_watch_reset_count_,ff92_watch_reset_last_pc_
Los contadores se actualizan en MMU::read() y MMU::write() antes de cualquier early return,
garantizando que todos los eventos se capturen.
Fase B: FF92/IE Trace Ring-Buffer
Se implementa un ring-buffer en CPU para trazar operaciones específicas:
FF92IETraceEventstruct con:type(FF92_W/FF92_R/IE_W),frame,pc,a8,effective_addr,val- Ring-buffer de tamaño fijo (256 eventos) que se sobrescribe circularmente
- Tracking en
CPU::step()dentro de los casos0xE0(LDH (a8),A) y0xF0(LDH A,(a8))
El trace captura la secuencia completa FF92_W → FF92_R → IE_W con evidencia irrefutable de direcciones efectivas y valores.
Fase C: JOYP Semantics Blindaje
Se implementa:
JOYPSelectLabelenum:BOTH_SELECTED,BUTTONS_SELECTED,DPAD_SELECTED,NONE_SELECTED- Función helper
get_joyp_select_label()que etiqueta correctamente el estado de selección basado en bits 4-5 - Contadores cumulativos por tipo de selección y source (program vs CPU poll):
- Writes:
joyp_write_buttons_selected_total_,joyp_write_dpad_selected_total_,joyp_write_none_selected_total_ - Reads (program):
joyp_read_buttons_selected_total_prog_, etc. - Reads (CPU poll):
joyp_read_buttons_selected_total_cpu_poll_, etc.
- Writes:
El campo select_label se añade a JOYPTraceEvent para etiquetar explícitamente cada evento.
Fase D: Autopress Edge-Triggered (Opcional)
Implementación de autopress edge-triggered en src/viboy.py que:
- Se activa cuando se detecta un write program a BUTTONS_SELECTED
- Mantiene START presionado hasta que se detecta un read program en BUTTONS_SELECTED o un write a NONE_SELECTED
- Requiere evidencia en el reporte de que el bit START cambia correctamente
Reportes Generados
Se actualiza tools/rom_smoke_0442.py para generar reportes específicos:
- Mario: Reporte FF92/IE trace con contadores, último evento, y tail del trace (últimos 50 eventos)
- Tetris DX: Reporte JOYP semantics con contadores por tipo de selección y source, y análisis de patrones
Los reportes se escriben tanto a stdout como a /tmp/reporte_step0487.md.
Archivos Afectados
src/core/cpp/MMU.hpp- EstructuraHRAMFF92SingleSource, enumJOYPSelectLabel, contadores cumulativossrc/core/cpp/MMU.cpp- Implementación de tracking FF92/IE y JOYP semanticssrc/core/cpp/CPU.hpp- EstructuraFF92IETraceEventy ring-buffersrc/core/cpp/CPU.cpp- Tracking de operaciones LDH en el tracesrc/core/cython/mmu.pxd- Declaraciones de nuevos getters FF92/IE/JOYPsrc/core/cython/mmu.pyx- Wrappers Python para getters FF92/IE/JOYPsrc/core/cython/cpu.pxd- Declaración deFF92IETraceEventsrc/core/cython/cpu.pyx- Wrappers Python para trace FF92/IEtests/test_ff92_watch_reliability_0487.py- Test clean-room para validar fiabilidad FF92 watchtools/rom_smoke_0442.py- Métodos_print_mario_ff92_ie_report()y_print_tetris_joyp_report()
Tests y Verificación
Validación de la implementación:
- Test clean-room FF92 watch reliability:
pytest tests/test_ff92_watch_reliability_0487.py -v # Resultado: 2 passed in 0.26sEl test valida que los contadores cumulativos funcionan correctamente y que no hay resets inesperados.
- ROM Mario (mario.gbc):
VIBOY_DEBUG_MARIO_FF92=1 python tools/rom_smoke_0442.py roms/mario.gbc --frames 100Resultado: Evidencia irrefutable de cadena FF92 → IE:
- FF92 writes: 34
- FF92 reads: 34
- IE writes: 35
- Trace muestra secuencia: FF92_W → FF92_R → IE_W repetida múltiples veces
- ROM Tetris DX (tetris_dx.gbc):
VIBOY_DEBUG_JOYP_TRACE=1 python tools/rom_smoke_0442.py roms/tetris_dx.gbc --frames 100Resultado: Patrones JOYP claramente identificados:
- BUTTONS_SELECTED writes: 56
- DPAD_SELECTED writes: 12199
- NONE_SELECTED writes: 12350
- Total reads: 3 (todos con NONE_SELECTED)
Validación de módulo compilado C++: Todos los getters expuestos correctamente a Python a través de Cython.
Fuentes Consultadas
- Pan Docs - CPU Instruction Set: LDH Instruction
- Pan Docs - Joypad Input: JOYP Register Semantics
- Pan Docs - Memory Map: HRAM Region (0xFF80-0xFFFE)
Integridad Educativa
Lo que Entiendo Ahora
- Single Source of Truth: Los mecanismos de diagnóstico deben tener una única fuente de verdad cumulativa que nunca se resetea, garantizando consistencia en los datos reportados.
- FF92 → IE Chain: Mario usa una secuencia específica de LDH para manipular IE, y el trace ring-buffer proporciona evidencia irrefutable de que esta cadena se ejecuta correctamente.
- JOYP Semantics: Los bits de selección (4-5) de JOYP tienen semántica específica que debe etiquetarse explícitamente para prevenir malinterpretaciones. El valor crudo "0x30" no significa "buttons selected" sin considerar la semántica correcta.
- Ring-Buffer Trace: Un ring-buffer de tamaño fijo es eficiente para trazar eventos recientes sin consumir memoria ilimitada, sobrescribiendo eventos antiguos circularmente.
Lo que Falta Confirmar
- Autopress Edge-Triggered: La implementación opcional de autopress requiere evidencia adicional de que el bit START cambia correctamente en el reporte.
- Performance Impact: El overhead de los contadores cumulativos y el ring-buffer trace debe medirse en ejecuciones largas para asegurar que no afecta significativamente el rendimiento.
Hipótesis y Suposiciones
Hipótesis confirmada: La cadena FF92 → IE en Mario se ejecuta correctamente. El trace muestra evidencia irrefutable de que:
- FF92_W (PC=0x1288) escribe en 0xFF92
- FF92_R (PC=0x1298) lee de 0xFF92
- IE_W (PC=0x129A) escribe en 0xFFFF
Esta secuencia se repite múltiples veces, confirmando que no hay bug en el direccionamiento LDH cuando a8 >= 0x80.
Próximos Pasos
- [ ] Analizar el reporte de Tetris DX para determinar si el patrón de JOYP requiere autopress edge-triggered
- [ ] Medir el overhead de los contadores cumulativos y ring-buffer trace en ejecuciones largas
- [ ] Implementar autopress edge-triggered si la evidencia del reporte lo justifica
- [ ] Documentar los criterios de aceptación para Mario y Tetris DX basados en los reportes generados