Page 1 of 3

Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 1:47 am
by ArsonIzer
A couple of months ago I tried implementing MMC1 and I got suck on it for a while, got demotivated, and had a break from the NES. Now I'm trying to get things rolling again and I thought I'd ask about it here.

My MMC1 mapper messes up pretty much every game. My other mappers (0, 2, 3, 7) work perfectly fine as far as I know, so I doubt it's a PPU issue. Most of the games have graphics issues, some games crash due to wrong register reads/writes. I checked the CHR switching a thousand times and compared it to other emulators' MMC1, but it always checks out so I have no clue why this happens. Here are some screenshots:

Abadox

Image

(Crashes on startup due to a read from "register 6" which doesn't exist apparently)


Addams Family

Image

Image

Image

(Scrambled graphics)


Adventures in the Magic Kingdom

Image

(Scrambled graphics)


Dungeon Magic

Image

Image

(Start screen is fine then flickering scrambled graphics)


Mega Man II

Image

Image

(Actually starts fine but then graphics get scrambled)

Some of these games have a fine start screen, others don't. All of them mess up beyond that.

If you want to see more games, please ask. If you want to see the MMC1 source, that's fine too.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 2:07 am
by thefox
Since you can get games to start, PRG switching has to be more or less OK. Quite obviously your CHR bankswitching is wrong, so just keep looking. You may find it helpful to render the currently mapped in CHR tiles in another window, then you can very easily compare to other emulators like FCEUX and Nintendulator.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 3:10 am
by ArsonIzer
thefox wrote:Quite obviously your CHR bankswitching is wrong, so just keep looking.

Code: Select all

    private void setBanks() {
        //4K CHR banks
        if (isSet(control, 4)) {
            switchCHR(4, (chrReg0 & 0x1F) % chrCount, 0);
            switchCHR(4, (chrReg1 & 0x1F) % chrCount, 1);
        } else {
            switchCHR(8, ((chrReg0 & 0x1F) >>> 1) % chrCount, 0);
        }

        if (!isSet(control, 3)) {
            switchPRG(32, ((prgReg & 0xF) >>> 1) % prgCount, 0);
        } else if (!isSet(control, 2)) {
            switchPRG(16, 0, 0);
            switchPRG(16, (prgReg & 0xF) % prgCount, 1);
        } else {
            switchPRG(16, (prgReg & 0xF) % prgCount, 0);
            switchPRG(16, (prgCount - 1) % prgCount, 1);
        }
    }
This is the piece of code I use for switching. Do you know what might be wrong with the CHR parts? The method switchCHR takes the following parameters respectively: the size of the bank in KB, the number of the bank in the cartridge, the position in which it has to be put (char bank 0 or 1).

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 4:28 am
by thefox
Hard to say. Note that many games like Mega Man 2 use CHR-RAM, so usually they'll use the 8 KB banking mode. Since Mega Man 2 crashes after a while, there could be a bug unrelated to CHR banking as well.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 5:33 am
by ArsonIzer
thefox wrote:Hard to say. Note that many games like Mega Man 2 use CHR-RAM, so usually they'll use the 8 KB banking mode. Since Mega Man 2 crashes after a while, there could be a bug unrelated to CHR banking as well.
Mega Man 2 never crashes, the graphics are just f'd up. Here's what happens further into the game:

Image

(After pressing start the start/password screen is screwed up)

Image

(After pressing start again the stage pick screen is screwed up)

Image

Image

Image

(Stages are graphically completely messed up but still playable as if the game was running completely normally)

Any ideas? The 4 other mappers work perfectly but this one doesn't at all.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 6:39 am
by cpow
ArsonIzer wrote:
thefox wrote:Quite obviously your CHR bankswitching is wrong, so just keep looking.

Code: Select all

    private void setBanks() {
...
    }
I'm not sure you need the >>>1 on the 8KB CHR-switching case. My MMC1 implementation uses the register bits identically for 8KB or 4KB switching, and I don't see graphics messed up like that.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 6:48 am
by tepples
What does it do with the MMC1 version of Holy Diver Batman?

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 7:11 am
by ArsonIzer
cpow wrote:I'm not sure you need the >>>1 on the 8KB CHR-switching case. My MMC1 implementation uses the register bits identically for 8KB or 4KB switching, and I don't see graphics messed up like that.
From the wiki:
Select 4 KB or 8 KB CHR bank at PPU $0000 (low bit ignored in 8 KB mode)
If I don't do that the bank at the location (sans the >>> 1) is outside of the number of available banks.
tepples wrote:What does it do with the MMC1 version of Holy Diver Batman?
Are you talking about the zip file with a bunch of strangely named .nes files? If so, all files starting with M1_ result in a black screen. The ones with M0_ and M2_ show a blue screen with yellow text (looks okay).

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 7:33 am
by cpow
ArsonIzer wrote:
cpow wrote:From the wiki:
Select 4 KB or 8 KB CHR bank at PPU $0000 (low bit ignored in 8 KB mode)
If I don't do that the bank at the location (sans the >>> 1) is outside of the number of available banks.
The distinction is, I think: it is ignored by the hardware, which doesn't mean you should shift it away.

Looking at my implementation, perhaps I am just getting lucky...my mapper implementation almost ignores the low bit.

The 4KB case:

Code: Select all

                     m_pCHRmemory [ 0 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+0 ];
                     m_pCHRmemory [ 1 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+1 ];
                     m_pCHRmemory [ 2 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+2 ];
                     m_pCHRmemory [ 3 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+3 ];
The 8KB case:

Code: Select all

                     m_pCHRmemory [ 0 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+0 ];
                     m_pCHRmemory [ 1 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+1 ];
                     m_pCHRmemory [ 2 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+2 ];
                     m_pCHRmemory [ 3 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+3 ];
                     m_pCHRmemory [ 4 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+4 ];
                     m_pCHRmemory [ 5 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+5 ];
                     m_pCHRmemory [ 6 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+6 ];
                     m_pCHRmemory [ 7 ] = m_CHRmemory [ ((m_reg[1]&0x1F)<<2)+7 ];
Note how the register value is shifted 2-left to account for the 4KB-to-1KB bank . That is done in both cases. In the 4KB case, there are 4 1KB banks to switch, so +0, +1, +2, and +3 toggle the 2 low bits that were added by the 4KB-to-1KB conversion.

In the 8KB case, there's the same 2 low bits that are added by the 4KB-to-1KB conversion, but I'm switching 8 banks, so +0, +1, +2, +3 and also +4, +5, +6, and +7. The first set should change the thirdmost low-bit, which wasn't added by my 4KB-to-1KB conversion [it was part of the original register value] to 0. The second set should change the thirdmost low-bit to 1. It is the "should" that indicates I must just be getting lucky. Note that the masking should be 0x1E in the 8KB switching case. Masking equals ignoring.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 8:38 am
by tokumaru
It appears there's something wrong with the name table mirroring as well.

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 9:41 am
by ArsonIzer
@cpow
Thanks for the explanation. I'm now masking it with 0x1E.

The issue remains though, but I don't think the graphics problems have anything to do with that. Mega Man 2 for instance, uses only 1 CHR bank and since it's combined with modulo chrCount, whatever number comes out, it's always going to switch to bank 0 (0 % 1 == 0, 345243 % 1 == 0, etc), so the correct CHR bank (0 in this situation) is always active. Something else is going on. Maybe looking at a screenshot next to FCEUX can help:

Image
Image
Image
Image
Image

I'm aware that it might be nametable-related, but why does every other mapper work fine except for this one where everything fails? Is this related to how/when my mapper switches nametable mirroring?

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 10:32 am
by tepples
ArsonIzer wrote:[The Holy Diver Batman test ROMs for MMC1] result in a black screen. The ones [for NROM and UNROM] show a blue screen with yellow text (looks okay).
The very first thing Holy Diver Batman tests is nametable mirroring. It will fail early and quickly if your emulator handles the mapper's mirroring wrong. Did you get a black screen with beeped Morse code, or a black screen with silence?

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 10:54 am
by ArsonIzer
tepples wrote:Did you get a black screen with beeped Morse code, or a black screen with silence?
I have no APU. Do I have to implement sound for this? That could take a while since I don't even have a basic understanding of audio. Is there something else I could do, for instance make a CPU log and check it against another?

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 11:27 am
by cpow
You're masking but did you remove the >>>1?

Re: Why does my MMC1 mapper act like this?

Posted: Fri Jun 20, 2014 12:08 pm
by tokumaru
ArsonIzer wrote:I'm aware that it might be nametable-related, but why does every other mapper work fine except for this one where everything fails?
Name table mirroring is controlled by the mapper, so it's not unusual that a single mapper has screwed up mirroring. A lot of your screenshots appear to be displaying the name table that should be hidden. Have you double checked your MMC1 mirroring logic?