Mapper Registers
Moderator: Moderators
Mapper Registers
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)
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.
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?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.
//-----------------------------------------------------------------------------
// 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?
- 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...
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 ];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!
Yeah, I think 4K banking is a good choice because it allows you to support NSF as well.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.
Download STREEMERZ for NES from fauxgame.com! — Some other stuff I've done: fo.aspekt.fi
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.
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.
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.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.
- That's because you barely know the Maxi 15 cart, emulated as mapper 234.kyuusaku wrote:What if a game comes along with readable mapper registers in $8000-FFFF?
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.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 could see a mapper providing read access to $8000-BFFF for registers (there goes 16K of your 32K, oh well!), but really? Come on.