Dr. Mario MMC1B and register writes

Discuss technical or other issues relating to programming the Nintendo Entertainment System, Famicom, or compatible systems. See the NESdev wiki for more information.

Moderator: Moderators

User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Dr. Mario MMC1B and register writes

Post by koitsu »

Can someone shed some light on what's actually going on below? This is taken from Dr. Mario, run under Nintendulator. The game consists of 32KBytes of PRG (total) and 32KBytes of CHR (total). According to Nestopia it's an SEROM game, using MMC1B.

The code below happens almost immediately (after waiting for PPU power-on/2 VBlanks).

The questions I have are:

1. What's with the INC $FC80? Won't this affect the PRG bank select later on (writes to $FFF0)?

2. What exactly is the mapper behaviour as a result of this? The CHR ROM page size selection makes sense, but the PRG setup doesn't. Basically I'm asking what gets swapped in/out PRG-wise and what is the PRG size chosen?

I'm going off of the information here:
http://wiki.nesdev.com/w/index.php/MMC1

Code: Select all

FC94  A2 FF     LDX #$FF                        A:80 X:00 Y:00 P:A4 SP:FD CYC: 20 SL:241
FC96  9A        TXS                             A:80 X:FF Y:00 P:A4 SP:FD CYC: 26 SL:241
FC97  EE 80 FC  INC $FC80 = D8                  A:80 X:FF Y:00 P:A4 SP:FF CYC: 32 SL:241 ; ?!?!?!?!?
FC9A  A9 10     LDA #$10                        A:80 X:FF Y:00 P:A4 SP:FF CYC: 50 SL:241 ; $10 = %00010000
FC9C  20 D5 B8  JSR $B8D5                       A:10 X:FF Y:00 P:24 SP:FF CYC: 56 SL:241
B8D5  8D FF 9F  STA $9FFF = 4F                  A:10 X:FF Y:00 P:24 SP:FD CYC: 74 SL:241 ; $9FFF = control reg
B8D8  4A        LSR A                           A:10 X:FF Y:00 P:24 SP:FD CYC: 86 SL:241 ;
B8D9  8D FF 9F  STA $9FFF = 4F                  A:08 X:FF Y:00 P:24 SP:FD CYC: 92 SL:241 ; Should result in CHR ROM
B8DC  4A        LSR A                           A:08 X:FF Y:00 P:24 SP:FD CYC:104 SL:241 ; bank mode being set to 1
B8DD  8D FF 9F  STA $9FFF = 4F                  A:04 X:FF Y:00 P:24 SP:FD CYC:110 SL:241 ; (4KByte CHR ROM swap)
B8E0  4A        LSR A                           A:04 X:FF Y:00 P:24 SP:FD CYC:122 SL:241 ;
B8E1  8D FF 9F  STA $9FFF = 4F                  A:02 X:FF Y:00 P:24 SP:FD CYC:128 SL:241 ;
B8E4  4A        LSR A                           A:02 X:FF Y:00 P:24 SP:FD CYC:140 SL:241 ;
B8E5  8D FF 9F  STA $9FFF = 4F                  A:01 X:FF Y:00 P:24 SP:FD CYC:146 SL:241 ;
B8E8  60        RTS                             A:01 X:FF Y:00 P:24 SP:FD CYC:158 SL:241
FC9F  A9 02     LDA #$02                        A:01 X:FF Y:00 P:24 SP:FF CYC:176 SL:241 ; $02 = %00000010
FCA1  20 E9 B8  JSR $B8E9                       A:02 X:FF Y:00 P:24 SP:FF CYC:182 SL:241 ;
B8E9  8D FF BF  STA $BFFF = FD                  A:02 X:FF Y:00 P:24 SP:FD CYC:200 SL:241 ; $BFFF = CHR bank 0 select
B8EC  4A        LSR A                           A:02 X:FF Y:00 P:24 SP:FD CYC:212 SL:241 ;
B8ED  8D FF BF  STA $BFFF = FD                  A:01 X:FF Y:00 P:24 SP:FD CYC:218 SL:241 ;
B8F0  4A        LSR A                           A:01 X:FF Y:00 P:24 SP:FD CYC:230 SL:241 ;
B8F1  8D FF BF  STA $BFFF = FD                  A:00 X:FF Y:00 P:27 SP:FD CYC:236 SL:241 ;
B8F4  4A        LSR A                           A:00 X:FF Y:00 P:27 SP:FD CYC:248 SL:241 ;
B8F5  8D FF BF  STA $BFFF = FD                  A:00 X:FF Y:00 P:26 SP:FD CYC:254 SL:241 ;
B8F8  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC:266 SL:241 ;
B8F9  8D FF BF  STA $BFFF = FD                  A:00 X:FF Y:00 P:26 SP:FD CYC:272 SL:241 ;
B8FC  60        RTS                             A:00 X:FF Y:00 P:26 SP:FD CYC:284 SL:241
FCA4  A9 03     LDA #$03                        A:00 X:FF Y:00 P:26 SP:FF CYC:302 SL:241 ; $03 = %00000011
FCA6  20 FD B8  JSR $B8FD                       A:03 X:FF Y:00 P:24 SP:FF CYC:308 SL:241 ;
B8FD  8D FF DF  STA $DFFF = 55                  A:03 X:FF Y:00 P:24 SP:FD CYC:326 SL:241 ; $DFFF = CHR bank 1 select
B900  4A        LSR A                           A:03 X:FF Y:00 P:24 SP:FD CYC:338 SL:241 ;
B901  8D FF DF  STA $DFFF = 55                  A:01 X:FF Y:00 P:25 SP:FD CYC:  3 SL:242 ;
B904  4A        LSR A                           A:01 X:FF Y:00 P:25 SP:FD CYC: 15 SL:242 ;
B905  8D FF DF  STA $DFFF = 55                  A:00 X:FF Y:00 P:27 SP:FD CYC: 21 SL:242 ;
B908  4A        LSR A                           A:00 X:FF Y:00 P:27 SP:FD CYC: 33 SL:242 ;
B909  8D FF DF  STA $DFFF = 55                  A:00 X:FF Y:00 P:26 SP:FD CYC: 39 SL:242 ;
B90C  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC: 51 SL:242 ;
B90D  8D FF DF  STA $DFFF = 55                  A:00 X:FF Y:00 P:26 SP:FD CYC: 57 SL:242 ;
B910  60        RTS                             A:00 X:FF Y:00 P:26 SP:FD CYC: 69 SL:242
FCA9  A9 00     LDA #$00                        A:00 X:FF Y:00 P:26 SP:FF CYC: 87 SL:242 ; $00 = %00000000
FCAB  20 11 B9  JSR $B911                       A:00 X:FF Y:00 P:26 SP:FF CYC: 93 SL:242 ;
B911  8D F0 FF  STA $FFF0 = 64                  A:00 X:FF Y:00 P:26 SP:FD CYC:111 SL:242 ; $FFF0 = PRG bank select
B914  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC:123 SL:242 ;
B915  8D F0 FF  STA $FFF0 = 64                  A:00 X:FF Y:00 P:26 SP:FD CYC:129 SL:242 ;
B918  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC:141 SL:242 ;
B919  8D F0 FF  STA $FFF0 = 64                  A:00 X:FF Y:00 P:26 SP:FD CYC:147 SL:242 ;
B91C  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC:159 SL:242 ;
B91D  8D F0 FF  STA $FFF0 = 64                  A:00 X:FF Y:00 P:26 SP:FD CYC:165 SL:242 ;
B920  4A        LSR A                           A:00 X:FF Y:00 P:26 SP:FD CYC:177 SL:242 ;
B921  8D F0 FF  STA $FFF0 = 64                  A:00 X:FF Y:00 P:26 SP:FD CYC:183 SL:242 ;
B924  60        RTS                             A:00 X:FF Y:00 P:26 SP:FD CYC:195 SL:242
FCAE  4C 00 80  JMP $8000                       A:00 X:FF Y:00 P:26 SP:FF CYC:213 SL:242
User avatar
Dwedit
Posts: 4921
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

A read-modify-write instruction writes to memory twice, first the original value, then the modified value. And from what I hear, the MMC1 accepts the first write of a Read-Modify-Write, and ignores the second write.
So it would happen like Bill & Ted: Accept the value read from ROM as the write, and ignore the second write of value+1.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
3gengames
Formerly 65024U
Posts: 2284
Joined: Sat Mar 27, 2010 12:57 pm

Post by 3gengames »

Yeah, seems they are resetting it, while everything else seems to just be writing each register. Not much else there. I have an MMC1 test ROM that doesn't do any INC $(Greater than 7E but less than FF) but can easily be modified if you'd want it to mess with real hardare, it's somewhere on the forums. But yeah, it accepts the first write.
lidnariq
Posts: 11429
Joined: Sun Apr 13, 2008 11:12 am

Re: Dr. Mario MMC1B and register writes

Post by lidnariq »

koitsu wrote:Basically I'm asking what gets swapped in/out PRG-wise and what is the PRG size chosen?
Because the game's only 32kB, nothing's swapped.

It does seem fragile, though, since the initial INC $FC80 does not seem to set the register value for what's mapped into $8000-$bfff until after the write to the control register.

Writing 0 to the PRG bank select does seem entirely superfluous.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post by koitsu »

Thanks guys. Yep, I forgot that INC was RMW, however that doesn't really provide enough insights to know what's going on with the PRG "stuff".

What effectively this means is that the MMC1 load register is always "off by one". Let me try to explain:

Code: Select all

;
; Focusing on MMC1 load registers, control register, and PRG bank register
;
; MMC1 load register    = %????????
; MMC1 control register = %?????
;
FC97  EE 80 FC  INC $FC80 = D8                  A:80 X:FF Y:00 P:A4 SP:FF CYC: 32 SL:241
;
; INC is an RMW instruction.  MMC1 ignores 2nd write.
; $FC80 would correlate with the load register at this point.
; $D8 gets written to MMC1 load register; we only care about bit 0.
; $D8 = %11011000, so at this point:
;
; MMC1 load register    = %???????0
; MMC1 control register = %?????
;
FC9A  A9 10     LDA #$10                        A:80 X:FF Y:00 P:A4 SP:FF CYC: 50 SL:241
FC9C  20 D5 B8  JSR $B8D5                       A:10 X:FF Y:00 P:24 SP:FF CYC: 56 SL:241 
B8D5  8D FF 9F  STA $9FFF = 4F                  A:10 X:FF Y:00 P:24 SP:FD CYC: 74 SL:241
;
; MMC1 load register    = %??????00
; MMC1 control register = %?????
;
B8D8  4A        LSR A                           A:10 X:FF Y:00 P:24 SP:FD CYC: 86 SL:241
B8D9  8D FF 9F  STA $9FFF = 4F                  A:08 X:FF Y:00 P:24 SP:FD CYC: 92 SL:241
;
; MMC1 load register    = %?????000
; MMC1 control register = %?????
;
B8DC  4A        LSR A                           A:08 X:FF Y:00 P:24 SP:FD CYC:104 SL:241
B8DD  8D FF 9F  STA $9FFF = 4F                  A:04 X:FF Y:00 P:24 SP:FD CYC:110 SL:241
;
; MMC1 load register    = %????0000
; MMC1 control register = %?????
;
B8E0  4A        LSR A                           A:04 X:FF Y:00 P:24 SP:FD CYC:122 SL:241
B8E1  8D FF 9F  STA $9FFF = 4F                  A:02 X:FF Y:00 P:24 SP:FD CYC:128 SL:241
;
; At this point the MMC1 load register contents, since 5
; writes have been issued, should result in the MMC1
; control register being internally set.  Thus:
;
; MMC1 load register    = %???00000
; MMC1 control register = %00000
;
; Bit 1,0 = mirroring         = one-screen, lower bank
; Bit 3,2 = PRG ROM bank mode = switch 32KBytes at $8000 ignoring low bit of bank #
; Bit 4   = CHR ROM bank mode = switch 8KBytes at a time
;
So in effect, the LDA #$10 + JSR $B8D5 wouldn't actually result in the MMC1 control register containing %10000 -- it would instead contain %00000.

After the next 5 writes to the load register, the result there would be %????1, and based on looking at the code, that's not very logical. Furthermore, I believe Dr. Mario uses 4KByte CHR-ROM switching, not 8KByte, but I could be wrong there. No emulator gives in-depth mapper debugging details so I really can't tell.

So can someone shed some light on this? This is incredibly confusing and has been absolutely the #1 reason I have avoided MMC1 like the plague over the years.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Re: Dr. Mario MMC1B and register writes

Post by koitsu »

lidnariq wrote:
koitsu wrote:Basically I'm asking what gets swapped in/out PRG-wise and what is the PRG size chosen?
Because the game's only 32kB, nothing's swapped.

It does seem fragile, though, since the initial INC $FC80 does not seem to set the register value for what's mapped into $8000-$bfff until after the write to the control register.

Writing 0 to the PRG bank select does seem entirely superfluous.
Hrm, so I guess in the case of MMC1 games which only contain 32KBytes of PRG (total), PRG-ROM bank swapping doesn't actually change anything since as you say PRG-ROM bank 0 and bank 1 are respectively hard-wired to $8000-BFFF and $C000-FFFF? That's more of an emulator-oriented question I suppose.

I wonder if there are any MMC1 games which have 32KBytes of PRG but would put PRG bank 1 into $8000-BFFF, meaning both $8000-BFFF and $C000-FFFF would point to PRG bank 1.
User avatar
Dwedit
Posts: 4921
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

I checked the MMC1 docs, it is possible to have 0,0 or F,F mapped into the 16k blocks. Even if there's 32k of total PRG, you can still have a 0,0 or 1,1 bank setup. Can't get 1,0 though.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

Yeah MMC1 is a crazy. I did some pretty heavy REing of the thing a couple months back.

First off you confusion of writing 00000 vs 10000 is because when you reset the MMC1 (write D7 == 1) fully resets the MMC1 with NOTHING in the register. It doesn't matter what D0 is on that write it's effectively not written. So the first write with D7 = 1 resets the register, then it takes *5 additional* writes to the control register for it to get transfered to one of the 4 regs. It looks like you're considering the reset write as the first write so it would only take 4 more writes which is not true.

Also I did some looking around on the 32KB situation previously. And all the 32KB PRG games appeared to be on SEROM which the ROMs A14 isn't tied to the MMC1. It's controlled directly by the NES (connected to the cart edge). So the MMC1 can't do jack with any SEROM game's PRG (which I assumed is all the 32KB prg games).

I wonder if there are any MMC1 games which have 32KBytes of PRG but would put PRG bank 1 into $8000-BFFF, meaning both $8000-BFFF and $C000-FFFF would point to PRG bank 1.
I don't quite follow what Dwedit is saying with his X,X format of notation. But for your question koitsu IF there were a game on say a SLROM board where the MMC1 actually controlled the ROM's A14 you could have the MMC1 in 16KB mode with $C000-FFFF fixed to bank 1, and then set the PRG reg (REG #3) to bank 1. That would end up putting the last bank (bank 1) into both $8000-BFFF AND $C000-FFFF. However I like I said before I don't know of any 32KB games that are on a SLROM board or similar. If you were making the game and selecting board set up you could certainly do that.

EDIT: Oh I get Dwedit's notation now 1,0 correlates to $8000 -> bank 1, $C000 -> bank 0

Yes that is correct. In 16KB mode if you fix $8000-BFFF it's always fixed to bank 0. If you fix $C000-FFFF it's always fixed to the last bank.
User avatar
koitsu
Posts: 4201
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post by koitsu »

Thanks guys -- you both have answered my questions. Now I actually understand.

I'm left wondering (well, pondering rather) why Nintendo decided to use INC $FC80 as a cheap way of writing LDA #$80 / STA $8000 (or any address from $8000 to $FFFF for that matter).

Colleagues of mine theorise that possibly it's some kind of anti-piracy measure where pirate mappers or pirate Famicoms might not handle that situation correctly -- though value $D8 (first write) has bit 7 set, as does $D9 (2nd write). So possibly $FC80 was chosen for a specific reason? I mean, the guy could have picked $FC94 (value $A2) and accomplished the same thing. Maybe on some pirated consoles that's a unique register location for the CPU or console itself? Really not sure. This is stuff we'll probably never know without getting in touch with whoever did the actual code at Nintendo, and I doubt that person even remembers why they did that. I know that, at least in the case of the SFC/SNES and copiers (specifically the Super Magicom), some games actually wrote to memory-mapped registers that were specific to the SMC, causing the copier to nuke sector 0 on an inserted floppy disk, solely as a form of anti-piracy. See registers $C000 to $C007, which correlate with registers per the MCS3201 floppy controller. Naughty..... :D

Another theory is that someone noticed (shortly before game launch) that the MMC1 wasn't being reset before registers were being used, and they didn't have time to reassemble the entire game to add LDA / STA statements (5 bytes, thus would require reassembly due to branch offsets being completely wrong), so they nuked some 3-byte code at the start and turned it into INC $FC80 to solve that problem. Again: doubt we'll ever know.

As for the PRG bank 0 selection being superfluous -- yeah, agreed. My guess based on looking at the code is that someone at Nintendo simply copy-pasted code from an existing MMC1 game that did have more than 32KBytes of PRG and simply set the bank to 0.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

If Dr. Mario uses 4 KiB pages, does it ever use 4 KiB pages from different 8 KiB banks? If not, it could have been CNROM.
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

I see nothing special here - the INC instruction is used to reset the shift registers, and there is no distinction between writes to R0,R1,R2 or R3 when bit 7 is set - it just reset the shift registers.

Almost all MMC1 games I've ever seen does this - it's really common and definitely no anti-piracy measure or whathever. Using INC instead of LDA/STA simply saves 2 bytes.

I think that when people doesn't understand something in a commercial game they will say it's an anti-piracy measure, just because they have nothing else to say.....

@Tepples : In fact DrMario could probably have been NROM without any problem, it's just that it didn't cost much more to add a 32k CHR and a MMC1 since it was released late in NES' life.

PS : I haven't checked, but I'm pretty sure PRG-A14 on SEROM should be controlled by the MMC1, allowing you to get a 0,0 or 1,1 configuration.
Useless, lumbering half-wits don't scare us.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

I agree we'll never know for sure but here's my take on the matter. As it's been said copy protection make more sense for something like INC $FF which is done by some games. While not all games may have implemented it properly (maybe just due to bad knowledge) I believe the MMC1 was implemented like this for some anti piracy. The main reason I conclude that is because the MMC1 had ADDED logic to prevent successive writes. It's not like an optimization caused this phenomenon. Additionally if you never toggle M2 the MMC1 WILL acknowledge subsequent writes, it's effectively left in the 'allow writes' state.

Bregalad wrote:PS : I haven't checked, but I'm pretty sure PRG-A14 on SEROM should be controlled by the MMC1, allowing you to get a 0,0 or 1,1 configuration.
Maybe your Euro carts are different but I have checked and that's not how it is on my SEROM boards. The MMC1 PRG A14 ROM output is floating.
tepples
Posts: 22705
Joined: Sun Sep 19, 2004 11:12 pm
Location: NE Indiana, USA (NTSC)
Contact:

Post by tepples »

Bregalad wrote:@Tepples : In fact DrMario could probably have been NROM without any problem
Not with the cut scenes. Beat level 05 of single-player on high speed. If you rip out the title screen and cut scenes, much as the pirate multicart version of Tengen's Tetяis ripped out the title screen, then yes, you might be able to fit it in NROM.
it's just that it didn't cost much more to add a 32k CHR and a MMC1 since it was released late in NES' life.
MMC1 cheaper than a 74HC161? Or perhaps they just stopped making CNROM boards by that time.
PS : I haven't checked, but I'm pretty sure PRG-A14 on SEROM should be controlled by the MMC1, allowing you to get a 0,0 or 1,1 configuration.
From the pictures of the Tetris and Dr. Mario carts on NesCartDB, A14 on the PRG ROM appears to go into a via behind the chip. Is anyone willing to sacrifice an SEROM board so that we can get a clear picture of where A14 on the PRG ROM comes from?
Last edited by tepples on Wed May 30, 2012 9:32 am, edited 1 time in total.
User avatar
infiniteneslives
Posts: 2104
Joined: Mon Apr 04, 2011 11:49 am
Location: WhereverIparkIt, USA
Contact:

Post by infiniteneslives »

tepples wrote: Is anyone willing to sacrifice an SEROM board so that we can get a clear picture of where A14 on the PRG ROM comes from?
I've already done it. You don't even need to sacrifice the board if you don't believe me and want to prove it to yourself. All you need is a multimeter and you can do some non-destructive testing.

The PRG ROMs A14 is connected to the 72 pin connector on SEROM boards.

EDIT: It's not concrete evidence but if you assume it's a two layer board you can deduce that pin 1 of the MMC1 (PRG A14 output) is floating. Based on pictures on the DB
User avatar
Bregalad
Posts: 8055
Joined: Fri Nov 12, 2004 2:49 pm
Location: Divonne-les-bains, France

Post by Bregalad »

OK - I'll take your word, and therefore it's impossible to get the "0,0" and "1,1" configurations.
Euro boards are the same as the US boards - only the Famicom boards are different.

This is weird because CHR-RAM based MMC1 carts lets you banskwtich the CHR-RAM in two 4k banks even if this makes few sense (similar to bankswitch 32kb PRG in two 16k banks).
Useless, lumbering half-wits don't scare us.
Post Reply