Seems there's two conflicting behaviors.
The infamous STAT bug that is required in games like Legend of Zerd supposedly acts as follows:
* when on a non-GBC system, and writing any value (even 00) to STAT, while the display is enabled, and you are in mode 1 (Vblank) [and possibly in Hblank as well], a spurious STAT IRQ is generated.
Then we have gekkio's STAT IRQ blocking test:
* https://github.com/Gekkio/mooneye-gb/bl ... blocking.s
This triggers a STAT IRQ, which then jumps here:
The write to STAT here, per the original STAT bug, fires another STAT IRQ. And this causes the test to fail.
Disabling the original STAT bug allows the STAT IRQ blocking test to pass. What am I missing here?
...
The way I'm trying to do interrupts:
The CPU has its own IE/IF bytes. Whenever an interrupt actually, truly fires, the IF bit gets set. Once IME&&IE&IF are all true, then the lowest bit (highest priority) interrupt fires. So in this case, STAT. The IF bit gets cleared now (edge sensitive.)
To emulate STAT IRQ blocking, I have the PPU keep its own IRQ flag. Every time the PPU mode or LY changes, I re-evaluate all of the STAT IRQ sources (Vblank, Hblank, OAM, LYC.) If any of them are set, the new PPU IRQ flag is set. If there's a 0->1 transition (rising edge), then I set the IF bit on the CPU. So in this way, the PPU IRQ flag remains set and subsequent IRQs get blocked.
... unless of course you then trigger the STAT bug.
The infamous STAT bug that is required in games like Legend of Zerd supposedly acts as follows:
* when on a non-GBC system, and writing any value (even 00) to STAT, while the display is enabled, and you are in mode 1 (Vblank) [and possibly in Hblank as well], a spurious STAT IRQ is generated.
Then we have gekkio's STAT IRQ blocking test:
* https://github.com/Gekkio/mooneye-gb/bl ... blocking.s
Code:
ld a, $10 ; enable mode=1 interrupt
ldh (<STAT), a
ei
ld a, INTR_STAT
ldh (<IE), a
ldh (<STAT), a
ei
ld a, INTR_STAT
ldh (<IE), a
This triggers a STAT IRQ, which then jumps here:
Code:
test_round2:
ld hl, fail_round2
ei
ld a, $78; enable all stat interrupts
ldh (<STAT), a
; Now that all stat interrupts are enabled, the only mode
; that can clear the internal interrupt line is mode 3
; However, if we arrange things so that LY=LYC coincidence
; is set before mode 3 and kept set, the interrupt line is not cleared
ld b, $00
ld hl, fail_round2
ei
ld a, $78; enable all stat interrupts
ldh (<STAT), a
; Now that all stat interrupts are enabled, the only mode
; that can clear the internal interrupt line is mode 3
; However, if we arrange things so that LY=LYC coincidence
; is set before mode 3 and kept set, the interrupt line is not cleared
ld b, $00
The write to STAT here, per the original STAT bug, fires another STAT IRQ. And this causes the test to fail.
Disabling the original STAT bug allows the STAT IRQ blocking test to pass. What am I missing here?
...
The way I'm trying to do interrupts:
The CPU has its own IE/IF bytes. Whenever an interrupt actually, truly fires, the IF bit gets set. Once IME&&IE&IF are all true, then the lowest bit (highest priority) interrupt fires. So in this case, STAT. The IF bit gets cleared now (edge sensitive.)
To emulate STAT IRQ blocking, I have the PPU keep its own IRQ flag. Every time the PPU mode or LY changes, I re-evaluate all of the STAT IRQ sources (Vblank, Hblank, OAM, LYC.) If any of them are set, the new PPU IRQ flag is set. If there's a 0->1 transition (rising edge), then I set the IF bit on the CPU. So in this way, the PPU IRQ flag remains set and subsequent IRQs get blocked.
... unless of course you then trigger the STAT bug.