Failing Blargg's MMC3 IRQ Tests

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

Post Reply
ace314159
Posts: 27
Joined: Tue Apr 17, 2018 3:40 am

Failing Blargg's MMC3 IRQ Tests

Post by ace314159 » Fri Jun 14, 2019 12:07 am

I'm trying to implement the MMC3 IRQ, but I'm failing the first of blargg's tests called clocking. I get the error "Should reload when clocked when counter is 0", but from my code I'm definitely reloading the counter. Looking at the source of the test, I'm presuming that it's a timing issue, but I've looked at the wiki and other forum posts, and I'm not sure where I'm going wrong. Am I missing something?

This code runs every time CHR is being read:

Code: Select all

if(this->prevA12 == 0 && (addr >> 15)) {
    if(this->CPUcycleCount - this->prevCPUCycleCount > 2) {
        if(this->IRQReload) {
            this->IRQReload = false;
            this->IRQCounter = this->IRQLatch;
        } else {
            this->IRQCounter--;
            if(this->IRQCounter == 0) {
                this->IRQCounter = this->IRQLatch;
                if(this->IRQEnabled) this->IRQCalled = true;
            }
        }
    }
    this->prevCPUCycleCount = this->CPUcycleCount;
}
this->prevA12 = addr >> 15;

User avatar
Quietust
Posts: 1492
Joined: Sun Sep 19, 2004 10:59 pm
Contact:

Re: Failing Blargg's MMC3 IRQ Tests

Post by Quietust » Fri Jun 14, 2019 5:02 am

I suspect the problem is that if IRQCounter is set to 0 before entering that block of code, it will become negative (or underflow to a very large positive integer).

For reference, here's what my MMC3 IRQ code looks like:

Code: Select all

void    MAPINT  PPUCycle (int Addr, int Scanline, int Cycle, int IsRendering)
{
        if (IRQaddr)
                IRQaddr--;
        if ((!IRQaddr) && (Addr & 0x1000))
        {
                unsigned char count = IRQcounter;
                if (!IRQcounter || IRQreload)
                        IRQcounter = IRQlatch;
                else    IRQcounter--;
                if ((count || IRQreload) && !IRQcounter && IRQenabled)
                        EMU->SetIRQ(0);
                IRQreload = 0;
        }
        if (Addr & 0x1000)
                IRQaddr = 8;
}
where writing to $C000 updates IRQlatch, $C001 clears IRQcounter and sets IRQreload, $E000 clears IRQenabled, and $E001 sets IRQenabled.
Last I checked, this code passes test #6 (MMC3_alt), not #5 (MMC3) - it's impossible to pass both of them at the same time, because they're designed for different chip revisions.
Quietust, QMT Productions
P.S. If you don't get this note, let me know and I'll write you another.

ace314159
Posts: 27
Joined: Tue Apr 17, 2018 3:40 am

Re: Failing Blargg's MMC3 IRQ Tests

Post by ace314159 » Fri Jun 14, 2019 11:56 am

I put an assert to ensure that IRQCounter is always nonzero before decrementing it, but it never caused an exception, so I'm pretty sure that that's not the cause.

I tried to implement my code the same way you have yours, and it still stopped at the same error, but I noticed that your IRQ checking code was in the PPUCycle function. Does that mean every ppu cycle does a read somewhere in CHR? Currently, I only run that code on the second write to $2006, a read or write to $2007, BG and sprite tile fetches.

Or is there another reason that you think that this error might occur?

supercat
Posts: 161
Joined: Thu Apr 18, 2019 9:13 am

Re: Failing Blargg's MMC3 IRQ Tests

Post by supercat » Fri Jun 14, 2019 2:47 pm

ace314159 wrote:I'm trying to implement the MMC3 IRQ, but I'm failing the first of blargg's tests called clocking. I get the error "Should reload when clocked when counter is 0", but from my code I'm definitely reloading the counter. Looking at the source of the test, I'm presuming that it's a timing issue, but I've looked at the wiki and other forum posts, and I'm not sure where I'm going wrong. Am I missing something?

This code runs every time CHR is being read:

Code: Select all

if(this->prevA12 == 0 && (addr >> 15))
...
this->prevA12 = addr >> 15;
The above two lines look awfully suspicious to me.

As for the question of whether accesses other that tile accesses are relevant, the real hardware counts multiple transitions on A12 as a single action unless A12 sits low continuously for awhile. No particular effort is made to calibrate the circuit that decides whether A12 has been low long enough to separate discrete actions; instead, software will ensure that the time between events will always be very short or very long. Non-contrived software that would work on all real variations of real hardware would likely be tolerant of a few variations in how scan lines are detected.

ace314159
Posts: 27
Joined: Tue Apr 17, 2018 3:40 am

Re: Failing Blargg's MMC3 IRQ Tests

Post by ace314159 » Fri Jun 14, 2019 4:31 pm

You're right! Those lines are wrong. I got confused and thought 0x1000 had a bit set in the 15th bit instead of the 12th bit, and so I shifted right by 15 to see if that bit was set. As a hack, I manually shifted the address from $2006 left 3 to make it work. I'm surprised the previous tests passed with this incorrect behavior. Thank you for realizing that critical flaw, but I'm still getting that same error. :(

You mentioned that there isn't any definite calibration to ensure that A12 has been low long enough, but I've looked at the forums and Quietust's code and saw that they waited for at least 8 PPU cycles to pass. Is that just an approximation?

The reason I asked for if accesses other than the tiles and $2006 and $2007 are relevant is because I was thinking that maybe I wasn't clocking the IRQ counter enough. Are you aware of any other reasons for this error?

Thank you for the help!

ace314159
Posts: 27
Joined: Tue Apr 17, 2018 3:40 am

Re: Failing Blargg's MMC3 IRQ Tests

Post by ace314159 » Fri Jun 14, 2019 5:02 pm

I figured it out! It turns out I wasn't supposed to reset the counter after writing to $E000. I guess I should've guessed that since the wiki says that the counter is unaffected, but the doc located at http://nesdev.com/mmc3.txt, had code showing that the counter was reset after writign to $E000. I'm assuming that doc is outdated?

However, in order for the code to work, I had to disable any existing IRQs that were called after a write to $E000. Is this supposed to occur or is something else wrong and this is just a hack to make it work?

User avatar
koitsu
Posts: 4216
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Failing Blargg's MMC3 IRQ Tests

Post by koitsu » Fri Jun 14, 2019 5:12 pm

ace314159 wrote:I figured it out! It turns out I wasn't supposed to reset the counter after writing to $E000. I guess I should've guessed that since the wiki says that the counter is unaffected, but the doc located at http://nesdev.com/mmc3.txt, had code showing that the counter was reset after writign to $E000. I'm assuming that doc is outdated?
Goroh's reverse-engineering efforts and documentation is from 22 years ago, while details of operation and newer findings/analysis are all in the wiki page; you should use the latter. Historic documents like Goroh's and many others are kept around for fair/legitimate reasons (read: they should not be deleted/removed, as the authors have not requested such). You will find conflicting information everywhere as you travel through documentations spanning the past 22+ years.

Post Reply