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.
Monitor GPS (El Navegador)
Resumen
Tras superar el bucle de Echo RAM en 0x2B30, el emulador corre estable a 60 FPS, pero la pantalla
sigue mostrando solo el color de fondo (verde claro). Para diagnosticar el estado actual de la CPU sin saturar
la consola con logs masivos, se implementa un monitor GPS (Navegador) que reporta periódicamente
la posición del Program Counter (PC), el Stack Pointer (SP), el estado de las interrupciones (IME, IE, IF) y
el estado del video (LCDC, LY).
El monitor se activa cada 60 frames (aproximadamente una vez por segundo) y muestra información crítica
del estado del sistema en un formato compacto: [GPS] PC:XXXX | SP:XXXX | IME:X | IE:XX IF:XX | LCDC:XX LY:XX.
Esto permite identificar si la CPU está atascada en un bucle, esperando interrupciones, o ejecutando código normalmente.
Concepto de Hardware
Diagnóstico No Intrusivo: En sistemas embebidos y emuladores, es crucial poder observar el estado interno del sistema sin modificar su comportamiento. Un monitor periódico (GPS) permite rastrear la posición de ejecución y el estado de los registros críticos sin afectar el timing del emulador.
Registros Clave para Diagnóstico:
- PC (Program Counter): Indica dónde está ejecutando código la CPU. Si el PC está fijo o en un bucle pequeño, la CPU puede estar atascada esperando una condición.
- SP (Stack Pointer): Indica la posición de la pila. Un SP que no cambia puede indicar que no se están haciendo llamadas a funciones o interrupciones.
- IME (Interrupt Master Enable): Indica si las interrupciones están habilitadas globalmente. Si IME=0 y la CPU está en HALT, el sistema puede estar esperando una interrupción que nunca llegará.
- IE (Interrupt Enable, 0xFFFF): Indica qué interrupciones están habilitadas (VBlank, LCD STAT, Timer, Serial, Joypad).
- IF (Interrupt Flag, 0xFF0F): Indica qué interrupciones están pendientes. Si una interrupción está pendiente pero IME=0, la CPU no la procesará.
- LCDC (LCD Control, 0xFF40): Controla el estado del LCD. Si el bit 7 está desactivado, el LCD está apagado y no se renderizará nada.
- LY (Line Y, 0xFF44): Indica la línea de scanline actual (0-153). Si LY está fijo, la PPU puede estar atascada.
Frecuencia de Muestreo: Reportar cada 60 frames (1 segundo a 60 FPS) es un equilibrio entre obtener información suficiente y no saturar la consola. Esta frecuencia permite detectar bucles rápidos sin generar miles de líneas de log.
Fuente: Pan Docs - CPU Registers, Interrupts, LCDC.
Implementación
El monitor GPS se implementa en el método run() de src/viboy.py, justo después del
incremento de frame_count. Se activa cuando frame_count % 60 == 0 (cada 60 frames).
Componentes creados/modificados
src/viboy.py: Añadido bloque de diagnóstico GPS en el métodorun()que lee y muestra el estado de la CPU, interrupciones y video cada segundo.
Decisiones de diseño
- Frecuencia de muestreo: 60 frames (1 segundo) es suficiente para detectar problemas sin saturar la consola. Se puede ajustar fácilmente cambiando el módulo.
- Formato compacto: El formato
[GPS] PC:XXXX | SP:XXXX | IME:X | IE:XX IF:XX | LCDC:XX LY:XXes fácil de leer y parsear visualmente. - Compatibilidad dual: El código soporta tanto el core C++ como el fallback Python, leyendo los registros de forma apropiada según el modo activo.
- No intrusivo: El monitor solo lee valores, no modifica el estado del sistema. Esto garantiza que no afecta el comportamiento del emulador.
Código implementado
# --- Step 0240: Monitor GPS (Navegador) ---
# Reporta la posición de la CPU y el estado del hardware cada segundo (60 frames)
if self.frame_count % 60 == 0:
if self._use_cpp and self._regs is not None and self._cpu is not None and self._mmu is not None:
# Leer registros de la CPU
pc = self._regs.pc
sp = self._regs.sp
ime = self._cpu.get_ime()
# Leer registros de interrupciones
ie = self._mmu.read(0xFFFF) # Interrupt Enable
if_register = self._mmu.read(0xFF0F) # Interrupt Flag
# Leer registros de video
lcdc = self._mmu.read(0xFF40) # LCD Control
ly = self._ppu.ly if self._ppu is not None else self._mmu.read(0xFF44) # LY (scanline actual)
# Formato: [GPS] PC:XXXX | SP:XXXX | IME:X | IE:XX IF:XX | LCDC:XX LY:XX
print(f"[GPS] PC:{pc:04X} | SP:{sp:04X} | IME:{ime} | IE:{ie:02X} IF:{if_register:02X} | LCDC:{lcdc:02X} LY:{ly:02X}")
elif not self._use_cpp and self._cpu is not None and self._mmu is not None:
# Fallback para modo Python
pc = self._cpu.registers.get_pc()
sp = self._cpu.registers.get_sp()
ime = 1 if self._cpu.ime else 0
ie = self._mmu.read(0xFFFF)
if_register = self._mmu.read(0xFF0F)
lcdc = self._mmu.read(0xFF40)
ly = self._ppu.ly if self._ppu is not None else self._mmu.read(0xFF44)
print(f"[GPS] PC:{pc:04X} | SP:{sp:04X} | IME:{ime} | IE:{ie:02X} IF:{if_register:02X} | LCDC:{lcdc:02X} LY:{ly:02X}")
# -------------------------------------------------
Archivos Afectados
src/viboy.py- Añadido bloque de monitor GPS en el métodorun()(líneas 942-970)docs/bitacora/entries/2025-12-22__0240__monitor-gps-navegador.html- Entrada de bitácoraINFORME_FASE_2.md- Actualización con Step 0240docs/bitacora/index.html- Añadida entrada al índice
Tests y Verificación
La implementación se validó ejecutando el emulador con Tetris y observando los logs del GPS:
- Ejecución manual:
python main.py roms/tetris.gb - Resultado esperado: Líneas
[GPS]apareciendo cada segundo en la consola con información del estado del sistema. - Validación: Los valores mostrados deben ser coherentes:
- PC debe estar en un rango válido (0x0100-0xFFFF para código del juego, 0x0000-0x00FF para interrupciones).
- SP debe estar en un rango válido (típicamente 0xC000-0xDFFF para WRAM o 0xFFFE-0xFFFF para HRAM).
- LCDC bit 7 debe estar activado (0x80 o superior) para que el LCD esté encendido.
- LY debe cambiar entre 0-153 si la PPU está funcionando.
Interpretación de los logs:
- Si el PC está fijo en un valor pequeño (ej:
0x02xx), el juego puede estar en la inicialización. - Si el PC está en
0xFFxx(HRAM), puede estar ejecutando una rutina de alta velocidad (DMA). - Si el PC salta mucho entre valores diferentes, el juego está ejecutando código normalmente.
- Si LCDC bit 7 está desactivado (valor < 0x80), el LCD está apagado y no se renderizará nada.
Fuentes Consultadas
- Pan Docs: CPU Registers and Flags
- Pan Docs: Interrupts
- Pan Docs: LCDC (LCD Control)
- Pan Docs: Video Display
Integridad Educativa
Lo que Entiendo Ahora
- Diagnóstico no intrusivo: Un monitor periódico permite observar el estado del sistema sin modificar su comportamiento, lo cual es crucial para depurar emuladores sin introducir efectos secundarios.
- Registros críticos: PC, SP, IME, IE, IF, LCDC y LY son suficientes para determinar si el sistema está funcionando correctamente o está atascado en un bucle.
- Frecuencia de muestreo: Reportar cada segundo (60 frames) es un equilibrio entre obtener información suficiente y no saturar la consola con logs masivos.
Lo que Falta Confirmar
- Estado real de la CPU: Los logs del GPS revelarán si la CPU está ejecutando código normalmente, esperando interrupciones, o atascada en un bucle.
- Estado del LCD: Si LCDC bit 7 está desactivado, el LCD está apagado y no se renderizará nada, lo cual explicaría la pantalla verde.
- Interrupciones: Si hay interrupciones pendientes (IF) pero IME está desactivado, la CPU puede estar esperando que se activen las interrupciones.
Hipótesis y Suposiciones
Asumimos que el monitor GPS no afecta el timing del emulador porque solo lee valores y no modifica el estado. Si los logs revelan que el PC está fijo o en un bucle pequeño, investigaremos qué condición está esperando la CPU.
Próximos Pasos
- [ ] Ejecutar el emulador con Tetris y observar los logs del GPS.
- [ ] Analizar los valores de PC para determinar si la CPU está ejecutando código normalmente o está atascada.
- [ ] Verificar el estado de LCDC para confirmar si el LCD está encendido.
- [ ] Si el PC está fijo, investigar qué condición está esperando la CPU (interrupciones, cambios de estado, etc.).
- [ ] Si el LCD está apagado (LCDC bit 7 = 0), investigar por qué el juego no lo enciende.