Update to v102r10 release.

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<T, Size> 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.
This commit is contained in:
Tim Allen 2017-02-23 08:25:01 +11:00
parent 8071da4c6a
commit 68f04c3bb8
41 changed files with 465 additions and 70 deletions

View File

@ -12,7 +12,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "higan"; 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 Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "http://byuu.org/"; static const string Website = "http://byuu.org/";

View File

@ -7,10 +7,6 @@ struct Interface {
string manufacturer; string manufacturer;
string name; string name;
bool overscan; bool overscan;
struct Capability {
bool states;
bool cheats;
} capability;
} information; } information;
struct Region { struct Region {

View File

@ -9,9 +9,6 @@ Interface::Interface() {
information.name = "Famicom"; information.name = "Famicom";
information.overscan = true; information.overscan = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::Famicom, "Famicom", "fc"}); media.append({ID::Famicom, "Famicom", "fc"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};

View File

@ -8,9 +8,6 @@ Settings settings;
#include "game-boy-color.cpp" #include "game-boy-color.cpp"
Interface::Interface() { Interface::Interface() {
information.capability.states = true;
information.capability.cheats = true;
Port hardwarePort{ID::Port::Hardware, "Hardware"}; Port hardwarePort{ID::Port::Hardware, "Hardware"};
{ Device device{ID::Device::Controls, "Controls"}; { Device device{ID::Device::Controls, "Controls"};

View File

@ -9,9 +9,6 @@ Interface::Interface() {
information.name = "Game Boy Advance"; information.name = "Game Boy Advance";
information.overscan = false; information.overscan = false;
information.capability.states = true;
information.capability.cheats = false;
media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"}); media.append({ID::GameBoyAdvance, "Game Boy Advance", "gba"});
Port hardwarePort{ID::Port::Hardware, "Hardware"}; Port hardwarePort{ID::Port::Hardware, "Hardware"};

View File

@ -9,9 +9,6 @@ Interface::Interface() {
information.name = "Mega Drive"; information.name = "Mega Drive";
information.overscan = true; information.overscan = true;
information.capability.states = false;
information.capability.cheats = false;
regions.append({"Autodetect"}); regions.append({"Autodetect"});
regions.append({"NTSC-J"}); regions.append({"NTSC-J"});
regions.append({"NTSC-U"}); regions.append({"NTSC-U"});

View File

@ -1,2 +1,53 @@
auto PSG::write(uint8 data) -> void { 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;
}
}
} }

26
higan/md/psg/noise.cpp Normal file
View File

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

View File

@ -4,13 +4,30 @@ namespace MegaDrive {
PSG psg; PSG psg;
#include "io.cpp" #include "io.cpp"
#include "tone.cpp"
#include "noise.cpp"
auto PSG::Enter() -> void { auto PSG::Enter() -> void {
while(true) scheduler.synchronize(), psg.main(); while(true) scheduler.synchronize(), psg.main();
} }
auto PSG::main() -> void { 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); step(1);
} }
@ -21,7 +38,19 @@ auto PSG::step(uint clocks) -> void {
auto PSG::power() -> void { auto PSG::power() -> void {
create(PSG::Enter, system.colorburst() / 16.0); 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();
} }
} }

View File

@ -11,6 +11,37 @@ struct PSG : Thread {
//io.cpp //io.cpp
auto write(uint8 data) -> void; 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; extern PSG psg;

16
higan/md/psg/tone.cpp Normal file
View File

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

View File

@ -11,7 +11,7 @@ auto YM2612::Enter() -> void {
auto YM2612::main() -> void { auto YM2612::main() -> void {
stream->sample(0.0, 0.0); stream->sample(0.0, 0.0);
step(144); step(1);
} }
auto YM2612::step(uint clocks) -> void { auto YM2612::step(uint clocks) -> void {
@ -20,8 +20,8 @@ auto YM2612::step(uint clocks) -> void {
} }
auto YM2612::power() -> void { auto YM2612::power() -> void {
create(YM2612::Enter, system.colorburst() * 15.0 / 7.0); create(YM2612::Enter, system.colorburst() * 15.0 / 7.0 / 144.0);
stream = Emulator::audio.createStream(2, system.colorburst() * 15.0 / 7.0 / 144.0); stream = Emulator::audio.createStream(2, frequency());
} }
} }

View File

@ -3,8 +3,15 @@
namespace MasterSystem { namespace MasterSystem {
Bus bus; Bus bus;
#include "serialization.cpp"
auto Bus::read(uint16 addr) -> uint8 { 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(auto data = cartridge.read(addr)) return data();
if(addr >= 0xc000) return ram[addr & 0x1fff]; if(addr >= 0xc000) return ram[addr & 0x1fff];
return 0x00; return 0x00;

View File

@ -1,10 +1,14 @@
struct Bus : Processor::Z80::Bus { struct Bus : Processor::Z80::Bus {
auto read(uint16 addr) -> uint8 override; auto read(uint16 addr) -> uint8 override;
auto read_(uint16 addr) -> uint8;
auto write(uint16 addr, uint8 data) -> void override; auto write(uint16 addr, uint8 data) -> void override;
auto in(uint8 addr) -> uint8 override; auto in(uint8 addr) -> uint8 override;
auto out(uint8 addr, uint8 data) -> void override; auto out(uint8 addr, uint8 data) -> void override;
//serialization.cpp
auto serialize(serializer&) -> void;
private: private:
uint8 ram[0x2000]; uint8 ram[0x2000];
}; };

View File

@ -0,0 +1,5 @@
auto Bus::serialize(serializer& s) -> void {
Processor::Z80::Bus::serialize(s);
s.array(ram);
}

View File

@ -4,6 +4,7 @@ namespace MasterSystem {
Cartridge cartridge; Cartridge cartridge;
#include "mapper.cpp" #include "mapper.cpp"
#include "serialization.cpp"
auto Cartridge::load() -> bool { auto Cartridge::load() -> bool {
information = {}; information = {};

View File

@ -14,6 +14,9 @@ struct Cartridge {
auto read(uint16 addr) -> maybe<uint8>; auto read(uint16 addr) -> maybe<uint8>;
auto write(uint16 addr, uint8 data) -> bool; auto write(uint16 addr, uint8 data) -> bool;
//serialization.cpp
auto serialize(serializer&) -> void;
private: private:
struct Information { struct Information {
uint pathID = 0; uint pathID = 0;

View File

@ -0,0 +1,3 @@
auto Cartridge::serialize(serializer& s) -> void {
if(ram.size) s.array(ram.data, ram.size);
}

View File

@ -3,6 +3,7 @@
namespace MasterSystem { namespace MasterSystem {
CPU cpu; CPU cpu;
#include "serialization.cpp"
auto CPU::Enter() -> void { auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main(); while(true) scheduler.synchronize(), cpu.main();
@ -48,7 +49,6 @@ auto CPU::setINT(bool value) -> void {
} }
auto CPU::power() -> void { auto CPU::power() -> void {
Z80::bus = &MasterSystem::bus;
Z80::power(); Z80::power();
create(CPU::Enter, system.colorburst()); create(CPU::Enter, system.colorburst());
@ -57,4 +57,8 @@ auto CPU::power() -> void {
memory::fill(&state, sizeof(State)); memory::fill(&state, sizeof(State));
} }
CPU::CPU() {
Z80::bus = &MasterSystem::bus;
}
} }

View File

@ -11,12 +11,17 @@ struct CPU : Processor::Z80, Thread {
auto power() -> void; auto power() -> void;
CPU();
//serialization.cpp
auto serialize(serializer&) -> void;
vector<Thread*> peripherals; vector<Thread*> peripherals;
private: private:
struct State { struct State {
boolean nmiLine; bool nmiLine;
boolean intLine; bool intLine;
} state; } state;
}; };

View File

@ -0,0 +1,7 @@
auto CPU::serialize(serializer& s) -> void {
Z80::serialize(s);
Thread::serialize(s);
s.integer(state.nmiLine);
s.integer(state.intLine);
}

View File

@ -7,8 +7,6 @@ Settings settings;
#include "game-gear.cpp" #include "game-gear.cpp"
Interface::Interface() { Interface::Interface() {
information.capability.states = false;
information.capability.cheats = false;
} }
auto Interface::manifest() -> string { auto Interface::manifest() -> string {
@ -49,11 +47,16 @@ auto Interface::run() -> void {
} }
auto Interface::serialize() -> serializer { auto Interface::serialize() -> serializer {
return {}; system.runToSave();
return system.serialize();
} }
auto Interface::unserialize(serializer& s) -> bool { 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 { auto Interface::cap(const string& name) -> bool {

View File

@ -40,6 +40,8 @@ struct Interface : Emulator::Interface {
auto serialize() -> serializer override; auto serialize() -> serializer override;
auto unserialize(serializer&) -> bool override; auto unserialize(serializer&) -> bool override;
auto cheatSet(const string_vector&) -> void override;
auto cap(const string& name) -> bool override; auto cap(const string& name) -> bool override;
auto get(const string& name) -> any override; auto get(const string& name) -> any override;
auto set(const string& name, const any& value) -> bool override; auto set(const string& name, const any& value) -> bool override;

View File

@ -6,6 +6,7 @@
#include <emulator/emulator.hpp> #include <emulator/emulator.hpp>
#include <emulator/thread.hpp> #include <emulator/thread.hpp>
#include <emulator/scheduler.hpp> #include <emulator/scheduler.hpp>
#include <emulator/cheat.hpp>
#include <processor/z80/z80.hpp> #include <processor/z80/z80.hpp>
@ -13,8 +14,9 @@ namespace MasterSystem {
#define platform Emulator::platform #define platform Emulator::platform
namespace File = Emulator::File; namespace File = Emulator::File;
using Scheduler = Emulator::Scheduler; using Scheduler = Emulator::Scheduler;
using Cheat = Emulator::Cheat;
extern Scheduler scheduler; extern Scheduler scheduler;
struct Interface; extern Cheat cheat;
struct Thread : Emulator::Thread { struct Thread : Emulator::Thread {
auto create(auto (*entrypoint)() -> void, double frequency) -> void { auto create(auto (*entrypoint)() -> void, double frequency) -> void {

View File

@ -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 { auto PSG::write(uint8 data) -> void {
bool l = data.bit(7); bool l = data.bit(7);
if(l) select = data.bits(4,6); if(l) select = data.bits(4,6);
@ -16,10 +12,6 @@ auto PSG::write(uint8 data) -> void {
case 1: { case 1: {
tone0.volume = data.bits(0,3); tone0.volume = data.bits(0,3);
if(tone0.pitch < 2) {
tone0.output = 1;
tone0.counter = tone0.pitch;
}
break; break;
} }
@ -31,10 +23,6 @@ auto PSG::write(uint8 data) -> void {
case 3: { case 3: {
tone1.volume = data.bits(0,3); tone1.volume = data.bits(0,3);
if(tone1.pitch < 2) {
tone1.output = 1;
tone1.counter = tone1.pitch;
}
break; break;
} }
@ -46,10 +34,6 @@ auto PSG::write(uint8 data) -> void {
case 5: { case 5: {
tone2.volume = data.bits(0,3); tone2.volume = data.bits(0,3);
if(tone2.pitch < 2) {
tone2.output = 1;
tone2.counter = tone2.pitch;
}
break; break;
} }
@ -62,7 +46,6 @@ auto PSG::write(uint8 data) -> void {
case 7: { case 7: {
noise.volume = data.bits(0,3); noise.volume = data.bits(0,3);
noise.output = 1;
break; break;
} }

View File

@ -2,11 +2,10 @@ auto PSG::Noise::run() -> void {
auto latch = clock; auto latch = clock;
counter++; counter++;
if(rate < 3) { if(rate == 0) output ^= !counter.bits(0,3);
clock ^= counter & ((16 << rate) - 1) == 0; if(rate == 1) output ^= !counter.bits(0,4);
} else { if(rate == 2) output ^= !counter.bits(0,5);
clock ^= psg.tone2.clock; if(rate == 3) output ^= psg.tone2.clock;
}
if(!latch && clock) { if(!latch && clock) {
auto eor = enable ? ~lfsr >> 3 : 0; auto eor = enable ? ~lfsr >> 3 : 0;

View File

@ -6,6 +6,7 @@ PSG psg;
#include "io.cpp" #include "io.cpp"
#include "tone.cpp" #include "tone.cpp"
#include "noise.cpp" #include "noise.cpp"
#include "serialization.cpp"
auto PSG::Enter() -> void { auto PSG::Enter() -> void {
while(true) scheduler.synchronize(), psg.main(); while(true) scheduler.synchronize(), psg.main();
@ -25,7 +26,7 @@ auto PSG::main() -> void {
lowpassLeft += (left - lowpassLeft) * 20.0 / 256.0; lowpassLeft += (left - lowpassLeft) * 20.0 / 256.0;
left = left * 2.0 / 6.0 + lowpassLeft * 3.0 / 4.0; left = left * 2.0 / 6.0 + lowpassLeft * 3.0 / 4.0;
left = sclamp<16>(left); left = sclamp<16>(left - 32768);
int right = 0; int right = 0;
if(tone0.output && tone0.right) right += levels[tone0.volume]; if(tone0.output && tone0.right) right += levels[tone0.volume];
@ -35,10 +36,10 @@ auto PSG::main() -> void {
lowpassRight += (right - lowpassRight) * 20.0 / 256.0; lowpassRight += (right - lowpassRight) * 20.0 / 256.0;
right = right * 2.0 / 6.0 + lowpassRight * 3.0 / 4.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); stream->sample(left / 32768.0, right / 32768.0);
step(1);
} }
auto PSG::step(uint clocks) -> void { auto PSG::step(uint clocks) -> void {

View File

@ -13,12 +13,18 @@ struct PSG : Thread {
auto write(uint8 data) -> void; auto write(uint8 data) -> void;
auto balance(uint8 data) -> void; auto balance(uint8 data) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
private: private:
struct Tone { struct Tone {
//tone.cpp //tone.cpp
auto run() -> void; auto run() -> void;
auto power() -> void; auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
uint4 volume; uint4 volume;
uint10 counter; uint10 counter;
uint10 pitch; uint10 pitch;
@ -34,6 +40,9 @@ private:
auto run() -> void; auto run() -> void;
auto power() -> void; auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
uint4 volume; uint4 volume;
uint6 counter; uint6 counter;
uint1 enable; uint1 enable;

View File

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

View File

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

View File

@ -2,9 +2,11 @@
namespace MasterSystem { namespace MasterSystem {
#include "peripherals.cpp"
System system; System system;
Scheduler scheduler; Scheduler scheduler;
Cheat cheat;
#include "peripherals.cpp"
#include "serialization.cpp"
auto System::run() -> void { auto System::run() -> void {
if(scheduler.enter() == Scheduler::Event::Frame) { 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 { auto System::load(Emulator::Interface* interface, Model model) -> bool {
information = {}; information = {};
information.model = model; information.model = model;
@ -24,6 +32,7 @@ auto System::load(Emulator::Interface* interface, Model model) -> bool {
auto document = BML::unserialize(information.manifest); auto document = BML::unserialize(information.manifest);
if(!cartridge.load()) return false; if(!cartridge.load()) return false;
serializeInit();
this->interface = interface; this->interface = interface;
information.colorburst = Emulator::Constants::Colorburst::NTSC; information.colorburst = Emulator::Constants::Colorburst::NTSC;
return information.loaded = true; return information.loaded = true;

View File

@ -6,6 +6,7 @@ struct System {
auto colorburst() const -> double { return information.colorburst; } auto colorburst() const -> double { return information.colorburst; }
auto run() -> void; auto run() -> void;
auto runToSave() -> void;
auto load(Emulator::Interface* interface, Model model) -> bool; auto load(Emulator::Interface* interface, Model model) -> bool;
auto save() -> void; auto save() -> void;
@ -13,6 +14,13 @@ struct System {
auto power() -> void; auto power() -> void;
//serialization.cpp
auto serializeInit() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;
auto serializeAll(serializer&) -> void;
auto serialize(serializer&) -> void;
private: private:
Emulator::Interface* interface = nullptr; Emulator::Interface* interface = nullptr;
@ -21,6 +29,7 @@ private:
Model model = Model::MasterSystem; Model model = Model::MasterSystem;
string manifest; string manifest;
double colorburst = 0.0; double colorburst = 0.0;
uint serializeSize = 0;
} information; } information;
}; };

View File

@ -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<Object, 8> is not serializable
}

View File

@ -6,6 +6,7 @@ VDP vdp;
#include "io.cpp" #include "io.cpp"
#include "background.cpp" #include "background.cpp"
#include "sprite.cpp" #include "sprite.cpp"
#include "serialization.cpp"
auto VDP::Enter() -> void { auto VDP::Enter() -> void {
while(true) scheduler.synchronize(), vdp.main(); while(true) scheduler.synchronize(), vdp.main();

View File

@ -28,6 +28,9 @@ struct VDP : Thread {
auto power() -> void; auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct State { struct State {
uint x; uint x;
uint y; uint y;
@ -47,6 +50,9 @@ struct VDP : Thread {
auto power() -> void; auto power() -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
struct Object { struct Object {
uint8 x; uint8 x;
uint8 y; uint8 y;
@ -65,6 +71,9 @@ struct VDP : Thread {
array<Object, 8> objects; array<Object, 8> objects;
} sprite; } sprite;
//serialization.cpp
auto serialize(serializer&) -> void;
private: private:
auto palette(uint5 index) -> uint12; auto palette(uint5 index) -> uint12;

View File

@ -10,9 +10,6 @@ Settings settings;
Interface::Interface() { Interface::Interface() {
information.overscan = true; information.overscan = true;
information.capability.states = true;
information.capability.cheats = true;
Port controllerPort{ID::Port::Controller, "Controller Port"}; Port controllerPort{ID::Port::Controller, "Controller Port"};
{ Device device{ID::Device::None, "None"}; { Device device{ID::Device::None, "None"};

View File

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

View File

@ -8,6 +8,7 @@ namespace Processor {
#include "memory.cpp" #include "memory.cpp"
#include "instruction.cpp" #include "instruction.cpp"
#include "instructions.cpp" #include "instructions.cpp"
#include "serialization.cpp"
auto Z80::power() -> void { auto Z80::power() -> void {
memory::fill(&r, sizeof(Registers)); memory::fill(&r, sizeof(Registers));

View File

@ -18,6 +18,9 @@ struct Z80 {
virtual auto in(uint8 addr) -> uint8 = 0; virtual auto in(uint8 addr) -> uint8 = 0;
virtual auto out(uint8 addr, uint8 data) -> void = 0; virtual auto out(uint8 addr, uint8 data) -> void = 0;
//serialization.cpp
virtual auto serialize(serializer&) -> void;
private: private:
bool _requested; bool _requested;
bool _granted; bool _granted;
@ -205,6 +208,9 @@ struct Z80 {
auto instructionXOR_a_n() -> void; auto instructionXOR_a_n() -> void;
auto instructionXOR_a_r(uint8&) -> void; auto instructionXOR_a_r(uint8&) -> void;
//serialization.cpp
auto serialize(serializer&) -> void;
//disassembler.cpp //disassembler.cpp
auto disassemble(uint16 pc) -> string; auto disassemble(uint16 pc) -> string;
auto disassemble(uint16 pc, uint8 prefix, uint8 code) -> string; auto disassemble(uint16 pc, uint8 prefix, uint8 code) -> string;
@ -229,11 +235,11 @@ struct Z80 {
uint16 sp; uint16 sp;
uint16 pc; uint16 pc;
boolean ei; //EI instruction executed bool ei; //EI instruction executed
boolean halt; //HALT instruction executed bool halt; //HALT instruction executed
boolean iff1; //interrupt flip-flop 1 bool iff1; //interrupt flip-flop 1
boolean iff2; //interrupt flip-flop 2 bool iff2; //interrupt flip-flop 2
uint2 im; //interrupt mode (0-2) uint2 im; //interrupt mode (0-2)
Pair* hlp = nullptr; Pair* hlp = nullptr;
} r; } r;

View File

@ -11,9 +11,6 @@ Interface::Interface() {
information.name = "Super Famicom"; information.name = "Super Famicom";
information.overscan = true; information.overscan = true;
information.capability.states = true;
information.capability.cheats = true;
media.append({ID::SuperFamicom, "Super Famicom", "sfc"}); media.append({ID::SuperFamicom, "Super Famicom", "sfc"});
Port controllerPort1{ID::Port::Controller1, "Controller Port 1"}; Port controllerPort1{ID::Port::Controller1, "Controller Port 1"};

View File

@ -7,9 +7,6 @@ Settings settings;
#include "wonderswan-color.cpp" #include "wonderswan-color.cpp"
Interface::Interface() { Interface::Interface() {
information.capability.states = true;
information.capability.cheats = true;
Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"}; Port hardwareHorizontalPort{ID::Port::HardwareHorizontal, "Hardware - Horizontal"};
Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"}; Port hardwareVerticalPort{ID::Port::HardwareVertical, "Hardware - Vertical"};