Different emulator behavior for MMC5, help me sort it out?

Are you new to 6502, NES, or even programming in general? Post any of your questions here. Remember - the only dumb question is the question that remains unasked.

Moderator: Moderators

Post Reply
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Different emulator behavior for MMC5, help me sort it out?

Post by Drakim »

So, after converting SMB3 disassembled from NESASM to ASM6 (posted here) I've started converting the game from MMC3 to MMC5.

I've already managed to get everything to work, from the banking switches to the IRQ stuff. However, I realized to my dismay that different emulators are behaving differently on me. I assume it's because MMC5 is a pretty complex mapper and not all emulators are in complete agreement about how it should behave.

I've tested four emulators now, and things work neatly in FCEUX and puNES, while I get a different behavior in Mesen and Nintendulator. One thing I noticed is that Mesen and Nintendulator has the exact same "different behavior", which convinces me that this is a pretty simple "gotcha" issue with the MMC5.

The issue happens in the IRQ handler, I've taken screenshots on the title screen to show:

Image

That checkers floor at the bottom of the title screen is in a second nametable (image) and the IRQ is set to trigger at scanline 193. When it triggers, it writes $0B and $00 to the PPUADDR ($2006) to make the PPU jump to the place where the checkers floor tiles are laid out, imposing it there in the same way you'd impose a status bar.

But for some reason I can't explain Mesen and Nintendulator manages to place the checkers floor board at the top of the screen. This could very well be the correct behavior while FCEUX and puNES are wrong. But I'm still pretty puzzled over what's going wrong. It somehow smoothly switches back to the first nametable after the checkered floor.

Experimenting a bit, I commented out the two writes to the PPUADDR, so that the IRQ still triggers at scanline 193 but doesn't make the PPU switch which tiles it's painting, and now I get consistent behavior between all 4 emulators (but obviously the floor becomes black instead of showing the checkers tiles).

Image

So, unless I'm on a wild goose case, the problem seems to be specifically with writing to the PPUADDR during the IRQ interrupt, but only for the MMC5 and not the MMC3. Doing this PPUADDR trick on the MMC3's IRQ handler does not cause the same issue on any emulator.

Does anybody know how these emulators treat MMC5 differently, and what might be causing this behavior?
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Different emulator behavior for MMC5, help me sort it ou

Post by Sour »

As is, I can't really say - using Mesen's debugging tools should make it relatively easy to figure out where/why things are going wrong, though. Have you checked that the IRQ is actually triggered on scanline 193 in Mesen? It seems unlikely that it is, considering the picture that's being rendered (e.g there might be a bug in some of the emulators' irq counter logic, etc.)

I should also point out that I remember checking Nintendulator's code for some of the MMC5 logic, but if I recall correctly, it was for the vertical split feature, which seems fairly unrelated here.
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Re: Different emulator behavior for MMC5, help me sort it ou

Post by Drakim »

I'm pretty sure the IRQ does trigger, since the checkers floor is far outside the visible viewport normally, so without the IRQ doing some magic it should be impossible to actually see it:

Image

I'm unsure how to use the debugger to help me though, as I'd normally use that debug my own asm code, not the behavior of emulator-powered registers and such.

Edit: and just for clarification, it does kinda look like the IRQ is triggering in the wrong place in the second result, but it does not happen if I don't touch the PPUADDR, which is crazy.
Sour
Posts: 891
Joined: Sun Feb 07, 2016 6:16 pm

Re: Different emulator behavior for MMC5, help me sort it ou

Post by Sour »

As far as I can tell, this is what's happening every frame:

1- On scanline 249, the irq enabled flag is set to false ($5204 bit 7)
2- On scanline 193, the irq is "triggered" which sets the "irq pending" flag, but no IRQ actually occurs because the "irq enabled" flag is off.
3- On scanline 248, the irq enabled flag is turned on. Because the IRQ Pending flag is still set, this immediately triggers an IRQ. The IRQ handler runs right away and ends up turning off the IRQ enabled flag on scanline 249 (step 1) instead of turning it off on scanline 193 like you were probably expecting.

The difference between puNES and Nintendulator/Mesen is that puNES does not trigger an IRQ when 3) occurs.
I am unsure which behavior is correct (and don't have any way of checking myself). The information about the MMC5 on the wiki is not very clear about this particular case, unfortunately.

Are you using disabling the irq counter via $5204 writes to acknowledge the IRQ? Based on the wiki's info, it looks like you're supposed to read $5204 to acknowledge the IRQ, rather than enabling/disabling the "IRQ enabled" flag.

Either way, though, it would be nice to actually test this on an MMC5, but the number of people with the setup to test code on a real MMC5 is pretty low, afaik.
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Re: Different emulator behavior for MMC5, help me sort it ou

Post by Drakim »

Ah, I see. It might be some nonsense flipping of the IRQ status reg that the Nintendo developers left in the game, which ends up behaving differently for the MMC3 IRQ and the MMC5 IRQ.

I'll go hunting the codebase for everything that manipulates the IRQ status reg and see if I can spot where and why.

Thanks for the help!
Drakim
Posts: 97
Joined: Mon Apr 04, 2016 3:19 am

Re: Different emulator behavior for MMC5, help me sort it ou

Post by Drakim »

Alright, I got consistency finally.

It seems that the emulators behave differently if you fail to acknowledge the IRQ with a read, and just turn off the IRQ instead. SMB3 didn't bother acknowledging the MMC3 IRQ and did just fine, so the behavior must be a bit different there.
Post Reply