(By the way, am I alone in thinking whoever came up with the terminology for this needs a major lesson in aesthetics?)
Hahahaha
I'm game. What is the general procedure for adding a new mapper to expand/swap the PRG section?
I've been meaning to write an actual comprehensive doc on the subject... but I never got around to it. Plus I'm lazy. It will require shifting around code and writing custom routines -- so if you're not comfortable with 6502, this might not be a good beginner project.
Anyway...
*cracks knuckles*
To understand how to change a mapper you must first know what a mapper does. The cartridge is able to map anything on the cart to the $4020-FFFF range. Everything lower than that is used by the actual NES and maps to system RAM, system registers, or whatnot. The "standard" way to map this range is:
$4020-5FFF = nothing (open bus)
$6000-7FFF = on-cartridge RAM (aka, SRAM when battery backed) ** only when present (not all carts have RAM)
$8000-FFFF = PRG ROM (the actual game code+data)
As you can see, this only allows for 32k of PRG to be "visible" at once... which is why mapper 0 (no mapper) is maxed out at 32k. That's not a whole lot of space.
Mappers get around this by employing something called "PRG swapping" (though the name is somewhat misleading). This breaks the $8000-FFFF range into smaller blocks, and assigns a flexible page number to each block... so that instead of mapping directly to a "hardwired" area in ROM, the address range is indexed by a page number.
One of the more common methods of PRG swapping is what is done by mapper 2. It splits PRG space into two 16k blocks ($8000-BFFF and $C000-FFFF). $C000 is "fixed" to the last 16k of PRG in the .nes file, but $8000 is "swappable". That is, the game can assign a page number to it, and when it reads from that range, the current page number selects where in the .nes file to read from.
For example... if a game reads from $9130, it could be reading from any of the following .nes offsets:
0x01140 (if page 0 "swapped in")
0x05140 (if page 1)
0x09140 (if page 2)
etc
There are, of course, lots of other swapping schemes, and different mappers all do it a little differently. Although they are all conceptually the same idea.
How you select a page depends on the mapper you're using. Usually it involves one or more writes to the $8000-FFFF area.
-----------------------
So how do you add a mapper? Adding a mapper to a mapper 0 game isn't really that hard. In fact... depending on the game, it might be as easy as just changing the mapper number in the header. However there's one big catch that most people don't realize...
On system startup, mapper registers (ie: what page is swapped in) are unprepped! Many people (and even some docs) claim that registers are set to certain values on startup... however 99% of the time they
are not. This means that to start up "safely", you must have the reset vector point to somewhere in the hardwired bank, and specifically select desired PRG pages before accessing any PRG in swappable areas.
For SMB1, this becomes a problem, as the Reset vector points to $8000, which is swappable on pretty much every mapper out there. So depending on which mapper you change to, you'll have to free up some space in $E000-FFFF or whatever is hardwired, run some custom code to prep the mapper (swap in desired startup pages) then jump to the game's normal startup code. SMB1 is notoriously tight on space, so freeing up this space is quite a task, and may involve shifting routines around and jumping through all sorts of hoops.
----------------
What about expanding?
Once you have a mapper in... expanding is as easy as adding more PRG in (as long as you keep the PRG a power-of-2 size). Just make sure you keep the hardwired pages where they need to be (ie: if the last 16k is hardwired... you'll need that 16k to stay at the end of the PRG .. so you'll want to add pages before it, rather than after it). Nothing really hard at all.
Using the space you gain from expanding is something else. To use the new space, you'll have to swap in the desired page, jump to it / read from it, then swap back to the page that was originally there. This isn't so horrible for quick custom routines... but when you're working around existing game routines (like say, you want to use the extra space for more levels or more text), you might have to gut and rewrite large portions of the game's routines in order to get it working the way you want.