Update to v089r08 release.

byuu says:

Changelog:
- Super Game Boy, BS-X Satellaview and Sufami Turbo cartridges all load
  manifests that specify their file names, and they all work
- Sufami Turbo can now properly handle carts without RAM, or empty slots
  entirely
- Emulator::Interface structures no longer specify any file names, ever
- exposed "capability.(cheats,states)" now. So far, this just means the
  GBA doesn't show the cheat editor, since it doesn't support cheat
  codes yet
- as such, state manager and cheat editor windows auto-hide (may be
  a tiny bit inconvenient, but it makes not having to sync them or deal
  with input when no cart is loaded easier)
- added "AbsoluteInput" type, which returns mouse coordinates from
  -32767,-32767 (top left) to +32767,+32767 (bottom right) or
  -32768,-32768 (offscreen)

AbsoluteInput is just something I'm toying with. Idea is to support eg
Super Scope or Justifier, or possibly some future Famicom controllers
that are absolute-indexed. The coordinates are scaled, so the bigger
your window, the more precise they are. But obviously you can't get more
precise than the emulated system, so 1x scale will behave the same
anyway. I haven't hooked it up yet, need to mess with the idea of custom
cursors via phoenix for that first. Also not sure if it will feel
smoother or not ... if you resize the window, your mouse will seem to
move slower. Still, not having to capture the mouse for SS/JS may be
nicer yet. But we'll see ... just experimenting for now.
This commit is contained in:
Tim Allen 2012-05-28 09:50:50 +10:00
parent 9a8a54c75e
commit 189e707594
25 changed files with 236 additions and 165 deletions

View File

@ -3,7 +3,7 @@
namespace Emulator { namespace Emulator {
static const char Name[] = "bsnes"; static const char Name[] = "bsnes";
static const char Version[] = "089.07"; static const char Version[] = "089.08";
static const char Author[] = "byuu"; static const char Author[] = "byuu";
static const char License[] = "GPLv3"; static const char License[] = "GPLv3";
} }

View File

@ -11,6 +11,10 @@ struct Interface {
bool overscan; bool overscan;
double aspectRatio; double aspectRatio;
bool resettable; bool resettable;
struct Capability {
bool states;
bool cheats;
} capability;
} information; } information;
struct Media { struct Media {
@ -18,7 +22,6 @@ struct Interface {
string name; string name;
string type; string type;
string path; string path;
string extension;
}; };
vector<Media> firmware; vector<Media> firmware;
@ -30,7 +33,7 @@ struct Interface {
string name; string name;
struct Input { struct Input {
unsigned id; unsigned id;
unsigned type; //0 = digital, 1 = analog unsigned type; //0 = digital, 1 = analog (relative), 2 = analog (absolute)
string name; string name;
unsigned guid; unsigned guid;
}; };
@ -46,7 +49,7 @@ struct Interface {
vector<Port> port; vector<Port> port;
struct Bind { struct Bind {
virtual void loadRequest(unsigned, const string&, const string&, const string&) {} virtual void loadRequest(unsigned, const string&, const string&) {}
virtual void loadRequest(unsigned, const string&) {} virtual void loadRequest(unsigned, const string&) {}
virtual void saveRequest(unsigned, const string&) {} virtual void saveRequest(unsigned, const string&) {}
virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; } virtual uint32_t videoColor(unsigned, uint16_t, uint16_t, uint16_t) { return 0u; }
@ -58,7 +61,7 @@ struct Interface {
} *bind; } *bind;
//callback bindings (provided by user interface) //callback bindings (provided by user interface)
void loadRequest(unsigned id, const string &name, const string &type, const string &path) { return bind->loadRequest(id, name, type, path); } void loadRequest(unsigned id, const string &name, const string &type) { return bind->loadRequest(id, name, type); }
void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); } void loadRequest(unsigned id, const string &path) { return bind->loadRequest(id, path); }
void saveRequest(unsigned id, const string &path) { return bind->saveRequest(id, path); } void saveRequest(unsigned id, const string &path) { return bind->saveRequest(id, path); }
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); } uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue) { return bind->videoColor(source, red, green, blue); }

View File

@ -109,8 +109,10 @@ Interface::Interface() {
information.overscan = true; information.overscan = true;
information.aspectRatio = 8.0 / 7.0; information.aspectRatio = 8.0 / 7.0;
information.resettable = true; information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "sys", "program.rom", "fc"}); media.append({ID::Famicom, "Famicom", "sys", "fc"});
{ {
Device device{0, ID::Port1 | ID::Port2, "Controller"}; Device device{0, ID::Port1 | ID::Port2, "Controller"};

View File

@ -14,7 +14,7 @@ namespace GameBoy {
#include "serialization.cpp" #include "serialization.cpp"
Cartridge cartridge; Cartridge cartridge;
void Cartridge::load(System::Revision revision, const string &manifest, bool preloaded) { void Cartridge::load(System::Revision revision, const string &manifest) {
information.markup = manifest; information.markup = manifest;
information.mapper = Mapper::Unknown; information.mapper = Mapper::Unknown;
information.ram = false; information.ram = false;
@ -49,7 +49,8 @@ void Cartridge::load(System::Revision revision, const string &manifest, bool pre
ramsize = numeral(ram["size"].data); ramsize = numeral(ram["size"].data);
ramdata = allocate<uint8>(ramsize, 0xff); ramdata = allocate<uint8>(ramsize, 0xff);
if(preloaded == false) { //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); if(rom["name"].exists()) interface->loadRequest(ID::ROM, rom["name"].data);
if(ram["name"].exists()) interface->loadRequest(ID::RAM, ram["name"].data); if(ram["name"].exists()) interface->loadRequest(ID::RAM, ram["name"].data);
if(ram["name"].exists()) memory.append({ID::RAM, ram["name"].data}); if(ram["name"].exists()) memory.append({ID::RAM, ram["name"].data});

View File

@ -51,7 +51,7 @@ struct Cartridge : MMIO, property<Cartridge> {
MMIO *mapper; MMIO *mapper;
bool bootrom_enable; bool bootrom_enable;
void load(System::Revision revision, const string &manifest, bool preloaded = false); void load(System::Revision revision, const string &manifest);
void unload(); void unload();
uint8 rom_read(unsigned addr); uint8 rom_read(unsigned addr);

View File

@ -120,13 +120,15 @@ Interface::Interface() {
information.overscan = false; information.overscan = false;
information.aspectRatio = 1.0; information.aspectRatio = 1.0;
information.resettable = false; information.resettable = false;
information.capability.states = true;
information.capability.cheats = true;
firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"}); firmware.append({ID::GameBoyBootROM, "Game Boy", "sys", "boot.rom"});
firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"}); firmware.append({ID::SuperGameBoyBootROM, "Super Game Boy", "sfc", "boot.rom"});
firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"}); firmware.append({ID::GameBoyColorBootROM, "Game Boy Color", "sys", "boot.rom"});
media.append({ID::GameBoy, "Game Boy", "sys", "program.rom", "gb" }); media.append({ID::GameBoy, "Game Boy", "sys", "gb" });
media.append({ID::GameBoyColor, "Game Boy Color", "sys", "program.rom", "gbc"}); media.append({ID::GameBoyColor, "Game Boy Color", "sys", "gbc"});
{ {
Device device{0, ID::Device, "Controller"}; Device device{0, ID::Device, "Controller"};

View File

@ -101,10 +101,12 @@ Interface::Interface() {
information.overscan = false; information.overscan = false;
information.aspectRatio = 1.0; information.aspectRatio = 1.0;
information.resettable = false; information.resettable = false;
information.capability.states = true;
information.capability.cheats = false;
firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"}); firmware.append({ID::BIOS, "Game Boy Advance", "sys", "bios.rom"});
media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "program.rom", "gba"}); media.append({ID::GameBoyAdvance, "Game Boy Advance", "sys", "gba"});
{ {
Device device{0, ID::Device, "Controller"}; Device device{0, ID::Device, "Controller"};

View File

@ -1,6 +1,6 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<cartridge region="NTSC"> <cartridge region="NTSC">
<rom> <rom name="program.rom" size="0x40000">
<map mode="linear" address="00-1f:8000-ffff"/> <map mode="linear" address="00-1f:8000-ffff"/>
<map mode="linear" address="80-9f:8000-ffff"/> <map mode="linear" address="80-9f:8000-ffff"/>
</rom> </rom>
@ -10,7 +10,7 @@
<map mode="linear" address="20-3f:8000-ffff"/> <map mode="linear" address="20-3f:8000-ffff"/>
<map mode="linear" address="a0-bf:8000-ffff"/> <map mode="linear" address="a0-bf:8000-ffff"/>
</rom> </rom>
<ram size="131072"> <ram>
<map mode="linear" address="60-63:8000-ffff"/> <map mode="linear" address="60-63:8000-ffff"/>
<map mode="linear" address="e0-e3:8000-ffff"/> <map mode="linear" address="e0-e3:8000-ffff"/>
</ram> </ram>
@ -20,7 +20,7 @@
<map mode="linear" address="40-5f:8000-ffff"/> <map mode="linear" address="40-5f:8000-ffff"/>
<map mode="linear" address="c0-df:8000-ffff"/> <map mode="linear" address="c0-df:8000-ffff"/>
</rom> </rom>
<ram size="131072"> <ram>
<map mode="linear" address="70-73:8000-ffff"/> <map mode="linear" address="70-73:8000-ffff"/>
<map mode="linear" address="f0-f3:8000-ffff"/> <map mode="linear" address="f0-f3:8000-ffff"/>
</ram> </ram>

View File

@ -5,7 +5,6 @@ namespace SuperFamicom {
#include "markup.cpp" #include "markup.cpp"
#include "serialization.cpp" #include "serialization.cpp"
Cartridge cartridge; Cartridge cartridge;
void Cartridge::load(const string &manifest) { void Cartridge::load(const string &manifest) {
@ -59,63 +58,63 @@ void Cartridge::load(const string &manifest) {
loaded = true; loaded = true;
} }
void Cartridge::load(const string &markup, const stream &stream) { void Cartridge::load_super_game_boy(const string &manifest) {
rom.copy(stream); XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
region = Region::NTSC; GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest);
//ram_size = 0;
has_gb_slot = false; if(rom["name"].exists()) interface->loadRequest(ID::SuperGameBoyROM, rom["name"].data);
has_bs_cart = false; if(ram["name"].exists()) interface->loadRequest(ID::SuperGameBoyRAM, ram["name"].data);
has_bs_slot = false; if(ram["name"].exists()) memory.append({ID::SuperGameBoyRAM, ram["name"].data});
has_st_slots = false;
has_nss_dip = false;
has_sa1 = false;
has_superfx = false;
has_armdsp = false;
has_hitachidsp = false;
has_necdsp = false;
has_epsonrtc = false;
has_sharprtc = false;
has_spc7110 = false;
has_sdd1 = false;
has_obc1 = false;
has_msu1 = false;
has_link = false;
parse_markup(markup);
//print(markup, "\n\n");
//Super Game Boy
if(cartridge.has_gb_slot()) {
sha256 = nall::sha256(GameBoy::cartridge.romdata, GameBoy::cartridge.romsize);
} }
//Broadcast Satellaview void Cartridge::load_satellaview(const string &manifest) {
else if(cartridge.has_bs_cart() && cartridge.has_bs_slot()) { XML::Document document(manifest);
sha256 = nall::sha256(bsxflash.memory.data(), bsxflash.memory.size()); auto &rom = document["cartridge"]["rom"];
if(rom["name"].exists()) {
interface->loadRequest(ID::BsxFlashROM, rom["name"].data);
}
} }
//Sufami Turbo void Cartridge::load_sufami_turbo_a(const string &manifest) {
else if(cartridge.has_st_slots()) { XML::Document document(manifest);
sha256 = nall::sha256(sufamiturbo.slotA.rom.data(), sufamiturbo.slotA.rom.size()); auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
if(rom["name"].exists()) {
interface->loadRequest(ID::SufamiTurboSlotAROM, rom["name"].data);
} }
//Super Famicom if(ram["name"].exists()) {
else { unsigned size = numeral(ram["size"].data);
sha256 = nall::sha256(rom.data(), rom.size()); sufamiturbo.slotA.ram.map(allocate<uint8>(size, 0xff), size);
interface->loadRequest(ID::SufamiTurboSlotARAM, ram["name"].data);
memory.append({ID::SufamiTurboSlotARAM, ram["name"].data});
} }
// if(ram_size > 0) { if(document["cartridge"]["linkable"].data == "true") {
// ram.map(allocate<uint8>(ram_size, 0xff), ram_size); interface->loadRequest(ID::SufamiTurboSlotB, "Sufami Turbo - Slot B", "st");
// interface->memory.append({ID::RAM, "save.ram"}); }
// } }
rom.write_protect(true); void Cartridge::load_sufami_turbo_b(const string &manifest) {
ram.write_protect(false); XML::Document document(manifest);
auto &rom = document["cartridge"]["rom"];
auto &ram = document["cartridge"]["ram"];
system.load(); if(rom["name"].exists()) {
loaded = true; interface->loadRequest(ID::SufamiTurboSlotBROM, rom["name"].data);
}
if(ram["name"].exists()) {
unsigned size = numeral(ram["size"].data);
sufamiturbo.slotB.ram.map(allocate<uint8>(size, 0xff), size);
interface->loadRequest(ID::SufamiTurboSlotBRAM, ram["name"].data);
memory.append({ID::SufamiTurboSlotBRAM, ram["name"].data});
}
} }
void Cartridge::unload() { void Cartridge::unload() {
@ -131,7 +130,6 @@ void Cartridge::unload() {
Cartridge::Cartridge() { Cartridge::Cartridge() {
loaded = false; loaded = false;
unload();
} }
Cartridge::~Cartridge() { Cartridge::~Cartridge() {

View File

@ -63,7 +63,10 @@ struct Cartridge : property<Cartridge> {
vector<Memory> memory; vector<Memory> memory;
void load(const string &manifest); void load(const string &manifest);
void load(const string &markup, const stream &stream); void load_super_game_boy(const string &manifest);
void load_satellaview(const string &manifest);
void load_sufami_turbo_a(const string &manifest);
void load_sufami_turbo_b(const string &manifest);
void unload(); void unload();
void serialize(serializer&); void serialize(serializer&);

View File

@ -103,14 +103,7 @@ void Cartridge::parse_markup_icd2(XML::Node &root) {
if(root.exists() == false) return; if(root.exists() == false) return;
has_gb_slot = true; has_gb_slot = true;
//Game Boy requires a cartridge to be loaded ... interface->loadRequest(ID::SuperGameBoy, "Game Boy", "gb");
//load "empty" cartridge, in case loadRequest() does not load one
vector<uint8> stream;
stream.resize(32768);
for(auto &byte : stream) byte = 0xff;
interface->load(ID::SuperGameBoyROM, vectorstream{stream}, "");
interface->loadRequest(ID::SuperGameBoyROM, "Game Boy", "gb", "program.rom");
icd2.revision = max(1, numeral(root["revision"].data)); icd2.revision = max(1, numeral(root["revision"].data));
@ -127,7 +120,7 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
has_bs_cart = root["mmio"].exists(); has_bs_cart = root["mmio"].exists();
has_bs_slot = true; has_bs_slot = true;
interface->loadRequest(ID::BsxFlashROM, "BS-X Satellaview", "bs", "program.rom"); interface->loadRequest(ID::Satellaview, "BS-X Satellaview", "bs");
if(has_bs_cart) { if(has_bs_cart) {
parse_markup_memory(bsxcartridge.psram, root["psram"], ID::BsxPSRAM, true); parse_markup_memory(bsxcartridge.psram, root["psram"], ID::BsxPSRAM, true);
@ -135,6 +128,8 @@ void Cartridge::parse_markup_bsx(XML::Node &root) {
for(auto &node : root["slot"]) { for(auto &node : root["slot"]) {
if(node.name != "map") continue; if(node.name != "map") continue;
if(bsxflash.memory.size() == 0) continue;
Mapping m(bsxflash.memory); Mapping m(bsxflash.memory);
parse_markup_map(m, node); parse_markup_map(m, node);
mapping.append(m); mapping.append(m);
@ -159,8 +154,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
if(root.exists() == false) return; if(root.exists() == false) return;
has_st_slots = true; has_st_slots = true;
interface->loadRequest(ID::SufamiTurboSlotAROM, "Sufami Turbo - Slot A", "st", "program.rom"); //load required slot A (will request slot B if slot A cartridge is linkable)
interface->loadRequest(ID::SufamiTurboSlotBROM, "Sufami Turbo - Slot B", "st", "program.rom"); interface->loadRequest(ID::SufamiTurboSlotA, "Sufami Turbo - Slot A", "st");
for(auto &slot : root) { for(auto &slot : root) {
if(slot.name != "slot") continue; if(slot.name != "slot") continue;
@ -170,6 +165,8 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
for(auto &leaf : node) { for(auto &leaf : node) {
if(leaf.name != "map") continue; if(leaf.name != "map") continue;
SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom; SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.rom : sufamiturbo.slotB.rom;
if(memory.size() == 0) continue;
Mapping m(memory); Mapping m(memory);
parse_markup_map(m, leaf); parse_markup_map(m, leaf);
if(m.size == 0) m.size = memory.size(); if(m.size == 0) m.size = memory.size();
@ -181,9 +178,11 @@ void Cartridge::parse_markup_sufamiturbo(XML::Node &root) {
for(auto &leaf : node) { for(auto &leaf : node) {
if(leaf.name != "map") continue; if(leaf.name != "map") continue;
SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram; SuperFamicom::Memory &memory = slotid == 0 ? sufamiturbo.slotA.ram : sufamiturbo.slotB.ram;
if(memory.size() == 0) continue;
Mapping m(memory); Mapping m(memory);
parse_markup_map(m, leaf); parse_markup_map(m, leaf);
if(m.size == 0) m.size = ram_size; if(m.size == 0) m.size = memory.size();
if(m.size) mapping.append(m); if(m.size) mapping.append(m);
} }
} }

View File

@ -7,16 +7,6 @@ namespace SuperFamicom {
SufamiTurbo sufamiturbo; SufamiTurbo sufamiturbo;
void SufamiTurbo::load() { void SufamiTurbo::load() {
slotA.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
slotB.ram.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
if(slotA.rom.data() == nullptr) {
slotA.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
}
if(slotB.rom.data() == nullptr) {
slotB.rom.map(allocate<uint8>(128 * 1024, 0xff), 128 * 1024);
}
} }
void SufamiTurbo::unload() { void SufamiTurbo::unload() {

View File

@ -34,14 +34,18 @@ unsigned Interface::group(unsigned id) {
case ID::NecDSPRAM: case ID::NecDSPRAM:
case ID::BsxPSRAM: case ID::BsxPSRAM:
return 0; return 0;
case ID::SuperGameBoy:
case ID::SuperGameBoyROM: case ID::SuperGameBoyROM:
case ID::SuperGameBoyRAM: case ID::SuperGameBoyRAM:
return 1; return 1;
case ID::Satellaview:
case ID::BsxFlashROM: case ID::BsxFlashROM:
return 2; return 2;
case ID::SufamiTurboSlotA:
case ID::SufamiTurboSlotAROM: case ID::SufamiTurboSlotAROM:
case ID::SufamiTurboSlotARAM: case ID::SufamiTurboSlotARAM:
return 3; return 3;
case ID::SufamiTurboSlotB:
case ID::SufamiTurboSlotBROM: case ID::SufamiTurboSlotBROM:
case ID::SufamiTurboSlotBRAM: case ID::SufamiTurboSlotBRAM:
return 4; return 4;
@ -50,7 +54,11 @@ unsigned Interface::group(unsigned id) {
} }
void Interface::load(unsigned id, const string &manifest) { void Interface::load(unsigned id, const string &manifest) {
cartridge.load(manifest); if(id == ID::SuperFamicom) cartridge.load(manifest);
if(id == ID::SuperGameBoy) cartridge.load_super_game_boy(manifest);
if(id == ID::Satellaview) cartridge.load_satellaview(manifest);
if(id == ID::SufamiTurboSlotA) cartridge.load_sufami_turbo_a(manifest);
if(id == ID::SufamiTurboSlotB) cartridge.load_sufami_turbo_b(manifest);
} }
void Interface::save() { void Interface::save() {
@ -107,12 +115,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
} }
if(id == ID::SuperGameBoyROM) { if(id == ID::SuperGameBoyROM) {
GameBoy::interface->load(GameBoy::ID::ROM, stream); stream.read(GameBoy::cartridge.romdata, min(GameBoy::cartridge.romsize, stream.size()));
GameBoy::cartridge.load(GameBoy::System::Revision::SuperGameBoy, manifest, true);
} }
if(id == ID::SuperGameBoyRAM) { if(id == ID::SuperGameBoyRAM) {
stream.read(GameBoy::cartridge.ramdata, GameBoy::cartridge.ramsize); stream.read(GameBoy::cartridge.ramdata, min(GameBoy::cartridge.ramsize, stream.size()));
} }
if(id == ID::BsxFlashROM) { if(id == ID::BsxFlashROM) {
@ -132,11 +139,11 @@ void Interface::load(unsigned id, const stream &stream, const string &manifest)
} }
if(id == ID::SufamiTurboSlotARAM) { if(id == ID::SufamiTurboSlotARAM) {
sufamiturbo.slotA.ram.copy(stream); stream.read(sufamiturbo.slotA.ram.data(), min(sufamiturbo.slotA.ram.size(), stream.size()));
} }
if(id == ID::SufamiTurboSlotBRAM) { if(id == ID::SufamiTurboSlotBRAM) {
sufamiturbo.slotB.ram.copy(stream); stream.read(sufamiturbo.slotB.ram.data(), min(sufamiturbo.slotB.ram.size(), stream.size()));
} }
} }
@ -284,13 +291,15 @@ Interface::Interface() {
information.overscan = true; information.overscan = true;
information.aspectRatio = 8.0 / 7.0; information.aspectRatio = 8.0 / 7.0;
information.resettable = true; information.resettable = true;
information.capability.states = true;
information.capability.cheats = true;
firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"}); firmware.append({ID::IPLROM, "Super Famicom", "sys", "spc700.rom"});
media.append({ID::SuperFamicom, "Super Famicom", "sys", "program.rom", "sfc"}); media.append({ID::SuperFamicom, "Super Famicom", "sys", "sfc"});
media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "program.rom", "gb" }); media.append({ID::SuperFamicom, "Super Game Boy", "sfc", "gb" });
media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "program.rom", "bs" }); media.append({ID::SuperFamicom, "BS-X Satellaview", "sfc", "bs" });
media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "program.rom", "st" }); media.append({ID::SuperFamicom, "Sufami Turbo", "sfc", "st" });
{ {
Device device{0, ID::Port1 | ID::Port2, "Controller"}; Device device{0, ID::Port1 | ID::Port2, "Controller"};

View File

@ -4,10 +4,14 @@ namespace SuperFamicom {
struct ID { struct ID {
enum : unsigned { enum : unsigned {
//cartridges
SuperFamicom, SuperFamicom,
}; SuperGameBoy,
Satellaview,
SufamiTurboSlotA,
SufamiTurboSlotB,
enum : unsigned { //memory
IPLROM, IPLROM,
ROM, ROM,
@ -32,9 +36,8 @@ struct ID {
SufamiTurboSlotBROM, SufamiTurboSlotBROM,
SufamiTurboSlotARAM, SufamiTurboSlotARAM,
SufamiTurboSlotBRAM, SufamiTurboSlotBRAM,
};
enum : unsigned { //controller ports
Port1 = 1, Port1 = 1,
Port2 = 2, Port2 = 2,
}; };

View File

@ -27,7 +27,7 @@ void Application::commandLineLoad(string pathname) {
for(auto &emulator : this->emulator) { for(auto &emulator : this->emulator) {
for(auto &media : emulator->media) { for(auto &media : emulator->media) {
if(media.type != "sys") continue; if(media.type != "sys") continue;
if(type != media.extension) continue; if(type != media.path) continue;
return utility->loadMedia(emulator, media, pathname); return utility->loadMedia(emulator, media, pathname);
} }
} }

View File

@ -67,7 +67,7 @@ void Browser::bootstrap() {
for(auto &media : emulator->media) { for(auto &media : emulator->media) {
bool found = false; bool found = false;
for(auto &folder : folderList) { for(auto &folder : folderList) {
if(folder.extension == media.extension) { if(folder.extension == media.path) {
found = true; found = true;
break; break;
} }
@ -75,7 +75,7 @@ void Browser::bootstrap() {
if(found == true) continue; if(found == true) continue;
Folder folder; Folder folder;
folder.extension = media.extension; folder.extension = media.path;
folder.path = application->basepath; folder.path = application->basepath;
folder.selection = 0; folder.selection = 0;
folderList.append(folder); folderList.append(folder);

View File

@ -28,9 +28,19 @@ void Presentation::synchronize() {
synchronizeVideo.setChecked(config->video.synchronize); synchronizeVideo.setChecked(config->video.synchronize);
synchronizeAudio.setChecked(config->audio.synchronize); synchronizeAudio.setChecked(config->audio.synchronize);
muteAudio.setChecked(config->audio.mute); muteAudio.setChecked(config->audio.mute);
toolsMenu.setVisible(application->active);
synchronizeTime.setVisible(application->active && system().rtc()); if(application->active == nullptr) {
toolsMenu.setVisible(false);
} else {
toolsMenu.setVisible(true);
saveStateMenu.setVisible(system().information.capability.states);
loadStateMenu.setVisible(system().information.capability.states);
stateMenuSeparator.setVisible(system().information.capability.states);
resizeWindow.setVisible(config->video.scaleMode != 2); resizeWindow.setVisible(config->video.scaleMode != 2);
stateManager.setVisible(system().information.capability.states);
cheatEditor.setVisible(system().information.capability.cheats);
synchronizeTime.setVisible(system().rtc());
}
} }
void Presentation::setSystemName(const string &name) { void Presentation::setSystemName(const string &name) {
@ -69,10 +79,10 @@ Presentation::Presentation() : active(nullptr) {
for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n}); for(unsigned n = 0; n < 5; n++) saveStateItem[n].setText({"Slot ", 1 + n});
loadStateMenu.setText("Load State"); loadStateMenu.setText("Load State");
for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n}); for(unsigned n = 0; n < 5; n++) loadStateItem[n].setText({"Slot ", 1 + n});
synchronizeTime.setText("Synchronize Time");
resizeWindow.setText("Resize Window"); resizeWindow.setText("Resize Window");
cheatEditor.setText("Cheat Editor");
stateManager.setText("State Manager"); stateManager.setText("State Manager");
cheatEditor.setText("Cheat Editor");
synchronizeTime.setText("Synchronize Time");
append(loadMenu); append(loadMenu);
for(auto &item : loadListSystem) loadMenu.append(*item); for(auto &item : loadListSystem) loadMenu.append(*item);
@ -97,9 +107,8 @@ Presentation::Presentation() : active(nullptr) {
for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]); for(unsigned n = 0; n < 5; n++) saveStateMenu.append(saveStateItem[n]);
toolsMenu.append(loadStateMenu); toolsMenu.append(loadStateMenu);
for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]); for(unsigned n = 0; n < 5; n++) loadStateMenu.append(loadStateItem[n]);
toolsMenu.append(synchronizeTime);
toolsMenu.append(stateMenuSeparator); toolsMenu.append(stateMenuSeparator);
toolsMenu.append(resizeWindow, cheatEditor, stateManager); toolsMenu.append(resizeWindow, stateManager, cheatEditor, synchronizeTime);
append(layout); append(layout);
layout.append(viewport, {0, 0, 720, 480}); layout.append(viewport, {0, 0, 720, 480});
@ -120,10 +129,10 @@ Presentation::Presentation() : active(nullptr) {
configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); }; configurationSettings.onActivate = [&] { settings->setVisible(); settings->panelList.setFocused(); };
for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); }; for(unsigned n = 0; n < 5; n++) saveStateItem[n].onActivate = [=] { utility->saveState(1 + n); };
for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); }; for(unsigned n = 0; n < 5; n++) loadStateItem[n].onActivate = [=] { utility->loadState(1 + n); };
synchronizeTime.onActivate = [&] { system().rtcsync(); };
resizeWindow.onActivate = [&] { utility->resize(true); }; resizeWindow.onActivate = [&] { utility->resize(true); };
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
stateManager.onActivate = [&] { ::stateManager->setVisible(); }; stateManager.onActivate = [&] { ::stateManager->setVisible(); };
cheatEditor.onActivate = [&] { ::cheatEditor->setVisible(); };
synchronizeTime.onActivate = [&] { system().rtcsync(); };
synchronize(); synchronize();
} }

View File

@ -43,11 +43,11 @@ struct Presentation : Window {
Item saveStateItem[5]; Item saveStateItem[5];
Menu loadStateMenu; Menu loadStateMenu;
Item loadStateItem[5]; Item loadStateItem[5];
Item synchronizeTime;
Separator stateMenuSeparator; Separator stateMenuSeparator;
Item resizeWindow; Item resizeWindow;
Item cheatEditor;
Item stateManager; Item stateManager;
Item cheatEditor;
Item synchronizeTime;
void synchronize(); void synchronize();
void setSystemName(const string &name); void setSystemName(const string &name);

View File

@ -102,7 +102,7 @@ int16_t DigitalInput::poll() {
// //
bool AnalogInput::bind(unsigned scancode, int16_t value) { bool RelativeInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard; using nall::Keyboard;
using nall::Mouse; using nall::Mouse;
@ -118,16 +118,9 @@ bool AnalogInput::bind(unsigned scancode, int16_t value) {
if(Joypad::isAnyAxis(scancode)) return append(encode); if(Joypad::isAnyAxis(scancode)) return append(encode);
return false; return false;
append:
if(mapping.position(encode)) return false; //mapping already bound
if(mapping.empty() || mapping == "None") mapping = encode; //remove "None"
else mapping.append(",", encode); //add to existing mapping list
AbstractInput::bind();
return true;
} }
int16_t AnalogInput::poll() { int16_t RelativeInput::poll() {
int16_t result = 0; int16_t result = 0;
for(auto &item : inputList) { for(auto &item : inputList) {
@ -144,6 +137,76 @@ int16_t AnalogInput::poll() {
// //
bool AbsoluteInput::bind(unsigned scancode, int16_t value) {
using nall::Keyboard;
using nall::Mouse;
if(scancode == Scancode::None || scancode == keyboard(0)[Keyboard::Escape]) {
inputList.reset();
mapping = "None";
return true;
}
string encode = Scancode::encode(scancode);
if(Mouse::isAnyAxis(scancode)) {
//only one input can be assigned for absolute positioning
inputList.reset();
mapping = encode;
return true;
}
return false;
}
int16_t AbsoluteInput::poll() {
int16_t result = -32768; //offscreen value
using nall::Mouse;
Position position = phoenix::Mouse::position();
Geometry geometry = presentation->geometry();
if(position.x < geometry.x
|| position.y < geometry.y
|| position.x >= geometry.x + geometry.width
|| position.y >= geometry.y + geometry.height) {
//cursor is offscreen
position.x = -32768;
position.y = -32768;
} else {
//convert from screen to viewport coordinates
double x = position.x - geometry.x;
double y = position.y - geometry.y;
//scale coordinate range to -0.5 to +0.5 (0.0 = center)
x = x * 1.0 / geometry.width - 0.5;
y = y * 1.0 / geometry.height - 0.5;
//scale coordinates to -32767 to +32767
signed px = (signed)(x * 65535.0);
signed py = (signed)(y * 65535.0);
//clamp to valid range
position.x = max(-32767, min(+32767, px));
position.y = max(-32767, min(+32767, py));
}
for(auto &item : inputList) {
if(item.scancode == mouse(0)[Mouse::Xaxis]) {
result = position.x;
}
if(item.scancode == mouse(0)[Mouse::Yaxis]) {
result = position.y;
}
}
return result;
}
//
HotkeyInput::HotkeyInput() { HotkeyInput::HotkeyInput() {
logic = 1; //AND logic = 1; //AND
inputManager->hotkeyMap.append(this); inputManager->hotkeyMap.append(this);
@ -201,8 +264,9 @@ void InputManager::bootstrap() {
AbstractInput *abstract = nullptr; AbstractInput *abstract = nullptr;
if(input.type == 0) abstract = new DigitalInput; if(input.type == 0) abstract = new DigitalInput;
if(input.type == 1) abstract = new AnalogInput; if(input.type == 1) abstract = new RelativeInput;
if(input.type >= 2) continue; if(input.type == 2) abstract = new AbsoluteInput;
if(input.type >= 3) continue;
abstract->name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name}; abstract->name = {emulator->information.name, "::", port.name, "::", device.name, "::", input.name};
abstract->name.replace(" ", ""); abstract->name.replace(" ", "");

View File

@ -23,7 +23,13 @@ struct DigitalInput : AbstractInput {
int16_t poll(); int16_t poll();
}; };
struct AnalogInput : AbstractInput { struct RelativeInput : AbstractInput {
using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value);
int16_t poll();
};
struct AbsoluteInput : AbstractInput {
using AbstractInput::bind; using AbstractInput::bind;
bool bind(unsigned scancode, int16_t value); bool bind(unsigned scancode, int16_t value);
int16_t poll(); int16_t poll();

View File

@ -1,8 +1,8 @@
#include "../ethos.hpp" #include "../ethos.hpp"
Interface *interface = nullptr; Interface *interface = nullptr;
void Interface::loadRequest(unsigned id, const string &name, const string &type, const string &path) { void Interface::loadRequest(unsigned id, const string &name, const string &type) {
return utility->loadRequest(id, name, type, path); return utility->loadRequest(id, name, type);
} }
void Interface::loadRequest(unsigned id, const string &path) { void Interface::loadRequest(unsigned id, const string &path) {

View File

@ -1,7 +1,7 @@
struct Interface : Emulator::Interface::Bind { struct Interface : Emulator::Interface::Bind {
void loadRequest(unsigned id, const string &name, const string &type);
void loadRequest(unsigned id, const string &path); void loadRequest(unsigned id, const string &path);
void saveRequest(unsigned id, const string &path); void saveRequest(unsigned id, const string &path);
void loadRequest(unsigned id, const string &name, const string &type, const string &path);
uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue); uint32_t videoColor(unsigned source, uint16_t red, uint16_t green, uint16_t blue);
void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height); void videoRefresh(const uint32_t *data, unsigned pitch, unsigned width, unsigned height);
void audioSample(int16_t lsample, int16_t rsample); void audioSample(int16_t lsample, int16_t rsample);

View File

@ -72,7 +72,8 @@ void InputSettings::synchronize() {
assign[2].setVisible(true); assign[2].setVisible(true);
} }
if(dynamic_cast<AnalogInput*>(selectedInput)) { if(dynamic_cast<RelativeInput*>(selectedInput)
|| dynamic_cast<AbsoluteInput*>(selectedInput)) {
assign[0].setText("Mouse X-axis"); assign[0].setText("Mouse X-axis");
assign[1].setText("Mouse Y-axis"); assign[1].setText("Mouse Y-axis");
assign[0].setVisible(true); assign[0].setVisible(true);
@ -170,7 +171,8 @@ void InputSettings::assignMouseInput(unsigned n) {
return inputEvent(mouse(0).button(n), 1, true); return inputEvent(mouse(0).button(n), 1, true);
} }
if(dynamic_cast<AnalogInput*>(activeInput)) { if(dynamic_cast<RelativeInput*>(activeInput)
|| dynamic_cast<AbsoluteInput*>(activeInput)) {
return inputEvent(mouse(0).axis(n), 1, true); return inputEvent(mouse(0).axis(n), 1, true);
} }
} }

View File

@ -11,7 +11,7 @@ void Utility::setInterface(Emulator::Interface *emulator) {
void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media) { void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media) {
string pathname; string pathname;
if(media.type != "sys") pathname = application->path({media.name, ".", media.type, "/"}); if(media.type != "sys") pathname = application->path({media.name, ".", media.type, "/"});
if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.extension); if(!directory::exists(pathname)) pathname = browser->select({"Load ", media.name}, media.path);
if(!directory::exists(pathname)) return; if(!directory::exists(pathname)) return;
if(!file::exists({pathname, "manifest.xml"})) return; if(!file::exists({pathname, "manifest.xml"})) return;
loadMedia(emulator, media, pathname); loadMedia(emulator, media, pathname);
@ -35,7 +35,7 @@ void Utility::loadMedia(Emulator::Interface *emulator, Emulator::Interface::Medi
} }
//request from emulation core to load non-volatile media folder //request from emulation core to load non-volatile media folder
void Utility::loadRequest(unsigned id, const string &name, const string &type, const string &path) { void Utility::loadRequest(unsigned id, const string &name, const string &type) {
string pathname = browser->select({"Load ", name}, type); string pathname = browser->select({"Load ", name}, type);
if(pathname.empty()) return; if(pathname.empty()) return;
this->path(system().group(id)) = pathname; this->path(system().group(id)) = pathname;
@ -43,8 +43,7 @@ void Utility::loadRequest(unsigned id, const string &name, const string &type, c
string manifest; string manifest;
manifest.readfile({pathname, "manifest.xml"}); manifest.readfile({pathname, "manifest.xml"});
mmapstream stream({pathname, path}); system().load(id, manifest);
system().load(id, stream, manifest);
} }
//request from emulation core to load non-volatile media file //request from emulation core to load non-volatile media file
@ -62,29 +61,6 @@ void Utility::saveRequest(unsigned id, const string &path) {
return system().save(id, stream); return system().save(id, stream);
} }
void Utility::loadMemory() {
// for(auto &memory : system().memory) {
// string pathname = path(system().group(memory.id));
// if(file::exists({pathname, memory.name}) == false) continue;
// filestream fs({pathname, memory.name});
// system().load(memory.id, fs);
// }
cheatEditor->load({pathname[0], "cheats.xml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
}
void Utility::saveMemory() {
// for(auto &memory : system().memory) {
// string pathname = path(system().group(memory.id));
// filestream fs({pathname, memory.name}, file::mode::write);
// system().save(memory.id, fs);
// }
cheatEditor->save({pathname[0], "cheats.xml"});
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
}
void Utility::connect(unsigned port, unsigned device) { void Utility::connect(unsigned port, unsigned device) {
if(application->active == nullptr) return; if(application->active == nullptr) return;
system().connect(port, device); system().connect(port, device);
@ -110,7 +86,8 @@ void Utility::load() {
title.rtrim<1>(" + "); title.rtrim<1>(" + ");
presentation->setTitle(title); presentation->setTitle(title);
loadMemory(); cheatEditor->load({pathname[0], "cheats.xml"});
stateManager->load({pathname[0], "bsnes/states.bsa"}, 1);
system().paletteUpdate(); system().paletteUpdate();
synchronizeDSP(); synchronizeDSP();
@ -124,7 +101,9 @@ void Utility::unload() {
if(application->active == nullptr) return; if(application->active == nullptr) return;
if(tracerEnable) tracerToggle(); if(tracerEnable) tracerToggle();
saveMemory(); cheatEditor->save({pathname[0], "cheats.xml"});
stateManager->save({pathname[0], "bsnes/states.bsa"}, 1);
system().unload(); system().unload();
path.reset(); path.reset();
pathname.reset(); pathname.reset();
@ -135,8 +114,9 @@ void Utility::unload() {
video.clear(); video.clear();
audio.clear(); audio.clear();
presentation->setTitle({Emulator::Name, " v", Emulator::Version}); presentation->setTitle({Emulator::Name, " v", Emulator::Version});
cheatEditor->synchronize(); cheatDatabase->setVisible(false);
stateManager->synchronize(); cheatEditor->setVisible(false);
stateManager->setVisible(false);
} }
void Utility::saveState(unsigned slot) { void Utility::saveState(unsigned slot) {

View File

@ -4,11 +4,9 @@ struct Utility {
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media); void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media);
void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname); void loadMedia(Emulator::Interface *emulator, Emulator::Interface::Media &media, const string &pathname);
void loadRequest(unsigned id, const string &name, const string &type, const string &path); void loadRequest(unsigned id, const string &name, const string &type);
void loadRequest(unsigned id, const string &path); void loadRequest(unsigned id, const string &path);
void saveRequest(unsigned id, const string &path); void saveRequest(unsigned id, const string &path);
void loadMemory();
void saveMemory();
void connect(unsigned port, unsigned device); void connect(unsigned port, unsigned device);
void power(); void power();