MMC1 questions

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

MMC1 questions

Post by DRW »

Originally, my next game was going to be an UNROM game, but my graphics artist insists on battery-backed save states instead of passwords, so I had to switch over to MMC1.

I did the Nerdy Nights chapter on MMC1 and read the corresponding wiki articles:
https://wiki.nesdev.com/w/index.php/MMC1
https://wiki.nesdev.com/w/index.php/Programming_MMC1

So far, everything works fine.

But I still have some questions about it:


1. About the fact that things like switching the bank need several consecutive writes, the wiki says:
If an NMI or IRQ can interrupt a series of writes, it is not easy to know what state the serial register was in before the interruption.
I actually have such a situation: If the game logic is still in the middle of running and the NMI starts, it immediately stops again, but not before calling the sound library, so that the music doesn't lag. In this case, it sets the bank as well.

So, is there even a way to reset only one specific register? I thought that setting a 1 in the highest bit of $8000-$FFFF will reset the whole mapper.
Does that mean whenever I switch the bank, I also have to set the screen mirroring again?


2. What happens if writing to a register, for example $8000, is interrupted by the NMI and the NMI then writes to $E000? Do the writes to $8000 and $E000 clash with each other as well or can they be mixed?
I.e. if setting screen mirroring is interrupted by setting the bank, do I have to reset and try again the screen mirroring?


3. The wiki suggests that you should mirror the start of the reset function as well as the three vectors to all banks. Because there are some MMC1 revisions that don't guarantee that the last bank is indeed treated as the fixed bank at startup, so any bank could be the active bank at the beginning.

Is this really something I need to consider or can I just ensure that I use an MMC1 version where the last bank is always guaranteed to be active?
In how far can you rely on this and in how far is this more a thing that you should take care of regardless?

Can I simply declare that my game has to run on an MMC1 SNROM-05 board and that's it? Or getting a specific revision something that you shouldn't rely on?


4. I've read somewhere that you should disable battery support when values are not written to WRAM. Am I remembering this correctly or do I confuse something here?
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: MMC1 questions

Post by lidnariq »

DRW wrote:So, is there even a way to reset only one specific register? I thought that setting a 1 in the highest bit of $8000-$FFFF will reset the whole mapper.
Resetting the MMC1 seems to only set the 'PP' bits of the register at $8000. It definitely does not change the CHR registers and I'm about 90% certain it does not change the PRG bank. I don't remember whether the PRG RAM protect bit is changed.
2. What happens if writing to a register, for example $8000, is interrupted by the NMI and the NMI then writes to $E000? Do the writes to $8000 and $E000 clash with each other as well or can they be mixed?
There is one 4-bit FIFO. On the 5th write, the register corresponding to the fifth write is what chooses where the value goes.
User avatar
rainwarrior
Posts: 8732
Joined: Sun Jan 22, 2012 12:03 pm
Location: Canada
Contact:

Re: MMC1 questions

Post by rainwarrior »

None of the original UxROM boards had PRG-RAM, as far as I know, but there's nothing about the mapper that forbids it. Some emulators can provide the PRG-RAM for it, and you can make a board that supports it. (There's also UNROM 512, but it explicitly uses flash for saves, so I don't think battery backed PRG-RAM is an option for that.)


3. I think there are a few MMC1 boards that are using a 32k PRG and don't bother to connect the PRG banking, so those MMC1 games that don't need to have the reset stub because of that. I don't think there's versions of the MMC1 chip that initialize the PRG bank? (Not entirely sure.)

If you're building an MMC1 clone on a CPLD you could just make it power on with the high bank where you want it, though.


4. A lot of battery backed mappers had ways to protect it when not otherwise in use. I couldn't say how often you will get save corruption if you don't do this, but I do think this practice existed for good reason.
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: MMC1 questions

Post by tepples »

rainwarrior wrote:None of the original UxROM boards had PRG-RAM, as far as I know, but there's nothing about the mapper that forbids it. Some emulators can provide the PRG-RAM for it, and you can make a board that supports it.
The circuit for this functionality uses a 74HC20 and can be seen in Family BASIC.
rainwarrior wrote:I think there are a few MMC1 boards that are using a 32k PRG and don't bother to connect the PRG banking, so those MMC1 games that don't need to have the reset stub because of that.
SEROM doesn't need reset code because the PRG ROM is mapped directly: A14 runs directly from the cart edge to PRG ROM A14. But the only MMC1 board with both WRAM and 32K PRG ROM (SIROM, Japan only) does use the MMC1's PRG A14 output.
rainwarrior wrote:A lot of battery backed mappers had ways to protect it when not otherwise in use. I couldn't say how often you will get save corruption if you don't do this, but I do think this practice existed for good reason.
I think save corruption comes from stray writes as the CPU and decoder start to lose power. As far as I'm aware, the 74HC20 circuit provides no protection against this effect, so the player has to be diligent about holding Reset when turning off the power. Perhaps a protection circuit like the Mitsumi MM1026 used in MMC4 games might help.
Pokun
Posts: 2681
Joined: Tue May 28, 2013 5:49 am
Location: Hokkaido, Japan

Re: MMC1 questions

Post by Pokun »

I thought for a long time that enabling the RAM write protection of MMC3 and MMC5 etc protected against stray writes when powering off. So that's why even MMC5 games like Just Breed asks the user to hold RESET when powering off.
User avatar
Dwedit
Posts: 4924
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Re: MMC1 questions

Post by Dwedit »

Have two bankswitch routines, one for the NMI, and one for the main game.
NMI's first bankswitch routine:
Reset the MMC1 (write anything 80-FF or whatever)
Do the 5 writes for the mapper
Set a flag to indicate that NMI has done mapper writes
Subsequent bankswitches don't need to reset the MMC1 first, but it's no problem if you do.

Game's bankswitch routine:
Do the 5 writes for the mapper
check the flag to see if NMI has done mapper writes
If it did, reset the mapper, then do the 5 mapper writes again.

For this, you need to have your bankswitch code live in the fixed bank, and have the final ROM bank be the fixed bank be at C000-FFFF. Just like UNROM.
Also, the mapper may begin in an unknown state, and on actual cartridges, you need a reset vector and small mapper reset code on every single 16K rom bank. Just a single mapper write and a jump to the real boot code, so it's tiny code.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: MMC1 questions

Post by tepples »

Or just ensure that your NMI routine never switches banks. Something that just sends OAM and VRAM updates and sets scroll should work.

To avoid music slowdown now that you've moved audio out of NMI, you can have the NMI routine increment the number of times the audio engine's update routine needs to be called, and then check that variable at multiple points during your main thread. This also handily avoids the thread safety problem where an NMI happens during jsr start_sound_effect, causing your audio engine's sound effect code to see an inconsistent state due to a partly initialized channel.
User avatar
tokumaru
Posts: 12427
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Re: MMC1 questions

Post by tokumaru »

tepples wrote:Or just ensure that your NMI routine never switches banks. Something that just sends OAM and VRAM updates and sets scroll should work.
If you can do that, great, but there are many legitimate reasons for switching banks in the NMI handler besides audio updates (e.g. copying CHR data from ROM to VRAM), so it's good to know how to do that correctly.
This also handily avoids the thread safety problem where an NMI happens during jsr start_sound_effect, causing your audio engine's sound effect code to see an inconsistent state due to a partly initialized channel.
Or you could leave the audio update/playback routine take care of actually initializing channels, with the main thread merely sending commands to the audio system through a queue, as opposed to directly manipulating its state.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: MMC1 questions

Post by infiniteneslives »

Just chiming in as I'm suprised it hasn't yet been mentioned. If the only thing motiviating the jump to mmc1 is saves slots, did you consider utilizing flash saves to PRG-ROM with a discrete mapper?

"UNROM-512" already has a defined and emulator supported PRG-ROM saving feature and has been utilized by multiple homebrews to date.

The cost of a CPLD, PRG-RAM, and battery circuit is significantly more than a single logic gate needed to unlock PRG-ROM saves.
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: MMC1 questions

Post by tepples »

How does that work when one erase sector is bigger than the NES's entire RAM? Or would most saving be done on a blank screen using CHR RAM as work RAM?
lidnariq
Posts: 11432
Joined: Sun Apr 13, 2008 11:12 am

Re: MMC1 questions

Post by lidnariq »

Other options:
- Don't hold total bytes of saves larger than you can keep in WRAM (There's nothing that says you have to use the entirety of the 4 KiB sector size.)
- Use two sectors, and swap which one holds the save back and forth.

Because of how flash works (converting bits from 1 to 0 can be done in any order at any time; converting bits back to 1 can only be done 32 kibit at a time) there may be more cleverness that takes advantage of this.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Re: MMC1 questions

Post by infiniteneslives »

Unlocking 32KB of CHR-RAM on mapper 2 is also relatively cheap/free. Plenty of scratch RAM to buffer sector data if needed. If one doesn't have 4-8KByte of PRG-ROM to spare doubling density is still significantly cheaper than ASIC mapper + PRG RAM + battery.

Don't get me wrong, managing flash saves is challenging in comparison. But several different developers and publishers are actively utilizing it.
If you're gonna play the Game Boy, you gotta learn to play it right. -Kenny Rogers
tepples
Posts: 22708
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Re: MMC1 questions

Post by tepples »

If only it were PowerPak supported...
User avatar
DRW
Posts: 2225
Joined: Sat Sep 07, 2013 2:59 pm

Re: MMC1 questions

Post by DRW »

About the idea of using a custom mapper (UNROM with battery save etc.): I would never do this. Not only do I insist on only using mappers that existed back in the NES days, I also make sure that I only use common mappers. Which is why I even wanted to avoid using UNROM with 256 KB because that one was only used for two unimportant games while the 128 KB version was used for many times.

With MMC1, I don't have that problem anymore. MMC1 with 256 KB is common enough. And it was used in "Final Fantasy", one of the most popular NES games.


I mirrored the vectors and the reset start code now because I saw that even "The Legend of Zelda" does it, so obviously the most common board types also have that issue.
Dwedit wrote:Have two bankswitch routines, one for the NMI, and one for the main game.
Instead of writing two switch functions, how about this:
You take a global variable. At the start of the function, you increment it and also save the new value to the stack.
At the end of the function, you check whether the variable still has the same value as the one on the stack. If not: Increment again, repeat.

This way, you can use the same function in all locations and you can have an arbitrary number of interrupts doing switches. Unless you try to do 256 bank switches at once, the function always knows whether it was interrupted by another bank switch because it doesn't have a "was interrupted" flag, but each call basically has its own ID that can be checked for changes. Since the absolute numerical value is not important (only the comparison with another value for equalness), the ID never gets decremented. It only gets incremented with every new bank switch call.
tepples wrote:This also handily avoids the thread safety problem where an NMI happens during jsr start_sound_effect, causing your audio engine's sound effect code to see an inconsistent state due to a partly initialized channel.
That's a really good point that I haven't considered yet. (Fortunately, my previous game never lags, so this wasn't a problem there.)
Since I use FamiTone, there's no way I can ensure that this doesn't happen, since, as far as I'm concerned, FamiTone is a black box.

But how do you avoid the lagging of music? If I simply set a counter in every NMI for every intended music call, this only means that a game logic lag will skip a certain amount of sound, but it will still lag.

For example, let's take an extreme example: Let's say my game logic lags for five seconds in a certain situation.
Calling the sound outside of NMI and using a counter to check how often it has to update the APU doesn't mean that the music doesn't lag. It just means it lags for five seconds and then it skips five seconds worth of music all at once.
Unless I clutter all my code with nothing but music calls, but then I have the problem that music is already updated, even though later parts of the game logic intend to add sound effects etc.

I guess I simply swallow potential music lag. Lagging shouldn't happen that often anyway and if it happens, who cares that the music becomes a bit slower?


This should also completely eliminate my initial problem: Other than for the music, my NMI never switches banks since it only does the bare minimum, updating the PPU by reading some pre-prepared data from RAM and nothing else.
(Stuff like CHR updates are done when rendering is turned off.)

So, if no interrupt ever switches a bank or writes to any other MMC1 register, can I be sure that an NMI that triggers in the middle of the bank switch function doesn't corrupt the bank switch status? (Of course, given that the NMI always correctly saves A, X and Y to the stack when it starts and restores them when it ends.)

rainwarrior wrote:4. A lot of battery backed mappers had ways to protect it when not otherwise in use. I couldn't say how often you will get save corruption if you don't do this, but I do think this practice existed for good reason.
Since I don't know anymore where I read this whole thing, I have to ask again:
What means are there in the MMC1 mapper to protect the battery data? How can I disable battery support and only enable it during writes? (If that is even a thing that can/should be done. As I said, I'm not quite sure what exactly I read there at all, I only vaguely remember something about protecting it by disabling it when not in use.)
My game "City Trouble":
Gameplay video: https://youtu.be/Eee0yurkIW4
Download (ROM, manual, artworks): http://www.denny-r-walter.de/city.html
User avatar
Bregalad
Posts: 8056
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Re: MMC1 questions

Post by Bregalad »

I mirrored the vectors and the reset start code now because I saw that even "The Legend of Zelda" does it, so obviously the most common board types also have that issue.
It has nothing to do with the board, but rather MMC1 chip version. I think earlier versions had unknown state at poweron, but this was fixed for MMC1A, and all MMCB series chips, which are most commons. The older MMC1 is extremely rare. Legend of Zelda was one of the first games using the chip.

As for the matter, I do not think it is necessary to have two functions, unless you are really tight on NMI VBlank time.
Something like this is enough:

Code: Select all

MMC1_switch:
   pha
   lda M2000
   and #$7f
   sta $2000
   pla
   sta $ffff
   lsr A
   sta $ffff
   lsr A
   sta $ffff
   lsr A$
   sta $ffff
   lsr A
   sta $ffff
   lda M2000
   sta $2000
   rts
M2000 is supposed to mirror the value last written to $2000. This prevent VBlank during the switching codes, and make it appen only after the switching is done. This can delay NMI, and as such, shorten VBlank time a little, but for 99% of the cases it'll do the trick. If full VBlank time is really needed, or if you're doing timed code synchronized directly from NMI this is unacceptable. Then you can either do what dwedit describes, or do something like that:

Code: Select all

MMC1_switch:
   inc while_accessing_mmc1
   sta $ffff
   lsr A
   sta $ffff
   lsr A
   sta $ffff
   lsr A$
   sta $ffff
   lsr A
   sta $ffff
   dec while_accessing_mmc1
   rts
In the VBlank, do not switch any bank if the "while_accessing_mmc1" flag is true/nonzero. This means in very rare cases the music code will not run when lagging, but it will still run normally if the game engine lags and it is outside of this routine.

If you want to be extremely perfectionist you could go for the extreme solution : Before switching banks in NMI, you check the programm counter value saved on the stack and see if it is within the MMC1 writing routine, and act accordingly. This way you can fully use VBlank time *and* you are not required to reset the mapper by writing $80, which seems to have a side effect of changing PRG-ROM switching mode.
Post Reply