Page 1 of 1

STAT bug vs STAT IRQ blocking

Posted: Fri Jun 21, 2019 12:42 pm
by Near
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:
* ... blocking.s

Code: Select all

  ld a, $10 ; enable mode=1 interrupt
  ldh (<STAT), a
  ld a, INTR_STAT
ldh (<IE), a
This triggers a STAT IRQ, which then jumps here:

Code: Select all

  ld hl, fail_round2

  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.

Re: STAT bug vs STAT IRQ blocking

Posted: Fri Jun 21, 2019 5:41 pm
by tepples
The way STAT blocking works is that the four results from the (enabled AND active) calculation are OR'd together. Thus a transition from "no STAT sources enabled and active" to "at least one STAT source enabled and active" causes the IRQ. Mode 1 (hblank) blocks mode 2 (OAM scan), but if I recall correctly, mode 1 does not block LY=LYC because the LY=LYC test happens one cycle after the end of hblank.

On DMG, a STAT write causes all sources to be enabled (but not necessarily active) for one cycle. It's as if all writes to STAT wrote $FF one cycle then the actual written value in the following cycle. Does emulating it that way work as expected? As far as I'm aware, the few times that this doesn't generate an IRQ are explainable as A. STAT blocking, or B. being in mode 3 where LY != LYC.

Should I forward this question to the gurus on the gbdev Discord server to see if they have a better answer?

Re: STAT bug vs STAT IRQ blocking

Posted: Fri Jun 21, 2019 6:59 pm
by Near
That sounds more complete than any info I've seen so far.

So it sounds like a cheap way to emulate this is to test for the STAT IRQ flag being 0 before firing the STAT bug IRQ.

Simulating the STAT interrupt enable bits all being set for one cycle (M or T? though it likely doesnt matter here) would be trickier. We would have to poll the STAT IRQ flag on every T-cycle, and then writes to STAT could set a "force-high" flag that gets lowered after every test.


Yep, that was it, thank you! On DMG hardware, force all STAT enable flags to 1 on STAT writes, call your STAT IRQ poll function, then set STAT enable flags to their true values. That stops the extra STAT IRQ on stat_irq_blocking, without stopping it in Legend of Zerd.

Then the last piece for Pinball Fantasies is just bus hold delays. Poll IF&IE two T-cycles into each read and cache the result, and then use the resulting value between instructions to start triggering IRQs. Apparently also needs the psychopathic ie_push thing emulated.

It could technically be polled before doing the read at all as well, which is exactly how you'd expect interrupts to work on the NES and SNES ... you basically just need the value of IE&IF two T-cycles before the edge of an instruction. It'd be a fun challenge to write a test ROM to determine exactly how many T-cycles before the instruction edge it tests IF&IE at, and if it differs between reads, writes, and idle cycles.

Re: STAT bug vs STAT IRQ blocking

Posted: Sat Aug 03, 2019 6:27 am
by ISSOtm
There is a last thing to note in regards to STAT IRQ blocking: the LYC coincidence interrupt appears to be delayed by 1 cycle after Mode 2, so it does not block if Mode 0 is enabled as well. (This also means it's delayed by 1 cycle, obviously.)