It is currently Mon Jan 22, 2018 9:19 pm

All times are UTC - 7 hours





Post new topic Reply to topic  [ 8 posts ] 
Author Message
 Post subject: How do I set up MMC3?
PostPosted: Sun Dec 03, 2017 3:29 pm 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 137
Location: Bulgaria
So far I've been writing programs with no mapper (UNROM). I wanted to try out something with MMC3, but it turns out that simply setting up the mapper is more confusing than I thought.
Now, I do realize there's already a thread like this, but there they simply focused on fixing the problem that the person, who tried to use the mapper had, so a lot of things remain unexplained to me.
What I ask is for someone to write a step-by-step guide, that details what to put into the header, how many PRG and CHR banks are correct to use, how does the number of banks affect where to write your initial code and any special code that might be necessary to set things up. If it's simple enough, you can also add bank-switching.
I ask this, not only to get a clear idea of how to do things, but for any user, who is just starting out with this mapper and would like to know how to work with it.

Thanks!

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
PostPosted: Sun Dec 03, 2017 4:29 pm 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19498
Location: NE Indiana, USA (NTSC)
For one thing, "UNROM" isn't "no mapper". It's a mapper that switches PRG ROM A17-A14 based on the contents of a 74HC161, OR'd with CPU A14 to provide a fixed bank at $C000.

Asking "how many PRG and CHR banks are correct to use" is is like asking "how long is a piece of string?". I can provide the minimum and maximum sizes, but I'm not going to make an exhaustive list of 16-byte headers for every possible combination of PRG ROM size, CHR ROM size, CHR RAM size, WRAM size, and mirroring behavior. Cartesian products get really big really fast.

MMC3 PRG ROM can be 16 KiB to 512 KiB in powers of two.
MMC3 WRAM is either unpopulated, 8 KiB, or 8 KiB with battery.
MMC3 CHR ROM can be unpopulated, 8 KiB to 256 KiB, or 8 KiB to 64 KiB if CHR RAM is also used, in powers of two.
MMC3 CHR RAM is either unpopulated, 8 KiB, or 32 KiB. At least one of CHR ROM or CHR RAM must be populated. A battery is not available.
MMC3 nametable mirroring can be either normal (switchable H/V) or based on the bit 7 of the CHR ROM bank (which allows oddball things like 1-screen, diagonal, or L-shaped mirroring).
TQROM rule: If a board has both CHR ROM and CHR RAM, then CHR ROM is limited to 64 KiB, and the more flexible mirroring is not available. A board containing both CHR RAM and WRAM doesn't exist officially. If it did, it would probably be as expensive as Koei's SOROM boards, as it'd need four memories.

The iNES mapper for MMC3 depends on the chosen options.

  • CHR ROM and CHR RAM: mapper 119 (byte 6: $7x, byte 7: $7x)
  • Oddball mirroring: mapper 118 (byte 6: $6x, byte 7: $7x)
  • Neither: mapper 4 (byte 6: 4x, byte 7: $0x)

Choose one combination of the above, and I can give you the remainder of the correct header for this particular combination.

In all cases, startup code must be in the very last 8 KiB bank of PRG ROM, which is fixed to $E000-$FFFF.


Top
 Profile  
 
PostPosted: Sun Dec 03, 2017 5:23 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1058
Your total PRG ROM size has to be less than or equal to 512KB. Valid values in the header for PRG banks are 1, 2, 4, 8, 16, 32. The mapper supports 512KB max for PRG, the ROM header specifies how many 16 KB banks there will be. 512/16=32. It should be a power of 2 because physical ROMs are.

Your total CHR ROM size has to be less than or equal to 256KB. Valid values in the header for PRG banks are 1, 2, 4, 8, 16, 32. The mapper supports 256KB max for PRG, the ROM header specifies how many 8KB banks there will be. 256/8=32. It should be a power of 2 because physical ROMs are.

Edit: 0 is valid too if you want CHR RAM.

Your reset label (and vectors) need to be in the last 8KB of your PRG data. MMC3 divides your PRG data into 8KB segments, but only the last 8KB segment (bank) is guaranteed a position on power on so reset code needs to be there. The last 8KB also needs to be mapped to $E000-$FFFF.

Disable IRQs by writing any value to $E000 after reset. (You can reenable them later when you learn about them.) To set vertical mirroring, write #$00 to $A000. To set horizontal mirroring, write #$01 to $A000. You can change this at any time, but definitely set it up to something known at the start.

Writing a value to $8000 affects how the CHR and PRG banks are arranged, as well as setting which bank a write to $8001 will change. The highest 2 bits written control how the CHR and PRG banks are arranged, the lowest 3 bits control which bank a write to $8001 will change.

For now, let's assume the highest bits are all zero. Writing #$06 to $8000 will say, "I want a write to $8001 to change which bank is at $8000-$9FFF now." Writing a 0 to $8001 will then map the first 8KB of your PRG data to $8000-$9FFF. Write a #$07 to $8000 will say, "I want a write to $8001 to change which bank is at $A000-$BFFF now." Write a 1 to $8001 will then map the second 8KB of your PRG data to $A000-$BFFF.

$8000 and $8001 are a bit like the $2006 and $2007 pair, except they can be written at any time and you write the even one once per value instead of twice. First you set what you want to change with the even register, then write the value to the odd register.

So say you're changing from a 32 KB NROM game with 8KB CHR and horizontal mirroring to MMC3.
Code:
.org $E000
reset:
sta $E000;Disable IRQs.
lda #$01
sta $A000;Horizontal mirroring

ldx #$00;Not specifically MMC3, but good to do early
stx $2000; Disable NMI
stx $2001; Disable Rendering

lda #$06;We want to change $8000-$9FFF to what it was when we were 32KB NROM
sta $8000;We'll now change $8000-$9FFF when we write to $8001
lda #0
sta $8001;Now the first 8KB segment of PRG is at $8000-$9FFF just like it was in the 32KB NROM version of the game.
;Now we need to make sure $A000-$BFFF is in the right place
lda #$07
sta $8000;We'll now change $A000-$BFFF when we write to $8001
lda #1
sta $8001;Now the second 8KB segment of PRG is at $A000-$BFFF just like it was in the 32KB NROM version of the game.

;$E000-$FFFF was already in the last 8KB of PRG, and that's guaranteed for MMC3.
;$C000-$DFFF is also fine. The high bits being clear when we last wrote to $8000 guarantee this.

;Done with PRG!
lda #0; Next write to $8001 will now change the 2KB at $0000-$07FF
sta $8000
lda #0
sta $8001;We want the first 2KB of our CHR there, so 0.

lda #1;Similar drill. Next write to $8001 will now change the 2KB at $0800-$0FFF
sta $8000
;This is the part that gets a bit confusing! We're selecting the start of a 1KB segment our CHR starts
;Odd numbers essentially become the even number below them.
;Since 0 is the first 1KB, and 1 is the second 1KB, and both were already mapped by changing a 2KB segment
;2 is the proper value for the next 2KB segment.
lda #2
sta $8001

lda #2
sta $8000;Next write to $8001 will now change 1KB at $1000-$13FF
lda #4;3 was the second part of the 2KB segment from the write above. So 4...
sta $8001

lda #3
sta $8000;Next write to $8001 will now change 1KB at $1400-$17FF
lda #5;Since we're writing 1KB segments now, odd numbers are valid.
sta $8001

lda #4
sta $8000;Next write to $8001 will now change 1KB at $1800-$1BFF
lda #6
sta $8001

lda #5
sta $8000;Next write to $8001 will now change 1KB at $1C00-$1FFF
lda #7
sta $8001

Hopefully with the above, the chart on the wiki will make more sense. The way it describes CHR banks is pretty technical. But it's basically:
When writing a 1KB CHR bank (2, 3, 4, or 5 was last written to $8000), the value written to $8001 directly corresponds to 1KB in CHR. 0 is first 1KB of CHR, 1 is second 1KB of CHR, 2 is third KB of CHR etc.
When writing a 2KB CHR bank, (0 or 1 was last written to $8000) write the 1KB segment you want to start on. (But... one is subtracted from odd values. So you can only start at 0, 2, 4 etc.)

The value written to $8001 for PRG chooses which 8KB segment, the value written to $8001 for CHR chooses which 1KB segment, basically.

As for how else to setup, it would be helpful to know what assembler you used to build your previous ROMs.

Edit2: As far how does the number of banks affect where to write your code. Hmm...For CHR, so long as you only add banks after the banks that are there, nothing in your code really needs to change to add CHR banks.

PRG gets a little trickier because the very last PRG bank is the fixed bank (where your reset code and vectors need to be.) If you add PRG banks after the last one, you need to move that stuff too. If you add PRG banks before the first one, your writes to $8001 all need to be offset.

If your 8KB banks in order are A, B, C, and D. 0 refers to A, 1, refers to B, 2 refers to C, 3 refers to D. If you add E before A, 0 now refers to E... so any writes you had that wanted to swap in A will now swap in E.

So ideally you add banks before your last two banks (which are fixed at specific places), but after all the other banks.

If you find any of this confusing, by all means ask! This is a general answer, but feel free to ask for a specific one.

Edit3: Swapped mirroring whoops! Changed less than to less than or equal to while I'm here...

_________________
https://kasumi.itch.io/indivisible


Last edited by Kasumi on Mon Dec 04, 2017 3:48 am, edited 3 times in total.

Top
 Profile  
 
PostPosted: Mon Dec 04, 2017 2:36 am 
Offline
User avatar

Joined: Fri Nov 12, 2004 2:49 pm
Posts: 7350
Location: Chexbres, VD, Switzerland
tepples wrote:
MMC3 CHR RAM is either unpopulated, 8 KiB, or 32 KiB. At least one of CHR ROM or CHR RAM must be populated. A battery is not available.

Didn't know it could have 32kb of CHR-RAM. Well obviously it can, but I don't think it's Nintendo-standard to do this.
Same for battery, definitely not used on CHR-RAMs by Nintendo-standard carts, but technically nothing prevents one to do exactly that.


Top
 Profile  
 
PostPosted: Mon Dec 04, 2017 9:19 am 
Offline

Joined: Sun Sep 19, 2004 11:12 pm
Posts: 19498
Location: NE Indiana, USA (NTSC)
Bregalad wrote:
tepples wrote:
MMC3 CHR RAM is either unpopulated, 8 KiB, or 32 KiB.

Didn't know it could have 32kb of CHR-RAM. Well obviously it can, but I don't think it's Nintendo-standard to do this.

One unlicensed game published by Retrotainment Games in 2016 uses (a clone of) MMC3 with 32 KiB of CHR RAM. I posted an explanation of how it lays out CHR RAM.


Top
 Profile  
 
PostPosted: Mon Dec 04, 2017 1:19 pm 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 137
Location: Bulgaria
Wow, thanks for your replies! Kasumi's guide was especially detailed and it cleared up a lot of stuff for me. I still have a few questions, though.

I am using ASM6 to compile my programs, and this made me wonder... how do you define the different banks that aren't mapped to any particular address? Given that there are gonna be a lot of PRG banks that simply won't fit into the address range, how do I declare them? NESASM3 has a .bank command, but ASM6 doesn't. What do I do here?

Also, when bankswitching a certain area, where code is being run from, will the CPU continue reading from the address it was on, when the bankswitch happen? For example, let's say I'm running some code from a bank in $8000-$9FFF, I bankswitch that area and the instruction that causes the switch happens at $817F. After the switch, will the CPU continue reading from $8180 or will it somehow reset to $8000?

Thanks!

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
PostPosted: Mon Dec 04, 2017 8:42 pm 
Offline
User avatar

Joined: Wed Apr 02, 2008 2:09 pm
Posts: 1058
I guess the simplest answer to your address question is there are two types of address.

Even with NROM, the first byte after your header could go into $8000 in NES Memory, or it could go into $C000 in NES memory.

The NES doesn't really care where things are in your rom, only where they'll be mapped in NES' own memory.

What .org does is specify where things should be mapped in NES memory for that particular block of code. (I'll explain why I said should instead of will later.)

Code:
   .org $8000
main:;endless loop
   jmp main


gets assembled as $4C (jmp) $00 $80

Code:
   .org $A000
main:;endless loop
   jmp main


gets assembled as $4C (jmp) $00 $A0

Without the .org, jmp main couldn't work. There'd be no way for the assembler to know WHERE to put the jump. (Well, it could use some arbitrary starting location to assemble bytes at.)

Now, say you have the second example (.org $A000) in your first bank (bank 0).

And you did this:
Code:
   lda #$06;
   sta $8000;We'll now change $8000-$9FFF when we write to $8001
   lda #0
   sta $8001;Now the first 8KB segment of PRG is at $8000-$9FFF
   jmp main


You've just put code designed to be at $A000 at $8000. So that jmp main would jmp to $A000, but that's not where you just bank swapped in the infinite loop bank. So your program would probably crash. If you did this instead:
Code:
   lda #$06;
   sta $8000;We'll now change $8000-$9FFF when we write to $8001
   lda #0
   sta $8001;Now the first 8KB segment of PRG is at $8000-$9FFF
   jmp $8000

You would jmp to where you put the main loop in memory. But then immediately after, it would still jmp to $A000 and crash. ("main" is replaced with $A000 when assembled)

This is why I said "where things should be mapped" rather than "will be mapped." You can totally put blocks of code in memory where they're not designed to be, but you shouldn't.

I don't use much asm6, but as far as I can tell, you use .base and .pad

For example:
Code:
;bank 0
   .base $8000;Assemble the next byte to be mapped at $8000
   nop;Now the next byte will be mapped at $8001
   .pad $A000;Fills from $8001 to $9FFF. (Will assemble the next byte at $A000)
;bank 1
   .base $A000
   ;code here
   .pad $C000
   
;banks 2 and 3
   .base $8000
   ;code here
   .pad $C000
;bank 4
   .base $A000
   ;code here
   .pad $C000
;bank 5
   .base $A000
   ;code here
   .pad $C000
;bank 6
   .base $8000
   ;code here
   .pad $A000
;bank 7
   .base $8000
   ;code here
   .pad $A000

.org is usually equivalent to .pad in asm6. The above demonstrates a few things. That it's possible to have two $A000-$BFFF banks in a row. That it's possible to have two $8000-$9FFF banks in a row. That it's possible to define two 8KB banks together as one 16KB bank so long as their addresses would have been contiguous. ("banks 2 and 3") A lot of people treat MMC3 as if it has 16KB banking and just swap both 8KB banks every time.
Quote:
will the CPU continue reading from the address it was on, when the bankswitch happen?

The simplest answer is the bank switch is basically completely invisible to the CPU. So yes, it will totally still try to run the instruction at the address following the current instruction. The bank switch might change what's there, of course. ;) (It wouldn't specifically be one byte greater, though. Different instructions are different sizes.)

Don't do this, but what follows is a detailed example:

Say this is bank zero.
Code:
   .org $8000
   lda #$06;
   sta $8000;We'll now change $8000-$9FFF when we write to $8001
   lda #1
   sta $8001;Now the second 8KB segment of PRG is at $8000-$9FFF
   jmp $8000

And this is bank one
Code:
   .org $8000
   nop;a byte for lda
   nop;a byte for #$06
   
   nop;a byte for sta
   nop;a byte for $00
   nop;a byte for $80
   
   nop;a byte for lda
   nop;a byte for #1
   
   nop;a byte for sta
   nop;a byte for $01
   nop;a byte for $80
   ;We are now at the same address
   ;as jmp $8000 in bank zero
   lda #$00;disable rendering
   sta $2000
   sta $2001

If the code was running at $8000 in bank zero:
Mapped at $800A is jmp $8000. But at $8007 is sta $8001 that changes what's mapped at $800A. (Really, what's mapped in the whole region from $8000-$9FFF)
So it continues from $800A in bank 1 rather than bank 0. In other words, rendering will get disabled, rather than looping by jumping back to $8000.

I've attached a test ROM that demonstrates this. (Note that it doesn't do a lot of initialization it probably should, but it's good enough to show the concept in the current version of FCEUX.)

Put a breakpoint for execution at $8000, reset or power on, then step through it. You can see the next instruction change.
Attachment:
mmc3bankswaptest.nes [32.02 KiB]
Downloaded 14 times


I'd recommend never swapping the bank you're currently in, though. (At least not with MMC3) A good practice is having your main loop in $E000-$FFF9. You can swap in a data bank into $8000-$9FFF from there. You can swap in a bank with subroutines to read that data in $A000-$BFFF. You jsr to the subroutine, when it returns you can swap both banks again from the safety of the fixed bank.

Even if some of this goes over your head, so long as you understand the recommendation, how to "make banks" in asm6, and the "two types of address" you're fine to continue. The bank switching the bank you're in question is more theoretical than practical. At least with MMC3. AxROM swaps all of $8000-$FFFF at once. Even then, I'd probably opt for code in RAM that does the swap and jmps to the right location in the new bank.

_________________
https://kasumi.itch.io/indivisible


Top
 Profile  
 
PostPosted: Tue Dec 05, 2017 4:02 pm 
Offline
Formerly AlienX
User avatar

Joined: Fri Apr 18, 2014 7:41 am
Posts: 137
Location: Bulgaria
Thank you very much! That really cleared up a lot. I will now try to compile a program, using MMC3. I'll ask, if I encounter any issues.

_________________
Greetings! I'm That Bio Mechanical Dude and I like creating various stuff like movies, games and of course chiptunes!
You can check out my YouTube Channel.
You can also follow me on Twitter.


Top
 Profile  
 
Display posts from previous:  Sort by  
Post new topic Reply to topic  [ 8 posts ] 

All times are UTC - 7 hours


Who is online

Users browsing this forum: toggle switch and 7 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