It is currently Thu Dec 13, 2018 5:17 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 42 posts ]  Go to page Previous  1, 2, 3
Author Message
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 12:49 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3724
Location: Mountain View, CA
So MMC1... it's a complicated mapper because of several different cartridge (board/PCB) variations, all of which behave a little differently, and offer different capabilities. I'll try to speak generally about the MMC1, e.g. what's most commonly used, but if you're doing reverse-engineering of an MMC1 game, you really have to know what cartridge/board type it uses (BootGod's database can tell you this).

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:
lda #$80
sta $8000

In English: load accumulator with value $80, then write that value to memory location $8000. This code should make you ask at least 2 questions:

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:
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)

Here we learn that the first 4 bits (bits 3 through 0) written to an address in the $E000-FFFF range control which 16KB PRG bank we want, and that the 5th bit (bit 4) controls something called PRG-RAM. This sounds great... but what is PRG-RAM? Does this bit matter? Should we be concerned? I'll talk about PRG-RAM at the very end of this post, because it's an (IMO) badly-phrased term that I've never liked but is easy to understand.

So we want PRG bank 5? Well that seems pretty simple then!

Code:
lda #5
sta $e000
lsr
sta $e000
lsr
sta $e000
lsr
sta $e000
lsr
sta $e000

After the 5th and final write to $E000, the PRG swap happens. As I said, it's instantaneous. But what was that I said earlier about "the final write is the one that ultimately decides what happens"? What did I mean by that? Well, the above code could also be this and it'd do the exact same thing:

Code:
lda #5
sta $8123
lsr
sta $a493
lsr
sta $9685
lsr
sta $c184
lsr
sta $ffff

This is because the 5-bit value the MMC1 internally holds is essentially always being updated when you write to an MMC1 register -- but the address of the 5th and final write is what dictates what happens. If the sta $ffff had instead been sta $a000, then we would have actually tried to swap in a CHR-ROM bank instead!

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.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 9:03 am 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
koitsu wrote:
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.
Rephrasing the highlighted part: Incorrect... see edit below This would be PRG bank 7 for 128KB games; 256KB games make the "fixed" bank switchable too so you are able to switch your "fixed" bank between PRG bank 7 or PRG bank 15. :)

That is quite a post koitsu! Don't even have time to read it all right now. Looking forward to immersing myself in it though. :)


edit: tepples is so helpful... I retract my statement above because he reminded me that I'm using a 512KB game and when using a 512 KB game:
tepples wrote:
In 256K MMC1 games using fixed $C000, the fixed bank is always 15. Only in 512K games is there a choice between bank 15 in $C000 (with any of 0-15 in $8000) and bank 31 in $C000 (with any of 16-31 in $8000).
512 K games provide so much space... I'm sorry koitsu for messing up in my reply.
edit2: Can both $8000 and $C000 really both hold bank 15 or bank 31? If so, is there any way that could be helpful? :)


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 10:17 am 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 152
Location: Colorado
Thanks Guys, I really feel like I'm starting to get this.

koitsu that post was really helpful. I feel like you really filled in some of the gaps I had in my understanding.

Also, whoever suggested Nova The Squirrel as a better starting point, thank you also. I've been going through that code today, and this is way better commented and documented than the game I had been going through.

Another question that I have from going though Nova:
In rainwarrior's example he had a .cfg file that laid out the memory and segments. I don't see anything like that in Nova. He uses a bunch of segments like
Code:
.segment "PRG8"

which I assume defines a program bank he can swap in, but I'm not sure if that's something the assembler just understands, or if he had to define his memory layout somewhere and I just haven't located where yet.

Thanks again everyone

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 10:44 am 
Offline

Joined: Sun Apr 13, 2008 11:12 am
Posts: 7818
Location: Seattle
battagline wrote:
In rainwarrior's example he had a .cfg file that laid out the memory and segments. I don't see anything like that in Nova. He uses a bunch of segments like
Nova The Squirrel's linker configuration file is called "nova.x" for some reason.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 11:23 am 
Offline
User avatar

Joined: Wed Sep 05, 2018 11:13 am
Posts: 152
Location: Colorado
lidnariq wrote:
battagline wrote:
In rainwarrior's example he had a .cfg file that laid out the memory and segments. I don't see anything like that in Nova. He uses a bunch of segments like
Nova The Squirrel's linker configuration file is called "nova.x" for some reason.


Ugh... I should have looked in the batch file. Now I'm seeing the -C nova.x

Thanks... sorry I missed that

_________________
A few of my web games
https://www.embed.com
Or if you're bored at work
https://www.classicsolitaire.com


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 11:57 am 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3724
Location: Mountain View, CA
unregistered wrote:
edit2: Can both $8000 and $C000 really both hold bank 15 or bank 31? If so, is there any way that could be helpful? :)

I did think about this when writing the post, but chose to omit it because I felt all it did was confuse the reader. I did not want to get into "edge case" scenarios, I wanted to explain general use and understanding of something on a general level, combined with code, and "why" that code works/what it does.

Anyway -- if you really want to know, then IMO, it's is a question for the hardware folks (lidnariq and others). Simply: for mappers like the MMC1 and as described above in my example (assume 128KB PRG game and that PRG bank 7 is hard-wired to $C000): could you also have PRG bank 7 mapped to $8000-BFFF simultaneously? Would there be any weird side-effects from this operationally, e.g. two sets of address lines both pointing to the same mask ROM region/area thus causing some oddities during reads from said mask ROM?


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 12:03 pm 
Offline
User avatar

Joined: Sun Jan 22, 2012 12:03 pm
Posts: 7015
Location: Canada
Yes, you can have the same bank mapped at two addresses at once, that is not a problem at all for the hardware, but it's not normally very useful to do so for PRG.

For CHR it's sometimes useful to have the same tiles, e.g. for sprite and background at the same time.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 1:39 pm 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
Thank you rainwarrior! :D tepples agrees:
tepples wrote:
On MMC1 and pretty much all other NES mappers, the switchable window can be set to the same bank as the fixed bank. On UNROM you can do 7 and 7; on UOROM you can do 15 and 15. On MMC3 you can do 62, 63, 62, 63. But it's not true everywhere, particularly on Game Boy.


rainwarrior, when using CHR-RAM, I lost all of the CHR-ROM banks and so, loading the same banks is kind of pointless for me, I guess. :) edit: Just checked the nesdev wiki and learned again that there are CHR-RAM banks when using 4k size CHR-RAM so, in my message to you here, I made another mistake today. :oops:


Last edited by unregistered on Thu Sep 20, 2018 2:13 pm, edited 1 time in total.

Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 1:49 pm 
Offline
User avatar

Joined: Sun Sep 19, 2004 9:28 pm
Posts: 3724
Location: Mountain View, CA
rainwarrior wrote:
Yes, you can have the same bank mapped at two addresses at once, that is not a problem at all for the hardware, but it's not normally very useful to do so for PRG.

For CHR it's sometimes useful to have the same tiles, e.g. for sprite and background at the same time.

Awesome -- thank you! (And excellent point about CHR!)


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Thu Sep 20, 2018 5:33 pm 
Offline
User avatar

Joined: Tue Jun 24, 2008 8:38 pm
Posts: 2129
Location: Fukuoka, Japan
For a simple menu where you would have a pointer in front of the selection, having a bank for that pointer only would be a waste so in that case I would use the same bank for the bg tiles and the necessary sprites. It's quite convenient to be able to do that. Works with mmc3, would have been surprised if not possible with mmc1 but you never know until you try, I guess. Now we know :)


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Fri Sep 21, 2018 12:22 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7604
Location: Chexbres, VD, Switzerland
Understanding the MMC1 is slightly more complex than other mappers because you have to deal with the loading shift register; however it's not an insurmontable complexity, as a programmer you can just use 4 subroutines to write to the 4 internal registers and use that almost transparently, so that you can use them like you'd use normal registers on another mapper. The last worry about it is the possibility than an interrupt occurs during register writes, but it is possible to have writing routines that deal with that problem entirely.

The thing is that it is pretmature to discuss about SOROM, SXROM and whathever, first he should understand the normal usage of MMC1 and not (yet) it's extensions. Once the concept is clear, the extensions are pretty simple to understand, too.


Top
 Profile  
 
 Post subject: Re: Learning MMC1
PostPosted: Fri Sep 21, 2018 2:07 pm 
Offline
User avatar

Joined: Thu Apr 23, 2009 11:21 pm
Posts: 940
Location: cypress, texas
Bregalad wrote:
The thing is that it is pretmature to discuss about SOROM, SXROM and whathever, first he should understand the normal usage of MMC1 and not (yet) it's extensions. Once the concept is clear, the extensions are pretty simple to understand, too.
Notice: I am a newbie. But, here is my small perspective: I didn't try to be detailed about SXROM; I didn't even mention "SXROM". I just wanted him to know how cool MMC1 can be. If you wait to provide some really cool aspects until after the consumer learns about how all of the mundane features work... that's kind of boring, to me at least. Remember: this is my small perspective. :)


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 42 posts ]  Go to page Previous  1, 2, 3

All times are UTC - 7 hours


Who is online

Users browsing this forum: No registered users and 3 guests


You cannot post new topics in this forum
You cannot reply to topics in this forum
You cannot edit your posts in this forum
You cannot delete your posts in this forum
You cannot post attachments in this forum

Search for:
Jump to:  
Powered by phpBB® Forum Software © phpBB Group