mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
8071da4c6a
commit
68f04c3bb8
|
@ -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/";
|
||||
|
|
|
@ -7,10 +7,6 @@ struct Interface {
|
|||
string manufacturer;
|
||||
string name;
|
||||
bool overscan;
|
||||
struct Capability {
|
||||
bool states;
|
||||
bool cheats;
|
||||
} capability;
|
||||
} information;
|
||||
|
||||
struct Region {
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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"});
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
|
@ -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());
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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];
|
||||
};
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
auto Bus::serialize(serializer& s) -> void {
|
||||
Processor::Z80::Bus::serialize(s);
|
||||
|
||||
s.array(ram);
|
||||
}
|
|
@ -4,6 +4,7 @@ namespace MasterSystem {
|
|||
|
||||
Cartridge cartridge;
|
||||
#include "mapper.cpp"
|
||||
#include "serialization.cpp"
|
||||
|
||||
auto Cartridge::load() -> bool {
|
||||
information = {};
|
||||
|
|
|
@ -14,6 +14,9 @@ struct Cartridge {
|
|||
auto read(uint16 addr) -> maybe<uint8>;
|
||||
auto write(uint16 addr, uint8 data) -> bool;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
struct Information {
|
||||
uint pathID = 0;
|
||||
|
|
|
@ -0,0 +1,3 @@
|
|||
auto Cartridge::serialize(serializer& s) -> void {
|
||||
if(ram.size) s.array(ram.data, ram.size);
|
||||
}
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -11,12 +11,17 @@ struct CPU : Processor::Z80, Thread {
|
|||
|
||||
auto power() -> void;
|
||||
|
||||
CPU();
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
vector<Thread*> peripherals;
|
||||
|
||||
private:
|
||||
struct State {
|
||||
boolean nmiLine;
|
||||
boolean intLine;
|
||||
bool nmiLine;
|
||||
bool intLine;
|
||||
} state;
|
||||
};
|
||||
|
||||
|
|
|
@ -0,0 +1,7 @@
|
|||
auto CPU::serialize(serializer& s) -> void {
|
||||
Z80::serialize(s);
|
||||
Thread::serialize(s);
|
||||
|
||||
s.integer(state.nmiLine);
|
||||
s.integer(state.intLine);
|
||||
}
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -6,6 +6,7 @@
|
|||
#include <emulator/emulator.hpp>
|
||||
#include <emulator/thread.hpp>
|
||||
#include <emulator/scheduler.hpp>
|
||||
#include <emulator/cheat.hpp>
|
||||
|
||||
#include <processor/z80/z80.hpp>
|
||||
|
||||
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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 {
|
||||
}
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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<Object, 8> objects;
|
||||
} sprite;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
private:
|
||||
auto palette(uint5 index) -> uint12;
|
||||
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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);
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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,10 +235,10 @@ 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
|
||||
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;
|
||||
|
|
|
@ -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"};
|
||||
|
|
|
@ -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"};
|
||||
|
||||
|
|
Loading…
Reference in New Issue