bsnes/gba/cartridge/cartridge.cpp

172 lines
5.0 KiB
C++

#include <gba/gba.hpp>
namespace GameBoyAdvance {
#include "eeprom.cpp"
#include "flashrom.cpp"
#include "serialization.cpp"
Cartridge cartridge;
string Cartridge::title() {
return information.title;
}
void Cartridge::load() {
interface->loadRequest(ID::Manifest, "manifest.bml", true);
auto document = BML::unserialize(information.markup);
information.title = document["information/title"].text();
unsigned rom_size = 0;
if(document["cartridge/rom"]) {
auto info = document["cartridge/rom"];
interface->loadRequest(ID::ROM, info["name"].text(), true);
rom_size = info["size"].decimal();
for(unsigned addr = rom_size; addr < rom.size; addr++) {
rom.data[addr] = rom.data[Bus::mirror(addr, rom_size)];
}
}
has_sram = false;
has_eeprom = false;
has_flashrom = false;
if(document["cartridge/ram"]) {
auto info = document["cartridge/ram"];
if(info["type"].text() == "SRAM" || info["type"].text() == "FRAM") {
has_sram = true;
ram.size = info["size"].decimal();
ram.mask = ram.size - 1;
for(unsigned n = 0; n < ram.size; n++) ram.data[n] = 0xff;
interface->loadRequest(ID::RAM, info["name"].text(), false);
memory.append({ID::RAM, info["name"].text()});
}
if(info["type"].text() == "EEPROM") {
has_eeprom = true;
eeprom.size = info["size"].decimal();
eeprom.bits = eeprom.size <= 512 ? 6 : 14;
if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size
eeprom.mask = rom_size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000;
eeprom.test = rom_size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000;
for(unsigned n = 0; n < eeprom.size; n++) eeprom.data[n] = 0xff;
interface->loadRequest(ID::EEPROM, info["name"].text(), false);
memory.append({ID::EEPROM, info["name"].text()});
}
if(info["type"].text() == "FlashROM") {
has_flashrom = true;
flashrom.id = info["id"].decimal();
flashrom.size = info["size"].decimal();
for(unsigned n = 0; n < flashrom.size; n++) flashrom.data[n] = 0xff;
//if FlashROM ID not provided; guess that it's a Macronix chip
//this will not work for all games; in which case, the ID must be specified manually
if(!flashrom.id && flashrom.size == 64 * 1024) flashrom.id = 0x1cc2;
if(!flashrom.id && flashrom.size == 128 * 1024) flashrom.id = 0x09c2;
interface->loadRequest(ID::FlashROM, info["name"].text(), false);
memory.append({ID::FlashROM, info["name"].text()});
}
}
sha256 = Hash::SHA256(rom.data, rom_size).digest();
system.load();
loaded = true;
}
void Cartridge::unload() {
if(loaded == false) return;
loaded = false;
memory.reset();
}
void Cartridge::power() {
eeprom.power();
flashrom.power();
}
uint8* Cartridge::ram_data() {
if(has_sram) return ram.data;
if(has_eeprom) return eeprom.data;
if(has_flashrom) return flashrom.data;
return nullptr;
}
unsigned Cartridge::ram_size() {
if(has_sram) return ram.size;
if(has_eeprom) return eeprom.size;
if(has_flashrom) return flashrom.size;
return 0;
}
auto Cartridge::read(uint8 *data, unsigned mode, uint32 addr) -> uint32 {
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
data += addr;
if(mode & Word) return data[0] << 0 | data[1] << 8 | data[2] << 16 | data[3] << 24;
if(mode & Half) return data[0] << 0 | data[1] << 8;
if(mode & Byte) return data[0];
return 0; //should never occur
}
auto Cartridge::write(uint8 *data, unsigned mode, uint32 addr, uint32 word) -> void {
if(mode & Word) addr &= ~3;
if(mode & Half) addr &= ~1;
data += addr;
if(mode & Word) {
data[0] = word >> 0;
data[1] = word >> 8;
data[2] = word >> 16;
data[3] = word >> 24;
} else if(mode & Half) {
data[0] = word >> 0;
data[1] = word >> 8;
} else if(mode & Byte) {
data[0] = word >> 0;
}
}
#define RAM_ANALYZE
auto Cartridge::rom_read(unsigned mode, uint32 addr) -> uint32 {
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.read();
return read(rom.data, mode, addr & 0x01ff'ffff);
}
auto Cartridge::rom_write(unsigned mode, uint32 addr, uint32 word) -> void {
if(has_eeprom && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1);
}
auto Cartridge::ram_read(unsigned mode, uint32 addr) -> uint32 {
if(has_sram) return read(ram.data, mode, addr & ram.mask);
if(has_flashrom) return flashrom.read(addr);
return cpu.pipeline.fetch.instruction;
}
auto Cartridge::ram_write(unsigned mode, uint32 addr, uint32 word) -> void {
if(has_sram) return write(ram.data, mode, addr & ram.mask, word);
if(has_flashrom) return flashrom.write(addr, word);
}
Cartridge::Cartridge() {
loaded = false;
rom.data = new uint8[rom.size = 32 * 1024 * 1024];
ram.data = new uint8[ram.size = 32 * 1024];
eeprom.data = new uint8[eeprom.size = 8 * 1024];
flashrom.data = new uint8[flashrom.size = 128 * 1024];
}
Cartridge::~Cartridge() {
delete[] rom.data;
delete[] ram.data;
delete[] eeprom.data;
delete[] flashrom.data;
}
}