#include namespace GameBoyAdvance { Cartridge cartridge; #include "mrom.cpp" #include "sram.cpp" #include "eeprom.cpp" #include "flash.cpp" #include "serialization.cpp" Cartridge::Cartridge() { mrom.data = new uint8[mrom.size = 32 * 1024 * 1024]; sram.data = new uint8[sram.size = 32 * 1024]; eeprom.data = new uint8[eeprom.size = 8 * 1024]; flash.data = new uint8[flash.size = 128 * 1024]; } Cartridge::~Cartridge() { delete[] mrom.data; delete[] sram.data; delete[] eeprom.data; delete[] flash.data; } auto Cartridge::load() -> bool { information = Information(); if(auto loaded = platform->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) { information.pathID = loaded.pathID(); } else return false; if(auto fp = platform->open(pathID(), "manifest.bml", File::Read, File::Required)) { information.manifest = fp->reads(); } else return false; auto document = BML::unserialize(information.manifest); information.title = document["information/title"].text(); hasSRAM = false; hasEEPROM = false; hasFLASH = false; if(auto node = document["board/rom"]) { mrom.size = min(32 * 1024 * 1024, node["size"].natural()); if(auto fp = platform->open(pathID(), node["name"].text(), File::Read, File::Required)) { fp->read(mrom.data, mrom.size); } } if(auto node = document["board/ram"]) { if(node["type"].text() == "sram") { hasSRAM = true; sram.size = min(32 * 1024, node["size"].natural()); sram.mask = sram.size - 1; for(auto n : range(sram.size)) sram.data[n] = 0xff; if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) { fp->read(sram.data, sram.size); } } if(node["type"].text() == "eeprom") { hasEEPROM = true; eeprom.size = min(8 * 1024, node["size"].natural()); eeprom.bits = eeprom.size <= 512 ? 6 : 14; if(eeprom.size == 0) eeprom.size = 8192, eeprom.bits = 0; //auto-detect size eeprom.mask = mrom.size > 16 * 1024 * 1024 ? 0x0fffff00 : 0x0f000000; eeprom.test = mrom.size > 16 * 1024 * 1024 ? 0x0dffff00 : 0x0d000000; for(auto n : range(eeprom.size)) eeprom.data[n] = 0xff; if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) { fp->read(eeprom.data, eeprom.size); } } if(node["type"].text() == "flash") { hasFLASH = true; flash.id = node["id"].natural(); flash.size = min(128 * 1024, node["size"].natural()); for(auto n : range(flash.size)) flash.data[n] = 0xff; //if flash 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(!flash.id && flash.size == 64 * 1024) flash.id = 0x1cc2; if(!flash.id && flash.size == 128 * 1024) flash.id = 0x09c2; if(auto fp = platform->open(pathID(), node["name"].text(), File::Read)) { fp->read(flash.data, flash.size); } } } information.sha256 = Hash::SHA256(mrom.data, mrom.size).digest(); return true; } auto Cartridge::save() -> void { auto document = BML::unserialize(information.manifest); if(auto node = document["board/ram"]) { if(auto fp = platform->open(pathID(), node["name"].text(), File::Write)) { if(node["type"].text() == "sram") fp->write(sram.data, sram.size); if(node["type"].text() == "eeprom") fp->write(eeprom.data, eeprom.size); if(node["type"].text() == "flash") fp->write(flash.data, flash.size); } } } auto Cartridge::unload() -> void { } auto Cartridge::power() -> void { eeprom.power(); flash.power(); } #define RAM_ANALYZE auto Cartridge::read(uint mode, uint32 addr) -> uint32 { if(addr < 0x0e00'0000) { if(hasEEPROM && (addr & eeprom.mask) == eeprom.test) return eeprom.read(); return mrom.read(mode, addr); } else { if(hasSRAM) return sram.read(mode, addr); if(hasFLASH) return flash.read(addr); return cpu.pipeline.fetch.instruction; } } auto Cartridge::write(uint mode, uint32 addr, uint32 word) -> void { if(addr < 0x0e00'0000) { if(hasEEPROM && (addr & eeprom.mask) == eeprom.test) return eeprom.write(word & 1); return mrom.write(mode, addr, word); } else { if(hasSRAM) return sram.write(mode, addr, word); if(hasFLASH) return flash.write(addr, word); } } }