Mapper Registers

Discuss emulation of the Nintendo Entertainment System and Famicom.

Moderator: Moderators

6T4
Posts: 48
Joined: Sat Feb 19, 2011 8:56 pm

Mapper Registers

Post by 6T4 »

I don't really understand how the mapper registers work in general. I have read several documents about the individual mappers and know that certain bits can be set to certain values, but are these registers read, write, or both then? Also, if anyone wants to give some suggestions about where and how this should be implemented in my emulator, I would appreciate that. (especially relating to mappers that can control mirroring)
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

Mapper registers are often mapped to the PRG-ROM space because you can't write to PRG-ROM, so these writes are instead routed to the mappers so you can control their functions. Most registers can't be read back, just written to. If you read from $8000-$FFFF you'll obviously get whatever the PRG-ROM contains. Some mappers have registers mapped to areas below $8000, and some of those can be read back, I think. You'll have to read each mapper's documentation, if it doesn't say anything about reading, the registers probably can't be read.

As for your emulator, to me it seems that all of the registers should be implemented as variables/arrays/etc. in your mapper file. Most of the mapper functions are actually just PRG and CHR mapping, and for that there is no need to keep any internal state, you just have to set the PRG and CHR pointers like you are already doing, but for some mappers it might be necessary to keep some internal data. For example, if a mapper has configurable bank sizes, it would be a good idea to have a variable remember the setting that was last selected by the program.
6T4
Posts: 48
Joined: Sat Feb 19, 2011 8:56 pm

Post by 6T4 »

tokumaru wrote:As for your emulator, to me it seems that all of the registers should be implemented as variables/arrays/etc. in your mapper file. Most of the mapper functions are actually just PRG and CHR mapping, and for that there is no need to keep any internal state, you just have to set the PRG and CHR pointers like you are already doing, but for some mappers it might be necessary to keep some internal data. For example, if a mapper has configurable bank sizes, it would be a good idea to have a variable remember the setting that was last selected by the program.
Okay, some follow up things then. Why wouldn't this code for mapper 7 have worked?
//-----------------------------------------------------------------------------
// Name: OnWrite()
// Desc: Called when there is a write to PRG-RAM memory ($8000-$FFFF).
//-----------------------------------------------------------------------------
BOOL __declspec(dllexport) OnWrite(WORD wAddress, BYTE byData)
{
// When a byte is written to this area of memory we need to
// switch the first PRG-RAM pointer to (value writen*0x8000)
// if (byData <= wNumPRGROMPages)
pCPU->pbyPRGROMBank1 = pabyPRGROM + ((byData % wNumPRGROMPages)*0x8000);
pCPU->pbyPRGROMBank2 = pabyPRGROM + 0x4000 + ((byData % wNumPRGROMPages)*0x8000);

// Set up the mirroring
NESPPU PPU = {0}; // Our global PPU.
if (byData & 0x10)
{PPU.apbyNameTables[0] = &PPU.abyNameTables[0x400];
PPU.apbyNameTables[1] = &PPU.abyNameTables[0x400];
PPU.apbyNameTables[2] = &PPU.abyNameTables[0x400];
PPU.apbyNameTables[3] = &PPU.abyNameTables[0x400];}
else
{PPU.apbyNameTables[0] = &PPU.abyNameTables[0];
PPU.apbyNameTables[1] = &PPU.abyNameTables[0];
PPU.apbyNameTables[2] = &PPU.abyNameTables[0];
PPU.apbyNameTables[3] = &PPU.abyNameTables[0];}


return TRUE;
} // end OnWrite()

Also, I had a post before about a lot of games larger than 128K not working.... would that have to do with setting up the prg rom banks in the "on read" section of the mapper dlls, or possibly also the io.cpp file; does it need to account for mirrors or something?
User avatar
Zepper
Formerly Fx3
Posts: 3264
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper »

- PRG banks are 8k (0x2000), not 32k (0x8000) for bankswitching. That's the error. Before someone replies something else, let me explain:

a) usual cases will use 8k banks. Non-standard configs are out of subject.
b) to switch a 32k bank, you would select a 32k page, then its 4x8k banks below...

Code: Select all

bank_8 = &PRG[ (value << 2) * 0x2000 ];
bank_A = &bank_8[ 0x2000 ];
bank_C = &bank_A[ 0x2000 ];
bank_E = &bank_C[ 0x2000 ];
User avatar
Dwedit
Posts: 4470
Joined: Fri Nov 19, 2004 7:35 pm
Contact:

Post by Dwedit »

The "prg banks are 8k" rule is not really set in stone, it's just a common convention among emulator developers because mappers that currently exist don't switch PRG in units smaller than 8KB. But there's nothing stopping mappers from switching in smaller units.
Here come the fortune cookies! Here come the fortune cookies! They're wearing paper hats!
User avatar
thefox
Posts: 3139
Joined: Mon Jan 03, 2005 10:36 am
Location: Tampere, Finland
Contact:

Post by thefox »

Dwedit wrote:The "prg banks are 8k" rule is not really set in stone, it's just a common convention among emulator developers because mappers that currently exist don't switch PRG in units smaller than 8KB. But there's nothing stopping mappers from switching in smaller units.
Yeah, I think 4K banking is a good choice because it allows you to support NSF as well.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

I don't even have generic bank pointers, instead memory decoding is up to the cartridge logic. What if a game comes along with readable mapper registers in $8000-FFFF? Pretty much every emulator would have to be significantly reworked. There isn't a huge performance benefit in abstracting mappers so I try to model how things really are.
User avatar
MottZilla
Posts: 2835
Joined: Wed Dec 06, 2006 8:18 pm

Post by MottZilla »

It's important to know what you plan to support though. If you don't plan to support many mappers, you can model things differently. If you plan to support the usual suspects, 8K PRG and 1K CHR segments will do.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

If I was programming an emulator, I would probably try emulating the cartridge separate from the console. There would be as much communication between them as the signals on the cartridge connector permit. This would allow the mappers to monitor ROM accesses just like a real mapper would, generate IRQs, control name table mirroring, etc. without any adjustments to the console side.

Since each cartridge would be responsible for managing its own contents, there would be no standard page size, and no worries that some new mapper might need an unsupported page size.

I don't know if making all memory access go through the cartridge would end up being too slow... I guess it wouldn't be a problem for current desktop computers.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

tokumaru wrote:If I was programming an emulator, I would probably try emulating the cartridge separate from the console. There would be as much communication between them as the signals on the cartridge connector permit. This would allow the mappers to monitor ROM accesses just like a real mapper would, generate IRQs, control name table mirroring, etc. without any adjustments to the console side.

Since each cartridge would be responsible for managing its own contents, there would be no standard page size, and no worries that some new mapper might need an unsupported page size.

I don't know if making all memory access go through the cartridge would end up being too slow... I guess it wouldn't be a problem for current desktop computers.
This is exactly what I do. It's not too slow for me and I'm on a single-core netbook, the performance is better than Nintendulator though sound isn't implemented yet. I call a cartridge function each and every CPU and PPU read/write so eventually MMC5 can be implemented without hacks. It also allows cartridges to spy on $0000-4017 access and overpower the bus (not sure if this is safe on the real thing). I even emulate oscillators running independently of the NES so that random synchronous logic (probably for expansion sound later) can be emulated correctly. I'm not sure if this kind of low level approach is for everyone but it's how I like to do things.
User avatar
tokumaru
Posts: 12106
Joined: Sat Feb 12, 2005 9:43 pm
Location: Rio de Janeiro - Brazil

Post by tokumaru »

That's pretty cool, kyuusaku. From your previous comment I imagined you were doing something like this, but I wasn't sure if it was the exact same thing I had in mind.
User avatar
Zepper
Formerly Fx3
Posts: 3264
Joined: Fri Nov 12, 2004 4:59 pm
Location: Brazil
Contact:

Post by Zepper »

kyuusaku wrote:What if a game comes along with readable mapper registers in $8000-FFFF?
- That's because you barely know the Maxi 15 cart, emulated as mapper 234.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

You're right, though it just requires a hook to the mapper code since the registers aren't readable. Still most emulators probably don't have provisions for it.
User avatar
koitsu
Posts: 4203
Joined: Sun Sep 19, 2004 9:28 pm
Location: A world gone mad

Post by koitsu »

kyuusaku wrote:I don't even have generic bank pointers, instead memory decoding is up to the cartridge logic. What if a game comes along with readable mapper registers in $8000-FFFF? Pretty much every emulator would have to be significantly reworked.
I don't even know how this would effectively work; games would (effectively, not literally) be completely unable to read anything from PRG-ROM. Oh, and let's not forget IRQ/BRK, NMI, and RESET vectors are also in that addressing space.

I could see a mapper providing read access to $8000-BFFF for registers (there goes 16K of your 32K, oh well!), but really? Come on.
User avatar
kyuusaku
Posts: 1665
Joined: Mon Sep 27, 2004 2:13 pm

Post by kyuusaku »

Game Genie maps registers to $8000-FFFF.
Post Reply