alkhimey wrote:
1. Why is mirroring needed. If I understand it right then there are places in memory that are exact copy of the $0000 - $0800 area. Is it right?
Mirroring exists when there's more address space designated than there is physical memory. For RAM mirroring, this happens because there is 8k addresses and only 2k of actual memory. So what happens is that the high bits of the address simply go unused, and the same byte of RAM appears at four seperate addresses.
RAM mirroring is easily accomplished by ANDing the address with a value:
Code:
byte read_ram(word address)
{
return RAM[ address & 0x7FF ];
}
Quote:
2. When I write an emulator can I use the standard algorithem (loop and exec the opcodes), or I should implement something spacial for ppu and the chip that handales the sound?
There are several ways to approach this. I break the emu up into sections (CPU, PPU, APU, mapper) and assign each of them a timestamp. I run the CPU first (the opcode loop you describe), and when it does something that affects another system (like say, writes to a PPU reg) I run the PPU up to the current CPU timestamp to "sync it up".
Quote:
3. Can someone explain the bank switching?
I wrote this doc recently:
http://www.romhacking.net/docs/353/Also, this might help too:
http://www.romhacking.net/docs/362/Quote:
4. In general, how do I manipulate the memory? Can you give an example of implementation? (I assume it is not as simple as array of bytes :roll: )
I keep one buffer for system RAM, one for on-cartridge PRG-RAM, one for CHR-ROM, one for CHR-RAM, and one for PRG-ROM. When loading the ROM, I load PRG/CHR ROM into the appropriate buffers. I also keep 16 function pointers for CPU reading, and 16 for CPU writing (one for each 4k of addressing space)... and any time the CPU reads or writes a byte (once every CPU cycle) I have it call the appropriate function.
ie:
readfunc[0] would read RAM ($0xxx)
readfunc[1] would read RAM ($1xxx)
readfunc[2] would read PPU Regs ($2xxx)
etc
that way reading can be done with:
value = readfunc[ address >> 12 ]( address );
and it will be mapped to the desired area.
For PRG/CHR swapping and NT Mirroring, I keep a series of pointers (one pointer represents a 4k bank of PRG, or a 1k bank of CHR, or a 1k nametable), and to swap PRG/CHR or change mirroring modes, I simply change what those pointers point to -- it's a lot easier to change a pointer than it is to copy large chunks of memory every time the game swaps (which is often several times per frame).