Page 1 of 2

About the mapper

Posted: Wed Apr 06, 2005 2:53 am
by xian106
I had found some files about mapper.
But I have a trouble still.For example I use mapper 1,it's MMC1.
It has 4 Registers.And some files told me the meaning of the value every bit of every register.
My problem is that how can I get the value of the registers?How can I read these registers and know their value?
Could anyone help me?

Posted: Wed Apr 06, 2005 5:35 am
by tepples
In hardware, a lot of registers are generally write-only. If there's a reason that you have to know what value was last written to each register, then you'll have to manually save the last written value somewhere in RAM whenever you write to the registers.

Posted: Wed Apr 06, 2005 6:22 am
by xian106
My English is too bad.I think you mistook my meaning.

In the "mapperw.txt",

Code: Select all

┌────────────────┐
 │ Mapper 1: MMC1 │
 └────────────────┘
 ┌───────────────┐ ┌────────────────────────────────────────────────────────┐
 │ $8000 ─ $9FFF ├─┤ RxxCFHPM                                               │
 │ (Register 0)  │ │ │  │││││                                               │
 └───────────────┘ │ │  ││││└─── Mirroring Flag                             │
                   │ │  ││││      0 = Horizontal                            │
                   │ │  ││││      1 = Vertical                              │
                   │ │  ││││                                                │
                   │ │  │││└──── One─Screen Mirroring                       │
                   │ │  │││       0 = All pages mirrored from PPU $2000     │
                   │ │  │││       1 = Regular mirroring                     │
                   │ │  │││                                                 │
                   │ │  ││└───── PRG Switching Area                         │
                   │ │  ││        0 = Swap ROM bank at $C000                │
                   │ │  ││        1 = Swap ROM bank at $8000                │
                   │ │  ││                                                  │
                   │ │  │└────── PRG Switching Size                         │
                   │ │  │         0 = Swap 32K of ROM at $8000              │
                   │ │  │         1 = Swap 16K of ROM based on bit 2        │
                   │ │  │                                                   │
                   │ │  └─────── <Carts with VROM>                          │
                   │ │           VROM Switching Size                        │
                   │ │            0 = Swap 8K of VROM at PPU $0000          │
                   │ │            1 = Swap 4K of VROM at PPU $0000 and $1000│
                   │ │           <1024K carts>                              │
                   │ │            0 = Ignore 256K selection register 0      │
                   │ │            1 = Acknowledge 256K selection register 1 │
                   │ │                                                      │
                   │ └────────── Reset Port                                 │
                   │              0 = Do nothing                            │
                   │              1 = Reset register 0                      │
                   └────────────────────────────────────────────────────────┘

 ┌───────────────┐ ┌────────────────────────────────────────────────────────┐
 │ $A000 ─ $BFFF ├─┤ RxxPCCCC                                               │
 │ (Register 1)  │ │ │  ││  │                                               │
 └───────────────┘ │ │  │└──┴──── Select VROM bank at $0000                 │
                   │ │  │         If bit 4 of register 0 is off, then switch│
                   │ │  │         a full 8K bank. Otherwise, switch 4K only.│
                   │ │  │                                                   │
                   │ │  └──────── 256K ROM Selection Register 0             │
                   │ │            <512K carts>                              │
                   │ │            0 = Swap banks from first 256K of PRG     │
                   │ │            1 = Swap banks from second 256K of PRG    │
                   │ │            <1024K carts with bit 4 of register 0 off>│
                   │ │            0 = Swap banks from first 256K of PRG     │
                   │ │            1 = Swap banks from third 256K of PRG     │
                   │ │            <1024K carts with bit 4 of register 0 on> │
                   │ │            Low bit of 256K PRG bank selection        │
                   │ │                                                      │
                   │ └─────────── Reset Port                                │
                   │              0 = Do nothing                            │
                   │              1 = Reset register 1                      │
                   └────────────────────────────────────────────────────────┘

 ┌───────────────┐ ┌────────────────────────────────────────────────────────┐
 │ $C000 ─ $DFFF ├─┤ RxxPCCCC                                               │
 │ (Register 2)  │ │ │  ││  │                                               │
 └───────────────┘ │ │  │└──┴── Select VROM bank at $1000                   │
                   │ │  │        If bit 4 of register 0 is on, then switch  │
                   │ │  │        a 4K bank at $1000. Otherwise ignore it.   │
                   │ │  │                                                   │
                   │ │  └────── 256K ROM Selection Register 1               │
Row 2,How can I get the value of "RxxCFHPM"? Or,If I want to "R"=1,C=F=H=P=M=0,so I write lda #80, sta ?????

Ok,if my English is too bad~~~A same way is,In "mapper1.txt" ,segment 2,

Code: Select all

It is known that the MMC1 chip has four registers, to which data can be written by sending values to certain memory locations in the NES CPU address space
What is the address?For example,segment 5 said,

Code: Select all

bit 0 - toggles between horizontal and vertical mirroring    0 = vertical, 1 = horizontal
If I want to do vertical mirroring,so I must write "0" .but what address I write "0" to?
Thank you.

Posted: Wed Apr 06, 2005 12:12 pm
by Bregalad
Be carfull, many MMC1 docs are obsolete, and there is stupid mistakes like backward mirroring. Yes, vertical mirroring is 0 and horizontal mirroring is 1, not the opposite.
Well, if you have trouble about controlling a mapper, don't begin with the MMC1. Writing to all mappers but MMC1 is easy.
Imagine you're using a simple CNROM board (mapper 3). You have only one register that switches a 8kb CHR-ROM page. Writing at any adress between $8000 and $ffff will write to the mapper and switch the page you want to, normally CNROM uses only 2 bits, so you can have 4 different 8kb CHRROM page (into one 32kb CHRROM chip). Just write the number of the CHR page you want to the mapper (0, 1, 2 or 3), that's it (in pratice you also have to worry about bus conflicts, but I won't go into details now).

Now let's process to the next level. You're using a UNROM board (mapper 2). You also have only one register, that expand PRGROM instead of CHRROM, and is 3 bit or 4 bit wide (depending of UNROM or UOROM boads). This allow you to swap a 16kb rom bank page at $8000-$bfff, but all the data at $c000-$ffff will remain always the same (the last 16kb page) because of the 74HC32 chip. This allow you to use 8 or 16 bank of 16kb to have a 128kb or 256kb PRGROM chip. Writing to the mapper works as simple as CNROM, write anything to $8000-$ffff will set one of your 8 (or 16) PRGROM banks.

Now let's process to the third level, the MMC1. It has 4 different registers, one for mirroring and bankswitching control, two for the CHRROM banking value and one for the PRGROM switching value.
Those are no usual registers, but boolean register so only the LSB is written to the mapper. Set the MSB if you want to "reset" the register (you have to always do it at CPU reset), regardless of the value of the register.
When you write to the MMC1, it does 5 internal "temporary" switches to get the value in any register, and when you set up the 5th value the register is uploaded.
The usual way to upload a register is like this. Typically you have a simple banksitching like UNROM does on the MMC1, so you have one 16kb bank switched at $8000-$bfff and a hardwired bank at $c000-$ffff and have vertical mirroring. Let's say you're using two 4kb ChrRom switch.
So, regardeless of 1 bit switching, you have to write $1e into reg 0 (%0001'1110 = $1e). The code is :

Code: Select all

lda #$80  ;Reset reg 0
sta $8000 ;This is needed only one at reset
lda #$fe
sta $8000 ;Write all 5 bits to the mappers
lsr A
sta $8000
lsr A
sta $8000
lsr A
sta $8000
lsr A
sta $8000 ;Reg 0 is uploaded NOW
rts
All MMC1 regs works the same, so you also have to swith your data 5 times in CHR ROM bank and PRG ROM bank registers, even if you don't use all the bits for your bankswitching. Typically, you have a 128kb PRG rom switch, so only 4 bits of reg 3 are used. Remember that bit 5 is still present in hardware, so you have to write 5 times to the mapper.

Posted: Wed Apr 06, 2005 7:03 pm
by xian106
Thank you.
I understand it now.
I had a try like this I made mapper=3,and I wrote "lda #1 ; sta $8001" ,that would use the second 8K CHR rom,yes? But in my .asm file,I don't know where I put my second 8K CHR rom in.I had put the first 8K CHR rom in $0000-$1fff".

I know how to swap PRG rom in atari game.It just have 4K space for PRG rom.And if I have 8K 12K or 16K PRG rom,I can change it to which rom I want to.There is no CHR rom in atari game.So had not had a try about the CHR rom.I think maybe the method is similar either atari or NES.

I must find more information about how to swap CHR rom in NES first.

Posted: Wed Apr 06, 2005 7:55 pm
by Memblers
You're using the NESAsm assembler, right? I'm not sure how to include the extra CHR banks in there. But you could always copy the extra ones on after assembly, with a command like "copy /b file.nes+file1.chr+file2.chr+file3.chr game.nes"

Also, when you use mapper 3 (and some of the other simple mappers) on a cart, you have to use special way to write to ROM because of bus conflicts. The value you write has to be in ROM.

Here's the clearest way to do it:

Code: Select all

banktable: .byte 0,1,2,3

 lda #2 ; bank to select
 tax
 sta banktable,x
And a quicker way:

Code: Select all

banktemp:
 lda #2
 sta banktemp+1

Posted: Wed Apr 06, 2005 8:52 pm
by xian106
Memblers,long time no come into contact with you.How are you?
I joined in a company writing some 8 bit games with 6502 assembler.

About the mapper~,I think I must study some example first.Do you konw where I can find some example using mapper 3?Using other simple mappers is good too.Thank you.

Posted: Wed Apr 06, 2005 10:08 pm
by Hyde
xian106 wrote:About the mapper ,I think I must study some example first.Do you konw where I can find some example using mapper 3?Using other simple mappers is good too.Thank you.
I don't think there is such thing as a good doc on the various existing mappers. Mapper 3 is really simple though: all you have to do is write to $8000-$ffff the number of the 8K CHR bank that you would like to be swapped into the VRAM $0000-$1fff area. You have to be aware of the existence of bus conflicts (or perhaps not?), however. Here:

Code: Select all

$8000:

.data $00, $01

lda #$00
sta $8000 ;swap page zero

lda #$01 ;swap page one
sta $8001

Posted: Wed Apr 06, 2005 10:40 pm
by xian106
If I write,

Code: Select all


$8000: 
lda #$00 
sta $8001 ;swap page zero 

lda #$01 ;swap page one 
sta $8002  

lda #$02
sta $9005 ;swap page two
Is it right also?Are all the address between $8000-$ffff the same?
And if page zero is $0000-$1fff,what is page one or two?
OK,and just ask same question,where I put my extra CHR rom in?I put the first 8K in $0000-$1fff,and where the second 8K?Thanks all of you.

Posted: Thu Apr 07, 2005 12:42 am
by Memblers
xian106 wrote:Memblers,long time no come into contact with you.How are you?
I joined in a company writing some 8 bit games with 6502 assembler.
I'm doing well. A company doing 8-bit games, that's awesome. :D
And if page zero is $0000-$1fff,what is page one or two?
They're also $0000-$1FFF, since that's the most the PPU can address at once. Normally an assembler wouldn't need to know where it's loaded, since it wouldn't need to be referenced in the code. But I'm not sure how NESAsm works for bankswitching.

And your bankswitching code as you posted it will work on emulators (since they can ignore bus conflicts). But with bus conflicts, the first one is right, but the second two wrote to the wrong addresses (should be $8006 and $800B in that example). And you can do these writes anywhere in $8000-$FFFF, it's the same.

Posted: Thu Apr 07, 2005 12:55 am
by xian106
I will keep on studying 8 bit games,never give it up~~~ :shock:

I will try to do mappers.
BTW,I use DASM too now.

Posted: Thu Apr 07, 2005 4:00 am
by xian106
Bregalad,you said,

Code: Select all

lda #$80  ;Reset reg 0 
sta $8000 ;This is needed only one at reset 
lda #$fe 
sta $8000 ;Write all 5 bits to the mappers 
lsr A 
sta $8000 
I think it must be

Code: Select all

lda #$80  ;Reset reg 0 
sta $8000 ;This is needed only one at reset 
lda #$1f 
sta $8000 ;Write all 5 bits to the mappers 
lsr A 
sta $8000 
YES?
Because horizontal mirroring is 1,so LSB is not "E" ,it must be "F".And if you use "F" for MSB,it will rest the register 0 again,Yes?

Posted: Thu Apr 07, 2005 10:14 am
by Bregalad
Yes, horizontal mirroring is 1, but I wanna to show something using vertical mirroring here.
Yes, I mess up, and I've set all unused bits, doing $fe instead of $1e ($1f will be the same with horizontal mirroring). I think this would "reset" the reg 0 again at the first write, and so the segond write will be the first, etc.... so the reg 0 would wait for a fiveth write again and again.... but set all unused bit exept bit 7 is no problem.
Did you understand or about bus conflicts ? This is not important when you're testing some of your code in emulation only, but this is VERY important on the real hardware. Writing to any adress $8000-$ffff will write to the mapper (D pins of the 74HC161 chips), but, the PRG rom chip is still enabled ( /OE enable pin is usually tied to GND, this means that the outpout is enabled), so if the walue at the adress you're writing is not exactly the same as the one who is stored in the ROM chip, bus conflicts will appears. This appends on the whole 8 bit data, not only to the pins used by the mapper. If the one outpout pin of the CPU is 0 (0v) and the output pin of the EPROM is 1 (+5v), a short would appear. Fortunataly, I think both chips are well protected agains this stuff, and this won't destroy the CPU or your ROM. But the voltage value at the 74HC161 is non determinated, and could be considerated as well as a low or a high voltage level. Anyway, this is very bad and you have to take care of this, just by writing to an array like Memblers said.

Posted: Thu Apr 07, 2005 10:26 am
by tepples
Bregalad wrote:Anyway, this is very bad and you have to take care of this, just by writing to an array like Memblers said.
Or, if you're switching to a fixed bank on C*ROM, U*ROM, or A*ROM, try writing over the instruction itself, using code similar to this:

Code: Select all

@bankswitch:
  lda #6
  sta @bankswitch+1  ; write over the instruction
MMC1, on the other hand, appears to contain circuitry to handle bus conflicts gracefully, disabling ROM output enable on writes.

Posted: Fri Apr 08, 2005 9:59 am
by Bregalad
Yes, but conflics doesn't appears at all on MMC1 and MMC3 cartidges, so you can forget them when writing software for advanced mappers.