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

Diagnóstico del Timer: TAC e Interrupciones

Fecha: 2025-12-18 Step ID: 0076 Estado: Draft

Resumen

Se añadió instrumentación de diagnóstico para monitorear el uso del Timer (TAC) y detectar si el juego está intentando usar la interrupción del Timer (vector 0x0050). El objetivo es determinar si el congelamiento en el logo de Pokémon se debe a que el Timer no está disparando interrupciones cuando debería, lo que bloquearía el RNG (Generador de Números Aleatorios) y la lógica del juego.

Se instrumentó el registro TAC (0xFF07) para detectar cuando el juego activa el Timer, y se añadió logging específico cuando se dispara la interrupción del Timer (vector 0x0050) en el despachador de interrupciones de la CPU.

Concepto de Hardware

El Timer de la Game Boy es un sistema de temporización crítico que muchos juegos utilizan para:

  • RNG (Generador de Números Aleatorios): El Timer incrementa continuamente, y los juegos lo usan como semilla para generar números aleatorios. Si el Timer no funciona, el RNG se congela.
  • Música y Sonido: El Timer puede usarse para sincronizar la reproducción de música y efectos de sonido.
  • Lógica del Juego: Muchos juegos dependen del Timer para avanzar animaciones, eventos temporales, etc.

El Timer tiene varios registros:

  • DIV (0xFF04): Divider Register - Contador que incrementa continuamente a 16384 Hz.
  • TIMA (0xFF05): Timer Counter - Contador configurable que puede generar interrupciones.
  • TMA (0xFF06): Timer Modulo - Valor de recarga cuando TIMA desborda.
  • TAC (0xFF07): Timer Control - Controla si TIMA está activo y su frecuencia.

Cuando TIMA hace overflow (pasa de 0xFF a 0x00), se recarga con TMA y se solicita una interrupción Timer (Bit 2 de IF, 0xFF0F), que tiene el vector 0x0050.

Hipótesis del diagnóstico: Si el juego activa el Timer (escribe en TAC con bit 2 = 1) pero nunca vemos la interrupción del Timer dispararse, entonces hay un bug en la implementación del Timer. Si vemos la interrupción dispararse, entonces el Timer funciona y el problema está en otro lugar.

Fuente: Pan Docs - Timer and Divider Registers

Implementación

Se añadió instrumentación en dos puntos clave:

1. Logging de TAC en MMU

En src/memory/mmu.py, se añadió logging cuando el juego escribe en el registro TAC (0xFF07):

# Interceptar escritura al registro TAC (0xFF07) - Timer Control
if addr == IO_TAC:
    # Instrumentación para diagnóstico: detectar configuración del Timer
    timer_enable = (value & 0x04) != 0
    clock_select = value & 0x03
    clock_names = {0: "4096Hz", 1: "262144Hz", 2: "65536Hz", 3: "16384Hz"}
    clock_name = clock_names.get(clock_select, "Unknown")
    logger.info(
        f"⏰ TAC UPDATE: {value:02X} (Enable={timer_enable}, Clock={clock_select} "
        f"({clock_name}))"
    )

Nota: Se corrigió un error de sintaxis inicial donde se intentaba usar clock_names.get() directamente dentro del f-string. La solución fue extraer el valor a una variable clock_name antes de usarlo en el f-string.

Esto permite detectar:

  • Si el juego activa el Timer (bit 2 = 1)
  • Qué frecuencia selecciona (bits 1-0)
  • Cuándo se configura el Timer

2. Logging de Interrupción del Timer en CPU

En src/cpu/core.py, se añadió logging específico cuando se dispara la interrupción del Timer (vector 0x0050):

# DIAGNÓSTICO ESPECÍFICO: Log cuando se dispara la interrupción del Timer
if interrupt_vector == 0x0050:
    logger.info(f"⚡ TIMER INTERRUPT DISPATCHED! (TIMA Overflow)")

Esto permite detectar si la interrupción del Timer se está disparando correctamente cuando TIMA hace overflow.

3. Verificación de la Lógica del Timer

Se revisó src/io/timer.py para verificar que la lógica de overflow está correcta. La implementación actual:

  • Incrementa TIMA cuando se alcanza el umbral según la frecuencia configurada.
  • Detecta overflow cuando TIMA pasa de 0xFF a 0x00.
  • Recarga TIMA con TMA cuando hay overflow.
  • Solicita la interrupción Timer (Bit 2 de IF) cuando hay overflow.

La lógica parece correcta según la documentación. El diagnóstico permitirá confirmar si el Timer está funcionando o si hay un bug sutil.

Archivos Afectados

  • src/memory/mmu.py - Añadido logging de TAC UPDATE cuando se escribe en 0xFF07 (con fix de sintaxis: extracción de variable para evitar error en f-string)
  • src/cpu/core.py - Añadido logging específico cuando se dispara la interrupción del Timer (vector 0x0050)
  • docs/bitacora/entries/2025-12-18__0076__diagnostico-timer-tac-interrupciones.html - Nueva entrada de bitácora
  • docs/bitacora/index.html - Actualizado con la nueva entrada
  • docs/bitacora/entries/2025-12-18__0075__diagnostico-stat-profundo-monitoreo-escrituras.html - Actualizado enlace "Siguiente"
  • INFORME_COMPLETO.md - Añadida entrada del paso 0076

Tests y Verificación

Este paso es de diagnóstico, no de implementación funcional. La verificación se realizará ejecutando el juego y analizando los logs:

  • Comando de ejecución: python main.py pkmn.gb
  • Entorno: Windows / Python 3.10+
  • Fix aplicado: Se corrigió un error de sintaxis en el logging de TAC donde se intentaba usar clock_names.get() directamente dentro del f-string. Se extrajo el valor a una variable antes de usarlo.
  • Qué buscar en los logs:
    • ⏰ TAC UPDATE: ¿El juego activa el Timer? (Enable=True)
    • ⚡ TIMER INTERRUPT DISPATCHED!: ¿Se dispara la interrupción del Timer?

Escenarios esperados:

  1. Si vemos TAC UPDATE con Enable=True pero NO vemos TIMER INTERRUPT:
    • El Timer está roto. El juego lo pide, pero nuestra implementación no genera la señal.
    • Solución: Revisar la lógica de tick() en timer.py.
  2. Si vemos TIMER INTERRUPT:
    • El Timer funciona. El problema está en otro lugar (¿Joypad bloqueado? ¿Bug de CPU oscuro?).
  3. Si NO vemos TAC UPDATE con Enable=True:
    • El juego no está usando el Timer. El problema está en otro lugar.

Estado: Pendiente de ejecución y análisis de logs.

Fuentes Consultadas

Integridad Educativa

Lo que Entiendo Ahora

  • Timer y RNG: Muchos juegos dependen del Timer para generar números aleatorios. Si el Timer no funciona, el RNG se congela y el juego puede quedarse bloqueado esperando un valor que nunca llega.
  • Interrupción del Timer: Cuando TIMA hace overflow, se solicita una interrupción (Bit 2 de IF) con vector 0x0050. Esta interrupción es crítica para la lógica de muchos juegos.
  • Diagnóstico sistemático: Al instrumentar TAC y la interrupción del Timer, podemos determinar si el problema es que el Timer no funciona o si está en otro lugar.

Lo que Falta Confirmar

  • ¿El juego activa el Timer? Necesitamos ejecutar el juego y ver si aparece ⏰ TAC UPDATE con Enable=True.
  • ¿Se dispara la interrupción del Timer? Necesitamos ver si aparece ⚡ TIMER INTERRUPT DISPATCHED! en los logs.
  • ¿Hay un bug en la lógica del Timer? Si el juego activa el Timer pero no vemos interrupciones, hay un bug en nuestra implementación.

Hipótesis y Suposiciones

Hipótesis principal: El juego se congela porque el Timer no está disparando interrupciones cuando debería, bloqueando el RNG y la lógica del juego.

Suposición: La implementación del Timer en timer.py es correcta, pero puede haber un bug sutil que impide que las interrupciones se disparen. El diagnóstico permitirá confirmar o descartar esta hipótesis.

Próximos Pasos

  • [ ] Ejecutar python main.py pkmn.gb y analizar los logs
  • [ ] Buscar ⏰ TAC UPDATE para ver si el juego activa el Timer
  • [ ] Buscar ⚡ TIMER INTERRUPT DISPATCHED! para ver si se dispara la interrupción
  • [ ] Si el Timer está roto: Revisar y corregir la lógica de tick() en timer.py
  • [ ] Si el Timer funciona: Continuar el diagnóstico en otra dirección (Joypad, CPU, etc.)