Mappers

This is an archive of a topic from NESdev BBS, taken in mid-October 2019 before a server upgrade.
View original topic
Mappers
by on (#94167)
Hello

I am just doing some research into the NES so as to create an emulator.

I am trying to get my head around mappers. I have read lots of documents around the net and some entries in this website's wiki.

Here is what I think I know and some questions. If you could please help me out I would be very grateful!

There are LOTs of different mappers, some more common than others - such as MMC 1, MMC 3.

The mapper is a chip on a cart's board which allows the 6502 to access more memory than is available on the NES by means of bank switching.

Question: Where are the mappers' registers stored?

Question: (Related to the above question) According to the Comprehensive NES Mapper Document v0.80 by \Firebug\, MMC 1 Register 0 is located at $8000 - $9FFF. This is a vast memory space for one register. What actually does this mean?

Question: When is the mapper interfaced with? I am assuming that when the CPU reads/writes to either PKG-RAM/ROM or PPU memory, it first checks the currently used MMC registers to see which bank to go to? Or when one of the MMC registers is changed, the system swaps respective banks and thus the CPU simply reads from the active bank?

Thank you all very much for your help,

Richard Hughes

by on (#94168)
You need to discover the term "mirroring".

by on (#94169)
A mapper is what we call a device in the cartridge that maps accesses to memory to various things. For instance, you can have a PRG ROM larger than 32k, and have the mapper switch which page appears when you read from 8000-BFFF.

A mapper's registers are its internal state. They are stored inside the mapper chip itself. When it says a register is mapped to $8000-9FFF, it means that when you write a byte to any of these addresses, that byte is written to the mapper's internal register (it doesn't matter which particular address as long as it's in that range). Without a mapper a write to $8000-FFFF does nothing, since you can't write to ROM. Mappers take advantage of this unused address space for writing to allow you to control their internal state.

Some mappers will also generate IRQ events, or perform other interesting functions (MMC5 has a built in multiplier). Some Famicom mappers have additional sound hardware as well.

by on (#94170)
Thank you both for the quick reply!

That is wonderfully clear, thanks.

So, as was mentioned by HardWareMan, would these values be mirrored throughout that whole memory space?

by on (#94175)
Like your example of the MMC1 reg0 it is mirrored through $8000 - $9FFF

This is done by ONLY looking at PRG /CE, and PRG A14 & A13. Those are they only address inputs the MMC1 has. So it doesn't matter what A0-A12 are hence the mirror from $8000-9FFF.

Not sure what you mean by "whole memory space" in your question. Really the answer is specific to a given register on a given mapper. Generally the mirror is only in the address range of the cartridge. It'll never mirror with $0000-2000 for example because that would conflict with SRAM.

by on (#94176)
In understand.

What do you mean by "PRG /CE, and PRG A14 & A13" - mappers-0.80.txt doesn't mention those expressions?

(By whole address space I meant from $8000-$9FFF - sorry)

Thanks,

Richard

by on (#94177)
PRG A15 is the signal that carries bit 15 of the address being read or written by the CPU. It distinguishes $0000-$7FFF from $8000-$FFFF. It is not brought out to the cartridge edge connector, unlike the next four signals I'm about to describe.

PRG A14 is the signal that carries bit 14 of the address being read or written by the CPU. It distinguishes $0000-$3FFF from $4000-$7FFF, or $8000-$BFFF from $C000-$FFFF.

PRG A13 is the signal that carries bit 13 of the address being read or written by the CPU. It distinguishes $0000-$1FFF from $2000-$3FFF, $4000-$5FFF from $6000-$7FFF, $8000-$9FFF from $A000-$BFFF, or $C000-$DFFF from $E000-$FFFF.

M2 is the CPU clock signal. It stands for "modified Phi2", where Phi2 is one of the clock phases in the 6502 CPU.

PRG /CE (program chip enable), also called /PRGSEL (program select), is a signal to enable the ROM chip. The / means "active low", or that the chip should be enabled when the signal is 0 and disabled when it is 1. It is computed from A15 and M2 by a decoder chip inside the NES Control Deck, and it is low when a ROM should be providing data.

by on (#94185)
rhughes wrote:
Thank you both for the quick reply!

That is wonderfully clear, thanks.

So, as was mentioned by HardWareMan, would these values be mirrored throughout that whole memory space?


I think you are confused. Many mappers' registers are NOT readable. Some mappers don't decode addresses entirely giving them a range of addresses that will have the same behavior. Such as MMC1's PRG Page register will be written if you write to anywhere between $E000 and $FFFF.

But what appears when the CPU reads $E000 to $FFFF depends on the mapper's settings, it will be a page of PRG ROM, NOT the MMC1 register value.

So there is alot of mirroring that goes on. This can be simulated with the AND bitwise operator.

by on (#94210)
I'll try to explain a bit about the mirroring: Mirroring is a consequence of partial decoding of the address lines. Address lines are connections between the cart and the console, which are used to tell the cart the address of the byte the CPU needs to access.

In order to make the mapper registers accessible to the program, the cart has to map them to certain addresses. The NES has 15 address lines connected to the cart (A0 through A14), enough to address anyone of 2^15 = 32768 bytes (hence why mapperless carts are limited to 32KB of ROM), but mappers don't have 32768 registers... even the most complex ones have no more than a couple dozen registers, so it would be a waste to watch all 15 address lines because that much precision isn't necessary.

So, to save on the number of pins a mapper has, it will often only watch enough address lines to tell apart the registers it has. So it's common to see mappers with one register using all of $8000-$FFFF to access it, mappers with two registers dividing the PRG-ROM space in two, and so on (there's no rule for this though, and some mappers divide the addressing space in pretty crazy ways). The mirroring is just a consequence of ignoring some address lines.

For example, if you ignore A0 (the lowest address line), but take all others into consideration, it will not be possible to distinguish between odd and even addresses, because the line that makes that selection is simply not connected to the mapper. For this reason, the mapper will act the same if $8000 or $8001 are accessed. $8002 and $8003 will also appear like the same address to the mapper. By ignoring some address lines, a bunch of addresses will look the same to the mapper, and this is why we often see vast ranges of addresses referencing a single register.

by on (#94233)
OK, I think I'm starting to understand.

So really the process of the CPU reading from PRG-ROM is as follows:

Read request -> Access PRG-ROM page described by the mapper in question -> Read byte(s) from the address on the active page

So the address range $8000 - $FFFF is really a window into a possibly bigger PRG-ROM address space stored elsewhere.

From what I have read, it seems that different mappers may affect all types of ROM spaces, but not RAM spaces - so, PRG-ROM and CHR-ROM, are there others?

If a NES ROM has mapper number 0 or its PRG-ROM < 32K, there is no mapper used, yes?

Thanks again,

Richard Hughes

by on (#94235)
rhughes wrote:
From what I have read, it seems that different mappers may affect all types of ROM spaces, but not RAM spaces - so, PRG-ROM and CHR-ROM, are there others?

Any memory can be bankswitched.

Games on the SOROM, SXROM, ETROM, and EWROM boards have more than 8 KiB of PRG RAM. They change which page appears in $6000-$7FFF by writing to the mapper. It's possible to bankswitch CHR RAM; this is seen in about six licensed games (Pinbot, High Speed, Videomation, the Oeka Kids games, and some Japan-only RPG that I forget).

Quote:
If a NES ROM has mapper number 0 or its PRG-ROM < 32K, there is no mapper used, yes?

It's possible to have 16 KiB of PRG ROM and 16 KiB of CHR ROM on a CNROM board (Joust), which needs a mapper to switch the CHR ROM. It's also possible to have 32 KiB of PRG ROM and 16 KiB of CHR RAM on a CPROM board (Videomation), which needs a mapper to switch the CHR RAM.

by on (#94236)
rhughes wrote:
Read request -> Access PRG-ROM page described by the mapper in question -> Read byte(s) from the address on the active page

Yeah, but there might be more than one slot for pages.

Quote:
So the address range $8000 - $FFFF is really a window into a possibly bigger PRG-ROM address space stored elsewhere.

Since there can be more than one slot, the range $8000-$FFFF can contain several windows. The most I've seen is 4 8KB windows (MMC3 and several other mappers). NSF uses 4KB banks I think.

Quote:
From what I have read, it seems that different mappers may affect all types of ROM spaces, but not RAM spaces - so, PRG-ROM and CHR-ROM, are there others?

The RAM at $000-$7FF can't be affected, because it's internal to the console, but RAM that's on the cart can ($6000-$7FFF, name table RAM, CHR-RAM). CHR-RAM is when the chip that usually contains the tiles is RAM instead of ROM, meaning it's empty when the program starts and the program itself has to write the tiles to it before being able to display them. the contents of CHR-RAM can be changed by the program as the game runs.

Quote:
If a NES ROM has mapper number 0 or its PRG-ROM < 32K, there is no mapper used, yes?

Mapper number 0 means no mapper, but ROMs that are 32Kb or smaller can use mappers.

by on (#94538)
Sometimes emulator authors need a little code example to make things clearer. Here is my implementation of UxROM boards:

Code:
int prgBank[2]; // 2 registers for PRG banks, one variable, one fixed

void Initialize(void)
{
    // Here, the two prgBank registers are set, the one being set to -1 is essentially constant, as it is never modified.
    // Consider also, the bits of the 2 registers:
    // bank[0] = 00 0000 0000 00.. .... .... ....
    // bank[1] = 11 1111 1111 11.. .... .... ....
    prgBank[0] = (+0 * 16384);
    prgBank[1] = (-1 * 16384);
}
void PokePrg(int addr, byte data)
{
    prgBank[0] = (data * 16384); // Here's the bank switch, this method is called whenever a write between $8000 and $FFFF occurs.
    // Multiplying by 16384 shifts the bits left by 14 leaving you with this:
    // NN NNNN NN.. .... .... ....
}

int DecodePrgAddress(int addr)
{
    // here, 'addr' could be anything between (and including) $8000 - $FFFF
    // since UxROM splits PRG ROM space into 2 16KB banks, we AND the address by $3FFF (16 * 1024 - 1)
    // then using A14, we can determine which bank is being accessed.
    // If A14 is 0, Bank $8000 - $BFFF is being accessed
    // If A14 is 1, Bank $C000 - $FFFF is being accessed
    // The final address will be composed like so:
    // PP PPPP PPAA AAAA AAAA AAAA
    // P: Bank number written to $8000 - $FFFF
    // A: Address used during reads from $8000 - $FFFF
    return prgBank[addr >> 14 & 1] | (addr & 0x3FFF);
}


I hope that was clear and informative, and that it didn't confuse you. But this is essentially how hardware performs address decoding. It's implemented as a series of registers, AND, OR, XOR, and NOT gates.

EDIT: I realized that if you subscribe to the iNES notion of what a PRG bank is, that this won't work as well. 'DecodePrgAddress' will return the proper index into a huge byte array consisting of the entire dump of a single PRG ROM chip, for example:

Code:
data = prgROM[DecodePrgAddress(PC)];

by on (#94546)
beannaich wrote:
EDIT: I realized that if you subscribe to the iNES notion of what a PRG bank is, that this won't work as well. 'DecodePrgAddress' will return the proper index into a huge byte array consisting of the entire dump of a single PRG ROM chip, for example:

Code:
data = prgROM[DecodePrgAddress(PC)];

That's a good point. In NESICIDE I drop the 16KB artificial boundary of a "bank" in the file while loading the file into the emulator. Inside the emulator there's only the concept of 8KB banks for both PRG and CHR. I realize this might not work well with NSF but as yet I have not tackled NSF. I would expect this is how most emulators internally represent things...trying to keep in sync with the iNES boundary is silly once you get to the actual machine emulation.

by on (#94553)
cpow wrote:
In NESICIDE I drop the 16KB artificial boundary of a "bank" in the file while loading the file into the emulator. Inside the emulator there's only the concept of 8KB banks for both PRG and CHR. I realize this might not work well with NSF but as yet I have not tackled NSF. I would expect this is how most emulators internally represent things...trying to keep in sync with the iNES boundary is silly once you get to the actual machine emulation.


Actually, PRG ROM banks are 16k ($4000), CHR ROM banks are 8k ($2000) stored in iNES format. When emulating, the smaller unit for PRG ROM banks is 8k ($2000), and 1k ($400) for CHR ROM banks.

As curiosity, RockNES supports NSF and loads using the config above. ^_^;; No problems AFAIK.

(* I believe this topic should be moved to the Newbie Help Center)

by on (#94613)
So is it one board type per mapper, or can one board have multiple mapper types? I know there are some sub types like A1, B1 etc...

by on (#94618)
TLSROM is always 118. TQROM is always 119. Any other T*ROM is 4. Most of the various S*ROM and T*ROM are for different sizes of PRG and CHR ROM, which need different pinouts, but for the most part, they behave identically.

There are only a couple boards that can be wired up for more than one mapper. One of them is UNROM, which is most commonly mapper 2, or mapper 180 if one logic chip is replaced (which one commercial game and one homebrew multicart use). Another is CNROM, which can be wired up as a CHR ROM switch (mapper 3) or as extra enables to perform weak copy protection (mapper 185).

Some boards have revision numbers, such as NES-CNROM-02 vs. NES-CNROM-05. These do not affect behavior but instead generally improve some aspect of the board's electrical reliability.

Some mapper chips have revisions that do affect behavior. The biggest examples I can think of are MMC1, whose PRG RAM enable (controlling access to 8 KiB of SRAM at $6000-$7FFF) behavior changed over the three revisions, and MMC3, whose IRQ behaves differently at the lowest period value on "old" and "new" chips. The newer NES 2.0 format allows mappers to have subtype codes to distinguish e.g. MMC1 and MMC3 revisions, but nobody has defined a registry of those subtypes.

The boards assigned to a particular mapper number may have different amounts of RAM on them. For example, mapper 1 (MMC1) can have no PRG RAM (common), 8 KiB of PRG RAM (also common), two 8 KiB chips (mostly in Koei sims), or one 32 KiB chip (rare, Japan only). Mapper 5 (MMC5) has the same options. How much RAM to emulate can be determined implicitly, by the hash of the PRG ROM, or explicitly, using the NES 2.0 format.

For historical reasons, sometimes two mappers are given the same number. BNROM and NINA-001 were both given mapper 34. You can tell them apart because BNROM will have 0 KiB of CHR ROM (it uses CHR RAM instead). A harder one is mapper 4, which covers both MMC3 (T*ROM boards) and MMC6 (HKROM board). MMC6 acts just like a late-model MMC3, but its PRG RAM enable behavior differs. Only two games were ever released with MMC6, so what most emulators do is detect them by their CRC.

by on (#94673)
One example I can think of for iNES mappers sharing the same number and having slightly differing behavior is the "VRC" series of Konami boards. The address lines being fed into the mapper differ almost from game to game. And the only way to know which wiring configuration to use is either hash checking or using a database. And mapper 4 also applies to HKROM (MMC6) since it operates similarly to the MMC3 with added battery buffered SRAM that is inside the mapper package. MMC6 was only ever used in two games if memory serves.

iNES doesn't even come close to addressing all the different layouts/boards. NES 2.0 is better, but still lacking.

Quote:
That's a good point. In NESICIDE I drop the 16KB artificial boundary of a "bank" in the file while loading the file into the emulator. Inside the emulator there's only the concept of 8KB banks for both PRG and CHR. I realize this might not work well with NSF but as yet I have not tackled NSF. I would expect this is how most emulators internally represent things...trying to keep in sync with the iNES boundary is silly once you get to the actual machine emulation.


In Nintemulator, I don't even use a set in stone bank size. All of my address decoding is done as near to identical to the hardware as possible. :) For a few reasons, first and foremost being that if a mapper can change a ROM layout with a single bit, when that bit gets updated the layout is instantly updated. I don't have to mess around with my "Switch PRG" functions whenever a write is made to a register. It also doesn't degrade performance as much as you'd think, and makes my code look nicer in my opinion!