It is currently Thu Nov 14, 2019 5:15 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 4 posts ] 
Author Message
PostPosted: Fri Jun 21, 2019 12:42 pm 
Offline

Joined: Mon Mar 27, 2006 5:23 pm
Posts: 1532
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

Code:
  ld a, $10 ; enable mode=1 interrupt
  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


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.


Top
 Profile  
 
PostPosted: Fri Jun 21, 2019 5:41 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 21688
Location: NE Indiana, USA (NTSC)
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?

_________________
Pin Eight | Twitter | GitHub | Patreon


Top
 Profile  
 
PostPosted: Fri Jun 21, 2019 6:59 pm 
Offline

Joined: Mon Mar 27, 2006 5:23 pm
Posts: 1532
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.


Top
 Profile  
 
PostPosted: Sat Aug 03, 2019 6:27 am 
Offline
User avatar

Joined: Fri Jan 04, 2019 5:31 pm
Posts: 43
Location: France, right of a pile of consoles
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.)

_________________
The French Lord of Laziness (and a huge Legend of Zelda fan)
https://github.com/ISSOtm
ASMu is laifu <3


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 4 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 1 guest


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group