Why does my MMC1 mapper act like this?

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Why does my MMC1 mapper act like this?
by on (#130032)
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?
by on (#130033)
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?
by on (#130034)
thefox wrote:
Quite obviously your CHR bankswitching is wrong, so just keep looking.


Code:
    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?
by on (#130035)
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?
by on (#130037)
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?
by on (#130040)
ArsonIzer wrote:
thefox wrote:
Quite obviously your CHR bankswitching is wrong, so just keep looking.


Code:
    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?
by on (#130042)
What does it do with the MMC1 version of Holy Diver Batman?
Re: Why does my MMC1 mapper act like this?
by on (#130044)
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:

Quote:
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?
by on (#130045)
ArsonIzer wrote:
cpow wrote:
From the wiki:

Quote:
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:
                     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:
                     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 [I switch CHR at 1KB granularity always]. 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?
by on (#130048)
It appears there's something wrong with the name table mirroring as well.
Re: Why does my MMC1 mapper act like this?
by on (#130049)
@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?
by on (#130050)
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?
by on (#130053)
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?
by on (#130055)
You're masking but did you remove the >>>1?
Re: Why does my MMC1 mapper act like this?
by on (#130058)
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?
Re: Why does my MMC1 mapper act like this?
by on (#130062)
ArsonIzer wrote:
tepples wrote:
Did you get a black screen with beeped Morse code

I have no APU. Do I have to implement sound for this?

That or log the timing and value of writes to $4008, so that you can at least tell when the key makes and breaks. Yet another way is to implement breakpoints in your emulator and then put a breakpoint on the Morse code subroutine to tell what the code is.

Quote:
Is there something else I could do, for instance make a CPU log and check it against another?

At least one version of Nintendulator can make CPU logs. You could make a CPU log in Nintendulator, make another in your emulator, and then write a short program to compare the PC and register values in the two to see where they diverge.

In any case, getting nametable mirroring correct for the MMC1 will help you get mirroring correct for the FME-7 and Action 53 mapper, as they're almost the same mirroring-wise.
Re: Why does my MMC1 mapper act like this?
by on (#130087)
tepples wrote:
That or log the timing and value of writes to $4008, so that you can at least tell when the key makes and breaks.


I did this. I have no clue how the APU works but I got writes alternating between 0 and 176. This was the output (delay being the amount of ms between each write to 0x4008):

    *beep* data: 176
    delay: 170
    *beep* data: 0
    delay: 48
    *beep* data: 176
    delay: 176
    *beep* data: 0
    delay: 160
    *beep* data: 176
    delay: 64
    *beep* data: 0
    delay: 48
    *beep* data: 176
    ................
    ................

I have yet to compare logs, although I thought this might tell you a bit more about what's wrong.
Re: Why does my MMC1 mapper act like this?
by on (#130089)
As an approximation, >128 means the beeper is being turned on, and 0 means it's being turned off. (The triangle channel is actually far more complicated than that, but this will at least let you render the Morse code error messages.) Try making a graph of on which frames the beeper is on by drawing a pixel as light if it's on or dark if it's off. You should see the dots and dashes of Morse code emerge from that.
Re: Why does my MMC1 mapper act like this?
by on (#130090)
tepples wrote:
Try making a graph of on which frames the beeper is on by drawing a pixel as light if it's on or dark if it's off. You should see the dots and dashes of Morse code emerge from that.


Holy crap. I didn't make a graph but I simply filled a string with dots and spaces and this ended up being the morse code:

Quote:
__ .. ._.


It's mirroring:

Quote:
MIR The nametable mirroring for this mapper doesn't match any of the
supported mappers. Check PA13-PA10, /PA13, CIRAM A10, and CIRAM
enable, and don't try running the 78.3 test on an emulator that
does not support NES 2.0 format.


That was actually really helpful, I didn't expect it to work. I'll try figuring out what's wrong with the mirroring. Is there any way I can test what's wrong with it, or do I manually have to go line by line through my code? I was stuck on this issue for weeks but I never managed to find anything, not sure if I will now.
Re: Why does my MMC1 mapper act like this?
by on (#130091)
For that, I guess someone might need to write a specific MMC1 test. Do you have a nametable viewer, PPU memory hex viewer, etc. in your emulator?
Re: Why does my MMC1 mapper act like this?
by on (#130106)
tepples wrote:
For that, I guess someone might need to write a specific MMC1 test. Do you have a nametable viewer, PPU memory hex viewer, etc. in your emulator?

No, no and no.

Right now I'm comparing logs (my emu vs nintendulator) with a test called mmc1_a12.nes. I'm not sure what exactly it tests, but it makes my emu crash and it's in cpow's github so I assume I should have at least that working should I want my MMC1 to function decently. If anything new comes up I'll be back. If you have any more suggestions/solutions I'd be glad to hear them.
Re: Why does my MMC1 mapper act like this?
by on (#130109)
ArsonIzer wrote:
tepples wrote:
For that, I guess someone might need to write a specific MMC1 test. Do you have a nametable viewer, PPU memory hex viewer, etc. in your emulator?

No, no and no.

It may seem like a waste of time at this point to implement stuff like a nametable viewer, pattern table viewer, hex editor (CPU/PPU memory), etc, but if you did implement them, you would probably end up saving time in the long run.
Re: Why does my MMC1 mapper act like this?
by on (#130111)
thefox wrote:
It may seem like a waste of time at this point to implement stuff like a nametable viewer, pattern table viewer, hex editor (CPU/PPU memory), etc, but if you did implement them, you would probably end up saving time in the long run.


It doesn't seem like a waste of time at all. I'm just the kind of guy who has the tendency to use the good ole familiar print line rather than learn how to debug if you know what I mean. I'd like to implement stuff like that, but I get lazy every time I think about it.

I'll try to implement at least a nametable viewer ASAP so I can solve this issue.
Re: Why does my MMC1 mapper act like this?
by on (#130112)
I can't stand when people bash good ol' printf debugging. I *love* that technique and just because I use it before I break out gdb or some other debugging tool does not mean I am a luddite or a Neanderthal. Having said that, I completely agree that developing little "windows into the soul of the machine" you're trying to emulate are incredibly valuable. Just look at how many I've added to NESICIDE. ;)
Re: Why does my MMC1 mapper act like this?
by on (#130119)
cpow wrote:
I can't stand when people bash good ol' printf debugging. I *love* that technique

Agreed. I often add a crude form of printf debugging to my NES games, involving two bytes that the program translates to a four-digit hex number and displays every frame. It has saved me from having to jump into FCEUX's step debugger as often.

Quote:
I completely agree that developing little "windows into the soul of the machine" you're trying to emulate are incredibly valuable.

Just make sure that these windows are actually accurate. It's possible to end up making a viewer that incorrectly reflects the machine's state. So make the pattern table and nametable viewer use the same function to read video memory as the renderer. (You'll need to provide a way to read video memory without read side effects so as not to confuse the scanline counter of MMC3 and MMC5, the automatic 4K switching of MMC2 and MMC4, or the use of nametable address bits for high CHR RAM address bits that Oeka Kids and Nanjing mappers do.)
Re: Why does my MMC1 mapper act like this?
by on (#130284)
cpow wrote:
The distinction is, I think: it is ignored by the hardware, which doesn't mean you should shift it away.


I'm shifting one bit to the right in MoarNES and all MMC1 games tested look perfect in it.
Re: Why does my MMC1 mapper act like this?
by on (#138505)
@ArsonIzer

I ran into exactly the same problem that you are experiencing with MMC1 with my experimental NES emulator. The images generated by my emulator look identical to yours.

I managed to solve the problem doing 2 things: 1) I reversed horizontal and vertical mapping. I currently have no mapper and MMC1 doing the opposite things at least according to the documentation. 2) I discovered that timing greatly affected things. I suggest introducing a longer delay within the vertical blanking period to enable the processor to run more cycles. See if that helps. If more CPU cycles within the vblank solves the problem, then you'll know that you have to improve the timing code.
Re: Why does my MMC1 mapper act like this?
by on (#139148)
I haven't touched the NES for several months again, so bear with me if I seem forgetful.

zeroone wrote:
@ArsonIzer

I ran into exactly the same problem that you are experiencing with MMC1 with my experimental NES emulator. The images generated by my emulator look identical to yours.

I managed to solve the problem doing 2 things: 1) I reversed horizontal and vertical mapping. I currently have no mapper and MMC1 doing the opposite things at least according to the documentation. 2) I discovered that timing greatly affected things. I suggest introducing a longer delay within the vertical blanking period to enable the processor to run more cycles. See if that helps. If more CPU cycles within the vblank solves the problem, then you'll know that you have to improve the timing code.


Could you elaborate on those points?

1. What do you mean by reversing horizontal and vertical mapping? If you mean that I should set mapping to horizontal when the MMC1 control bit is indicating vertical and vice versa, then no luck. It also seems weird that that would work perfectly.

2. I especially don't get this one. I can't assume that timing is the problem when all tested mapper 0, 2, 3, and 7 games work fine.

For those who are still interested in this error, I found out that there is something wrong with the pattern tables. I looked at FCEUX and my emulator side by side, running the same game, and when the game selects data from the pattern tables, the game running in FCEUX seems to have access to 2 different tables, while mine has access to one (so 0x0000 and 0x1000 are always the same). This means that it's probably either the data being extracted wrong from the ROM, or the mapping always causes the same pattern table to be mapped to the 2 PPU "slots". I hope to figure out quickly which one it is, when I get back to it. If someone has any suggestions until then, that would definitely be appreciated.

EDIT: It seems like I might have 2 separate issues. One is that the pattern tables are accessed wrong SOMEHOW. The other one is that the nametables are wrong. There are games that seem fine-ish and just have peculiar graphic anomalies, while others are completely scrambled. The only MMC1 game that functions fine is Bionic Commando. It has no CHR banks though, maybe that's it. That being said, Mega Man 2 has no CHR banks either and as seen on the first page of this thread, it doesn't work fine at all.

EDIT 2: Bionic commando 2 and Mega Man 2 have no CHR banks yet they (exclusively) use 8K banking. These 2 work, but Mega Man 2 has strange nametable deformities. Addams Family, Adventures in the Magic Kingdom, Darkwing Duck, and Double Dragon have 16 CHR banks, exclusively use 4K banking and all 4 suffer from incorrect pattern tables. What's going on here?
Re: Why does my MMC1 mapper act like this?
by on (#139228)
As an experiment, run the CPU much longer in the VBlank period (~75000 CPU cycles).
Re: Why does my MMC1 mapper act like this?
by on (#139437)
zeroone wrote:
As an experiment, run the CPU much longer in the VBlank period (~75000 CPU cycles).


I tried running the PPU only one cycle per CPU cycle while VBlank is active, i.e. the CPU runs 3 times faster/the PPU runs 3 times slower during VBlank. This does not have any effect on any of the games.
Re: Why does my MMC1 mapper act like this?
by on (#139467)
ArsonIzer wrote:
I tried running the PPU only one cycle per CPU cycle while VBlank is active, i.e. the CPU runs 3 times faster/the PPU runs 3 times slower during VBlank. This does not have any effect on any of the games.


When the PPU is disabled (sprite/background rendering turned off), make sure that the PPU renderer is not modifying the V and T loopy registers; only the CPU should be messing around with them.
Re: Why does my MMC1 mapper act like this?
by on (#139533)
zeroone wrote:
When the PPU is disabled (sprite/background rendering turned off), make sure that the PPU renderer is not modifying the V and T loopy registers; only the CPU should be messing around with them.


Yes, I saw your thread about that, but that's not the case with mine. I made a 100% sure that it doesn't touch loopyV and loopyT if it's not rendering. LoopyV and LoopyT updating is probably not the issue.
Re: Why does my MMC1 mapper act like this?
by on (#140513)
Update: I fixed the bug that makes the emulator pick the wrong pattern table from the CHR data in a rom. This fixes a lot of the wrong graphics, but some MMC1 ROMs still crash (double dragon, abadox), and some still have confusing nametable issues (mega man 2, final fantasy, adventures in the magic kingdom). I'll look into those ASAP.
Re: Why does my MMC1 mapper act like this?
by on (#172798)
ArsonIzer wrote:

Mega Man 2 never crashes, the graphics are just f'd up. Here's what happens further into the game:


Image

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

(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.


I managed to solve this! :)

My own emulator suffered from the same problem until I realised that MMC1, unlike simpler mappers such as NROM, can change the mirroring mode multiple times during the game. I was originally setting the mirroring mode from the cartridge rom property but for MMC1 one must also update it memory mapping whenever a change in the mirroring mode occurs. I fixed this by exposing an event in my Cartridge class that is fired whenever the program writes to the $8000-$9FFF addtress range in MMC1 (the rom's control register range).
Re: Why does my MMC1 mapper act like this?
by on (#172800)
colinvella wrote:
I was originally setting the mirroring mode from the cartridge rom property but for MMC1 one must also update it memory mapping whenever a change in the mirroring mode occurs.

If the mirroring is mapper controlled the ROM header's mirroring setting is irrelevant.
Re: Why does my MMC1 mapper act like this?
by on (#174469)
Quote:
I managed to solve this! :)

My own emulator suffered from the same problem until I realised that MMC1, unlike simpler mappers such as NROM, can change the mirroring mode multiple times during the game. I was originally setting the mirroring mode from the cartridge rom property but for MMC1 one must also update it memory mapping whenever a change in the mirroring mode occurs. I fixed this by exposing an event in my Cartridge class that is fired whenever the program writes to the $8000-$9FFF addtress range in MMC1 (the rom's control register range).


From the posted Megaman 2 screenshots. That seems to be the problem. MMC1 is typically the first mapper that novice emulator devs implement, that support dynamic mirroring via registers. Simpler mappers like NROM usually specify mirroring once in the ROM file itself.
Re: Why does my MMC1 mapper act like this?
by on (#174473)
rainwarrior wrote:
colinvella wrote:
I was originally setting the mirroring mode from the cartridge rom property but for MMC1 one must also update it memory mapping whenever a change in the mirroring mode occurs.

If the mirroring is mapper controlled the ROM header's mirroring setting is irrelevant.

One additional note: You might want to consider initializing the mapper's mirroring setting from the ROM header's setting to stay compatible with some incorrect ROM headers (e.g. mapper 4 being used for mapper 206 games).
Re: Why does my MMC1 mapper act like this?
by on (#174474)
Or mappers where only some of the boards have a mapping register. It doesn't hurt, unless there is a known initial value that software relies on…which is somewhat unusual.