mirror of https://github.com/bsnes-emu/bsnes.git
Update to v089r16 release.
byuu says: Changelog: - eliminated <mmio>, <mcu> tags [they are merged to their parent nodes now] - added <ram name= size=> tag to EpsonRTC, SharpRTC - added <firmware> tag to DSP-n, ST-01n, ST-018, Cx4 - interface->path(0) now returns the system folder, which can be used for storage now - as a fun proof-of-concept, I've simulated SNES warm power cycles by saving and loading work RAM (same effect if you instantly swapped carts) - long-term, I'm not sure how I want to do this. The power menu option makes no sense with warm RAM - I like the idea of decaying RAM based on timestamp from last play; and power can just force the timestamp to 0 (which will corrupt all RAM) - Interface::firmware is gone. The cores now load firmware inside their boot up routines - you now get a message on the screen if the emulator can't find firmware, should help with "I just get a black screen" messages I'd like to start preparing for a v090 release. I think we're almost there now. Have to update nall/cartridge and purify to handle XML changes first.
This commit is contained in:
parent
791e64951b
commit
c1318961d8
|
@ -3,7 +3,7 @@
|
|||
|
||||
namespace Emulator {
|
||||
static const char Name[] = "bsnes";
|
||||
static const char Version[] = "089.15";
|
||||
static const char Version[] = "089.16";
|
||||
static const char Author[] = "byuu";
|
||||
static const char License[] = "GPLv3";
|
||||
}
|
||||
|
|
|
@ -21,10 +21,9 @@ struct Interface {
|
|||
unsigned id;
|
||||
string name;
|
||||
string type;
|
||||
string path;
|
||||
string load;
|
||||
};
|
||||
|
||||
vector<Media> firmware;
|
||||
vector<Media> media;
|
||||
|
||||
struct Device {
|
||||
|
@ -58,6 +57,7 @@ struct Interface {
|
|||
virtual int16_t inputPoll(unsigned, unsigned, unsigned) { return 0; }
|
||||
virtual unsigned dipSettings(const XML::Node&) { return 0; }
|
||||
virtual string path(unsigned) { return ""; }
|
||||
virtual void notify(const string &text) { print(text, "\n"); }
|
||||
} *bind;
|
||||
|
||||
//callback bindings (provided by user interface)
|
||||
|
@ -70,6 +70,7 @@ struct Interface {
|
|||
int16_t inputPoll(unsigned port, unsigned device, unsigned input) { return bind->inputPoll(port, device, input); }
|
||||
unsigned dipSettings(const XML::Node &node) { return bind->dipSettings(node); }
|
||||
string path(unsigned group) { return bind->path(group); }
|
||||
template<typename... Args> void notify(Args&... args) { return bind->notify({std::forward<Args>(args)...}); }
|
||||
|
||||
//information
|
||||
virtual double videoFrequency() = 0;
|
||||
|
@ -78,7 +79,7 @@ struct Interface {
|
|||
//media interface
|
||||
virtual bool loaded() { return false; }
|
||||
virtual string sha256() { return ""; }
|
||||
virtual unsigned group(unsigned id) { return 0u; }
|
||||
virtual unsigned group(unsigned id) = 0;
|
||||
virtual void load(unsigned id, const string &manifest) {}
|
||||
virtual void save() {}
|
||||
virtual void load(unsigned id, const stream &memory, const string &markup = "") {}
|
||||
|
|
|
@ -20,6 +20,18 @@ string Interface::sha256() {
|
|||
return cartridge.sha256();
|
||||
}
|
||||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
switch(id) {
|
||||
case ID::ProgramROM:
|
||||
case ID::ProgramRAM:
|
||||
case ID::CharacterROM:
|
||||
case ID::CharacterRAM:
|
||||
return 1;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const string &manifest) {
|
||||
cartridge.load(manifest);
|
||||
}
|
||||
|
@ -112,7 +124,7 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::Famicom, "Famicom", "sys", "fc"});
|
||||
media.append({ID::Famicom, "Famicom", "fc"});
|
||||
|
||||
{
|
||||
Device device{0, ID::Port1 | ID::Port2, "Controller"};
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace Famicom {
|
|||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
System,
|
||||
Famicom,
|
||||
};
|
||||
|
||||
|
@ -26,6 +27,7 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
bool loaded();
|
||||
string sha256();
|
||||
unsigned group(unsigned id);
|
||||
void load(unsigned id, const string &manifest);
|
||||
void save();
|
||||
void load(unsigned id, const stream &stream, const string &manifest = "");
|
||||
|
|
|
@ -49,6 +49,8 @@ void Cartridge::load(System::Revision revision, const string &manifest) {
|
|||
ramsize = numeral(ram["size"].data);
|
||||
ramdata = allocate<uint8>(ramsize, 0xff);
|
||||
|
||||
system.load(revision);
|
||||
|
||||
//Super Game Boy core loads memory from Super Famicom core
|
||||
if(revision != System::Revision::SuperGameBoy) {
|
||||
if(rom["name"].exists()) interface->loadRequest(ID::ROM, rom["name"].data);
|
||||
|
@ -71,8 +73,6 @@ void Cartridge::load(System::Revision revision, const string &manifest) {
|
|||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
}
|
||||
|
||||
system.load(revision);
|
||||
|
||||
loaded = true;
|
||||
sha256 = nall::sha256(romdata, romsize);
|
||||
}
|
||||
|
|
|
@ -28,6 +28,23 @@ string Interface::sha256() {
|
|||
return cartridge.sha256();
|
||||
}
|
||||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
switch(id) {
|
||||
case ID::GameBoyBootROM:
|
||||
case ID::SuperGameBoyBootROM:
|
||||
case ID::GameBoyColorBootROM:
|
||||
return 0;
|
||||
case ID::ROM:
|
||||
case ID::RAM:
|
||||
if(system.revision() == System::Revision::GameBoy) return ID::GameBoy;
|
||||
if(system.revision() == System::Revision::SuperGameBoy) return ID::SuperGameBoy;
|
||||
if(system.revision() == System::Revision::GameBoyColor) return ID::GameBoyColor;
|
||||
throw;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const string &manifest) {
|
||||
if(id == ID::GameBoy) cartridge.load(System::Revision::GameBoy, manifest);
|
||||
if(id == ID::SuperGameBoy) cartridge.load(System::Revision::SuperGameBoy, manifest);
|
||||
|
@ -123,12 +140,8 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"});
|
||||
firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"});
|
||||
firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"});
|
||||
|
||||
media.append({ID::GameBoy, "Game Boy", "sys", "gb" });
|
||||
media.append({ID::GameBoyColor, "Game Boy Color", "sys", "gbc"});
|
||||
media.append({ID::GameBoy, "Game Boy", "gb" });
|
||||
media.append({ID::GameBoyColor, "Game Boy Color", "gbc"});
|
||||
|
||||
{
|
||||
Device device{0, ID::Device, "Controller"};
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace GameBoy {
|
|||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
System,
|
||||
GameBoy,
|
||||
SuperGameBoy,
|
||||
GameBoyColor,
|
||||
|
@ -39,6 +40,7 @@ struct Interface : Emulator::Interface {
|
|||
|
||||
bool loaded();
|
||||
string sha256();
|
||||
unsigned group(unsigned id);
|
||||
void load(unsigned id, const string &manifest);
|
||||
void save();
|
||||
void load(unsigned id, const stream &stream, const string &manifest = "");
|
||||
|
|
|
@ -41,12 +41,24 @@ void System::runthreadtosave() {
|
|||
}
|
||||
|
||||
void System::init() {
|
||||
assert(interface != 0);
|
||||
assert(interface != nullptr);
|
||||
}
|
||||
|
||||
void System::load(Revision revision) {
|
||||
this->revision = revision;
|
||||
serialize_init();
|
||||
if(revision == Revision::SuperGameBoy) return; //Super Famicom core loads boot ROM for SGB
|
||||
|
||||
string manifest, firmware;
|
||||
manifest.readfile({interface->path(ID::System), "manifest.xml"});
|
||||
XML::Document document(manifest);
|
||||
interface->loadRequest(
|
||||
revision == Revision::GameBoy ? ID::GameBoyBootROM : ID::GameBoyColorBootROM,
|
||||
document["system"]["cpu"]["firmware"]["name"].data
|
||||
);
|
||||
if(!file::exists({interface->path(ID::System), document["system"]["cpu"]["firmware"]["name"].data})) {
|
||||
interface->notify("Error: required firmware ", firmware, " not found.\n");
|
||||
}
|
||||
}
|
||||
|
||||
void System::power() {
|
||||
|
|
|
@ -16,6 +16,20 @@ bool Interface::loaded() {
|
|||
return cartridge.loaded();
|
||||
}
|
||||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
switch(id) {
|
||||
case ID::BIOS:
|
||||
return ID::System;
|
||||
case ID::ROM:
|
||||
case ID::RAM:
|
||||
case ID::EEPROM:
|
||||
case ID::FlashROM:
|
||||
return ID::GameBoyAdvance;
|
||||
}
|
||||
|
||||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const string &manifest) {
|
||||
cartridge.load(manifest);
|
||||
}
|
||||
|
@ -104,9 +118,7 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = false;
|
||||
|
||||
firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"});
|
||||
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "gba"});
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"});
|
||||
|
||||
{
|
||||
Device device{0, ID::Device, "Controller"};
|
||||
|
|
|
@ -4,6 +4,7 @@ namespace GameBoyAdvance {
|
|||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
System,
|
||||
GameBoyAdvance,
|
||||
};
|
||||
|
||||
|
@ -25,6 +26,7 @@ struct Interface : Emulator::Interface {
|
|||
double audioFrequency();
|
||||
|
||||
bool loaded();
|
||||
unsigned group(unsigned id);
|
||||
void load(unsigned id, const string &manifest);
|
||||
void save();
|
||||
void load(unsigned id, const stream &stream, const string &manifest = "");
|
||||
|
|
|
@ -23,6 +23,15 @@ void System::power() {
|
|||
}
|
||||
|
||||
void System::load() {
|
||||
string manifest;
|
||||
manifest.readfile({interface->path(ID::System), "manifest.xml"});
|
||||
XML::Document document(manifest);
|
||||
string firmware = document["system"]["cpu"]["firmware"]["name"].data;
|
||||
interface->loadRequest(ID::BIOS, firmware);
|
||||
if(!file::exists({interface->path(ID::System), firmware})) {
|
||||
interface->notify("Error: required firmware ", firmware, " not found.\n");
|
||||
}
|
||||
|
||||
serialize_init();
|
||||
}
|
||||
|
||||
|
|
|
@ -66,6 +66,14 @@ namespace nall {
|
|||
return memory;
|
||||
}
|
||||
|
||||
static bool read(const string &filename, uint8_t *data, unsigned size) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::read) == false) return false;
|
||||
fp.read(data, size);
|
||||
fp.close();
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool write(const string &filename, const uint8_t *data, unsigned size) {
|
||||
file fp;
|
||||
if(fp.open(filename, mode::write) == false) return false;
|
||||
|
|
|
@ -4,16 +4,12 @@
|
|||
<rom name="program.rom" size="0x100000"/>
|
||||
<ram name="save.rwm" size="0x8000"/>
|
||||
<psram name="flash.rwm" size="0x40000"/>
|
||||
<mcu>
|
||||
<map address="00-3f:8000-ffff"/>
|
||||
<map address="80-bf:8000-ffff"/>
|
||||
<map address="40-7f:0000-ffff"/>
|
||||
<map address="c0-ff:0000-ffff"/>
|
||||
<map address="20-3f:6000-7fff"/>
|
||||
</mcu>
|
||||
<mmio>
|
||||
<map address="00-3f:5000-5fff"/>
|
||||
<map address="80-bf:5000-5fff"/>
|
||||
</mmio>
|
||||
<map address="00-3f:8000-ffff"/>
|
||||
<map address="80-bf:8000-ffff"/>
|
||||
<map address="40-7f:0000-ffff"/>
|
||||
<map address="c0-ff:0000-ffff"/>
|
||||
<map address="20-3f:6000-7fff"/>
|
||||
<map address="00-3f:5000-5fff"/>
|
||||
<map address="80-bf:5000-5fff"/>
|
||||
</bsx>
|
||||
</cartridge>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<system name="Game Boy Advance">
|
||||
<bios firmware="bios.rom" sha256="fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"/>
|
||||
<cpu>
|
||||
<firmware name="bios.rom" size="16384" sha256="fd2547724b505f487e6dcb29ec2ecff3af35a841a77ab2e85fd87350abd36570"/>
|
||||
</cpu>
|
||||
</system>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<system name="Game Boy Color">
|
||||
<boot firmware="boot.rom" sha256="4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9"/>
|
||||
<cpu>
|
||||
<firmware name="boot.rom" size="2048" sha256="4bf5021be357ce523a59ac5f4efff5d6371ae50112a6db0adf4a75916ad760a9"/>
|
||||
</cpu>
|
||||
</system>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<system name="Game Boy">
|
||||
<boot firmware="boot.rom" sha256="cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7"/>
|
||||
<cpu>
|
||||
<firmware name="boot.rom" size="256" sha256="cf053eccb4ccafff9e67339d4e78e98dce7d1ed59be819d2a1ba2232c6fce1c7"/>
|
||||
</cpu>
|
||||
</system>
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
<?xml version='1.0' encoding='UTF-8'?>
|
||||
<system name="Super Famicom">
|
||||
<smp firmware="spc700.rom" sha256="c95f88b299030d5afa55b1031e2b5ef2dff650c4b4e6bb6f8b1359436521278f"/>
|
||||
<smp>
|
||||
<firmware name="spc700.rom" size="64" sha256="c95f88b299030d5afa55b1031e2b5ef2dff650c4b4e6bb6f8b1359436521278f"/>
|
||||
</smp>
|
||||
</system>
|
||||
|
|
|
@ -1,11 +1,11 @@
|
|||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<cartridge region="NTSC">
|
||||
<boot firmware="boot.rom" sha256="0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360"/>
|
||||
<rom name="program.rom" size="0x80000">
|
||||
<map mode="linear" address="00-7f:8000-ffff"/>
|
||||
<map mode="linear" address="80-ff:8000-ffff"/>
|
||||
</rom>
|
||||
<icd2 revision="1">
|
||||
<firmware name="boot.rom" size="256" sha256="0e4ddff32fc9d1eeaae812a157dd246459b00c9e14f2f61751f661f32361e360"/>
|
||||
<map address="00-3f:6000-7fff"/>
|
||||
<map address="80-bf:6000-7fff"/>
|
||||
</icd2>
|
||||
|
|
File diff suppressed because one or more lines are too long
|
@ -91,6 +91,8 @@ void Cartridge::load_satellaview(const string &manifest) {
|
|||
auto &rom = document["cartridge"]["rom"];
|
||||
|
||||
if(rom["name"].exists()) {
|
||||
unsigned size = numeral(rom["size"].data);
|
||||
bsxflash.memory.map(allocate<uint8>(size, 0xff), size);
|
||||
interface->loadRequest(ID::BsxFlashROM, rom["name"].data);
|
||||
}
|
||||
}
|
||||
|
@ -101,6 +103,8 @@ void Cartridge::load_sufami_turbo_a(const string &manifest) {
|
|||
auto &ram = document["cartridge"]["ram"];
|
||||
|
||||
if(rom["name"].exists()) {
|
||||
unsigned size = numeral(rom["size"].data);
|
||||
sufamiturbo.slotA.rom.map(allocate<uint8>(size, 0xff), size);
|
||||
interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].data);
|
||||
}
|
||||
|
||||
|
@ -122,6 +126,8 @@ void Cartridge::load_sufami_turbo_b(const string &manifest) {
|
|||
auto &ram = document["cartridge"]["ram"];
|
||||
|
||||
if(rom["name"].exists()) {
|
||||
unsigned size = numeral(rom["size"].data);
|
||||
sufamiturbo.slotB.rom.map(allocate<uint8>(size, 0xff), size);
|
||||
interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].data);
|
||||
}
|
||||
|
||||
|
|
|
@ -110,6 +110,12 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
|||
|
||||
interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
|
||||
|
||||
string firmware = root["firmware"]["name"].data;
|
||||
interface->loadRequest(ID::SuperGameBoyBootROM, firmware);
|
||||
if(!file::exists({interface->path(ID::SuperFamicom), firmware})) {
|
||||
interface->notify("Error: required firmware ", firmware, " not found.\n");
|
||||
}
|
||||
|
||||
icd2.revision = max(1, numeral(root["revision"].data));
|
||||
|
||||
for(auto &node : root) {
|
||||
|
@ -122,7 +128,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
|
|||
|
||||
void Cartridge::parse_markup_bsx(XML::Node &root) {
|
||||
if(root.exists() == false) return;
|
||||
has_bs_cart = root["mmio"].exists();
|
||||
has_bs_cart = root["map"].exists();
|
||||
has_bs_slot = true;
|
||||
|
||||
interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs");
|
||||
|
@ -142,14 +148,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&BSXCartridge::mmio_read, &bsxcartridge}, {&BSXCartridge::mmio_write, &bsxcartridge});
|
||||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mcu"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&BSXCartridge::mcu_read, &bsxcartridge}, {&BSXCartridge::mcu_write, &bsxcartridge});
|
||||
parse_markup_map(m, node);
|
||||
|
@ -238,7 +237,7 @@ void Cartridge::parse_markup_sa1(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&SA1::mmio_read, &sa1}, {&SA1::mmio_write, &sa1});
|
||||
parse_markup_map(m, node);
|
||||
|
@ -267,7 +266,7 @@ void Cartridge::parse_markup_superfx(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&SuperFX::mmio_read, &superfx}, {&SuperFX::mmio_write, &superfx});
|
||||
parse_markup_map(m, node);
|
||||
|
@ -279,8 +278,8 @@ void Cartridge::parse_markup_armdsp(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
has_armdsp = true;
|
||||
|
||||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
string firmware = root["firmware"]["name"].data;
|
||||
string sha256 = root["firmware"]["sha256"].data;
|
||||
|
||||
interface->loadRequest(ID::ArmDSP, firmware);
|
||||
|
||||
|
@ -300,8 +299,8 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
|||
|
||||
hitachidsp.frequency = numeral(root["frequency"].data);
|
||||
if(hitachidsp.frequency == 0) hitachidsp.frequency = 20000000;
|
||||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
string firmware = root["firmware"]["name"].data;
|
||||
string sha256 = root["firmware"]["sha256"].data;
|
||||
|
||||
interface->loadRequest(ID::HitachiDSP, firmware);
|
||||
|
||||
|
@ -313,7 +312,8 @@ void Cartridge::parse_markup_hitachidsp(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&HitachiDSP::dsp_read, &hitachidsp}, {&HitachiDSP::dsp_write, &hitachidsp});
|
||||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
|
@ -333,8 +333,8 @@ void Cartridge::parse_markup_necdsp(XML::Node &root) {
|
|||
= root["model"].data == "uPD7725" ? NECDSP::Revision::uPD7725
|
||||
: root["model"].data == "uPD96050" ? NECDSP::Revision::uPD96050
|
||||
: NECDSP::Revision::uPD7725;
|
||||
string firmware = root["firmware"].data;
|
||||
string sha256 = root["sha256"].data;
|
||||
string firmware = root["firmware"]["name"].data;
|
||||
string sha256 = root["firmware"]["sha256"].data;
|
||||
|
||||
if(necdsp.revision == NECDSP::Revision::uPD7725) {
|
||||
interface->loadRequest(ID::Nec7725DSP, firmware);
|
||||
|
@ -373,7 +373,7 @@ void Cartridge::parse_markup_epsonrtc(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
has_epsonrtc = true;
|
||||
|
||||
string name = root["name"].data;
|
||||
string name = root["ram"]["name"].data;
|
||||
interface->loadRequest(ID::EpsonRTC, name);
|
||||
memory.append({ID::EpsonRTC, name});
|
||||
|
||||
|
@ -389,7 +389,7 @@ void Cartridge::parse_markup_sharprtc(XML::Node &root) {
|
|||
if(root.exists() == false) return;
|
||||
has_sharprtc = true;
|
||||
|
||||
string name = root["name"].data;
|
||||
string name = root["ram"]["name"].data;
|
||||
interface->loadRequest(ID::SharpRTC, name);
|
||||
memory.append({ID::SharpRTC, name});
|
||||
|
||||
|
@ -422,16 +422,9 @@ void Cartridge::parse_markup_spc7110(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&SPC7110::mmio_read, &spc7110}, {&SPC7110::mmio_write, &spc7110});
|
||||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["dcu"]) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&SPC7110::dcu_read, &spc7110}, {&SPC7110::dcu_write, &spc7110});
|
||||
Mapping m({&SPC7110::read, &spc7110}, {&SPC7110::write, &spc7110});
|
||||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
}
|
||||
|
@ -457,9 +450,9 @@ void Cartridge::parse_markup_sdd1(XML::Node &root) {
|
|||
mapping.append(m);
|
||||
}
|
||||
|
||||
for(auto &node : root["mmio"]) {
|
||||
for(auto &node : root) {
|
||||
if(node.name != "map") continue;
|
||||
Mapping m({&SDD1::mmio_read, &sdd1}, {&SDD1::mmio_write, &sdd1});
|
||||
Mapping m({&SDD1::read, &sdd1}, {&SDD1::write, &sdd1});
|
||||
parse_markup_map(m, node);
|
||||
mapping.append(m);
|
||||
}
|
||||
|
|
|
@ -42,6 +42,11 @@ void BSXCartridge::memory_write(Memory &memory, unsigned addr, uint8 data) {
|
|||
|
||||
//mcu_access() allows mcu_read() and mcu_write() to share decoding logic
|
||||
uint8 BSXCartridge::mcu_access(bool write, unsigned addr, uint8 data) {
|
||||
if((addr & 0x40f000) == 0x005000) { //$00-3f|80-bf:5000-5fff
|
||||
if(write == 0) return mmio_read(addr);
|
||||
if(write == 1) return mmio_write(addr, data), 0;
|
||||
}
|
||||
|
||||
if((addr & 0xe08000) == 0x008000) { //$00-1f:8000-ffff
|
||||
if(r07 == 1) {
|
||||
addr = ((addr & 0x1f0000) >> 1) | (addr & 0x7fff);
|
||||
|
@ -101,7 +106,7 @@ uint8 BSXCartridge::mmio_read(unsigned addr) {
|
|||
return r[n];
|
||||
}
|
||||
|
||||
if((addr & 0xf8f000) == 0x105000) { //$10-17:5000-5fff
|
||||
if((addr & 0xf8f000) == 0x105000) { //$10-17:5000-5fff
|
||||
return memory_read(ram, ((addr >> 16) & 7) * 0x1000 + (addr & 0xfff));
|
||||
}
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ void SDD1::init() {
|
|||
void SDD1::load() {
|
||||
//hook S-CPU DMA MMIO registers to gather information for struct dma[];
|
||||
//buffer address and transfer size information for use in SDD1::mcu_read()
|
||||
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, {&SDD1::mmio_read, &sdd1}, {&SDD1::mmio_write, &sdd1});
|
||||
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, {&SDD1::mmio_read, &sdd1}, {&SDD1::mmio_write, &sdd1});
|
||||
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x4300, 0x437f, {&SDD1::read, &sdd1}, {&SDD1::write, &sdd1});
|
||||
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x4300, 0x437f, {&SDD1::read, &sdd1}, {&SDD1::write, &sdd1});
|
||||
}
|
||||
|
||||
void SDD1::unload() {
|
||||
|
@ -42,7 +42,7 @@ void SDD1::reset() {
|
|||
}
|
||||
}
|
||||
|
||||
uint8 SDD1::mmio_read(unsigned addr) {
|
||||
uint8 SDD1::read(unsigned addr) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if((addr & 0x4380) == 0x4300) {
|
||||
|
@ -59,7 +59,7 @@ uint8 SDD1::mmio_read(unsigned addr) {
|
|||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void SDD1::mmio_write(unsigned addr, uint8 data) {
|
||||
void SDD1::write(unsigned addr, uint8 data) {
|
||||
addr &= 0xffff;
|
||||
|
||||
if((addr & 0x4380) == 0x4300) {
|
||||
|
|
|
@ -8,8 +8,8 @@ struct SDD1 {
|
|||
void power();
|
||||
void reset();
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 mmc_read(unsigned addr);
|
||||
uint8 mcu_read(unsigned addr);
|
||||
|
|
|
@ -110,8 +110,9 @@ void SPC7110::reset() {
|
|||
r4834 = 0x00;
|
||||
}
|
||||
|
||||
uint8 SPC7110::mmio_read(unsigned addr) {
|
||||
uint8 SPC7110::read(unsigned addr) {
|
||||
cpu.synchronize_coprocessors();
|
||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800;
|
||||
addr = 0x4800 | (addr & 0x3f);
|
||||
|
||||
switch(addr) {
|
||||
|
@ -198,7 +199,7 @@ uint8 SPC7110::mmio_read(unsigned addr) {
|
|||
return cpu.regs.mdr;
|
||||
}
|
||||
|
||||
void SPC7110::mmio_write(unsigned addr, uint8 data) {
|
||||
void SPC7110::write(unsigned addr, uint8 data) {
|
||||
cpu.synchronize_coprocessors();
|
||||
addr = 0x4800 | (addr & 0x3f);
|
||||
|
||||
|
@ -260,17 +261,6 @@ void SPC7110::mmio_write(unsigned addr, uint8 data) {
|
|||
}
|
||||
}
|
||||
|
||||
//============
|
||||
//SPC7110::DCU
|
||||
//============
|
||||
|
||||
uint8 SPC7110::dcu_read(unsigned) {
|
||||
return mmio_read(0x4800);
|
||||
}
|
||||
|
||||
void SPC7110::dcu_write(unsigned, uint8) {
|
||||
}
|
||||
|
||||
//===============
|
||||
//SPC7110::MCUROM
|
||||
//===============
|
||||
|
|
|
@ -15,11 +15,8 @@ struct SPC7110 : Coprocessor {
|
|||
|
||||
void add_clocks(unsigned clocks);
|
||||
|
||||
uint8 mmio_read(unsigned addr);
|
||||
void mmio_write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 dcu_read(unsigned);
|
||||
void dcu_write(unsigned, uint8);
|
||||
uint8 read(unsigned addr);
|
||||
void write(unsigned addr, uint8 data);
|
||||
|
||||
uint8 mcurom_read(unsigned addr);
|
||||
void mcurom_write(unsigned addr, uint8 data);
|
||||
|
|
|
@ -96,8 +96,8 @@ void CPU::op_step() {
|
|||
}
|
||||
|
||||
void CPU::enable() {
|
||||
function<uint8 (unsigned)> read = { &CPU::mmio_read, (CPU*)&cpu };
|
||||
function<void (unsigned, uint8)> write = { &CPU::mmio_write, (CPU*)&cpu };
|
||||
function<uint8 (unsigned)> read = {&CPU::mmio_read, (CPU*)&cpu};
|
||||
function<void (unsigned, uint8)> write = {&CPU::mmio_write, (CPU*)&cpu};
|
||||
|
||||
bus.map(Bus::MapMode::Direct, 0x00, 0x3f, 0x2140, 0x2183, read, write);
|
||||
bus.map(Bus::MapMode::Direct, 0x80, 0xbf, 0x2140, 0x2183, read, write);
|
||||
|
@ -121,7 +121,6 @@ void CPU::enable() {
|
|||
|
||||
void CPU::power() {
|
||||
cpu_version = config.cpu.version;
|
||||
for(auto &n : wram) n = random(config.cpu.wram_init_value);
|
||||
|
||||
regs.a = regs.x = regs.y = 0x0000;
|
||||
regs.s = 0x01ff;
|
||||
|
@ -156,7 +155,8 @@ void CPU::reset() {
|
|||
}
|
||||
|
||||
CPU::CPU() {
|
||||
PPUcounter::scanline = { &CPU::scanline, this };
|
||||
PPUcounter::scanline = {&CPU::scanline, this};
|
||||
for(auto &n : wram) n = random(config.cpu.wram_init_value);
|
||||
}
|
||||
|
||||
CPU::~CPU() {
|
||||
|
|
|
@ -25,6 +25,8 @@ string Interface::sha256() {
|
|||
|
||||
unsigned Interface::group(unsigned id) {
|
||||
switch(id) {
|
||||
case ID::IPLROM:
|
||||
return 0;
|
||||
case ID::ROM:
|
||||
case ID::RAM:
|
||||
case ID::SA1ROM:
|
||||
|
@ -46,27 +48,31 @@ unsigned Interface::group(unsigned id) {
|
|||
case ID::SDD1ROM:
|
||||
case ID::SDD1RAM:
|
||||
case ID::OBC1RAM:
|
||||
case ID::SuperGameBoyBootROM:
|
||||
case ID::BsxROM:
|
||||
case ID::BsxRAM:
|
||||
case ID::BsxPSRAM:
|
||||
return 0;
|
||||
return 1;
|
||||
case ID::SuperGameBoy:
|
||||
case ID::SuperGameBoyROM:
|
||||
case ID::SuperGameBoyRAM:
|
||||
return 1;
|
||||
return 2;
|
||||
case ID::Satellaview:
|
||||
case ID::BsxFlashROM:
|
||||
return 2;
|
||||
return 3;
|
||||
case ID::SufamiTurboSlotA:
|
||||
case ID::SufamiTurboSlotAROM:
|
||||
case ID::SufamiTurboSlotARAM:
|
||||
return 3;
|
||||
return 4;
|
||||
case ID::SufamiTurboSlotB:
|
||||
case ID::SufamiTurboSlotBROM:
|
||||
case ID::SufamiTurboSlotBRAM:
|
||||
return 4;
|
||||
return 5;
|
||||
}
|
||||
return 0;
|
||||
|
||||
print(id, "\n");
|
||||
return 1;
|
||||
throw;
|
||||
}
|
||||
|
||||
void Interface::load(unsigned id, const string &manifest) {
|
||||
|
@ -143,6 +149,10 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
|
|||
|
||||
if(id == ID::OBC1RAM) obc1.ram.read(stream);
|
||||
|
||||
if(id == ID::SuperGameBoyBootROM) {
|
||||
stream.read(GameBoy::system.bootROM.sgb, min(stream.size(), 256u));
|
||||
}
|
||||
|
||||
if(id == ID::SuperGameBoyROM) {
|
||||
stream.read(GameBoy::cartridge.romdata, min(GameBoy::cartridge.romsize, stream.size()));
|
||||
}
|
||||
|
@ -306,12 +316,10 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"});
|
||||
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "sys", "sfc"});
|
||||
media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "gb" });
|
||||
media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "bs" });
|
||||
media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "st" });
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "sfc"});
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "gb", "Super Game Boy.sfc"});
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "bs", "BS-X Satellaview.sfc"});
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "st", "Sufami Turbo.sfc"});
|
||||
|
||||
{
|
||||
Device device{0, ID::Port1 | ID::Port2, "Controller"};
|
||||
|
|
|
@ -4,14 +4,15 @@ namespace SuperFamicom {
|
|||
|
||||
struct ID {
|
||||
enum : unsigned {
|
||||
//cartridges
|
||||
//cartridges (folders)
|
||||
System,
|
||||
SuperFamicom,
|
||||
SuperGameBoy,
|
||||
Satellaview,
|
||||
SufamiTurboSlotA,
|
||||
SufamiTurboSlotB,
|
||||
|
||||
//memory
|
||||
//memory (files)
|
||||
IPLROM,
|
||||
|
||||
ROM,
|
||||
|
@ -43,6 +44,7 @@ struct ID {
|
|||
|
||||
OBC1RAM,
|
||||
|
||||
SuperGameBoyBootROM,
|
||||
SuperGameBoyROM,
|
||||
SuperGameBoyRAM,
|
||||
|
||||
|
|
|
@ -92,6 +92,17 @@ void System::term() {
|
|||
}
|
||||
|
||||
void System::load() {
|
||||
string path = interface->path(ID::System), manifest;
|
||||
manifest.readfile({path, "manifest.xml"});
|
||||
XML::Document document(manifest);
|
||||
string firmware = document["system"]["smp"]["firmware"]["name"].data;
|
||||
interface->loadRequest(ID::IPLROM, document["system"]["smp"]["firmware"]["name"].data);
|
||||
if(!file::exists({interface->path(ID::System), firmware})) {
|
||||
interface->notify("Error: required firmware ", firmware, " not found.\n");
|
||||
}
|
||||
|
||||
file::read({path, "wram.rwm"}, cpu.wram, 128 * 1024);
|
||||
|
||||
region = config.region;
|
||||
expansion = config.expansion_port;
|
||||
if(region == Region::Autodetect) {
|
||||
|
@ -132,6 +143,9 @@ void System::load() {
|
|||
}
|
||||
|
||||
void System::unload() {
|
||||
string path = interface->path(ID::System);
|
||||
file::write({path, "wram.rwm"}, cpu.wram, 128 * 1024);
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxsatellaview.unload();
|
||||
if(cartridge.has_gb_slot()) icd2.unload();
|
||||
if(cartridge.has_bs_cart()) bsxcartridge.unload();
|
||||
|
|
|
@ -11,12 +11,5 @@ void Application::bootstrap() {
|
|||
emulator.append(new GameBoy::Interface);
|
||||
emulator.append(new GameBoyAdvance::Interface);
|
||||
|
||||
for(auto &system : emulator) {
|
||||
system->bind = interface;
|
||||
|
||||
for(auto &firmware : system->firmware) {
|
||||
filestream fs{application->path({firmware.name, ".", firmware.type, "/", firmware.path})};
|
||||
system->load(firmware.id, fs);
|
||||
}
|
||||
}
|
||||
for(auto &system : emulator) system->bind = interface;
|
||||
}
|
||||
|
|
|
@ -31,8 +31,8 @@ void Application::commandLineLoad(string pathname) {
|
|||
|
||||
for(auto &emulator : this->emulator) {
|
||||
for(auto &media : emulator->media) {
|
||||
if(media.type != "sys") continue;
|
||||
if(type != media.path) continue;
|
||||
if(!media.load.empty()) continue;
|
||||
if(type != media.type) continue;
|
||||
return utility->loadMedia(emulator, media, pathname);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -67,7 +67,7 @@ void Browser::bootstrap() {
|
|||
for(auto &media : emulator->media) {
|
||||
bool found = false;
|
||||
for(auto &folder : folderList) {
|
||||
if(folder.extension == media.path) {
|
||||
if(folder.extension == media.type) {
|
||||
found = true;
|
||||
break;
|
||||
}
|
||||
|
@ -75,7 +75,7 @@ void Browser::bootstrap() {
|
|||
if(found == true) continue;
|
||||
|
||||
Folder folder;
|
||||
folder.extension = media.path;
|
||||
folder.extension = media.type;
|
||||
folder.path = application->basepath;
|
||||
folder.selection = 0;
|
||||
folderList.append(folder);
|
||||
|
|
|
@ -144,12 +144,16 @@ void Presentation::bootstrap() {
|
|||
|
||||
for(auto &media : emulator->media) {
|
||||
Item *item = new Item;
|
||||
item->setText({media.name, " ..."});
|
||||
item->onActivate = [=, &media] {
|
||||
utility->loadMedia(iEmulator->interface, media);
|
||||
};
|
||||
if(media.type == "sys") loadListSystem.append(item);
|
||||
if(media.type != "sys") loadListSubsystem.append(item);
|
||||
if(media.load.empty()) {
|
||||
item->setText({media.name, " ..."});
|
||||
loadListSystem.append(item);
|
||||
} else {
|
||||
item->setText({nall::basename(media.load), " ..."});
|
||||
loadListSubsystem.append(item);
|
||||
}
|
||||
}
|
||||
|
||||
iEmulator->menu.setText(emulator->information.name);
|
||||
|
|
|
@ -114,3 +114,7 @@ unsigned Interface::dipSettings(const XML::Node &node) {
|
|||
string Interface::path(unsigned group) {
|
||||
return utility->path(group);
|
||||
}
|
||||
|
||||
void Interface::notify(const string &text) {
|
||||
MessageWindow::information(*presentation, text);
|
||||
}
|
||||
|
|
|
@ -8,6 +8,7 @@ struct Interface : Emulator::Interface::Bind {
|
|||
int16_t inputPoll(unsigned port, unsigned device, unsigned input);
|
||||
unsigned dipSettings(const XML::Node &node);
|
||||
string path(unsigned group);
|
||||
void notify(const string &text);
|
||||
};
|
||||
|
||||
extern Interface *interface;
|
||||
|
|
|
@ -10,8 +10,8 @@ void Utility::setInterface(Emulator::Interface *emulator) {
|
|||
//load menu option selected
|
||||
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media) {
|
||||
string pathname;
|
||||
if(media.type != "sys") pathname = application->path({media.name, ".", media.type, "/"});
|
||||
if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.path);
|
||||
if(!media.load.empty()) pathname = application->path({media.load, "/"});
|
||||
if(!directory::exists(pathname)) pathname = browser->select("Load Media", media.type);
|
||||
if(!directory::exists(pathname)) return;
|
||||
if(!file::exists({pathname, "manifest.xml"})) return;
|
||||
loadMedia(emulator, media, pathname);
|
||||
|
@ -21,8 +21,9 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
|||
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname) {
|
||||
unload();
|
||||
setInterface(emulator);
|
||||
path(system().group(media.id)) = pathname;
|
||||
if(media.type == "sys") this->pathname.append(pathname);
|
||||
path(0) = application->path({media.name, ".sys/"});
|
||||
path(media.id) = pathname;
|
||||
if(media.load.empty()) this->pathname.append(pathname);
|
||||
|
||||
string manifest;
|
||||
manifest.readfile({pathname, "manifest.xml"});
|
||||
|
@ -38,7 +39,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
|
|||
void Utility::loadRequest(unsigned id, const string &name, const string &type) {
|
||||
string pathname = browser->select({"Load ", name}, type);
|
||||
if(pathname.empty()) return;
|
||||
this->path(system().group(id)) = pathname;
|
||||
path(id) = pathname;
|
||||
this->pathname.append(pathname);
|
||||
|
||||
string manifest;
|
||||
|
|
Loading…
Reference in New Issue