This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Timer Diagnostics: TAC and Interruptions
Summary
Added diagnostic instrumentation to monitor Timer usage (TAC) and detect if the game is trying to use the Timer interrupt (vector 0x0050). The goal is to determine if the freeze on the Pokémon logo is due to the Timer not triggering interrupts when it should, which would block the RNG (Random Number Generator) and game logic.
Instrumented the TAC register (0xFF07) to detect when the game activates the Timer, and added specific logging when the Timer interrupt (vector 0x0050) is fired in the CPU interrupt dispatcher.
Hardware Concept
The Game Boy Timer is a critical timing system that many games use to:
- RNG (Random Number Generator):The Timer continually increments, and games use it as a seed to generate random numbers. If the Timer doesn't work, the RNG freezes.
- Music and Sound:The Timer can be used to synchronize the playback of music and sound effects.
- Game Logic:Many games depend on the Timer to advance animations, temporary events, etc.
The Timer has several registers:
- DIV (0xFF04):Divider Register - Counter that continuously increments at 16384 Hz.
- TIMA (0xFF05):Timer Counter - Configurable counter that can generate interrupts.
- TMA (0xFF06):Timer Module - Recharge value when TIMA overflows.
- TAC (0xFF07):Timer Control - Controls whether TIMA is active and its frequency.
When TIMA overflows (goes from 0xFF to 0x00), it is reloaded with TMA and a Timer interrupt is requested (IF Bit 2, 0xFF0F), which has the vector 0x0050.
Diagnostic hypothesis:If the game activates the Timer (writes to TAC with bit 2 = 1) but we never see the Timer interrupt fire, then there is a bug in the Timer implementation. If we see the interrupt trigger, then the Timer is working and the problem is somewhere else.
Source: Pan Docs - Timer and Divider Registers
Implementation
Instrumentation was added at two key points:
1. CT logging in MMU
Insrc/memory/mmu.py, added logging when the game writes to the TAC log (0xFF07):
# Intercept write to TAC register (0xFF07) - Timer Control
if addr == IO_TAC:
# Diagnostic instrumentation: detect Timer configuration
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}))"
)
Note:Fixed an initial syntax error where trying to useclock_names.get()directly inside the f-string. The solution was to extract the value to a variableclock_namebefore using it in the f-string.
This allows detecting:
- If the game activates the Timer (bit 2 = 1)
- What frequency do you select (bits 1-0)
- When is the Timer set?
2. CPU Timer Interrupt Logging
Insrc/cpu/core.py, added specific logging when the Timer interrupt is triggered (vector 0x0050):
# SPECIFIC DIAGNOSIS: Log when the Timer interruption is triggered
if interrupt_vector == 0x0050:
logger.info(f"⚡ TIMER INTERRUPT DISPATCHED! (TIMA Overflow)")
This allows detecting if the Timer interrupt is firing correctly when TIMA overflows.
3. Timer Logic Verification
It was reviewedsrc/io/timer.pyto verify that the overflow logic is correct. The current implementation:
- Increases TIMA when the threshold is reached according to the configured frequency.
- Detects overflow when TIMA goes from 0xFF to 0x00.
- Recharge TIMA with TMA when there is overflow.
- Requests the Timer interrupt (IF Bit 2) when there is overflow.
The logic seems correct according to the documentation. The diagnostic will confirm if the Timer is working or if there is a subtle bug.
Affected Files
src/memory/mmu.py- Added TAC UPDATE logging when writing to 0xFF07 (with syntax fix: variable extraction to avoid f-string error)src/cpu/core.py- Added specific logging when Timer interrupt is triggered (vector 0x0050)docs/bitacora/entries/2025-12-18__0076__diagnosis-timer-tac-interruptions.html- New log entrydocs/bitacora/index.html- Updated with new entrydocs/bitacora/entries/2025-12-18__0075__diagnostico-stat-profundo-monitoreo-escrituras.html- Updated "Next" linkCOMPLETE_REPORT.md- Added entry for step 0076
Tests and Verification
This step is diagnostic, not functional implementation. Verification will be carried out by running the game and analyzing the logs:
- Execute command:
python main.py pkmn.gb - Around:Windows/Python 3.10+
- Fix applied:Fixed a syntax error in the TAC log where trying to use
clock_names.get()directly inside the f-string. Extracted the value to a variable before using it. - What to look for in the logs:
⏰ TAC UPDATE: Does the game activate the Timer? (Enable=True)⚡ TIMER INTERRUPT DISPATCHED!: Does the Timer interrupt trigger?
Expected scenarios:
- If we see TAC UPDATE with Enable=True but we DO NOT see TIMER INTERRUPT:
- The Timer is broken. The game asks for it, but our implementation does not generate the signal.
- Solution: Review the logic
tick()intimer.py.
- If we see TIMER INTERRUPT:
- The Timer works. The problem is somewhere else (Joypad stuck? Dark CPU bug?).
- If we DO NOT see TAC UPDATE with Enable=True:
- The game is not using the Timer. The problem is somewhere else.
State:Pending execution and log analysis.
Sources consulted
- Bread Docs:Timer and Divider Registers
- Bread Docs:Interrupts
Educational Integrity
What I Understand Now
- Timer and RNG:Many games depend on the Timer to generate random numbers. If the Timer doesn't work, the RNG freezes and the game can get stuck waiting for a value that never arrives.
- Timer interruption:When TIMA overflows, an interrupt (IF Bit 2) with vector 0x0050 is requested. This interruption is critical to the logic of many games.
- Systematic diagnosis:By instrumenting TAC and the Timer interruption, we can determine if the problem is that the Timer is not working or if it is somewhere else.
What remains to be confirmed
- Does the game activate the Timer?We need to run the game and see if it appears
⏰ TAC UPDATEwith Enable=True. - Does the Timer interrupt trigger?We need to see if it appears
⚡ TIMER INTERRUPT DISPATCHED!in the logs. - Is there a bug in the Timer logic?If the game activates the Timer but we do not see interruptions, there is a bug in our implementation.
Hypotheses and Assumptions
Main hypothesis:The game freezes because the Timer is not firing interrupts when it should, blocking RNG and game logic.
Assumption:The implementation of the Timer intimer.pyis correct, but there may be a subtle bug that prevents interrupts from firing. The diagnosis will allow us to confirm or rule out this hypothesis.
Next Steps
- [ ] Execute
python main.py pkmn.gband analyze the logs - [ ] Look for
⏰ TAC UPDATEto see if the game activates the Timer - [ ] Look for
⚡ TIMER INTERRUPT DISPATCHED!to see if the interrupt is triggered - [ ] If the Timer is broken: Check and correct the timing logic.
tick()intimer.py - [ ] If the Timer works: Continue the diagnosis in another direction (Joypad, CPU, etc.)