diff --git a/higan/emulator/emulator.hpp b/higan/emulator/emulator.hpp index d6d9f093..28f520ef 100644 --- a/higan/emulator/emulator.hpp +++ b/higan/emulator/emulator.hpp @@ -12,7 +12,7 @@ using namespace nall; namespace Emulator { static const string Name = "higan"; - static const string Version = "102.09"; + static const string Version = "102.10"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "http://byuu.org/"; diff --git a/higan/emulator/interface.hpp b/higan/emulator/interface.hpp index 3228e2ef..c7276a65 100644 --- a/higan/emulator/interface.hpp +++ b/higan/emulator/interface.hpp @@ -7,10 +7,6 @@ struct Interface { string manufacturer; string name; bool overscan; - struct Capability { - bool states; - bool cheats; - } capability; } information; struct Region { diff --git a/higan/fc/interface/interface.cpp b/higan/fc/interface/interface.cpp index bf2e7e56..9dc4602c 100644 --- a/higan/fc/interface/interface.cpp +++ b/higan/fc/interface/interface.cpp @@ -9,9 +9,6 @@ Interface::Interface() { information.name = "Famicom"; information.overscan = true; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::Famicom, "Famicom", "fc"}); Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; diff --git a/higan/gb/interface/interface.cpp b/higan/gb/interface/interface.cpp index a67ecceb..af499c16 100644 --- a/higan/gb/interface/interface.cpp +++ b/higan/gb/interface/interface.cpp @@ -8,9 +8,6 @@ Settings settings; #include "game-boy-color.cpp" Interface::Interface() { - information.capability.states = true; - information.capability.cheats = true; - Port hardwarePort{ID::Port::Hardware, "Hardware"}; { Device device{ID::Device::Controls, "Controls"}; diff --git a/higan/gba/interface/interface.cpp b/higan/gba/interface/interface.cpp index 6084b4c1..efd7c328 100644 --- a/higan/gba/interface/interface.cpp +++ b/higan/gba/interface/interface.cpp @@ -9,9 +9,6 @@ Interface::Interface() { information.name = "Game Boy Advance"; information.overscan = false; - information.capability.states = true; - information.capability.cheats = false; - media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"}); Port hardwarePort{ID::Port::Hardware, "Hardware"}; diff --git a/higan/md/interface/interface.cpp b/higan/md/interface/interface.cpp index b4de245a..4ad25fa0 100644 --- a/higan/md/interface/interface.cpp +++ b/higan/md/interface/interface.cpp @@ -9,9 +9,6 @@ Interface::Interface() { information.name = "Mega Drive"; information.overscan = true; - information.capability.states = false; - information.capability.cheats = false; - regions.append({"Autodetect"}); regions.append({"NTSC-J"}); regions.append({"NTSC-U"}); diff --git a/higan/md/psg/io.cpp b/higan/md/psg/io.cpp index bd4c579e..d2c711a7 100644 --- a/higan/md/psg/io.cpp +++ b/higan/md/psg/io.cpp @@ -1,2 +1,53 @@ auto PSG::write(uint8 data) -> void { + bool l = data.bit(7); + if(l) select = data.bits(4,6); + + switch(select) { + + case 0: { + if(l) tone0.pitch.bits(0,3) = data.bits(0,3); + else tone0.pitch.bits(4,9) = data.bits(0,5); + break; + } + + case 1: { + tone0.volume = data.bits(0,3); + break; + } + + case 2: { + if(l) tone1.pitch.bits(0,3) = data.bits(0,3); + else tone1.pitch.bits(4,9) = data.bits(0,5); + break; + } + + case 3: { + tone1.volume = data.bits(0,3); + break; + } + + case 4: { + if(l) tone2.pitch.bits(0,3) = data.bits(0,3); + else tone2.pitch.bits(4,9) = data.bits(0,5); + break; + } + + case 5: { + tone2.volume = data.bits(0,3); + break; + } + + case 6: { + noise.rate = data.bits(0,1); + noise.enable = data.bit(2); + noise.lfsr = 0x8000; + break; + } + + case 7: { + noise.volume = data.bits(0,3); + break; + } + + } } diff --git a/higan/md/psg/noise.cpp b/higan/md/psg/noise.cpp new file mode 100644 index 00000000..d416e30d --- /dev/null +++ b/higan/md/psg/noise.cpp @@ -0,0 +1,26 @@ +auto PSG::Noise::run() -> void { + auto latch = clock; + + counter++; + if(rate == 0) output ^= !counter.bits(0,3); + if(rate == 1) output ^= !counter.bits(0,4); + if(rate == 2) output ^= !counter.bits(0,5); + if(rate == 3) output ^= psg.tone2.clock; + + if(!latch && clock) { + auto eor = enable ? ~lfsr >> 3 : 0; + lfsr = (lfsr ^ eor) << 15 | lfsr >> 1; + } + + output = lfsr.bit(0); +} + +auto PSG::Noise::power() -> void { + volume = ~0; + counter = 0; + enable = 0; + rate = 0; + lfsr = 0x8000; + clock = 0; + output = 0; +} diff --git a/higan/md/psg/psg.cpp b/higan/md/psg/psg.cpp index 20bfa81f..b57331ef 100644 --- a/higan/md/psg/psg.cpp +++ b/higan/md/psg/psg.cpp @@ -4,13 +4,30 @@ namespace MegaDrive { PSG psg; #include "io.cpp" +#include "tone.cpp" +#include "noise.cpp" auto PSG::Enter() -> void { while(true) scheduler.synchronize(), psg.main(); } auto PSG::main() -> void { - stream->sample(0.0); + tone0.run(); + tone1.run(); + tone2.run(); + noise.run(); + + int output = 0; + if(tone0.output) output += levels[tone0.volume]; + if(tone1.output) output += levels[tone1.volume]; + if(tone2.output) output += levels[tone2.volume]; + if(noise.output) output += levels[noise.volume]; + + lowpass += (output - lowpass) * 20.0 / 256.0; + output = output * 2.0 / 6.0 + lowpass * 3.0 / 4.0; + output = sclamp<16>(output - 32768); + + stream->sample(output / 32768.0); step(1); } @@ -21,7 +38,19 @@ auto PSG::step(uint clocks) -> void { auto PSG::power() -> void { create(PSG::Enter, system.colorburst() / 16.0); - stream = Emulator::audio.createStream(1, system.colorburst() / 16.0); + stream = Emulator::audio.createStream(1, frequency()); + + select = 0; + lowpass = 0; + for(auto n : range(15)) { + levels[n] = 0x3fff * pow(2, n * -2.0 / 6.0) + 0.5; + } + levels[15] = 0; + + tone0.power(); + tone1.power(); + tone2.power(); + noise.power(); } } diff --git a/higan/md/psg/psg.hpp b/higan/md/psg/psg.hpp index cd988d7c..c9e802f8 100644 --- a/higan/md/psg/psg.hpp +++ b/higan/md/psg/psg.hpp @@ -11,6 +11,37 @@ struct PSG : Thread { //io.cpp auto write(uint8 data) -> void; + +private: + struct Tone { + //tone.cpp + auto run() -> void; + auto power() -> void; + + uint4 volume; + uint10 counter; + uint10 pitch; + uint1 clock; + uint1 output; + } tone0, tone1, tone2; + + struct Noise { + //noise.cpp + auto run() -> void; + auto power() -> void; + + uint4 volume; + uint6 counter; + uint1 enable; + uint2 rate; + uint16 lfsr; + uint1 clock; + uint1 output; + } noise; + + uint3 select; + int lowpass; + uint16 levels[16]; }; extern PSG psg; diff --git a/higan/md/psg/tone.cpp b/higan/md/psg/tone.cpp new file mode 100644 index 00000000..052b1831 --- /dev/null +++ b/higan/md/psg/tone.cpp @@ -0,0 +1,16 @@ +auto PSG::Tone::run() -> void { + clock = 0; + if(--counter) return; + + clock = 1; + counter = pitch; + output ^= 1; +} + +auto PSG::Tone::power() -> void { + volume = ~0; + counter = 0; + pitch = 0; + clock = 0; + output = 0; +} diff --git a/higan/md/ym2612/ym2612.cpp b/higan/md/ym2612/ym2612.cpp index 195d34ce..87e9705a 100644 --- a/higan/md/ym2612/ym2612.cpp +++ b/higan/md/ym2612/ym2612.cpp @@ -11,7 +11,7 @@ auto YM2612::Enter() -> void { auto YM2612::main() -> void { stream->sample(0.0, 0.0); - step(144); + step(1); } auto YM2612::step(uint clocks) -> void { @@ -20,8 +20,8 @@ auto YM2612::step(uint clocks) -> void { } auto YM2612::power() -> void { - create(YM2612::Enter, system.colorburst() * 15.0 / 7.0); - stream = Emulator::audio.createStream(2, system.colorburst() * 15.0 / 7.0 / 144.0); + create(YM2612::Enter, system.colorburst() * 15.0 / 7.0 / 144.0); + stream = Emulator::audio.createStream(2, frequency()); } } diff --git a/higan/ms/bus/bus.cpp b/higan/ms/bus/bus.cpp index b85d9055..875c1c19 100644 --- a/higan/ms/bus/bus.cpp +++ b/higan/ms/bus/bus.cpp @@ -3,8 +3,15 @@ namespace MasterSystem { Bus bus; +#include "serialization.cpp" auto Bus::read(uint16 addr) -> uint8 { + auto data = read_(addr); + if(auto result = cheat.find(addr, data)) data = result(); + return data; +} + +auto Bus::read_(uint16 addr) -> uint8 { if(auto data = cartridge.read(addr)) return data(); if(addr >= 0xc000) return ram[addr & 0x1fff]; return 0x00; diff --git a/higan/ms/bus/bus.hpp b/higan/ms/bus/bus.hpp index c8826a35..7e4c38a4 100644 --- a/higan/ms/bus/bus.hpp +++ b/higan/ms/bus/bus.hpp @@ -1,10 +1,14 @@ struct Bus : Processor::Z80::Bus { auto read(uint16 addr) -> uint8 override; + auto read_(uint16 addr) -> uint8; auto write(uint16 addr, uint8 data) -> void override; auto in(uint8 addr) -> uint8 override; auto out(uint8 addr, uint8 data) -> void override; + //serialization.cpp + auto serialize(serializer&) -> void; + private: uint8 ram[0x2000]; }; diff --git a/higan/ms/bus/serialization.cpp b/higan/ms/bus/serialization.cpp new file mode 100644 index 00000000..897396f0 --- /dev/null +++ b/higan/ms/bus/serialization.cpp @@ -0,0 +1,5 @@ +auto Bus::serialize(serializer& s) -> void { + Processor::Z80::Bus::serialize(s); + + s.array(ram); +} diff --git a/higan/ms/cartridge/cartridge.cpp b/higan/ms/cartridge/cartridge.cpp index 91566cb7..a330e8df 100644 --- a/higan/ms/cartridge/cartridge.cpp +++ b/higan/ms/cartridge/cartridge.cpp @@ -4,6 +4,7 @@ namespace MasterSystem { Cartridge cartridge; #include "mapper.cpp" +#include "serialization.cpp" auto Cartridge::load() -> bool { information = {}; diff --git a/higan/ms/cartridge/cartridge.hpp b/higan/ms/cartridge/cartridge.hpp index eef14940..348ab248 100644 --- a/higan/ms/cartridge/cartridge.hpp +++ b/higan/ms/cartridge/cartridge.hpp @@ -14,6 +14,9 @@ struct Cartridge { auto read(uint16 addr) -> maybe; auto write(uint16 addr, uint8 data) -> bool; + //serialization.cpp + auto serialize(serializer&) -> void; + private: struct Information { uint pathID = 0; diff --git a/higan/ms/cartridge/serialization.cpp b/higan/ms/cartridge/serialization.cpp new file mode 100644 index 00000000..602e2ad2 --- /dev/null +++ b/higan/ms/cartridge/serialization.cpp @@ -0,0 +1,3 @@ +auto Cartridge::serialize(serializer& s) -> void { + if(ram.size) s.array(ram.data, ram.size); +} diff --git a/higan/ms/cpu/cpu.cpp b/higan/ms/cpu/cpu.cpp index 1ade980e..58b0d255 100644 --- a/higan/ms/cpu/cpu.cpp +++ b/higan/ms/cpu/cpu.cpp @@ -3,6 +3,7 @@ namespace MasterSystem { CPU cpu; +#include "serialization.cpp" auto CPU::Enter() -> void { while(true) scheduler.synchronize(), cpu.main(); @@ -48,7 +49,6 @@ auto CPU::setINT(bool value) -> void { } auto CPU::power() -> void { - Z80::bus = &MasterSystem::bus; Z80::power(); create(CPU::Enter, system.colorburst()); @@ -57,4 +57,8 @@ auto CPU::power() -> void { memory::fill(&state, sizeof(State)); } +CPU::CPU() { + Z80::bus = &MasterSystem::bus; +} + } diff --git a/higan/ms/cpu/cpu.hpp b/higan/ms/cpu/cpu.hpp index 82360c8a..2d71cbbf 100644 --- a/higan/ms/cpu/cpu.hpp +++ b/higan/ms/cpu/cpu.hpp @@ -11,12 +11,17 @@ struct CPU : Processor::Z80, Thread { auto power() -> void; + CPU(); + + //serialization.cpp + auto serialize(serializer&) -> void; + vector peripherals; private: struct State { - boolean nmiLine; - boolean intLine; + bool nmiLine; + bool intLine; } state; }; diff --git a/higan/ms/cpu/serialization.cpp b/higan/ms/cpu/serialization.cpp new file mode 100644 index 00000000..321bcc0a --- /dev/null +++ b/higan/ms/cpu/serialization.cpp @@ -0,0 +1,7 @@ +auto CPU::serialize(serializer& s) -> void { + Z80::serialize(s); + Thread::serialize(s); + + s.integer(state.nmiLine); + s.integer(state.intLine); +} diff --git a/higan/ms/interface/interface.cpp b/higan/ms/interface/interface.cpp index c024e2aa..9abd82d9 100644 --- a/higan/ms/interface/interface.cpp +++ b/higan/ms/interface/interface.cpp @@ -7,8 +7,6 @@ Settings settings; #include "game-gear.cpp" Interface::Interface() { - information.capability.states = false; - information.capability.cheats = false; } auto Interface::manifest() -> string { @@ -49,11 +47,16 @@ auto Interface::run() -> void { } auto Interface::serialize() -> serializer { - return {}; + system.runToSave(); + return system.serialize(); } auto Interface::unserialize(serializer& s) -> bool { - return false; + return system.unserialize(s); +} + +auto Interface::cheatSet(const string_vector& list) -> void { + cheat.assign(list); } auto Interface::cap(const string& name) -> bool { diff --git a/higan/ms/interface/interface.hpp b/higan/ms/interface/interface.hpp index 344c1751..8c206f0b 100644 --- a/higan/ms/interface/interface.hpp +++ b/higan/ms/interface/interface.hpp @@ -40,6 +40,8 @@ struct Interface : Emulator::Interface { auto serialize() -> serializer override; auto unserialize(serializer&) -> bool override; + auto cheatSet(const string_vector&) -> void override; + auto cap(const string& name) -> bool override; auto get(const string& name) -> any override; auto set(const string& name, const any& value) -> bool override; diff --git a/higan/ms/ms.hpp b/higan/ms/ms.hpp index f7a2f13a..76540292 100644 --- a/higan/ms/ms.hpp +++ b/higan/ms/ms.hpp @@ -6,6 +6,7 @@ #include #include #include +#include #include @@ -13,8 +14,9 @@ namespace MasterSystem { #define platform Emulator::platform namespace File = Emulator::File; using Scheduler = Emulator::Scheduler; + using Cheat = Emulator::Cheat; extern Scheduler scheduler; - struct Interface; + extern Cheat cheat; struct Thread : Emulator::Thread { auto create(auto (*entrypoint)() -> void, double frequency) -> void { diff --git a/higan/ms/psg/io.cpp b/higan/ms/psg/io.cpp index 844b4345..0612cf6c 100644 --- a/higan/ms/psg/io.cpp +++ b/higan/ms/psg/io.cpp @@ -1,7 +1,3 @@ -//note: tone is supposed to reload counters on volume writes -//however, if this is always done, the output is very grainy -//as such, this behavior is suppressed when pitch >= 2 (which is a hack) - auto PSG::write(uint8 data) -> void { bool l = data.bit(7); if(l) select = data.bits(4,6); @@ -16,10 +12,6 @@ auto PSG::write(uint8 data) -> void { case 1: { tone0.volume = data.bits(0,3); - if(tone0.pitch < 2) { - tone0.output = 1; - tone0.counter = tone0.pitch; - } break; } @@ -31,10 +23,6 @@ auto PSG::write(uint8 data) -> void { case 3: { tone1.volume = data.bits(0,3); - if(tone1.pitch < 2) { - tone1.output = 1; - tone1.counter = tone1.pitch; - } break; } @@ -46,10 +34,6 @@ auto PSG::write(uint8 data) -> void { case 5: { tone2.volume = data.bits(0,3); - if(tone2.pitch < 2) { - tone2.output = 1; - tone2.counter = tone2.pitch; - } break; } @@ -62,7 +46,6 @@ auto PSG::write(uint8 data) -> void { case 7: { noise.volume = data.bits(0,3); - noise.output = 1; break; } diff --git a/higan/ms/psg/noise.cpp b/higan/ms/psg/noise.cpp index e3c41123..08675afb 100644 --- a/higan/ms/psg/noise.cpp +++ b/higan/ms/psg/noise.cpp @@ -2,11 +2,10 @@ auto PSG::Noise::run() -> void { auto latch = clock; counter++; - if(rate < 3) { - clock ^= counter & ((16 << rate) - 1) == 0; - } else { - clock ^= psg.tone2.clock; - } + if(rate == 0) output ^= !counter.bits(0,3); + if(rate == 1) output ^= !counter.bits(0,4); + if(rate == 2) output ^= !counter.bits(0,5); + if(rate == 3) output ^= psg.tone2.clock; if(!latch && clock) { auto eor = enable ? ~lfsr >> 3 : 0; diff --git a/higan/ms/psg/psg.cpp b/higan/ms/psg/psg.cpp index d824ef69..d800d4ae 100644 --- a/higan/ms/psg/psg.cpp +++ b/higan/ms/psg/psg.cpp @@ -6,6 +6,7 @@ PSG psg; #include "io.cpp" #include "tone.cpp" #include "noise.cpp" +#include "serialization.cpp" auto PSG::Enter() -> void { while(true) scheduler.synchronize(), psg.main(); @@ -25,7 +26,7 @@ auto PSG::main() -> void { lowpassLeft += (left - lowpassLeft) * 20.0 / 256.0; left = left * 2.0 / 6.0 + lowpassLeft * 3.0 / 4.0; - left = sclamp<16>(left); + left = sclamp<16>(left - 32768); int right = 0; if(tone0.output && tone0.right) right += levels[tone0.volume]; @@ -35,10 +36,10 @@ auto PSG::main() -> void { lowpassRight += (right - lowpassRight) * 20.0 / 256.0; right = right * 2.0 / 6.0 + lowpassRight * 3.0 / 4.0; - right = sclamp<16>(right); + right = sclamp<16>(right - 32768); - step(1); stream->sample(left / 32768.0, right / 32768.0); + step(1); } auto PSG::step(uint clocks) -> void { diff --git a/higan/ms/psg/psg.hpp b/higan/ms/psg/psg.hpp index 69d3c1fd..dfe41db3 100644 --- a/higan/ms/psg/psg.hpp +++ b/higan/ms/psg/psg.hpp @@ -13,12 +13,18 @@ struct PSG : Thread { auto write(uint8 data) -> void; auto balance(uint8 data) -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + private: struct Tone { //tone.cpp auto run() -> void; auto power() -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + uint4 volume; uint10 counter; uint10 pitch; @@ -34,6 +40,9 @@ private: auto run() -> void; auto power() -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + uint4 volume; uint6 counter; uint1 enable; diff --git a/higan/ms/psg/serialization.cpp b/higan/ms/psg/serialization.cpp new file mode 100644 index 00000000..b71592c0 --- /dev/null +++ b/higan/ms/psg/serialization.cpp @@ -0,0 +1,37 @@ +auto PSG::serialize(serializer& s) -> void { + Thread::serialize(s); + + tone0.serialize(s); + tone1.serialize(s); + tone2.serialize(s); + noise.serialize(s); + + s.integer(select); + s.integer(lowpassLeft); + s.integer(lowpassRight); + s.array(levels); +} + +auto PSG::Tone::serialize(serializer& s) -> void { + s.integer(volume); + s.integer(counter); + s.integer(pitch); + s.integer(clock); + s.integer(output); + + s.integer(left); + s.integer(right); +} + +auto PSG::Noise::serialize(serializer& s) -> void { + s.integer(volume); + s.integer(counter); + s.integer(enable); + s.integer(rate); + s.integer(lfsr); + s.integer(clock); + s.integer(output); + + s.integer(left); + s.integer(right); +} diff --git a/higan/ms/system/serialization.cpp b/higan/ms/system/serialization.cpp new file mode 100644 index 00000000..e3638bbb --- /dev/null +++ b/higan/ms/system/serialization.cpp @@ -0,0 +1,66 @@ +auto System::serializeInit() -> void { + serializer s; + + uint signature = 0; + char version[16] = {0}; + char hash[64] = {0}; + char description[512] = {0}; + + s.integer(signature); + s.array(version); + s.array(hash); + s.array(description); + + serializeAll(s); + information.serializeSize = s.size(); +} + +auto System::serialize() -> serializer { + serializer s{information.serializeSize}; + + uint signature = 0x31545342; + char version[16] = {0}; + char hash[64] = {0}; + char description[512] = {0}; + memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size()); + memory::copy(&hash, (const char*)cartridge.sha256(), 64); + + s.integer(signature); + s.array(version); + s.array(hash); + s.array(description); + + serializeAll(s); + return s; +} + +auto System::unserialize(serializer& s) -> bool { + uint signature = 0; + char version[16] = {0}; + char hash[64] = {0}; + char description[512] = {0}; + + s.integer(signature); + s.array(version); + s.array(hash); + s.array(description); + + if(signature != 0x31545342) return false; + if(string{version} != Emulator::SerializerVersion) return false; + + power(); + serializeAll(s); + return true; +} + +auto System::serializeAll(serializer& s) -> void { + system.serialize(s); + bus.serialize(s); + cartridge.serialize(s); + cpu.serialize(s); + vdp.serialize(s); + psg.serialize(s); +} + +auto System::serialize(serializer& s) -> void { +} diff --git a/higan/ms/system/system.cpp b/higan/ms/system/system.cpp index e873b9c4..6a7d370e 100644 --- a/higan/ms/system/system.cpp +++ b/higan/ms/system/system.cpp @@ -2,9 +2,11 @@ namespace MasterSystem { -#include "peripherals.cpp" System system; Scheduler scheduler; +Cheat cheat; +#include "peripherals.cpp" +#include "serialization.cpp" auto System::run() -> void { if(scheduler.enter() == Scheduler::Event::Frame) { @@ -13,6 +15,12 @@ auto System::run() -> void { } } +auto System::runToSave() -> void { + scheduler.synchronize(cpu); + scheduler.synchronize(vdp); + scheduler.synchronize(psg); +} + auto System::load(Emulator::Interface* interface, Model model) -> bool { information = {}; information.model = model; @@ -24,6 +32,7 @@ auto System::load(Emulator::Interface* interface, Model model) -> bool { auto document = BML::unserialize(information.manifest); if(!cartridge.load()) return false; + serializeInit(); this->interface = interface; information.colorburst = Emulator::Constants::Colorburst::NTSC; return information.loaded = true; diff --git a/higan/ms/system/system.hpp b/higan/ms/system/system.hpp index d1ee6e89..2f676f51 100644 --- a/higan/ms/system/system.hpp +++ b/higan/ms/system/system.hpp @@ -6,6 +6,7 @@ struct System { auto colorburst() const -> double { return information.colorburst; } auto run() -> void; + auto runToSave() -> void; auto load(Emulator::Interface* interface, Model model) -> bool; auto save() -> void; @@ -13,6 +14,13 @@ struct System { auto power() -> void; + //serialization.cpp + auto serializeInit() -> void; + auto serialize() -> serializer; + auto unserialize(serializer&) -> bool; + auto serializeAll(serializer&) -> void; + auto serialize(serializer&) -> void; + private: Emulator::Interface* interface = nullptr; @@ -21,6 +29,7 @@ private: Model model = Model::MasterSystem; string manifest; double colorburst = 0.0; + uint serializeSize = 0; } information; }; diff --git a/higan/ms/vdp/serialization.cpp b/higan/ms/vdp/serialization.cpp new file mode 100644 index 00000000..0caccf01 --- /dev/null +++ b/higan/ms/vdp/serialization.cpp @@ -0,0 +1,64 @@ +auto VDP::serialize(serializer& s) -> void { + Thread::serialize(s); + + background.serialize(s); + sprite.serialize(s); + + s.array(vram); + s.array(cram); + + s.integer(io.vcounter); + s.integer(io.hcounter); + s.integer(io.lcounter); + s.integer(io.intLine); + s.integer(io.intFrame); + s.integer(io.spriteOverflow); + s.integer(io.spriteCollision); + s.integer(io.fifthSprite); + s.integer(io.controlLatch); + s.integer(io.controlData); + s.integer(io.code); + s.integer(io.address); + s.integer(io.vramLatch); + s.integer(io.externalSync); + s.integer(io.extendedHeight); + s.integer(io.mode4); + s.integer(io.spriteShift); + s.integer(io.lineInterrupts); + s.integer(io.leftClip); + s.integer(io.horizontalScrollLock); + s.integer(io.verticalScrollLock); + s.integer(io.spriteDouble); + s.integer(io.spriteTile); + s.integer(io.lines240); + s.integer(io.lines224); + s.integer(io.frameInterrupts); + s.integer(io.displayEnable); + s.integer(io.nameTableMask); + s.integer(io.nameTableAddress); + s.integer(io.colorTableAddress); + s.integer(io.patternTableAddress); + s.integer(io.spriteAttributeTableMask); + s.integer(io.spriteAttributeTableAddress); + s.integer(io.spritePatternTableMask); + s.integer(io.spritePatternTableAddress); + s.integer(io.backdropColor); + s.integer(io.hscroll); + s.integer(io.vscroll); + s.integer(io.lineCounter); +} + +auto VDP::Background::serialize(serializer& s) -> void { + s.integer(state.x); + s.integer(state.y); + s.integer(output.color); + s.integer(output.palette); + s.integer(output.priority); +} + +auto VDP::Sprite::serialize(serializer& s) -> void { + s.integer(state.x); + s.integer(state.y); + s.integer(output.color); + //todo: array is not serializable +} diff --git a/higan/ms/vdp/vdp.cpp b/higan/ms/vdp/vdp.cpp index b8025a24..5e7d43b2 100644 --- a/higan/ms/vdp/vdp.cpp +++ b/higan/ms/vdp/vdp.cpp @@ -6,6 +6,7 @@ VDP vdp; #include "io.cpp" #include "background.cpp" #include "sprite.cpp" +#include "serialization.cpp" auto VDP::Enter() -> void { while(true) scheduler.synchronize(), vdp.main(); diff --git a/higan/ms/vdp/vdp.hpp b/higan/ms/vdp/vdp.hpp index cb9eee1e..50132c2d 100644 --- a/higan/ms/vdp/vdp.hpp +++ b/higan/ms/vdp/vdp.hpp @@ -28,6 +28,9 @@ struct VDP : Thread { auto power() -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + struct State { uint x; uint y; @@ -47,6 +50,9 @@ struct VDP : Thread { auto power() -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + struct Object { uint8 x; uint8 y; @@ -65,6 +71,9 @@ struct VDP : Thread { array objects; } sprite; + //serialization.cpp + auto serialize(serializer&) -> void; + private: auto palette(uint5 index) -> uint12; diff --git a/higan/pce/interface/interface.cpp b/higan/pce/interface/interface.cpp index b0b0b6ec..5e47c2be 100644 --- a/higan/pce/interface/interface.cpp +++ b/higan/pce/interface/interface.cpp @@ -10,9 +10,6 @@ Settings settings; Interface::Interface() { information.overscan = true; - information.capability.states = true; - information.capability.cheats = true; - Port controllerPort{ID::Port::Controller, "Controller Port"}; { Device device{ID::Device::None, "None"}; diff --git a/higan/processor/z80/serialization.cpp b/higan/processor/z80/serialization.cpp new file mode 100644 index 00000000..19cbea53 --- /dev/null +++ b/higan/processor/z80/serialization.cpp @@ -0,0 +1,27 @@ +auto Z80::serialize(serializer& s) -> void { + s.integer(r.af.word); + s.integer(r.bc.word); + s.integer(r.de.word); + s.integer(r.hl.word); + s.integer(r.ix.word); + s.integer(r.iy.word); + s.integer(r.ir.word); + s.integer(r.sp); + s.integer(r.pc); + s.integer(r.af_.word); + s.integer(r.bc_.word); + s.integer(r.de_.word); + s.integer(r.hl_.word); + s.integer(r.ei); + s.integer(r.halt); + s.integer(r.iff1); + s.integer(r.iff2); + s.integer(r.im); + + //todo: r.hlp is not serializable +} + +auto Z80::Bus::serialize(serializer& s) -> void { + s.integer(_requested); + s.integer(_granted); +} diff --git a/higan/processor/z80/z80.cpp b/higan/processor/z80/z80.cpp index 3ce14d05..ec9ee18d 100644 --- a/higan/processor/z80/z80.cpp +++ b/higan/processor/z80/z80.cpp @@ -8,6 +8,7 @@ namespace Processor { #include "memory.cpp" #include "instruction.cpp" #include "instructions.cpp" +#include "serialization.cpp" auto Z80::power() -> void { memory::fill(&r, sizeof(Registers)); diff --git a/higan/processor/z80/z80.hpp b/higan/processor/z80/z80.hpp index 4a19ee9a..e51a6e80 100644 --- a/higan/processor/z80/z80.hpp +++ b/higan/processor/z80/z80.hpp @@ -18,6 +18,9 @@ struct Z80 { virtual auto in(uint8 addr) -> uint8 = 0; virtual auto out(uint8 addr, uint8 data) -> void = 0; + //serialization.cpp + virtual auto serialize(serializer&) -> void; + private: bool _requested; bool _granted; @@ -205,6 +208,9 @@ struct Z80 { auto instructionXOR_a_n() -> void; auto instructionXOR_a_r(uint8&) -> void; + //serialization.cpp + auto serialize(serializer&) -> void; + //disassembler.cpp auto disassemble(uint16 pc) -> string; auto disassemble(uint16 pc, uint8 prefix, uint8 code) -> string; @@ -229,11 +235,11 @@ struct Z80 { uint16 sp; uint16 pc; - boolean ei; //EI instruction executed - boolean halt; //HALT instruction executed - boolean iff1; //interrupt flip-flop 1 - boolean iff2; //interrupt flip-flop 2 - uint2 im; //interrupt mode (0-2) + bool ei; //EI instruction executed + bool halt; //HALT instruction executed + bool iff1; //interrupt flip-flop 1 + bool iff2; //interrupt flip-flop 2 + uint2 im; //interrupt mode (0-2) Pair* hlp = nullptr; } r; diff --git a/higan/sfc/interface/interface.cpp b/higan/sfc/interface/interface.cpp index f1edde83..a50c931d 100644 --- a/higan/sfc/interface/interface.cpp +++ b/higan/sfc/interface/interface.cpp @@ -11,9 +11,6 @@ Interface::Interface() { information.name = "Super Famicom"; information.overscan = true; - information.capability.states = true; - information.capability.cheats = true; - media.append({ID::SuperFamicom, "Super Famicom", "sfc"}); Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; diff --git a/higan/ws/interface/interface.cpp b/higan/ws/interface/interface.cpp index c6ae68ec..27192384 100644 --- a/higan/ws/interface/interface.cpp +++ b/higan/ws/interface/interface.cpp @@ -7,9 +7,6 @@ Settings settings; #include "wonderswan-color.cpp" Interface::Interface() { - information.capability.states = true; - information.capability.cheats = true; - Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"};