MMC5 IRQ anyone?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
MMC5 IRQ anyone?
by on (#48955)
- OK, so I'm aware of this (blargg's RE) and this (wiki).

- My emu uses cycle clocking instead of scanline clocking for IRQs. Then, a way to detect scanlines is to compare the last line with the current line - if not equals, a scanline was detected. This occurs on scanline cycle zero.

- Next, I'm trying to get CastleVania 3 (USA) working. The questions:

a) Under which conditions is the irq counter incremented?
b) It's stated that an actual IRQ is only sent to the CPU if both the IRQ Enable flag and IRQ Pending flag are raised. What time is an IRQ acknowledged (stops firing off)?
c) Is the irq counter reset to zero on scanline 21?

by on (#48962)
From Disch's mapper document.

Reading $5204 will clear the pending flag (acknowledging the IRQ).

Also..

Detailed Operation:

The IRQ counter is an up counter, rather than a down counter (like MMC3). Every time the MMC5 detects a
scanline, it does the following:

- If In Frame Signal is clear...
a) Set In Frame signal
b) Reset IRQ counter to 0
c) Clear IRQ pending flag (automatically acknowledging IRQ)

- otherwise...
a) Increment IRQ counter
b) If IRQ counter now equals the trigger value, raise IRQ pending flag


I suggest you get the mapper document set as it has all the information you need to get CV3 running. CV3 really doesn't require very complete MMC5 emulation to get running.

by on (#48964)
EDIT: here's two screenshots - the beginning and the vertical scroll parts.

Image Image

- Here's my IRQ logic (source):
Code:
unsigned char ppu_is_rendering(void)
{
   return (ppu->screen_on && (ppu->scanline >= 21) && (ppu->scanline <= 260));
}

static void irq_mmc5_clock(const int PPU_line, const unsigned short PPU_addr, int cycle)

   if( !ppu_is_rendering() )
   {
      map005.reads &= 0xBF;
      return;
   }
   
   //detect a scanline
   if(PPU_line == last_line)
      return;
   last_line = PPU_line;
   
   if( map005.reads & 0x40 )
   {
      map005.irq_counter++;
      if(map005.irq_counter == map005.irq_latch)
      {
         map005.reads |= 0x80;
         if(map005.irq_flag)
            cpu_irqtrigger(TIRQ_MPR);
      }
   }
   else
   {
      map005.reads = 0x40;
      map005.irq_counter = 0;
      cpu_irqcancel(TIRQ_MPR);
   }
}

by on (#48982)
I'm not on my own computer now so I can't check my stuff -- this is just from memory, so I might mistaken.

Quote:
b) It's stated that an actual IRQ is only sent to the CPU if both the IRQ Enable flag and IRQ Pending flag are raised. What time is an IRQ acknowledged (stops firing off)?


On $5204 read. And *maybe* when the IRQ counter is zero'd (In Frame flag set). But I'm still not sure about that. Would love somebody to test it to confirm.

I'm reasonably sure it is not automatically acknowledged at the end of the frame. iirc turning the PPU off leaves the IRQ flag "frozen" at its current state until the PPU is turned back on or until $5204 is read.


Quote:
c) Is the irq counter reset to zero on scanline 21?


The IRQ counter is only set to zero when the in-frame flag becomes set.

The in-frame signal is cleared:
- at the end of rendering
- when the PPU is switched off

The code you pasted looks alright to me... other than maybe the last else block. Try this instead:

Code:
   else
   {
      map005.reads |= 0x40;  // don't automatically acknowledge
      map005.irq_counter = 0;
   }


If that doesn't help, I don't know what the problem is. Perhaps you could post your $5204 / $5205 code as well.

Also your IRQs are probably firing either a little late, or almost a full scanline early. All rendered scanlines (including -1) are counted, but not until near the END of the scanline. You seem to be doing it at the start of the scanline, which would be quite a bit early (if you're counting -1) or a little late (if you're not counting -1)

*converts your scanline numbers*
0 - 19 = VBlank I assume
20 = -1
21 - 260 = 0 - 239

So it looks like you're not counting -1... which is probably fine.

by on (#48987)
...

by on (#48998)
re: the vertical scrolling portion problem. I was seeing the same thing when I was incorrectly handling IRQs in my CPU. Basically, I was treating them as a one-shot event. When the MMC5 generated an IRQ, I'd check if the IRQ disable flag was clear and, if so, run the IRQ code. If not, I'd never check again; if the IRQ disable flag was set when the IRQ was generated, then later cleared, the IRQ code would never run.

by on (#49001)
- "Absolutely" my fault. I'm really sorry, folks. Thanks a lot for the time. Things are working very nice.

- I have something like...

pointer_A = pointer_B;

and pointer_B = CHR bank.

- Heck, I wasn't updating pointer_A on pointer_B changes. >_<

*slaps own face*