About MMC1..

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
About MMC1..
by on (#27927)
Hi all,

According to the wiki, writting any MMC1 register with a value whose highest bit is set makes the control register to be ORed with 0x0C. As far as I understand it, this only affects CHR-ROM bank mode, as PRG-ROM bits are left untouched. The point here is that I don't see why the wiki states, literally, "..locking PRG ROM at $C000-$FFF to the last bank.". So, although the bank mode is not modified, the PRG banks are swapped while CHR banks are left intact.. Why is that?

I know MMC1 reset state is not known at all but.. would it be accurate to apply a reset shift register as MMC1's initial state?

On the other hand, I think there's a typo in "Variants" section. Shouldn't SXROM combination be "512KB of PRG ROM and 16KB of PRG RAM" instead of "512KB of PRG ROM and 32KB of PRG RAM" (as SOROM chooses between TWO 8KB banks)?

One more thing. Which exact registers and bits are "upper CHR bank select line" and "second-highest CHRbank select line" referring to in "Variants" section?

Thx.

by on (#27928)
You must be misreading the wiki page. It states that ORing with $0C affects only the PRG bank mode ($08) and PRG bank location ($04), not the CHR bank mode ($10) or the mirroring mode ($03).
Code:
4bit0
-----
CPRMM
|||||
|||++- Mirroring (0: one-screen, lower bank; 1: one-screen, upper bank;
|||               2: vertical; 3: horizontal)
||+--- PRG ROM bank location (0: switch 16 KB at $C000; 1: switch 16 KB at $8000
||                            only used when PRG bank mode bit below is set to 1)
|+---- PRG ROM bank mode (0: switch 32 KB at $8000, ignoring low bit of bank number;
|                         1: switch 16 KB at address specified by location bit above)
+----- CHR ROM bank mode (0: switch 8 KB at a time; 1: switch two separate 4 KB banks)

by on (#27929)
Whoops! I confused ORing with ANDing, although I had it correctly implemented in my emu.. XD

Question #1 solved.

Thx.

by on (#27938)
"Resetting" the MMC1 accurately reset its shift register, and SXROM has 32 KB of SRAM (allthrought I cannot check if all 32KB is acessible because I have not a such cart, I'm pretty sure it is because both game that uses it use only 16 KB of SRAM, but one uses banks 0 and 2 and the other banks 0 and 1, if I remember correctly).

by on (#27939)
I recall having read somewhere in the forums someone (Disch? Blargg?) talking about some partial tests done on MMC1 reset state, from which it was told the initial values for the regs are something like

Code:
Regs[ 0 ] = 0x0F;
Regs[ 1 ] = Regs[ 2 ] = Regs[ 3 ] = 0x1F;

By doing so, several games don't even start running, while using 0x00 for all of them seems to work fine.

On the SRAM side, ogghh.. it is quite a pain to implement this mapper properly for programs defined by the iNES header, as every MMC1 revision interprets differently every bit of its registers. How do you guys work this problem out in your emus?

by on (#27941)
Muchaserres wrote:
I recall having read somewhere in the forums someone (Disch? Blargg?)


I think it was Blargg. Anyways:

-) Contents of all registers are unreliable at startup (they contain powerup garbage, just like unprepped RAM does)

-) Some MMC1 revisions (don't know which) OR reg0 with 0x0C on startup. Several games rely on this.

-) Some MMC1 revisions do not have the WRAM disable bit (bit 4 of reg3 does nothing). Some games (Kid Icarus comes to mind) do not clear the bit before attempting to access WRAM -- so if you set this reg to $1F, that could be why these games are not even starting.

-) Other MMC1 revisions have the WRAM disable bit. On some revisions its initial state is unreliable, on others its set initially, and on others its clear initially (iirc)


In my emu I zero everything except reg0, which I set to $0C.


Quote:
as every MMC1 revision interprets differently every bit of its registers. How do you guys work this problem out in your emus?


Here's snippits from my source:

Code:
int NESMpr001::Load()
{
   NESMapperMMC1::Load();

   static const ch_t* vnames[MMC1_COUNT] = {
      shT("Generic MMC1 (256k PRG, 8k RAM)"),
      shT("SUROM (512k PRG, 8k RAM)"),
      shT("SOROM (256k PRG, 16k RAM)"),
      shT("SXROM (512k PRG, 32k RAM)")
   };

   mSubMpr.nVal1Count =         MMC1_COUNT;
   mSubMpr.szVal1Names =         vnames;

   pFileInfo->nPRGRAMSize = 0x8000;      // SXROM uses 32k

   return 0;
}

void NESMpr001::SyncRAM(int cu)
{
   NESMapperMMC1::SyncRAM(cu);

   switch(mSubMpr.nVal1)
   {
   case MMC1_GENERIC:
   case MMC1_SUROM:
      SwapPRG_8k(6,0,0,1);
      break;

   case MMC1_SOROM:
      if(nRegs[0] & 0x10)      SwapPRG_8k(6,(nRegs[1] & nRegs[2] & 0x10) >> 4,0,1);
      else               SwapPRG_8k(6,(nRegs[1] >> 3) & 1,0,1);
      break;

   case MMC1_SXROM:
      if(nRegs[0] & 0x10)      SwapPRG_8k(6,(nRegs[1] & nRegs[2] & 0x0C) >> 2,0,1);
      else               SwapPRG_8k(6,(nRegs[1]            & 0x0C) >> 2,0,1);
      break;
   }
}

void NESMpr001::SyncPRG(int cu)
{
   if(cu)      CUAPU();

   switch(mSubMpr.nVal1)
   {
   case MMC1_GENERIC:
   case MMC1_SOROM:
      NESMapperMMC1::SyncPRG(0);
      break;

   case MMC1_SUROM:
   case MMC1_SXROM:
      if(nRegs[0] & 0x10)      MMC1PRG(0x0F,(nRegs[1] & nRegs[2] & 0x10),0);
      else               MMC1PRG(0x0F,(nRegs[1]            & 0x10),0);
      break;
   }
}


NESMpr001 is derived from NESMapperMMC1 which handles basic MMC1 functionality (combining the 5 writes into 1 full write, setting internal registers, etc). The SyncXXX functions are virtual and are called after every complete register write. (This inheritance setup also makes mapper 105 much easier)

The strings and junk in my "Load" function eventually fed to mapper config box which can be opened through the UI:

Image

I also plan on eventually setting which to use automatically via a CRC.

by on (#27942)
Disch wrote:
The strings and junk in my "Load" function eventually fed to mapper config box which can be opened through the UI:

Image

Wouldn't switching between SNROM and SUROM be automatic based on PRG size, as with SOROM vs. SXROM? So then you could simplify it down to one dialog box for SN vs. SO (PRG size <= 256 KiB) and one for SU vs. SX (larger PRG).

Another tip: User interface guidelines recommend using verbs as the labels of buttons that do something, to reinforce to the user that they do something. This means "OK" could become "Apply and Restart".

Quote:
I also plan on eventually setting which to use automatically via a CRC.

Out of whose database?

by on (#27943)
tepples wrote:
Wouldn't switching between SNROM and SUROM be automatic based on PRG size, as with SOROM vs. SXROM? So then you could simplify it down to one dialog box for SN vs. SO (PRG size <= 256 KiB) and one for SU vs. SX (larger PRG).


I suppose I could do that. I thought about it at first but for whatever reason decided against it.

Removing 2 options doesn't make the box much simpler... I mean 4 options is hardly overwhelming. Plus removing options is removing options. That is -- less flexibility. And I don't really expect people to go putzing around with these options unless they really know what they're doing anyway. If there's a CRC in there, they won't need to.

Quote:
Another tip: User interface guidelines recommend using verbs as the labels of buttons that do something, to reinforce to the user that they do something. This means "OK" could become "Apply and Restart".


Good idea.

*does*

Quote:
Out of whose database?


Probably will start one of my own. I plan on having it stored externally (in a text file or something) so that people can add their hacks or whatever to it.

by on (#27946)
Disch wrote:
Plus removing options is removing options. That is -- less flexibility.

In this post, Jesse wrote:
Options are just a way to hide bad design. :)


Disch wrote:
And I don't really expect people to go putzing around with these options unless they really know what they're doing anyway. If there's a CRC in there, they won't need to.

Are you intending your emulator to be used mostly for running copies of commercial games or mostly for running homebrew?

by on (#27947)
EDIT -- blah

I don't feel like debating/arguing/defending my design to you. Thanks for the suggestions. I'll leave it at that.

by on (#27948)
Point taken.

Anyone else need a point of the MMC1's operation clarified?

by on (#27959)
I don't think the MMC1 has an random state at startup, as most games only have the reset code in the last bank, so technically they relies on the last bank to be switced in $c000-$ffff on power-on.
Everything else may be reliable or not, depending on the version of the MMC1. SRAM protection is purely hardware stuff, I don't think many game will turn this on, and write to SRAM while excepting writes to be ignored. I guess this can be pretty much ignored when it comes to emulation, but developpers should still know about that bit, so that they don't set it and exept the SRAM to work as usual on real hardware (this will most likely work in most emus, and proabely with MMC1 and MMC1A too).

by on (#27960)
Bregalad wrote:
I don't think the MMC1 has an random state at startup, as most games only have the reset code in the last bank, so technically they relies on the last bank to be switced in $c000-$ffff on power-on.


I thought that too, until I was corrected

Later revisions do start with those PRG mode bits set (which puts the last 16k at $C000), but earlier ones do not.

From an emulator standpoint -- I would just set those bits at startup all the time.

Quote:
Everything else may be reliable or not, depending on the version of the MMC1. SRAM protection is purely hardware stuff, I don't think many game will turn this on, and write to SRAM while excepting writes to be ignored. I guess this can be pretty much ignored when it comes to emulation,


I don't know of any MMC1 games that do it, but I believe Low G Man (MMC3) relies on WRAM disabling.

by on (#27961)
Low G Man relies on SRAM to be absent (and not to be disabled) and to return open bus.
I'm 100% sure the MMC1A has the last bank set on startup, then, only the first MMC1 have a completely unreliable state (Original Zelda, Metroid, Kid Icarus and possibly a couple of other games should then have multiple reset code).

by on (#27963)
Bregalad wrote:
Low G Man relies on SRAM to be absent (and not to be disabled) and to return open bus.


Perhaps. I suppose it would work the same if you simply don't give the game SRAM at all.

At any rate, if you emulate MMC3's WRAM disable, it doesn't matter if you give the game SRAM or not because Low G Man disables it.

by on (#27967)
Disch wrote:
tepples wrote:
Wouldn't switching between SNROM and SUROM be automatic based on PRG size, as with SOROM vs. SXROM? So then you could simplify it down to one dialog box for SN vs. SO (PRG size <= 256 KiB) and one for SU vs. SX (larger PRG).


I suppose I could do that. I thought about it at first but for whatever reason decided against it.


My guess is that one cannot be confident enough in any rom file out there, cause both header bank sizes and the file size itself do not always match the real value, in the first case due to poor editing by rom dumpers and in the second because there are several overdumps and bad dumps with increased or decreased file sizes (according to MMC1 revisions, I've seen rom files 512 KB long whose real value is 256 KB, and viceversa).

In my opinion, the real (and idealistic) solution to this comes by adding the revision number of the mapper into the rom file header. Does iNES2 achieve this? (sorry, I'm not up to date on that topic).

M.

by on (#27972)
Quote:
In my opinion, the real (and idealistic) solution to this comes by adding the revision number of the mapper into the rom file header.

Agreed. Consistently different state at power up demands representation, especially if code relies on it. Doesn't matter if they all say MMC1B on the chip, for example; still different hardware.

But, if the (correct) ROM/CHR size provides the information, it doesn't need to be in the header. Overdumps are corrupt, so the solution to that is to fix the dumps, not add redundant information to the header.

by on (#27982)
blargg wrote:
But, if the (correct) ROM/CHR size provides the information, it doesn't need to be in the header. Overdumps are corrupt, so the solution to that is to fix the dumps, not add redundant information to the header.


I agree. But one never knows what files the final user will input into the emulator, so the code must be prepared for the worst case. Bad dumps erradication seems to be a faraonic (if not impossible) task, so the way to go in this case comes by looking up CRC based configuration tables. And.. well, in my eyes, that's like adding a big hack into the emu.

I mean, I agree in that PRG/CHR sizes should suffice, but using them blindly may lead to undesired trouble. And, in my opinion, the user should be kept away from any technical details, as with the real thing, so offering the possibility to choose between mapper revisions is quite interesting for a developer (we all), but may not for the casual. Aaarghh..! This is the kind of decision that stops me from advancing in my NES emulation project.

M.

by on (#27986)
Quote:
In my opinion, the real (and idealistic) solution to this comes by adding the revision number of the mapper into the rom file header. Does iNES2 achieve this? (sorry, I'm not up to date on that topic).

I disagree, as many games in fact uses more than one version of the MMC1 at the same time, and if any relies on boot-up states, then make emulator have this boot state and let's doccument the actual things proprely on the Wiki when it comes to differences between versions.

And no emulator should expect it's used to use bad dumps or overdums, if they crash, it's 100% of the user's fault. ROM sites should state clearly that download of over/bad dumps are discouraged and worthless (in fact they should even remove such dumps).

by on (#50555)
Maybe it will be a good idea in an emulator to have a title screen for problematic mappers, where the user can manually choose a specific version of the mapper, after being warned about the possibility of choosing an incorrect one. It is also possible to have a "per mapper" database with the checksums of known roms using a mapper and the appropriate "submapper" may be SUGGESTED to the user at startup as a pre-selected option in a list. It is also possible to take the presence of specific title "keywords" in the title/filename of the rom into account when deciding which version of the mapper to SUGGEST to the player. I hope this makes some sense :).