MMC1 usually is set up for 16KB PRG banks as the Wiki denotes. That's great, because we don't have to worry about a "page/bank size mismatch" between the NES ROM header and actual mapper. Makes things easier to comprehend.
In that configuration, usually the very last 16KB PRG bank is "fixed" or "hard-wired" to $C000-FFFF in CPU memory space. This would be PRG bank 7 for 128KB games, or PRG bank 15 for 256KB ones. So what that means is that you can "swap" what $8000-BFFF contains at any moment during run-time to any 16KB PRG bank you want, but $C000-FFFF would remain unchanged/constant no matter what.
Your program/code has to know what page/bank number contains what -- that's for you to manage/deal with, and maybe "how" to organise that and keep track of everything adds to your confusion (I'm betting so! Asking here for advice on that is excellent).
For example, let's pretend you have the following layout of PRG: remember, banks are 16KB each:
- PRG bank 0: contains code/data specific to the very end of the game (boss battle, etc.)
- PRG bank 1: contains code/data specific to the very end of the game (finishing animation, credits, etc.)
- PRG bank 2: empty (all zeros or all $FFs)
- PRG bank 3: contains code/data specific to the main chunk of the game
- PRG bank 4: contains code/data specific to the main chunk of the game
- PRG bank 5: contains code/data for intro and title screen
- PRG bank 6: empty (all zeros or all $FFs)
- PRG bank 7: contains critical code: PRG swapping code, NMI routine, etc. -- and this also contains the values in $FFFA-FFFF for vectors
When the NES powers on, your CPU memory map for $8000-FFFF will look like this, due to "fixed" or "hard-wired" stuff I described above:
- $8000-BFFF: assume unknown contents (unknown PRG bank/page used)
- $C000-FFFF: PRG bank 7
Your code that's pointed to via the RESET vector (code that gets executed on power-on or when the reset button is pressed) should actually reset the MMC1, followed by choosing what PRG bank to assign $8000-BFFF, followed by doing whatever you want. In the above example, you'd probably want bank 5, because that's the intro and title screen.
So we're supposed to reset the MMC1 chip itself first, to ensure it's in a consistent/known state, and to basically "ready it" for the very first time you plan on doing PRG or CHR bank swaps. https://wiki.nesdev.com/w/index.php/Pro ... #PRG_banks has code that tells us how to do that, as well as a preceding paragraph that explains even more nuances about the MMC1, requiring you to jump through some hoops to get it to initialise sanely...
But the code that is shown in the "PRG banks" section is confusing because it conflates several things into one and is not showing the person actually how to reset the MMC1. It's also "optimised" thus in a way, "showing off", trying to cover edge cases and the power-on default complication, rather than explaining how to reset the MMC1. I do not like this example -- it's good/fine, but it doesn't *teach* anyone how to do something.
Here's how you actually reset the MMC1, without all the extra hullabaloo, i.e. focused just on doing that:
Code: Select all
lda #$80
sta $8000
Q1: What is special about the value $80?
Q2: Isn't memory location $8000 ROM? How can you write a value to it?!
The layman's way to understand how a mapper works: it's able to "see" reads and writes to addresses located within portions of CPU memory space. So while a *write* to an address that's ROM obviously can't work (you can't write data to ROM), the mapper actually sees this operation and says "I care about that address, that makes me do X/Y/Z", which then does something. It's implemented purely through hardware.
These addresses are commonly called "registers" or "MMIO registers". Do not confuse this term with the 6502's registers (A/X/Y). Yup, another terminology overlap that you'll have to get used to. Go off of context. :-)
So when doing sta $8000, the MMC1 is able to actually see that you've attempted a write to a memory location it cares about. How do we know that? Well, it's documented here, but as I said earlier, the details are very confusing: https://wiki.nesdev.com/w/index.php/MMC1#Registers
When you read that section to figure out what register $8000 actually does to the MMC1, you'll see it covered by _two_ separate definitions in that page, both of which include that address their ranges:
* Load register ($8000-$FFFF)
* Control (internal, $8000-$9FFF)
So what gives? IMO, this is another example of where the documentation is "reference material" and really only makes sense to those already familiar with it (or hardware people). rainwarrior's stated in the past that a lot of what's on the Wiki is mainly reference material, which is different from training material, and I very much agree. But the Programming MMC1 page doesn't explain it either. Frustrating. I also don't know why this is called the "Load register" -- this further adds to the confusion (especially since it's something you _write_ to).
This is when someone practical has to explain how they understand it purely from a programming perspective. Believe it or not, this is where Disch's MMC1 document 001.txt does a way better job! Refer to that, or go with what I said below, as it's how I think of it all with MMC1:
When writing to registers that the MMC1 cares about, it only looks at 2 bits of the data written: bit 7 and bit 0. Which address you're writing to matters (more on that in a moment -- the MMC1 is a little funky in this regard, it'll make sense shortly).
Bit 7 is the "MMC1 reset" bit, and bit 0 is the "MMC1 data" bit. If bit 7 is set, then the MMC1 is internally reset -- it internally causes $C000-FFFF to be "fixed" or "hard-wired" to the last PRG bank (though this is often true anyway as I said much earlier), thus inadvertently selecting a 16KB PRG bank size, and then internally preps itself so that the next write to any MMC1 register will be the 1st of 5 writes that have to happen (this will make sense soon, trust me). Likewise, if bit 7 is set, it doesn't care about any of the other bits.
Value $80 is %10000000 in binary as you know, so bit 7 is set. The Programming MMC1 example had a value of $FF simply out of convenience; it had already loaded X with $FF (which has bit 7 set) to set up the stack pointer, so writing $FF to $8000 would do the same thing as writing $80 to $8000.
As for the address itself ($8000): you could also have written $80 to memory location $FFFF, or $9FFF, or anything in the $8000-FFFF range. You can reset the MMC1 having bit 7 set when writing to any of those addresses. So $8000 isn't "special" per se when it comes to bit 7.
But if bit 7 is 0, things are different. In that situation, the address you're writing to matters. This leads me to talk about what's special about bit 0. It's called "data bit" in both the Wiki as well as Disch's docs. So let's talk about that:
The MMC1 internally only "keeps track" of 5 bits worth of data that been written to it via registers. But you can't write a raw 5-bit value to the MMC1 in one go -- you have to write each of the 5 bits, one at a time. This is due to how the hardware works (I don't want to go into it). In fact, the MMC1 won't do anything until you've written to it 5 times -- and in fact, the final write is the one that ultimately decides what happens (hold on, I'll explain that in a second).
Knowing all of this, the code you've seen that does stuff like lda/sta/lsr/sta/lsr/sta/lsr/sta/lsr/sta should start to make a little more sense! lsr stands for Logical Shift Right, which is a right bitshift (equivalent of >> in C and other languages) of the value in the accumulator. The MMC1 also wants the LSB (lowest bit) first.
So on the Wiki, when you see a register description that says "internal", it's talking about the data that you write to it via that single-bit-at-a-time model.
Now that we've covered that, let's circle all the way back to our game/ROM example. We've reset the MMC1, but now we want to use PRG bank 5 for $8000-BFFF. How do we do that? Back to the MMC1 Wiki we go. The section titled "PRG bank (internal, $E000-$FFFF)" looks quite relevant!
Code: Select all
4bit0
-----
RPPPP
|||||
|++++- Select 16 KB PRG ROM bank (low bit ignored in 32 KB mode)
+----- PRG RAM chip enable (0: enabled; 1: disabled; ignored on MMC1A)
So we want PRG bank 5? Well that seems pretty simple then!
Code: Select all
lda #5
sta $e000
lsr
sta $e000
lsr
sta $e000
lsr
sta $e000
lsr
sta $e000
Code: Select all
lda #5
sta $8123
lsr
sta $a493
lsr
sta $9685
lsr
sta $c184
lsr
sta $ffff
Also: after the 5th and final write, the MMC1's internal 5-bit buffer essentially "starts over" from the 1st position again. You don't need to reset the MMC1 to achieve that.
Two things I want to cover -- one which I mentioned earlier (PRG-RAM), and one which I didn't (mirroring):
PRG-RAM -- someone's smarmy term for basically battery-backed RAM or what some call "save RAM" (sometimes called SRAM, although SRAM technically means "static RAM") located physically on the cartridge. Simply put, you'd know this as "games that have save capability", e.g. Zelda 1 and 2. It doesn't *have* to be battery-backed, though -- it could really just be some normal/extra RAM -- but in most cases it's battery-backed. PRG-RAM is normally mapped to $6000-7FFF in CPU memory space, which is (obviously) a 8KB region. But the RAM chip could actually be larger than 8KB... what if it was 16KB? Well then, you'd need a way to switch between the two halves to be able to use all 16KB, wouldn't you? There are variants of the cartridge boards using MMC1 that have this capability; refer to the Wiki, specifically SOROM, SUROM, and SXROM. Otherwise, if the cartridge simply lacks PRG-RAM altogether, then bit 4 of $E000-FFFF essentially "does nothing" (from a programmer's perspective).
Mirroring -- this refers to PPU nametable mirroring (this wiki page is great, BTW). Normally, the mirroring on cartridges is defined when the cartridge is manufactured: it's dot of solder on the board that either results in horizontal or vertical mirroring. But sometimes the mapper can control this capability in real-time; the MMC1 is no exception to this. Bits 1 and 0 of the MMC1 data written to $8000-9FFF control mirroring when using the MMC1. Just see the Wiki. :-)
So there's your MMC1 primer.
This is exactly why mappers like the MMC3 (mapper 4) are actually a lot easier to comprehend. They're more logical from a programmer's perspective. MMC3 understands all 8 bits at once in a single write -- you don't have to write one bit at at a time. For PRG swapping on MMC3, you literally can do a single write to $8000 (all 8 bits are used for controlling things) with specific bits set to say "I want to change a PRG bank at address $XXXX), followed by a write to $8001 containing the PRG bank number you want.