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

STAT Interrupt Verification and Testing

Date:2025-12-18 StepID:0072 State: Verified

Summary

A complete suite of unit tests was created to verify that the implementation of STAT interrupts and LYC logging is working correctly. The tests validate the LY==LYC comparison, updating STAT bit 2, requesting STAT interrupts under different conditions (LYC matching, H-Blank, V-Blank, OAM Search), and rising edge detection. All tests pass, confirming that the implementation is correct and functional.

Hardware Concept

STAT interrupts are critical for many Game Boy games that need to synchronize with specific render events. The tests validate that the emulator correctly implements the behavior of real hardware:

  • LY==LYC comparison: STAT bit 2 is dynamically updated when LY matches LYC.
  • Interrupts by mode: Interrupts can be enabled when the PPU enters H-Blank, V-Blank or OAM Search.
  • Rising edge detection: Interrupts are only fired when the condition goes from False to True, not while it remains True.
  • Immediate verification: When written to LYC, it is immediately checked to see if LY matches the new value.

Fountain: Pan Docs - LCD Status Register (STAT), LYC Register, STAT Interrupt

Implementation

The file was createdtests/test_ppu_stat.pywith 7 unit tests that cover all critical aspects of STAT interrupts:

Tests implemented

  • test_lyc_coincidence_flag: Verifies that STAT bit 2 is set when LY == LYC and set to off when LY != LYC.
  • test_stat_interrupt_lyc_coincidence: Verifies that STAT interrupt is requested when LY == LYC and STAT bit 6 is active.
  • test_stat_interrupt_rising_edge: Verifies that the interrupt is only fired on the rising edge, not multiple times on the same line.
  • test_stat_interrupt_mode_hblank: Verifies that STAT interrupt is requested when entering H-Blank and bit 3 is active.
  • test_stat_interrupt_mode_vblank: Verifies that STAT interrupt is requested when entering V-Blank and bit 4 is active.
  • test_stat_interrupt_mode_oam_search: Verifies that STAT interrupt is requested when entering OAM Search and bit 5 is active.
  • test_lyc_write_triggers_check: Verifies that writing to LYC immediately checks whether LY matches the new value.

Design decisions

  • Complete coverage: The tests cover all the main use cases of STAT interrupts, ensuring that the implementation works correctly in all scenarios.
  • Deterministic tests: All tests are deterministic and do not depend on external timing or random state.
  • Hardware validation: Each test validates a specific behavior of the real hardware, not just that the code does not crash.

Affected Files

  • tests/test_ppu_stat.py(new) - Complete suite of tests for STAT interrupts and LYC register:
    • 7 unit tests that validate all critical aspects.
    • Deterministic tests that do not depend on external timing.
    • Complete coverage of major use cases.

Tests and Verification

Test Execution: pytest tests/test_ppu_stat.py -v

  • Around: Windows, Python 3.13.5
  • Result: ✅ 7 PASSED testsin 0.26s
  • How valid:
    • STAT bit 2 is updated correctly when LY == LYC.
    • STAT interrupts are successfully requested when the configured conditions are met.
    • Rising edge detection works correctly (no multiple interrupts on the same line).
    • Immediate verification when writing to LYC works correctly.

Test code (example: test_lyc_coincidence_flag):

def test_lyc_coincidence_flag(self) -> None:
    """Test: STAT bit 2 is set when LY == LYC."""
    mmu = MMU(None)
    ppu = PPU(mmu)
    mmu.set_ppu(ppu)
    
    # Turn on LCD (LCDC bit 7 = 1)
    mmu.write_byte(IO_LCDC, 0x80)
    
    #Set LYC = 10
    mmu.write_byte(IO_LYC, 10)
    
    # Advance PPU until LY = 10
    ppu.step(4560)
    assert ppu.get_ly() == 10
    
    # Verify that STAT bit 2 is active
    stat = mmu.read_byte(IO_STAT)
    assert (stat & 0x04) != 0, "Bit 2 of STAT must be active when LY == LYC"

Why this test demonstrates something about the hardware: This test verifies that STAT bit 2 (LYC=LY Coincidence Flag) is dynamically updated by the hardware when LY matches LYC. In real hardware, this bit is read-only and is automatically updated by the PPU. The test confirms that our emulator implements this behavior correctly.

Sources consulted

Educational Integrity

What I Understand Now

  • Unit tests are essential: Unit tests allow you to verify that the implementation works correctly without having to run full games. This is especially useful for validating specific hardware behaviors that may be difficult to observe at runtime.
  • Rising edge detection is critical: Rising edge detection prevents multiple interrupts from being fired on the same line, which is the actual behavior of the hardware. Tests confirm that this implementation works correctly.
  • Immediate verification when changing LYC: When the game writes to LYC, the hardware immediately checks whether LY matches the new value. This is critical for games that change LYC dynamically during rendering.

What remains to be confirmed

  • Behavior in real games: Although the tests pass, it is important to verify that STAT interrupts work correctly in real games that use them (such as Pokémon Red). If a game continues to freeze, there may be a timing or synchronization problem that unit tests do not detect.
  • Exact interruption timing: The tests verify that interrupts are requested when the conditions are met, but do not verify the exact timing (how many cycles after the event the interrupt is requested). In most cases this is not critical, but can be important for some games.

Hypotheses and Assumptions

It is assumed that if all unit tests pass, the implementation is correct. However, if a real game remains frozen, there may be an integration or timing issue that the tests do not detect. In that case, it would be necessary to investigate further with logs or runtime debugging.

Next Steps

  • [ ] Verify that STAT interrupts work correctly in real games (e.g. Pokémon Red)
  • [ ] If a game is still frozen, investigate with logs or runtime debugging
  • [ ] Document exact timing of STAT interrupts if more detailed information is found
  • [ ] Continue with other pending emulator features (APU, rendering improvements, etc.)