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:
Tim Allen 2016-06-25 18:53:11 +10:00
parent f48b332c83
commit 3a9c7c6843
44 changed files with 306 additions and 193 deletions

View File

@ -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/";

View File

@ -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); }

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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"};

View File

@ -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;
}

View File

@ -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;

View File

@ -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"};

View File

@ -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;

View File

@ -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"};

View File

@ -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

View File

@ -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;

View File

@ -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();
}
}

View File

@ -54,49 +54,60 @@ 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);
fp->write(data, 16);
}
}
}
}
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);
fp->write(data, 16);
}
}
}
}
auto Cartridge::saveSPC7110(Markup::Node node) -> void {

View File

@ -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);
}
}

View File

@ -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;
};

View File

@ -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);
}
}
}

View File

@ -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];
};

View File

@ -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;

View File

@ -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 {

View File

@ -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);

View File

@ -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;

View File

@ -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));

View File

@ -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;

View File

@ -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" }});

View File

@ -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 {

View File

@ -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);

View File

@ -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);

View File

@ -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];

View File

@ -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);

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -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();
}

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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);

View File

@ -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})

View File

@ -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;

View File

@ -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);
}

View File

@ -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;

View File

@ -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"};

View File

@ -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; }
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_; }
T value;
friend C;
};
template<typename T> struct property {
property() = default;
property(const T& value) : value(value) {}
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; }
T value;
friend C;
};
operator T&() { return value; } //direct assignment
auto operator->() -> T* { return &value; } //class-member access
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_; }
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;
};
};
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;
function<R (const T&)> getter;
function<R (T&, const T&)> setter;
};
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;
};
}