The game will read from/write to various areas in addressing space, as an emulator you will have to treat each area as it exists on the system
For example, your emu should keep a buffer which is has the PRG, and reads from $8000-$FFFF should read bytes from this buffer and give it back to the game. Therefore when a game does something like "LDA $8000" it will get the proper byte from PRG. However other areas do other things... for example "LDA $0357" will read from system RAM, not from PRG -- and "LDA $2002" will read from a PPU register, not from ROM or RAM.
My method (and I'd assume most emus methods) for handling this is creating Read/Write function pointers. Whenever the CPU reads a byte, you will call the appropriate function pointer which returns the desired info. My emu has this organized so that each 4k of addressing space has its own function pointer.. to paraphrase my code:
Code:
ReadProcs[0] = ReadMemory_RAM; // $0000-$0FFF
ReadProcs[1] = ReadMemory_RAM; // $1000-$1FFF
ReadProcs[2] = ReadMemory_PPU; //$2000-$2FFF
//etc
ReadProcs[8] = ReadMemory_PRG; //$8000-$8FFF
When the CPU performs a read, it calls the appropriate function pointer which returns the desired data:
Code:
#define CPURead(a) ReadProcs[(a) >> 12](a)
My read procs would look something like:
Code:
u8 ReadMemory_RAM(u16 a)
{
return SystemRAM[a & 0x07FF];
}
To handle PRG in this fashion, I keep a big buffer containing all the PRG for the ROM, but also keep 10 pointers which represent each of the 4k pages of PRG.
Code:
u8* pPRGBuffer; /* this will be allocated with malloc or new[] and the PRG will be store here*/
u8* pPRG[10]; /* these will be what the emu actually uses -- each pointer will point to different areas in pPRGBuffer */
pPRG[0] would represent the PRG at $6000-$6FFF, pPRG[1] would represent the PRG at $7000-$7FFF, pPRG[2] would be $8000-$8FFF, etc. Note that most games don't have PRG at $6000-$7FFF, however some mappers (FME-07) do, so it helps to prepare for it.
Whenver your emu does ANY reading from PRG space, it should go through these pointers and not through 'pPRGBuffer' directly -- as these pointers will represent the current PRG banks which are swapped in.
My previously mentioned 'ReadMemory_PRG' function might look something like:
Code:
u8 ReadMemory_PRG(u16 a)
{
return pPRG[(a >> 12) - 6][a & 0x0FFF];
}
This way... PRG swapping can be easily implimented just by changing a few pointers... rather than copying large portions of memory:
Code:
void Swap8kPRG(u8 where,u8 page)
{
u32 offset = page << 13;
pPRG[where] = pPRGBuffer + offset;
pPRG[where + 1] = pPRGBuffer + offset + 0x2000;
}
When pulling out your Reset (or NMI/IRQ vectors), you should treat it just like any other read.
That's my advice anyway.