Disch wrote:
blargg is too fast =o. His outlined methods don't quite match what I'm doing though, so my post wasn't a total waste.
Your method also matches what my emulator does - define "default" routines for each address range, and allow mappers and such to
hook onto them, passing control to the original routine after handling its own stuff.
An example:
Code:
// emu.c:
void Write4 (int addr, byte val)
{
/* handle $4000-$4017 */
}
WRITEPTR writes[16] = {foo,foo,foo,foo,Write4,etc};
...
// fds.c
static WRITEPTR old4;
void fdsWrite4 (int addr, byte val)
{
if ((addr >= 0x4020) && (addr <= 0x407F))
/* handle FDS stuff */
old4(addr, val);
}
void fds_init (void)
{
old4 = writes[4];
writes[4] = fdsWrite4;
}
Mind you, my emulator does this without any direct access to 'writes' - it uses a pair of functions 'GetCPUWriteFunc(bank)' and 'SetCPUWriteFunc(bank,ptr)' which are exposed through the mapper interface, and it has the same thing for CPU reads (as well as PPU read/write, which was needed for a 'sane' MMC5 implementation).
The biggest advantage of this is that it allows you to daisy-chain as many handlers you want to
dynamically (i.e. without the central I/O code ever having to know about any specific extensions), which can increase code re-use with some of the MMC3 "supermappers" found in pirate multicarts, improve separation between mapper code and mapper sound code (for cleaner NSF support), and even allow simple 'proper' Game Genie support (handled in my emulator by overriding the default read handlers for $8000-$FFFF to perform the value substitutions).
The downside is that it can degrade performance, for which my emulator is a prime example.