bsnes-classic/bsnes/snes/memory/memory.cpp

179 lines
4.4 KiB
C++

#include <snes.hpp>
#define MEMORY_CPP
namespace SNES {
Bus bus;
#include "serialization.cpp"
namespace memory {
MMIOAccess mmio;
StaticRAM wram(128 * 1024);
StaticRAM apuram(64 * 1024);
StaticRAM vram(64 * 1024);
StaticRAM oam(544);
StaticRAM cgram(512);
UnmappedMemory memory_unmapped;
UnmappedMMIO mmio_unmapped;
};
unsigned UnmappedMemory::size() const { return 16 * 1024 * 1024; }
uint8 UnmappedMemory::read(unsigned) { return cpu.regs.mdr; }
void UnmappedMemory::write(unsigned, uint8) {}
uint8 UnmappedMMIO::mmio_read(unsigned) { return cpu.regs.mdr; }
void UnmappedMMIO::mmio_write(unsigned, uint8) {}
MMIO* MMIOAccess::handle(unsigned addr) {
return mmio[addr];
}
void MMIOAccess::map(unsigned addr, MMIO &access) {
assert(addr >= Min && addr <= Max);
mmio[addr] = &access;
}
void MMIOAccess::map(unsigned addr_lo, unsigned addr_hi, MMIO &access) {
assert(addr_lo <= addr_hi);
assert(addr_lo >= Min && addr_lo <= Max);
assert(addr_hi >= Min && addr_hi <= Max);
for(unsigned addr = addr_lo; addr <= addr_hi; addr++) {
mmio[addr] = &access;
}
}
unsigned MMIOAccess::size() const { return 0x8000; }
uint8 MMIOAccess::read(unsigned addr) {
if(!debugger_access()) return mmio[addr]->mmio_read(addr);
return 0;
}
void MMIOAccess::write(unsigned addr, uint8 data) {
if(!debugger_access()) mmio[addr]->mmio_write(addr, data);
}
MMIOAccess::MMIOAccess() {
for(unsigned i = 0; i < 0x8000; i++) mmio[i] = &memory::mmio_unmapped;
}
unsigned Bus::mirror(unsigned addr, unsigned size) {
unsigned base = 0;
if(size) {
unsigned mask = 1 << 23;
while(addr >= size) {
while(!(addr & mask)) mask >>= 1;
addr -= mask;
if(size > mask) {
size -= mask;
base += mask;
}
mask >>= 1;
}
base += addr;
}
return base;
}
void Bus::map(unsigned addr, Memory &access, unsigned offset) {
Page &p = page[addr >> 8];
p.access = &access;
p.offset = addr - offset;
}
void Bus::map(
MapMode mode,
uint8 bank_lo, uint8 bank_hi,
uint16 addr_lo, uint16 addr_hi,
Memory &access, unsigned offset, unsigned size
) {
assert(bank_lo <= bank_hi);
assert(addr_lo <= addr_hi);
uint8 page_lo = addr_lo >> 8;
uint8 page_hi = addr_hi >> 8;
unsigned index = 0;
switch(mode) {
case MapMode::Direct: {
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
for(unsigned page = page_lo; page <= page_hi; page++) {
map((bank << 16) + (page << 8), access, (bank << 16) + (page << 8));
}
}
} break;
case MapMode::Linear: {
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
for(unsigned page = page_lo; page <= page_hi; page++) {
map((bank << 16) + (page << 8), access, mirror(offset + index, access.size()));
index += 256;
if(size) index %= size;
}
}
} break;
case MapMode::Shadow: {
for(unsigned bank = bank_lo; bank <= bank_hi; bank++) {
index += page_lo * 256;
if(size) index %= size;
for(unsigned page = page_lo; page <= page_hi; page++) {
map((bank << 16) + (page << 8), access, mirror(offset + index, access.size()));
index += 256;
if(size) index %= size;
}
index += (255 - page_hi) * 256;
if(size) index %= size;
}
} break;
}
}
bool Bus::load_cart() {
if(cartridge.loaded() == true) return false;
map_reset();
map_xml();
map_system();
return true;
}
void Bus::unload_cart() {
}
void Bus::map_reset() {
map(MapMode::Direct, 0x00, 0xff, 0x0000, 0xffff, memory::memory_unmapped);
map(MapMode::Shadow, 0x00, 0x3f, MMIOAccess::Min, MMIOAccess::Max, memory::mmio);
map(MapMode::Shadow, 0x80, 0xbf, MMIOAccess::Min, MMIOAccess::Max, memory::mmio);
memory::mmio.map(MMIOAccess::Min, MMIOAccess::Max, memory::mmio_unmapped);
}
void Bus::map_xml() {
foreach(m, cartridge.mapping) {
if(m.memory) {
map(m.mode, m.banklo, m.bankhi, m.addrlo, m.addrhi, *m.memory, m.offset, m.size);
} else if(m.mmio) {
memory::mmio.map(m.addrlo, m.addrhi, *m.mmio);
}
}
}
void Bus::map_system() {
map(MapMode::Linear, 0x00, 0x3f, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000);
map(MapMode::Linear, 0x80, 0xbf, 0x0000, 0x1fff, memory::wram, 0x000000, 0x002000);
map(MapMode::Linear, 0x7e, 0x7f, 0x0000, 0xffff, memory::wram);
}
void Bus::power() {
foreach(n, memory::wram) n = config.cpu.wram_init_value;
}
void Bus::reset() {
}
}