This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.
Deep STAT Diagnostics: Write Monitoring
Summary
Added deep diagnostic instrumentation to monitor all writes to the STAT register (0xFF41) and detect if the game attempts to activate bit 6 (LYC interrupt enable). Additionally, logging on the PPU has been enhanced to include the IE (Interrupt Enable) value when an active STAT signal is detected. The goal is to determine if the game is actually trying to use the STAT interrupt by LYC or if the problem is somewhere else.
Critical finding:The Pokémon Red game does NOT set STAT bit 6 or set LYC. There is only one write to STAT that disables all interrupts (0x00). This means that the problem is NOT that the STAT interrupt is not firing, but that the game is simply not using it.
Hardware Concept
The Game Boy's STAT register (0xFF41) allows the software to configure which PPU conditions generate interrupts:
- Bit 3: Enable interrupt when the PPU enters Mode 0 (H-Blank)
- Bit 4: Enable interrupt when the PPU enters Mode 1 (V-Blank)
- Bit 5: Enable interrupt when the PPU enters Mode 2 (OAM Search)
- Bit 6: Enable interrupt when LY == LYC (line match)
If a game wants to use the STAT interrupt by LYC, it must: 1. Write a value to LYC (0xFF45) to set the target line 2. Activate STAT bit 6 by writing a value with bit 6 to 1
If the game does NOT do these two things, then it is NOT trying to use the STAT interrupt by LYC, and the freezing problem must be somewhere else (another interrupt, STAT polling, V-Blank waiting, etc.).
Fountain:Pan Docs - LCD Status Register (STAT), LYC Register (0xFF45)
Implementation
Two diagnostic logging improvements were added:
1. STAT Write Monitoring (MMU)
Insrc/memory/mmu.py, improved logging when writing to STAT (0xFF41):
- The previous STAT value is saved before writing the new one.
- It is shown explicitly if bit 6 (LYC interrupt enable) is activated in the format:
LYC_INT_ENABLE=True/False - The old and new value is displayed in hexadecimal format
This allows you to immediately identify if the game is trying to trigger the STAT interrupt by LYC.
2. IE Logging in Active STAT Signals (PPU)
Insrc/gpu/ppu.py, the value of IE (Interrupt Enable, 0xFFFF) was added in all "STAT SIGNAL ACTIVE" logs:
- IE is read when an active STAT signal is detected
- The IE value is displayed and whether bit 1 (STAT interrupt enable) is active
- This allows you to determine if the PPU generates the signal but the CPU ignores it because IE bit 1 is disabled
Design decisions
It was decided to use logging at the INFO level so that these messages are visible during diagnostics, but they can be easily disabled by changing the logger level if the output becomes saturated.
Affected Files
src/memory/mmu.py- Improved write logging in STAT to show LYC_INT_ENABLEsrc/gpu/ppu.py- Added IE value in logs of active STAT signals
Tests and Verification
Tested ROM:Pokémon Red (user-contributed ROM, not distributed)
Command executed: python main.py pkmn.gb 2>&1 | Select-String -Pattern "LYC_INT_ENABLE|STAT SIGNAL|STAT UPDATE"
Around:Windows 10, Python 3.10+
Result:
- It was detectedSINGLE ONEwrite to STAT:
STAT UPDATE: Old=01 New=00 | LYC_INT_ENABLE=False - The game writes 0x00 to STAT, disabling all STAT interrupts
- NOno write detected with
LYC_INT_ENABLE=True - NOno write detected in LYC (0xFF45)
- NOno active STAT signal detected by LYC
Interpretation:
- The gameNOyou are trying to use STAT interrupt by LYC
- The freezing problemNOis that the STAT interrupt does not fire
- The game is probably waiting for something else (STAT polling, another interrupt, etc.)
State: draft- More research is needed to determine what the game is waiting for.
Sources consulted
- Pan Docs: LCD Status Register (STAT) -https://gbdev.io/pandocs/LCDC.html#lcd-status-register-stat-ff41
- Pan Docs: LYC Register (0xFF45) -https://gbdev.io/pandocs/LCDC.html#lyc-ly-compare-ff45
Educational Integrity
What I Understand Now
- The game does NOT use the STAT interrupt by LYC:The diagnostic clearly shows that Pokémon Red does not activate STAT bit 6 or configure LYC. This rules out the hypothesis that the problem is that the STAT interrupt does not fire.
- The problem is somewhere else:If the game does not attempt to use the STAT interrupt by LYC, then the freeze must be due to another cause: STAT polling waiting for a specific mode, waiting for V-Blank, another interrupt, or a timing problem.
- Systematic diagnosis:Adding detailed logging allows you to identify exactly what the game is doing and what it is waiting for, which is essential for diagnosing emulation problems.
What remains to be confirmed
- What is waiting for the game?If it's not the STAT interrupt by LYC, what is it? STAT polling waiting for V-Blank? Another interruption? A timing problem?
- Why does it freeze?I need to further investigate what the game code is doing when it freezes. Are you in an infinite loop waiting for something? What condition are you waiting for?
- Does it work with other games?It would be helpful to try other games to see if the problem is specific to Pokémon Red or general.
Hypotheses and Assumptions
Current hypothesis:The game is polling the STAT register waiting for the PPU to enter a specific mode (probably V-Blank or H-Blank) before continuing. If the PPU is not updating STAT correctly or if there is a timing problem, the game will wait forever.
Unverified assumption:I assume the game is doing STAT polling, but I haven't verified this directly. I need to add more logging to see what the game is reading and what it is waiting for.
Next Steps
- [ ] Add STAT read log to see if the game is polling
- [ ] Add LY reads log to see if the game is waiting for a specific line
- [ ] Investigate what the game code is doing when it freezes (execution trace)
- [ ] Try other games to see if the problem is specific to Pokémon Red
- [ ] Check if there are timing problems that prevent the PPU from reaching V-Blank