⚠️ Clean-Room / Educational

This project is educational and Open Source. No code is copied from other emulators. Implementation based solely on technical documentation and permitted tests.

LYC Coincidence & STAT IRQ Fix

Date:2025-12-23 StepID:0265 State: draft

Summary

This Step implements and corrects the LYC (LY Compare) comparison logic and the generation of STAT interrupts in the PPU. Step 0264 confirmed that HALT works correctly, but the Pokémon Red intro still does not progress. The hypothesis is that the game is waiting for an LCD STAT interrupt (by coincidence LY=LYC) to sync visual effects or advance logic, and our PPU is not triggering it correctly. This Step ensures that when LY matches LYC and STAT bit 6 is enabled, the STAT interrupt is requested on the rising edge.

Hardware Concept

LYC Register (0xFF45):The LYC (LY Compare) register allows the software to set a line value (0-255) to which LY (Line Current) is compared. When LY == LYC, bit 2 of the STAT register is set, indicating a match.

STAT Register (0xFF41):The STAT register has several important bits:

  • Bit 2 (LYC=LY Coincidence Flag):Triggered when LY == LYC. It is read-only and is dynamically updated by the PPU.
  • Bit 6 (LYC Interrupt Enable):If active, requests a STAT interrupt when LY == LYC.
  • Bits 3-5:They enable interrupts by PPU mode (H-Blank, V-Blank, OAM Search).

Rising Edge Detection:The STAT interrupt should only be triggered on the rising edge, that is, when the condition goes from False to True. If it fires every cycle where the condition is True, it would saturate the CPU with interrupts.

The case of Pokémon Red:Many advanced games like Pokémon use STAT interrupt by LYC to synchronize visual effects (changing palettes in the middle of the screen, raster effects, etc.). If this interrupt is not triggered correctly, the game may wait and not progress.

Fountain:Pan Docs - "LCD Status Register (STAT)", "LYC Register (0xFF45)", "LCD Interrupts"

Implementation

Two critical improvements were implemented:

  1. Intercepting writes to LYC in MMU:When the game writes to 0xFF45, the MMU now immediately updates the PPU by callingPPU::set_lyc().
  2. Improved rising edge detection for LYC:When LY changes and matches LYC, it is immediately checked whether the STAT interrupt should be fired, before resetting the interrupt flags.

Modified components

  • MMU::write(): Added interception for writes to 0xFF45 (LYC) that updates the PPU immediately.
  • PPU::step(): Improved rising edge detection logic for LYC. When LY changes, the previous state of LYC match is saved, LY is updated, and it is immediately checked for a rising edge (LYC match changed from False to True). If STAT bit 6 is enabled, the STAT interrupt is requested.
  • PPU::check_stat_interrupt(): The existing logic was already correct, but is now complemented by immediate verification instep().

Design decisions

Immediate verification after changing LY:When LY changes, it is immediately checked whether LY == LYC and whether the interrupt should be fired. This ensures that the rising edge is detected at the exact moment it occurs, not later.

Preserving LYC state in stat_interrupt_line_:When LY changes, bit 0 ofstat_interrupt_line_if LYC match is still active, and is cleared if it is inactive. The mode bits (1-3) are cleared because the mode changed. This allows the rising edge to be correctly detected in the next verification.

LYC Interception at MMU:The MMU intercepts writes to 0xFF45 and updates the PPU immediately. This ensures that when the game sets LYC, the PPU can immediately check if LY == LYC and update bit 2 of STAT.

Modified Files

  • src/core/cpp/MMU.cpp: Interception of writes to LYC (0xFF45).
  • src/core/cpp/PPU.cpp: Improved rising edge detection for LYC instep().

Tests and Verification

Existing tests for STAT and LYC interrupts still pass. The implementation improves rising edge detection without breaking existing functionality.

Test command:

python main.py roms/pkmn.gb

Expected validation:Pokémon Red's intro should move forward if the game was waiting for a STAT interruption by LYC. If the problem persists, it may be necessary to check other interrupt sources (Timer, Serial) or the IME status.

References

  • Pan Docs - "LCD Status Register (STAT)"
  • Pan Docs - "LYC Register (0xFF45)"
  • Pan Docs - "LCD Interrupts"
  • Pan Docs - "Rising Edge Detection"