From 68f04c3bb85bd47e4429b4b01f98c7f96d44ce8b Mon Sep 17 00:00:00 2001 From: Tim Allen Date: Thu, 23 Feb 2017 08:25:01 +1100 Subject: [PATCH] Update to v102r10 release. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit byuu says: Changelog: - removed Emulator::Interface::Capabilities¹ - MS: improved the PSG emulation a bit - MS: added cheat code support - MS: added save state support² - MD: emulated the PSG³ ¹: there's really no point to it anymore. I intend to add cheat codes to the GBA core, as well as both cheat codes and save states to the Mega Drive core. I no longer intend to emulate any new systems, so these values will always be true. Further, the GUI doesn't respond to these values to disable those features anymore ever since the hiro rewrite, so they're double useless. ²: right now, the Z80 core is using a pointer for HL-\>(IX,IY) overrides. But I can't reliably serialize pointers, so I need to convert the Z80 core to use an integer here. The save states still appear to work fine, but there's the potential for an instruction to execute incorrectly if you're incredibly unlucky, so this needs to be fixed as soon as possible. Further, I still need a way to serialize array objects, and I should also add nall::Boolean serialization support. ³: I don't have a system in place to share identical sound chips. But this chip is so incredibly simple that it's not really much trouble to duplicate it. Further, I can strip out the stereo sound support code from the Game Gear portion, so it's even tinier. Note that the Mega Drive only just barely uses the PSG. Not at all in Altered Beast, and only for a tiny part of the BGM music on Sonic 1, plus his jump sound effect. --- higan/emulator/emulator.hpp | 2 +- higan/emulator/interface.hpp | 4 -- higan/fc/interface/interface.cpp | 3 -- higan/gb/interface/interface.cpp | 3 -- higan/gba/interface/interface.cpp | 3 -- higan/md/interface/interface.cpp | 3 -- higan/md/psg/io.cpp | 51 +++++++++++++++++++++ higan/md/psg/noise.cpp | 26 +++++++++++ higan/md/psg/psg.cpp | 33 +++++++++++++- higan/md/psg/psg.hpp | 31 +++++++++++++ higan/md/psg/tone.cpp | 16 +++++++ higan/md/ym2612/ym2612.cpp | 6 +-- higan/ms/bus/bus.cpp | 7 +++ higan/ms/bus/bus.hpp | 4 ++ higan/ms/bus/serialization.cpp | 5 ++ higan/ms/cartridge/cartridge.cpp | 1 + higan/ms/cartridge/cartridge.hpp | 3 ++ higan/ms/cartridge/serialization.cpp | 3 ++ higan/ms/cpu/cpu.cpp | 6 ++- higan/ms/cpu/cpu.hpp | 9 +++- higan/ms/cpu/serialization.cpp | 7 +++ higan/ms/interface/interface.cpp | 11 +++-- higan/ms/interface/interface.hpp | 2 + higan/ms/ms.hpp | 4 +- higan/ms/psg/io.cpp | 17 ------- higan/ms/psg/noise.cpp | 9 ++-- higan/ms/psg/psg.cpp | 7 +-- higan/ms/psg/psg.hpp | 9 ++++ higan/ms/psg/serialization.cpp | 37 +++++++++++++++ higan/ms/system/serialization.cpp | 66 +++++++++++++++++++++++++++ higan/ms/system/system.cpp | 11 ++++- higan/ms/system/system.hpp | 9 ++++ higan/ms/vdp/serialization.cpp | 64 ++++++++++++++++++++++++++ higan/ms/vdp/vdp.cpp | 1 + higan/ms/vdp/vdp.hpp | 9 ++++ higan/pce/interface/interface.cpp | 3 -- higan/processor/z80/serialization.cpp | 27 +++++++++++ higan/processor/z80/z80.cpp | 1 + higan/processor/z80/z80.hpp | 16 +++++-- higan/sfc/interface/interface.cpp | 3 -- higan/ws/interface/interface.cpp | 3 -- 41 files changed, 465 insertions(+), 70 deletions(-) create mode 100644 higan/md/psg/noise.cpp create mode 100644 higan/md/psg/tone.cpp create mode 100644 higan/ms/bus/serialization.cpp create mode 100644 higan/ms/cartridge/serialization.cpp create mode 100644 higan/ms/cpu/serialization.cpp create mode 100644 higan/ms/psg/serialization.cpp create mode 100644 higan/ms/system/serialization.cpp create mode 100644 higan/ms/vdp/serialization.cpp create mode 100644 higan/processor/z80/serialization.cpp 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"};