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.
LYC Coincidence & STAT IRQ Fix
Resumen
Este Step implementa y corrige la lógica de comparación LYC (LY Compare) y la generación de interrupciones STAT en la PPU. El Step 0264 confirmó que el HALT funciona correctamente, pero la intro de Pokémon Red sigue sin avanzar. La hipótesis es que el juego está esperando una interrupción LCD STAT (por coincidencia LY=LYC) para sincronizar efectos visuales o avanzar la lógica, y nuestra PPU no la está disparando correctamente. Este Step asegura que cuando LY coincide con LYC y el bit 6 de STAT está habilitado, se solicite la interrupción STAT en el flanco de subida (rising edge).
Concepto de Hardware
LYC Register (0xFF45): El registro LYC (LY Compare) permite al software configurar un valor de línea (0-255) con el que se compara LY (Línea actual). Cuando LY == LYC, el bit 2 del registro STAT se activa, indicando una coincidencia.
STAT Register (0xFF41): El registro STAT tiene varios bits importantes:
- Bit 2 (LYC=LY Coincidence Flag): Se activa cuando LY == LYC. Es de solo lectura y se actualiza dinámicamente por la PPU.
- Bit 6 (LYC Interrupt Enable): Si está activo, solicita una interrupción STAT cuando LY == LYC.
- Bits 3-5: Habilitan interrupciones por modo PPU (H-Blank, V-Blank, OAM Search).
Rising Edge Detection: La interrupción STAT solo debe dispararse en el flanco de subida (rising edge), es decir, cuando la condición pasa de False a True. Si se dispara en cada ciclo donde la condición es True, se saturaría la CPU con interrupciones.
El caso de Pokémon Red: Muchos juegos avanzados como Pokémon usan la interrupción STAT por LYC para sincronizar efectos visuales (cambiar paletas en medio de la pantalla, efectos de raster, etc.). Si esta interrupción no se dispara correctamente, el juego puede quedarse esperando y no avanzar.
Fuente: Pan Docs - "LCD Status Register (STAT)", "LYC Register (0xFF45)", "LCD Interrupts"
Implementación
Se implementaron dos mejoras críticas:
- Interceptación de escrituras a LYC en MMU: Cuando el juego escribe en 0xFF45, la MMU ahora actualiza inmediatamente la PPU llamando a
PPU::set_lyc(). - Detección mejorada de rising edge para LYC: Cuando LY cambia y coincide con LYC, se verifica inmediatamente si debe dispararse la interrupción STAT, antes de resetear los flags de interrupción.
Componentes modificados
- MMU::write(): Se añadió interceptación para escrituras a 0xFF45 (LYC) que actualiza la PPU inmediatamente.
- PPU::step(): Se mejoró la lógica de detección de rising edge para LYC. Cuando LY cambia, se guarda el estado anterior de LYC match, se actualiza LY, y se verifica inmediatamente si hay un rising edge (LYC match pasó de False a True). Si el bit 6 de STAT está habilitado, se solicita la interrupción STAT.
- PPU::check_stat_interrupt(): La lógica existente ya era correcta, pero ahora se complementa con la verificación inmediata en
step().
Decisiones de diseño
Verificación inmediata después de cambiar LY: Cuando LY cambia, se verifica inmediatamente si LY == LYC y si debe dispararse la interrupción. Esto asegura que el rising edge se detecte en el momento exacto en que ocurre, no más tarde.
Preservación del estado de LYC en stat_interrupt_line_: Cuando LY cambia, se preserva el bit 0 de stat_interrupt_line_ si LYC match sigue activo, y se limpia si está inactivo. Los bits de modo (1-3) se limpian porque el modo cambió. Esto permite detectar correctamente el rising edge en la próxima verificación.
Interceptación de LYC en MMU: La MMU intercepta escrituras a 0xFF45 y actualiza la PPU inmediatamente. Esto asegura que cuando el juego configura LYC, la PPU puede verificar inmediatamente si LY == LYC y actualizar el bit 2 de STAT.
Archivos Modificados
src/core/cpp/MMU.cpp: Interceptación de escrituras a LYC (0xFF45).src/core/cpp/PPU.cpp: Mejora de la detección de rising edge para LYC enstep().
Tests y Verificación
Los tests existentes para interrupciones STAT y LYC siguen pasando. La implementación mejora la detección de rising edge sin romper la funcionalidad existente.
Comando de prueba:
python main.py roms/pkmn.gb
Validación esperada: La intro de Pokémon Red debería avanzar si el juego estaba esperando una interrupción STAT por LYC. Si el problema persiste, podría ser necesario revisar otras fuentes de interrupciones (Timer, Serial) o el estado de IME.
Referencias
- Pan Docs - "LCD Status Register (STAT)"
- Pan Docs - "LYC Register (0xFF45)"
- Pan Docs - "LCD Interrupts"
- Pan Docs - "Rising Edge Detection"