mirror of https://github.com/bsnes-emu/bsnes.git
Update to v099r09 release.
byuu says: Changelog: - Emulator::Interface::Medium::bootable removed - Emulator::Interface::load(bool required) argument removed [File::Required makes no sense on a folder] - Super Famicom.sys now has user-configurable properties (CPU,PPU1,PPU2 version; PPU1 VRAM size, Region override) - old nall/property removed completely - volatile flags supported on coprocessor RAM files now (still not in icarus, though) - (hopefully) fixed SNES Multitap support (needs testing) - fixed an OAM tiledata range clipping limit in 128KiB VRAM mode (doesn't fix Yoshi's Island, sadly) - (hopefully, again) fixed the input polling bug hex_usr reported - re-added dialog box for when File::Required files are missing - really cool: if you're missing a boot ROM, BIOS ROM, or IPL ROM, it warns you immediately - you don't have to select a game before seeing the error message anymore - fixed cheats.bml load/save location
This commit is contained in:
parent
f48b332c83
commit
3a9c7c6843
|
@ -11,7 +11,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "higan";
|
||||
static const string Version = "099.08";
|
||||
static const string Version = "099.09";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
|
|
@ -20,8 +20,7 @@ struct Interface {
|
|||
struct Medium {
|
||||
uint id;
|
||||
string name;
|
||||
string type;
|
||||
bool bootable; //false for cartridge slots (eg Sufami Turbo cartridges)
|
||||
string type; //extension
|
||||
};
|
||||
vector<Medium> media;
|
||||
|
||||
|
@ -46,7 +45,7 @@ struct Interface {
|
|||
struct Bind {
|
||||
virtual auto path(uint) -> string { return ""; }
|
||||
virtual auto open(uint, string, vfs::file::mode, bool) -> vfs::shared::file { return {}; }
|
||||
virtual auto load(uint, string, string, bool) -> maybe<uint> { return nothing; }
|
||||
virtual auto load(uint, string, string) -> maybe<uint> { return nothing; }
|
||||
virtual auto videoRefresh(const uint32*, uint, uint, uint) -> void {}
|
||||
virtual auto audioSample(const double*, uint) -> void {}
|
||||
virtual auto inputPoll(uint, uint, uint) -> int16 { return 0; }
|
||||
|
@ -59,7 +58,7 @@ struct Interface {
|
|||
//callback bindings (provided by user interface)
|
||||
auto path(uint id) -> string { return bind->path(id); }
|
||||
auto open(uint id, string name, vfs::file::mode mode, bool required = false) -> vfs::shared::file { return bind->open(id, name, mode, required); }
|
||||
auto load(uint id, string name, string type, bool required = false) -> maybe<uint> { return bind->load(id, name, type, required); }
|
||||
auto load(uint id, string name, string type) -> maybe<uint> { return bind->load(id, name, type); }
|
||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void { return bind->videoRefresh(data, pitch, width, height); }
|
||||
auto audioSample(const double* samples, uint channels) -> void { return bind->audioSample(samples, channels); }
|
||||
auto inputPoll(uint port, uint device, uint input) -> int16 { return bind->inputPoll(port, device, input); }
|
||||
|
|
|
@ -15,7 +15,7 @@ auto Cartridge::main() -> void {
|
|||
}
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
if(auto pathID = interface->load(ID::Famicom, "Famicom", "fc", File::Required)) {
|
||||
if(auto pathID = interface->load(ID::Famicom, "Famicom", "fc")) {
|
||||
information.pathID = pathID();
|
||||
}
|
||||
|
||||
|
|
|
@ -24,14 +24,13 @@ auto Gamepad::latch(bool data) -> void {
|
|||
counter = 0;
|
||||
|
||||
if(latched == 0) {
|
||||
auto id = ID::Device::Gamepad;
|
||||
a = interface->inputPoll(port, id, A);
|
||||
b = interface->inputPoll(port, id, B);
|
||||
select = interface->inputPoll(port, id, Select);
|
||||
start = interface->inputPoll(port, id, Start);
|
||||
up = interface->inputPoll(port, id, Up);
|
||||
down = interface->inputPoll(port, id, Down);
|
||||
left = interface->inputPoll(port, id, Left);
|
||||
right = interface->inputPoll(port, id, Right);
|
||||
a = interface->inputPoll(port, ID::Device::Gamepad, A);
|
||||
b = interface->inputPoll(port, ID::Device::Gamepad, B);
|
||||
select = interface->inputPoll(port, ID::Device::Gamepad, Select);
|
||||
start = interface->inputPoll(port, ID::Device::Gamepad, Start);
|
||||
up = interface->inputPoll(port, ID::Device::Gamepad, Up);
|
||||
down = interface->inputPoll(port, ID::Device::Gamepad, Down);
|
||||
left = interface->inputPoll(port, ID::Device::Gamepad, Left);
|
||||
right = interface->inputPoll(port, ID::Device::Gamepad, Right);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -19,7 +19,7 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::Famicom, "Famicom", "fc", true});
|
||||
media.append({ID::Famicom, "Famicom", "fc"});
|
||||
|
||||
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
|
||||
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
|
||||
|
|
|
@ -19,17 +19,17 @@ auto Cartridge::load(System::Revision revision) -> bool {
|
|||
|
||||
switch(revision) {
|
||||
case System::Revision::GameBoy:
|
||||
if(auto pathID = interface->load(ID::GameBoy, "Game Boy", "gb", true)) {
|
||||
if(auto pathID = interface->load(ID::GameBoy, "Game Boy", "gb")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
case System::Revision::SuperGameBoy:
|
||||
if(auto pathID = interface->load(ID::SuperGameBoy, "Game Boy", "gb", true)) {
|
||||
if(auto pathID = interface->load(ID::SuperGameBoy, "Game Boy", "gb")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
case System::Revision::GameBoyColor:
|
||||
if(auto pathID = interface->load(ID::GameBoyColor, "Game Boy Color", "gbc", true)) {
|
||||
if(auto pathID = interface->load(ID::GameBoyColor, "Game Boy Color", "gbc")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
|
@ -92,7 +92,7 @@ auto Cartridge::load(System::Revision revision) -> bool {
|
|||
case Mapper::HuC3: mapper = &huc3; break;
|
||||
}
|
||||
|
||||
sha256 = Hash::SHA256(romdata, romsize).digest();
|
||||
information.sha256 = Hash::SHA256(romdata, romsize).digest();
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
struct Cartridge : MMIO, property<Cartridge> {
|
||||
struct Cartridge : MMIO {
|
||||
auto pathID() const -> uint { return information.pathID; }
|
||||
auto sha256() const -> string { return information.sha256; }
|
||||
auto manifest() const -> string { return information.manifest; }
|
||||
auto title() const -> string { return information.title; }
|
||||
|
||||
|
@ -44,6 +45,7 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
string sha256;
|
||||
string manifest;
|
||||
string title;
|
||||
|
||||
|
@ -57,9 +59,6 @@ struct Cartridge : MMIO, property<Cartridge> {
|
|||
uint ramsize = 0;
|
||||
} information;
|
||||
|
||||
uint _pathID = 0;
|
||||
readonly<string> sha256;
|
||||
|
||||
uint8* romdata = nullptr;
|
||||
uint romsize = 0;
|
||||
|
||||
|
|
|
@ -20,8 +20,8 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::GameBoy, "Game Boy", "gb" , true});
|
||||
media.append({ID::GameBoyColor, "Game Boy Color", "gbc", true});
|
||||
media.append({ID::GameBoy, "Game Boy", "gb" });
|
||||
media.append({ID::GameBoyColor, "Game Boy Color", "gbc"});
|
||||
|
||||
Port hardwarePort{ID::Port::Hardware, "Hardware"};
|
||||
|
||||
|
|
|
@ -26,7 +26,7 @@ Cartridge::~Cartridge() {
|
|||
auto Cartridge::load() -> bool {
|
||||
information = Information();
|
||||
|
||||
if(auto pathID = interface->load(ID::GameBoyAdvance, "Game Boy Advance", "gba", File::Required)) {
|
||||
if(auto pathID = interface->load(ID::GameBoyAdvance, "Game Boy Advance", "gba")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
|
||||
|
|
|
@ -19,7 +19,7 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = false;
|
||||
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba", true});
|
||||
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"});
|
||||
|
||||
Port hardwarePort{ID::Port::Hardware, "Hardware"};
|
||||
|
||||
|
|
|
@ -1,3 +1,12 @@
|
|||
system name:Super Famicom
|
||||
system region=auto name:Super Famicom
|
||||
cpu version=2
|
||||
ram name=work.ram size=0x20000 volatile
|
||||
smp
|
||||
rom name=ipl.rom size=64
|
||||
ppu1 version=1
|
||||
ram name=video.ram size=0x8000 volatile
|
||||
ram name=object.ram size=544 volatile
|
||||
ppu2 version=3
|
||||
ram name=palette.ram size=512 volatile
|
||||
dsp
|
||||
ram name=apu.ram size=0x20000 volatile
|
||||
|
|
|
@ -29,7 +29,7 @@ auto Cartridge::load() -> bool {
|
|||
information = Information();
|
||||
has = Has();
|
||||
|
||||
if(auto pathID = interface->load(ID::SuperFamicom, "Super Famicom", "sfc", File::Required)) {
|
||||
if(auto pathID = interface->load(ID::SuperFamicom, "Super Famicom", "sfc")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
|
||||
|
|
|
@ -9,7 +9,7 @@ auto Cartridge::loadCartridge(Markup::Node node) -> void {
|
|||
}
|
||||
}
|
||||
if(board["sufamiturbo"]) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo", "st")) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboA, "Sufami Turbo - Slot A", "st")) {
|
||||
sufamiturboA.pathID = pathID();
|
||||
}
|
||||
}
|
||||
|
@ -53,7 +53,7 @@ auto Cartridge::loadSufamiTurboA(Markup::Node node) -> void {
|
|||
loadMemory(sufamiturboA.ram, node["board/ram"], File::Optional, sufamiturboA.pathID);
|
||||
|
||||
if(node["board/linkable"]) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo", "st")) {
|
||||
if(auto pathID = interface->load(ID::SufamiTurboB, "Sufami Turbo - Slot B", "st")) {
|
||||
sufamiturboB.pathID = pathID();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -54,33 +54,41 @@ auto Cartridge::saveSuperFX(Markup::Node node) -> void {
|
|||
}
|
||||
|
||||
auto Cartridge::saveARMDSP(Markup::Node node) -> void {
|
||||
if(auto name = node["ram"]["name"].text()) {
|
||||
if(!node["ram/volatile"]) {
|
||||
if(auto name = node["ram/name"].text()) {
|
||||
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) {
|
||||
for(auto n : range(16 * 1024)) fp->write(armdsp.programRAM[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::saveHitachiDSP(Markup::Node node) -> void {
|
||||
saveMemory(hitachidsp.ram, node["ram"]);
|
||||
if(auto name = node["dram"]["name"].text()) {
|
||||
|
||||
if(!node["dram/volatile"]) {
|
||||
if(auto name = node["dram/name"].text()) {
|
||||
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) {
|
||||
for(auto n : range(3 * 1024)) fp->write(hitachidsp.dataRAM[n]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::saveNECDSP(Markup::Node node) -> void {
|
||||
if(!node["dram/volatile"]) {
|
||||
uint size = necdsp.revision == NECDSP::Revision::uPD7725 ? 256 : 2048;
|
||||
if(auto name = node["dram"]["name"].text()) {
|
||||
if(auto name = node["dram/name"].text()) {
|
||||
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) {
|
||||
for(auto n : range(size)) fp->writel(necdsp.dataRAM[n], 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
|
||||
if(auto name = node["ram"]["name"].text()) {
|
||||
if(!node["ram/volatile"]) {
|
||||
if(auto name = node["ram/name"].text()) {
|
||||
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) {
|
||||
uint8 data[16] = {0};
|
||||
epsonrtc.save(data);
|
||||
|
@ -88,9 +96,11 @@ auto Cartridge::saveEpsonRTC(Markup::Node node) -> void {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::saveSharpRTC(Markup::Node node) -> void {
|
||||
if(auto name = node["ram"]["name"].text()) {
|
||||
if(!node["ram/volatile"]) {
|
||||
if(auto name = node["ram/name"].text()) {
|
||||
if(auto fp = interface->open(ID::SuperFamicom, name, File::Write)) {
|
||||
uint8 data[16] = {0};
|
||||
sharprtc.save(data);
|
||||
|
@ -98,6 +108,7 @@ auto Cartridge::saveSharpRTC(Markup::Node node) -> void {
|
|||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
auto Cartridge::saveSPC7110(Markup::Node node) -> void {
|
||||
saveMemory(spc7110.ram, node["ram"]);
|
||||
|
|
|
@ -1,10 +1,6 @@
|
|||
Gamepad::Gamepad(bool port) : Controller(port) {
|
||||
latched = 0;
|
||||
counter = 0;
|
||||
|
||||
b = y = select = start = 0;
|
||||
up = down = left = right = 0;
|
||||
a = x = l = r = 0;
|
||||
}
|
||||
|
||||
auto Gamepad::data() -> uint2 {
|
||||
|
@ -36,18 +32,17 @@ auto Gamepad::latch(bool data) -> void {
|
|||
counter = 0;
|
||||
|
||||
if(latched == 0) {
|
||||
auto id = ID::Device::Gamepad;
|
||||
b = interface->inputPoll(port, id, B);
|
||||
y = interface->inputPoll(port, id, Y);
|
||||
select = interface->inputPoll(port, id, Select);
|
||||
start = interface->inputPoll(port, id, Start);
|
||||
up = interface->inputPoll(port, id, Up);
|
||||
down = interface->inputPoll(port, id, Down);
|
||||
left = interface->inputPoll(port, id, Left);
|
||||
right = interface->inputPoll(port, id, Right);
|
||||
a = interface->inputPoll(port, id, A);
|
||||
x = interface->inputPoll(port, id, X);
|
||||
l = interface->inputPoll(port, id, L);
|
||||
r = interface->inputPoll(port, id, R);
|
||||
b = interface->inputPoll(port, ID::Device::Gamepad, B);
|
||||
y = interface->inputPoll(port, ID::Device::Gamepad, Y);
|
||||
select = interface->inputPoll(port, ID::Device::Gamepad, Select);
|
||||
start = interface->inputPoll(port, ID::Device::Gamepad, Start);
|
||||
up = interface->inputPoll(port, ID::Device::Gamepad, Up);
|
||||
down = interface->inputPoll(port, ID::Device::Gamepad, Down);
|
||||
left = interface->inputPoll(port, ID::Device::Gamepad, Left);
|
||||
right = interface->inputPoll(port, ID::Device::Gamepad, Right);
|
||||
a = interface->inputPoll(port, ID::Device::Gamepad, A);
|
||||
x = interface->inputPoll(port, ID::Device::Gamepad, X);
|
||||
l = interface->inputPoll(port, ID::Device::Gamepad, L);
|
||||
r = interface->inputPoll(port, ID::Device::Gamepad, R);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ private:
|
|||
bool latched;
|
||||
uint counter;
|
||||
|
||||
bool b, y, select, start;
|
||||
bool up, down, left, right;
|
||||
bool a, x, l, r;
|
||||
boolean b, y, select, start;
|
||||
boolean up, down, left, right;
|
||||
boolean a, x, l, r;
|
||||
};
|
||||
|
|
|
@ -6,27 +6,42 @@ Multitap::Multitap(bool port) : Controller(port) {
|
|||
|
||||
auto Multitap::data() -> uint2 {
|
||||
if(latched) return 2; //multitap detection
|
||||
uint index, port1, port2;
|
||||
uint counter, a, b;
|
||||
|
||||
if(iobit()) {
|
||||
index = counter1;
|
||||
if(index >= 16) return 3;
|
||||
counter = counter1;
|
||||
if(counter >= 16) return 3;
|
||||
counter1++;
|
||||
if(index >= 12) return 0;
|
||||
port1 = 0; //controller 1
|
||||
port2 = 1; //controller 2
|
||||
if(counter >= 12) return 0;
|
||||
a = 0; //controller 1
|
||||
b = 1; //controller 2
|
||||
} else {
|
||||
index = counter2;
|
||||
if(index >= 16) return 3;
|
||||
counter = counter2;
|
||||
if(counter >= 16) return 3;
|
||||
counter2++;
|
||||
if(index >= 12) return 0;
|
||||
port1 = 2; //controller 3
|
||||
port2 = 3; //controller 4
|
||||
if(counter >= 12) return 0;
|
||||
a = 2; //controller 3
|
||||
b = 3; //controller 4
|
||||
}
|
||||
|
||||
bool data1 = interface->inputPoll(port, ID::Device::Multitap, port1 * 12 + index);
|
||||
bool data2 = interface->inputPoll(port, ID::Device::Multitap, port2 * 12 + index);
|
||||
return data2 << 1 | data1 << 0;
|
||||
auto& A = gamepads[a];
|
||||
auto& B = gamepads[b];
|
||||
|
||||
switch(counter) {
|
||||
case 0: return A.b << 0 | B.b << 1;
|
||||
case 1: return A.y << 0 | B.y << 1;
|
||||
case 2: return A.select << 0 | B.select << 1;
|
||||
case 3: return A.start << 0 | B.start << 1;
|
||||
case 4: return (A.up & !A.down) << 0 | (B.up & !B.down) << 1;
|
||||
case 5: return (A.down & !A.up) << 0 | (B.down & !B.up) << 1;
|
||||
case 6: return (A.left & !A.right) << 0 | (B.left & !B.right) << 1;
|
||||
case 7: return (A.right & !A.left) << 0 | (B.right & !B.left) << 1;
|
||||
case 8: return A.a << 0 | B.a << 1;
|
||||
case 9: return A.x << 0 | B.x << 1;
|
||||
case 10: return A.l << 0 | B.l << 1;
|
||||
case 11: return A.r << 0 | B.r << 1;
|
||||
}
|
||||
unreachable;
|
||||
}
|
||||
|
||||
auto Multitap::latch(bool data) -> void {
|
||||
|
@ -34,4 +49,22 @@ auto Multitap::latch(bool data) -> void {
|
|||
latched = data;
|
||||
counter1 = 0;
|
||||
counter2 = 0;
|
||||
|
||||
if(latched == 0) {
|
||||
for(uint id : range(4)) {
|
||||
auto& gamepad = gamepads[id];
|
||||
gamepad.b = interface->inputPoll(port, ID::Device::Multitap, id * 12 + B);
|
||||
gamepad.y = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Y);
|
||||
gamepad.select = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Select);
|
||||
gamepad.start = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Start);
|
||||
gamepad.up = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Up);
|
||||
gamepad.down = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Down);
|
||||
gamepad.left = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Left);
|
||||
gamepad.right = interface->inputPoll(port, ID::Device::Multitap, id * 12 + Right);
|
||||
gamepad.a = interface->inputPoll(port, ID::Device::Multitap, id * 12 + A);
|
||||
gamepad.x = interface->inputPoll(port, ID::Device::Multitap, id * 12 + X);
|
||||
gamepad.l = interface->inputPoll(port, ID::Device::Multitap, id * 12 + L);
|
||||
gamepad.r = interface->inputPoll(port, ID::Device::Multitap, id * 12 + R);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,4 +1,8 @@
|
|||
struct Multitap : Controller {
|
||||
enum : uint {
|
||||
Up, Down, Left, Right, B, A, Y, X, L, R, Select, Start,
|
||||
};
|
||||
|
||||
Multitap(bool port);
|
||||
|
||||
auto data() -> uint2;
|
||||
|
@ -8,4 +12,10 @@ private:
|
|||
bool latched;
|
||||
uint counter1;
|
||||
uint counter2;
|
||||
|
||||
struct Gamepad {
|
||||
boolean b, y, select, start;
|
||||
boolean up, down, left, right;
|
||||
boolean a, x, l, r;
|
||||
} gamepads[4];
|
||||
};
|
||||
|
|
|
@ -18,7 +18,7 @@ struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread {
|
|||
auto joypWrite(bool p15, bool p14) -> void override;
|
||||
|
||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
||||
auto load(uint id, string name, string type, bool required) -> maybe<uint> override;
|
||||
auto load(uint id, string name, string type) -> maybe<uint> override;
|
||||
|
||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override;
|
||||
auto audioSample(const double* samples, uint channels) -> void override;
|
||||
|
|
|
@ -92,8 +92,8 @@ auto ICD2::open(uint id, string name, vfs::file::mode mode, bool required) -> vf
|
|||
return interface->open(id, name, mode, required);
|
||||
}
|
||||
|
||||
auto ICD2::load(uint id, string name, string type, bool required) -> maybe<uint> {
|
||||
return interface->load(id, name, type, required);
|
||||
auto ICD2::load(uint id, string name, string type) -> maybe<uint> {
|
||||
return interface->load(id, name, type);
|
||||
}
|
||||
|
||||
auto ICD2::videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void {
|
||||
|
|
|
@ -85,6 +85,11 @@ auto CPU::main() -> void {
|
|||
instruction();
|
||||
}
|
||||
|
||||
auto CPU::load(Markup::Node node) -> bool {
|
||||
version = max(1, min(2, node["cpu/version"].natural()));
|
||||
return true;
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
for(auto& byte : wram) byte = random(0x55);
|
||||
|
||||
|
|
|
@ -16,6 +16,7 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
|
|
@ -228,6 +228,10 @@ auto DSP::write(uint8 addr, uint8 data) -> void {
|
|||
|
||||
/* initialization */
|
||||
|
||||
auto DSP::load(Markup::Node node) -> bool {
|
||||
return true;
|
||||
}
|
||||
|
||||
auto DSP::power() -> void {
|
||||
memory::fill(&state, sizeof(State));
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@ struct DSP : Thread {
|
|||
auto write(uint8 addr, uint8 data) -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
|
|
@ -20,10 +20,7 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "sfc", true });
|
||||
media.append({ID::SuperFamicom, "Game Boy", "gb", false});
|
||||
media.append({ID::SuperFamicom, "BS Memory", "bs", false});
|
||||
media.append({ID::SuperFamicom, "Sufami Turbo", "st", false});
|
||||
media.append({ID::SuperFamicom, "Super Famicom", "sfc"});
|
||||
|
||||
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};
|
||||
Port controllerPort2{ID::Port::Controller2, "Controller Port 2"};
|
||||
|
@ -53,7 +50,7 @@ Interface::Interface() {
|
|||
}
|
||||
|
||||
{ Device device{ID::Device::Multitap, "Multitap"};
|
||||
for(uint p = 1, n = 0; p <= 4; p++, n += 12) {
|
||||
for(uint p = 1; p <= 4; p++) {
|
||||
device.inputs.append({0, {"Port ", p, " - ", "Up" }});
|
||||
device.inputs.append({0, {"Port ", p, " - ", "Down" }});
|
||||
device.inputs.append({0, {"Port ", p, " - ", "Left" }});
|
||||
|
|
|
@ -10,6 +10,12 @@ struct ID {
|
|||
SufamiTurboB,
|
||||
};
|
||||
|
||||
struct Port { enum : uint {
|
||||
Controller1,
|
||||
Controller2,
|
||||
Expansion,
|
||||
};};
|
||||
|
||||
struct Device { enum : uint {
|
||||
None,
|
||||
Gamepad,
|
||||
|
@ -23,12 +29,6 @@ struct ID {
|
|||
SuperDisc,
|
||||
S21FX,
|
||||
};};
|
||||
|
||||
struct Port { enum : uint {
|
||||
Controller1,
|
||||
Controller2,
|
||||
Expansion,
|
||||
};};
|
||||
};
|
||||
|
||||
struct Interface : Emulator::Interface {
|
||||
|
|
|
@ -139,7 +139,7 @@ auto PPU::Object::tilefetch() -> void {
|
|||
|
||||
uint mx = !sprite.hflip ? tx : (tileWidth - 1) - tx;
|
||||
uint pos = tiledataAddress + ((chry + ((chrx + mx) & 15)) << 4);
|
||||
uint15 addr = (pos & 0x7ff0) + (y & 7);
|
||||
uint16 addr = (pos & 0xfff0) + (y & 7);
|
||||
|
||||
oamTile[n].data.bits( 0,15) = ppu.vram[addr + 0];
|
||||
ppu.addClocks(2);
|
||||
|
|
|
@ -87,6 +87,14 @@ auto PPU::addClocks(uint clocks) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
auto PPU::load(Markup::Node node) -> bool {
|
||||
ppu1.version = max(1, min(1, node["ppu1/version"].natural()));
|
||||
ppu2.version = max(1, min(3, node["ppu2/version"].natural()));
|
||||
ppu.vram.mask = node["ppu1/ram/size"].natural() - 1;
|
||||
if(ppu.vram.mask != 0xffff) ppu.vram.mask = 0x7fff;
|
||||
return true;
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
for(auto& n : vram.data) n = random(0x0000);
|
||||
for(auto& n : oam) n = random(0x00);
|
||||
|
|
|
@ -11,6 +11,7 @@ struct PPU : Thread, PPUcounter {
|
|||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
@ -33,9 +34,9 @@ struct PPU : Thread, PPUcounter {
|
|||
|
||||
privileged:
|
||||
struct VRAM {
|
||||
auto& operator[](uint offset) { return data[offset & size - 1]; }
|
||||
auto& operator[](uint offset) { return data[offset & mask]; }
|
||||
uint16 data[64 * 1024];
|
||||
uint size = 0x8000;
|
||||
uint mask = 0x7fff;
|
||||
} vram;
|
||||
uint8 oam[544];
|
||||
uint8 cgram[512];
|
||||
|
|
|
@ -14,8 +14,8 @@ auto PPU::serialize(serializer& s) -> void {
|
|||
Thread::serialize(s);
|
||||
PPUcounter::serialize(s);
|
||||
|
||||
s.integer(vram.size);
|
||||
s.array(vram.data, vram.size);
|
||||
s.integer(vram.mask);
|
||||
s.array(vram.data, vram.mask + 1);
|
||||
s.array(oam);
|
||||
s.array(cgram);
|
||||
|
||||
|
|
|
@ -30,6 +30,16 @@ auto SMP::main() -> void {
|
|||
instruction();
|
||||
}
|
||||
|
||||
auto SMP::load(Markup::Node node) -> bool {
|
||||
if(auto name = node["smp/rom/name"].text()) {
|
||||
if(auto fp = interface->open(ID::System, name, File::Read, File::Required)) {
|
||||
fp->read(iplrom, 64);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
auto SMP::power() -> void {
|
||||
//targets not initialized/changed upon reset
|
||||
timer0.target = 0;
|
||||
|
|
|
@ -9,6 +9,7 @@ struct SMP : Processor::SPC700, Thread {
|
|||
auto portWrite(uint2 port, uint8 data) -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto load(Markup::Node) -> bool;
|
||||
auto power() -> void;
|
||||
auto reset() -> void;
|
||||
|
||||
|
|
|
@ -1,11 +0,0 @@
|
|||
struct Peripherals {
|
||||
auto unload() -> void;
|
||||
auto reset() -> void;
|
||||
auto connect(uint port, uint device) -> void;
|
||||
|
||||
Controller* controllerPort1 = nullptr;
|
||||
Controller* controllerPort2 = nullptr;
|
||||
Expansion* expansionPort = nullptr;
|
||||
};
|
||||
|
||||
extern Peripherals peripherals;
|
|
@ -1,5 +1,5 @@
|
|||
auto System::serialize() -> serializer {
|
||||
serializer s(_serializeSize);
|
||||
serializer s(serializeSize);
|
||||
|
||||
uint signature = 0x31545342;
|
||||
char version[16] = {0};
|
||||
|
@ -39,7 +39,7 @@ auto System::unserialize(serializer& s) -> bool {
|
|||
//internal
|
||||
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
s.integer((uint&)_region);
|
||||
s.integer((uint&)information.region);
|
||||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
|
@ -86,5 +86,5 @@ auto System::serializeInit() -> void {
|
|||
s.array(description);
|
||||
|
||||
serializeAll(s);
|
||||
_serializeSize = s.size();
|
||||
serializeSize = s.size();
|
||||
}
|
||||
|
|
|
@ -52,24 +52,28 @@ auto System::term() -> void {
|
|||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
bus.reset();
|
||||
information = Information();
|
||||
|
||||
if(auto fp = interface->open(ID::System, "manifest.bml", File::Read, File::Required)) {
|
||||
information.manifest = fp->reads();
|
||||
} else return false;
|
||||
|
||||
auto document = BML::unserialize(information.manifest);
|
||||
auto system = document["system"];
|
||||
|
||||
if(auto iplrom = document["system/smp/rom/name"].text()) {
|
||||
if(auto fp = interface->open(ID::System, iplrom, File::Read, File::Required)) {
|
||||
fp->read(smp.iplrom, 64);
|
||||
} else return false;
|
||||
}
|
||||
|
||||
bus.reset();
|
||||
if(!cpu.load(system)) return false;
|
||||
if(!smp.load(system)) return false;
|
||||
if(!ppu.load(system)) return false;
|
||||
if(!dsp.load(system)) return false;
|
||||
if(!cartridge.load()) return false;
|
||||
_region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
|
||||
_cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370;
|
||||
_apuFrequency = 24'606'720;
|
||||
|
||||
information.region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
|
||||
if(system["region"].text() == "NTSC") information.region = Region::NTSC;
|
||||
if(system["region"].text() == "PAL" ) information.region = Region::PAL;
|
||||
|
||||
information.cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370;
|
||||
information.apuFrequency = 24'606'720;
|
||||
|
||||
if(cartridge.has.ICD2) icd2.load();
|
||||
if(cartridge.has.MCC) mcc.load();
|
||||
|
@ -91,7 +95,7 @@ auto System::load() -> bool {
|
|||
if(cartridge.has.SufamiTurboSlots) sufamiturboA.load(), sufamiturboB.load();
|
||||
|
||||
serializeInit();
|
||||
return _loaded = true;
|
||||
return information.loaded = true;
|
||||
}
|
||||
|
||||
auto System::save() -> void {
|
||||
|
@ -123,7 +127,7 @@ auto System::unload() -> void {
|
|||
if(cartridge.has.SufamiTurboSlots) sufamiturboA.unload(), sufamiturboB.unload();
|
||||
|
||||
cartridge.unload();
|
||||
_loaded = false;
|
||||
information.loaded = false;
|
||||
}
|
||||
|
||||
auto System::power() -> void {
|
||||
|
|
|
@ -1,14 +1,12 @@
|
|||
struct Interface;
|
||||
|
||||
#include "peripherals.hpp"
|
||||
|
||||
struct System {
|
||||
enum class Region : bool { NTSC = 0, PAL = 1 };
|
||||
|
||||
inline auto loaded() const -> bool { return _loaded; }
|
||||
inline auto region() const -> Region { return _region; }
|
||||
inline auto cpuFrequency() const -> uint { return _cpuFrequency; }
|
||||
inline auto apuFrequency() const -> uint { return _apuFrequency; }
|
||||
inline auto loaded() const -> bool { return information.loaded; }
|
||||
inline auto region() const -> Region { return information.region; }
|
||||
inline auto cpuFrequency() const -> uint { return information.cpuFrequency; }
|
||||
inline auto apuFrequency() const -> uint { return information.apuFrequency; }
|
||||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
|
@ -29,26 +27,33 @@ struct System {
|
|||
auto serialize() -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
string manifest;
|
||||
bool loaded = false;
|
||||
Region region = Region::NTSC;
|
||||
uint cpuFrequency = 0;
|
||||
uint apuFrequency = 0;
|
||||
} information;
|
||||
|
||||
private:
|
||||
uint serializeSize = 0;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
auto serializeAll(serializer&) -> void;
|
||||
auto serializeInit() -> void;
|
||||
|
||||
bool _loaded = false;
|
||||
Region _region = Region::NTSC;
|
||||
uint _cpuFrequency = 0;
|
||||
uint _apuFrequency = 0;
|
||||
uint _serializeSize = 0;
|
||||
|
||||
friend class Cartridge;
|
||||
friend class Device;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
struct Peripherals {
|
||||
auto unload() -> void;
|
||||
auto reset() -> void;
|
||||
auto connect(uint port, uint device) -> void;
|
||||
|
||||
Controller* controllerPort1 = nullptr;
|
||||
Controller* controllerPort2 = nullptr;
|
||||
Expansion* expansionPort = nullptr;
|
||||
};
|
||||
|
||||
struct Random {
|
||||
auto seed(uint seed) -> void;
|
||||
|
@ -59,4 +64,6 @@ private:
|
|||
uint iter = 0;
|
||||
};
|
||||
|
||||
extern System system;
|
||||
extern Peripherals peripherals;
|
||||
extern Random random;
|
||||
|
|
|
@ -151,16 +151,15 @@ InputManager::InputManager() {
|
|||
inputEmulator.name = emulator->information.name;
|
||||
|
||||
for(auto& port : emulator->ports) {
|
||||
inputEmulator.ports.append(InputPort());
|
||||
auto& inputPort = inputEmulator.ports.right();
|
||||
auto& inputPort = inputEmulator.ports(port.id);
|
||||
inputPort.name = port.name;
|
||||
for(auto& device : port.devices) {
|
||||
inputPort.devices.append(InputDevice());
|
||||
auto& inputDevice = inputPort.devices.right();
|
||||
auto& inputDevice = inputPort.devices(device.id);
|
||||
inputDevice.name = device.name;
|
||||
for(auto& input : device.inputs) {
|
||||
inputDevice.mappings.append(new InputMapping());
|
||||
auto& inputMapping = inputDevice.mappings.right();
|
||||
auto inputMapping = new InputMapping;
|
||||
inputDevice.mappings.append(inputMapping);
|
||||
|
||||
inputMapping->name = input.name;
|
||||
inputMapping->link = &input;
|
||||
input.userData = (uintptr)inputMapping;
|
||||
|
|
|
@ -7,7 +7,6 @@ Presentation::Presentation() {
|
|||
libraryMenu.setText("Library");
|
||||
for(auto& emulator : program->emulators) {
|
||||
for(auto& medium : emulator->media) {
|
||||
if(!medium.bootable) continue;
|
||||
auto item = new MenuItem{&libraryMenu};
|
||||
item->setText({medium.name, " ..."}).onActivate([=] {
|
||||
program->loadMedium(*emulator, medium);
|
||||
|
|
|
@ -9,10 +9,16 @@ auto Program::open(uint id, string name, vfs::file::mode mode, bool required) ->
|
|||
return vfs::memory::file::open(manifest.output.data<uint8_t>(), manifest.output.size());
|
||||
}
|
||||
}
|
||||
if(required) {
|
||||
MessageDialog()
|
||||
.setTitle({"Error"})
|
||||
.setText({"Error: missing required file:\n\n", path(id), name})
|
||||
.error();
|
||||
}
|
||||
return {};
|
||||
}
|
||||
|
||||
auto Program::load(uint id, string name, string type, bool required) -> maybe<uint> {
|
||||
auto Program::load(uint id, string name, string type) -> maybe<uint> {
|
||||
string location = BrowserDialog()
|
||||
.setTitle({"Load ", name})
|
||||
.setPath({settings["Library/Location"].text(), name})
|
||||
|
|
|
@ -7,7 +7,7 @@ struct Program : Emulator::Interface::Bind {
|
|||
//interface.cpp
|
||||
auto path(uint id) -> string override;
|
||||
auto open(uint id, string name, vfs::file::mode mode, bool required) -> vfs::shared::file override;
|
||||
auto load(uint id, string name, string type, bool required) -> maybe<uint> override;
|
||||
auto load(uint id, string name, string type) -> maybe<uint> override;
|
||||
auto videoRefresh(const uint32* data, uint pitch, uint width, uint height) -> void override;
|
||||
auto audioSample(const double* samples, uint channels) -> void override;
|
||||
auto inputPoll(uint port, uint device, uint input) -> int16 override;
|
||||
|
|
|
@ -149,9 +149,9 @@ auto CheatEditor::saveCheats() -> void {
|
|||
count++;
|
||||
}
|
||||
if(count) {
|
||||
file::write({program->mediumPaths(0), "cheats.bml"}, document);
|
||||
file::write({program->mediumPaths(1), "cheats.bml"}, document);
|
||||
} else {
|
||||
file::remove({program->mediumPaths(0), "cheats.bml"});
|
||||
file::remove({program->mediumPaths(1), "cheats.bml"});
|
||||
}
|
||||
doReset(true);
|
||||
}
|
||||
|
|
|
@ -46,13 +46,13 @@ auto Cartridge::load() -> bool {
|
|||
|
||||
switch(system.model()) {
|
||||
case Model::WonderSwan:
|
||||
if(auto pathID = interface->load(ID::WonderSwan, "WonderSwan", "ws", File::Required)) {
|
||||
if(auto pathID = interface->load(ID::WonderSwan, "WonderSwan", "ws")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
case Model::WonderSwanColor:
|
||||
case Model::SwanCrystal:
|
||||
if(auto pathID = interface->load(ID::WonderSwanColor, "WonderSwan Color", "wsc", File::Required)) {
|
||||
if(auto pathID = interface->load(ID::WonderSwanColor, "WonderSwan Color", "wsc")) {
|
||||
information.pathID = pathID();
|
||||
} else return false;
|
||||
break;
|
||||
|
|
|
@ -19,8 +19,8 @@ Interface::Interface() {
|
|||
information.capability.states = true;
|
||||
information.capability.cheats = true;
|
||||
|
||||
media.append({ID::WonderSwan, "WonderSwan", "ws", true});
|
||||
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc", true});
|
||||
media.append({ID::WonderSwan, "WonderSwan", "ws" });
|
||||
media.append({ID::WonderSwanColor, "WonderSwan Color", "wsc"});
|
||||
|
||||
Port hardwarePort{ID::Port::Hardware, "Hardware"};
|
||||
|
||||
|
|
|
@ -2,40 +2,66 @@
|
|||
|
||||
namespace nall {
|
||||
|
||||
template<typename C> struct property {
|
||||
template<typename T> struct readonly {
|
||||
auto operator->() const -> const T* { return &value; }
|
||||
auto operator()() const -> const T& { return value; }
|
||||
template<typename T> struct property {
|
||||
property() = default;
|
||||
property(const T& value) : value(value) {}
|
||||
|
||||
operator T&() { return value; } //direct assignment
|
||||
auto operator->() -> T* { return &value; } //class-member access
|
||||
|
||||
operator const T&() const { return value; }
|
||||
private:
|
||||
auto operator->() -> T* { return &value; }
|
||||
operator T&() { return value; }
|
||||
auto operator=(const T& value_) -> const T& { return value = value_; }
|
||||
auto operator()() const -> const T& { return value; }
|
||||
auto get() const -> const T& { return value; }
|
||||
|
||||
auto operator=(const T& value) -> T& { return this->value = value; }
|
||||
auto set(const T& value) -> T& { return this->value = value; }
|
||||
|
||||
T value;
|
||||
friend C;
|
||||
};
|
||||
|
||||
template<typename T> struct writeonly {
|
||||
auto operator=(const T& value_) -> void { value = value_; }
|
||||
private:
|
||||
auto operator->() const -> const T* { return &value; }
|
||||
auto operator()() const -> const T& { return value; }
|
||||
operator const T&() const { return value; }
|
||||
auto operator->() -> T* { return &value; }
|
||||
operator T&() { return value; }
|
||||
template<typename T, typename R = T> struct functional_property {
|
||||
functional_property(
|
||||
const function<R (const T&)>& getter = {},
|
||||
const function<R (T&, const T&)>& setter = {},
|
||||
const T& value = {}
|
||||
) {
|
||||
getter ? this->getter = getter : this->getter = [](const T& self) -> R { return self; };
|
||||
setter ? this->setter = setter : this->setter = [](T& self, const T& value) -> R { return self = value; };
|
||||
this->setter(this->value, value);
|
||||
}
|
||||
|
||||
operator R() const { return getter(value); }
|
||||
auto operator()() const -> R { return getter(value); }
|
||||
auto get() const -> R { return getter(value); }
|
||||
|
||||
auto operator=(const T& value) -> R { return setter(this->value, value); }
|
||||
auto set(const T& value) -> R { return setter(this->value, value); }
|
||||
|
||||
T value;
|
||||
friend C;
|
||||
function<R (const T&)> getter;
|
||||
function<R (T&, const T&)> setter;
|
||||
};
|
||||
|
||||
template<typename T> struct readwrite {
|
||||
auto operator->() const -> const T* { return &value; }
|
||||
auto operator()() const -> const T& { return value; }
|
||||
operator const T&() const { return value; }
|
||||
auto operator->() -> T* { return &value; }
|
||||
operator T&() { return value; }
|
||||
auto operator=(const T& value_) -> const T& { return value = value_; }
|
||||
T value;
|
||||
};
|
||||
template<typename T, typename R = T> struct virtual_property {
|
||||
virtual_property(
|
||||
const function<R ()>& getter = {},
|
||||
const function<R (const T&)>& setter = {},
|
||||
const T& value = {}
|
||||
) {
|
||||
this->getter = getter;
|
||||
this->setter = setter;
|
||||
if(this->setter) this->setter(value);
|
||||
}
|
||||
|
||||
operator R() const { return getter(); }
|
||||
auto operator()() const -> R { return getter(); }
|
||||
auto get() const -> R { return getter(); }
|
||||
|
||||
auto operator=(const T& value) -> R { return setter(value); }
|
||||
auto set(const T& value) -> R { return setter(value); }
|
||||
|
||||
function<R ()> getter;
|
||||
function<R (const T&)> setter;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue