mirror of https://github.com/bsnes-emu/bsnes.git
v111.7
Added System/Serialization/Synchronize setting to settings.bml This option is for experimental deterministic rewind support. It does not currently work with SuperFX and SA-1 games, and as such is set to true (force synchronize) for now.
This commit is contained in:
parent
95addddc46
commit
2de906ea46
|
@ -29,7 +29,7 @@ using namespace nall;
|
|||
|
||||
namespace Emulator {
|
||||
static const string Name = "bsnes";
|
||||
static const string Version = "111.6";
|
||||
static const string Version = "111.7";
|
||||
static const string Author = "byuu";
|
||||
static const string License = "GPLv3";
|
||||
static const string Website = "https://byuu.org";
|
||||
|
|
|
@ -81,7 +81,7 @@ struct Interface {
|
|||
virtual auto synchronize(uint64 timestamp = 0) -> void {}
|
||||
|
||||
//state functions
|
||||
virtual auto serialize() -> serializer { return {}; }
|
||||
virtual auto serialize(bool synchronize = true) -> serializer { return {}; }
|
||||
virtual auto unserialize(serializer&) -> bool { return false; }
|
||||
|
||||
//cheat functions
|
||||
|
|
|
@ -239,9 +239,8 @@ auto Interface::synchronize(uint64 timestamp) -> void {
|
|||
if(cartridge.has.SharpRTC) sharprtc.synchronize(timestamp);
|
||||
}
|
||||
|
||||
auto Interface::serialize() -> serializer {
|
||||
system.runToSave();
|
||||
return system.serialize();
|
||||
auto Interface::serialize(bool synchronize) -> serializer {
|
||||
return system.serialize(synchronize);
|
||||
}
|
||||
|
||||
auto Interface::unserialize(serializer& s) -> bool {
|
||||
|
|
|
@ -58,7 +58,7 @@ struct Interface : Emulator::Interface {
|
|||
auto rtc() -> bool override;
|
||||
auto synchronize(uint64 timestamp) -> void override;
|
||||
|
||||
auto serialize() -> serializer override;
|
||||
auto serialize(bool synchronize = true) -> serializer override;
|
||||
auto unserialize(serializer&) -> bool override;
|
||||
|
||||
auto read(uint24 address) -> uint8 override;
|
||||
|
|
|
@ -6,9 +6,13 @@ struct ProtectableMemory : Memory {
|
|||
}
|
||||
|
||||
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
for(uint address : range(size)) self.data[address] = fill;
|
||||
if(self.size != size) {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
}
|
||||
for(uint address : range(size)) {
|
||||
self.data[address] = fill;
|
||||
}
|
||||
}
|
||||
|
||||
inline auto data() -> uint8* override {
|
||||
|
|
|
@ -6,9 +6,13 @@ struct ReadableMemory : Memory {
|
|||
}
|
||||
|
||||
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
for(uint address : range(size)) self.data[address] = fill;
|
||||
if(self.size != size) {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
}
|
||||
for(uint address : range(size)) {
|
||||
self.data[address] = fill;
|
||||
}
|
||||
}
|
||||
|
||||
inline auto data() -> uint8* override {
|
||||
|
|
|
@ -6,9 +6,13 @@ struct WritableMemory : Memory {
|
|||
}
|
||||
|
||||
inline auto allocate(uint size, uint8 fill = 0xff) -> void override {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
for(uint address : range(size)) self.data[address] = fill;
|
||||
if(self.size != size) {
|
||||
delete[] self.data;
|
||||
self.data = new uint8[self.size = size];
|
||||
}
|
||||
for(uint address : range(size)) {
|
||||
self.data[address] = fill;
|
||||
}
|
||||
}
|
||||
|
||||
inline auto data() -> uint8* override {
|
||||
|
|
|
@ -73,9 +73,14 @@ namespace SuperFamicom {
|
|||
extern Scheduler scheduler;
|
||||
|
||||
struct Thread {
|
||||
enum : uint { Size = 16_KiB * sizeof(void*) };
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, uint frequency_) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), entrypoint);
|
||||
if(!thread) {
|
||||
thread = co_create(Thread::Size, entrypoint);
|
||||
} else {
|
||||
co_derive(thread, Thread::Size, entrypoint);
|
||||
}
|
||||
frequency = frequency_;
|
||||
clock = 0;
|
||||
}
|
||||
|
@ -89,6 +94,26 @@ namespace SuperFamicom {
|
|||
s.integer(clock);
|
||||
}
|
||||
|
||||
auto serializeStack(serializer& s) -> void {
|
||||
auto stack = new uint8_t[Thread::Size];
|
||||
|
||||
if(s.mode() == serializer::Size) {
|
||||
s.array(stack, Thread::Size);
|
||||
}
|
||||
|
||||
if(s.mode() == serializer::Load) {
|
||||
s.array(stack, Thread::Size);
|
||||
memory::copy(thread, stack, Thread::Size);
|
||||
}
|
||||
|
||||
if(s.mode() == serializer::Save) {
|
||||
memory::copy(stack, thread, Thread::Size);
|
||||
s.array(stack, Thread::Size);
|
||||
}
|
||||
|
||||
delete[] stack;
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint32_t frequency = 0;
|
||||
int64_t clock = 0;
|
||||
|
|
|
@ -1,49 +1,51 @@
|
|||
auto System::serialize() -> serializer {
|
||||
serializer s(serializeSize);
|
||||
auto System::serialize(bool synchronize) -> serializer {
|
||||
if(synchronize) runToSave();
|
||||
|
||||
uint signature = 0x31545342;
|
||||
uint serializeSize = information.serializeSize[synchronize];
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
memory::copy(&version, (const char*)Emulator::SerializerVersion, Emulator::SerializerVersion.size());
|
||||
|
||||
serializer s(serializeSize);
|
||||
s.integer(signature);
|
||||
s.integer(serializeSize);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
|
||||
s.boolean(synchronize);
|
||||
s.boolean(hacks.fastPPU);
|
||||
|
||||
serializeAll(s);
|
||||
serializeAll(s, synchronize);
|
||||
return s;
|
||||
}
|
||||
|
||||
auto System::unserialize(serializer& s) -> bool {
|
||||
uint signature = 0;
|
||||
uint serializeSize = 0;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
bool synchronize = false;
|
||||
bool fastPPU = false;
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(serializeSize);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
s.boolean(synchronize);
|
||||
s.boolean(fastPPU);
|
||||
|
||||
if(signature != 0x31545342) return false;
|
||||
if(serializeSize != information.serializeSize[synchronize]) return false;
|
||||
if(string{version} != Emulator::SerializerVersion) return false;
|
||||
|
||||
s.boolean(hacks.fastPPU);
|
||||
if(fastPPU != hacks.fastPPU) return false;
|
||||
|
||||
power(/* reset = */ false);
|
||||
serializeAll(s);
|
||||
serializeInit(); //hacks.fastPPU setting changes serializeSize
|
||||
|
||||
serializeAll(s, synchronize);
|
||||
return true;
|
||||
}
|
||||
|
||||
//internal
|
||||
|
||||
auto System::serialize(serializer& s) -> void {
|
||||
}
|
||||
|
||||
auto System::serializeAll(serializer& s) -> void {
|
||||
system.serialize(s);
|
||||
auto System::serializeAll(serializer& s, bool synchronize) -> void {
|
||||
random.serialize(s);
|
||||
cartridge.serialize(s);
|
||||
cpu.serialize(s);
|
||||
|
@ -80,24 +82,34 @@ auto System::serializeAll(serializer& s) -> void {
|
|||
controllerPort1.serialize(s);
|
||||
controllerPort2.serialize(s);
|
||||
expansionPort.serialize(s);
|
||||
|
||||
if(!synchronize) {
|
||||
cpu.serializeStack(s);
|
||||
smp.serializeStack(s);
|
||||
ppu.serializeStack(s);
|
||||
for(auto coprocessor : cpu.coprocessors) {
|
||||
coprocessor->serializeStack(s);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
//perform dry-run state save:
|
||||
//determines exactly how many bytes are needed to save state for this cartridge,
|
||||
//as amount varies per game (eg different RAM sizes, special chips, etc.)
|
||||
auto System::serializeInit() -> void {
|
||||
auto System::serializeInit(bool synchronize) -> uint {
|
||||
serializer s;
|
||||
|
||||
uint signature = 0;
|
||||
uint serializeSize = 0;
|
||||
char version[16] = {};
|
||||
char description[512] = {};
|
||||
|
||||
s.integer(signature);
|
||||
s.integer(serializeSize);
|
||||
s.array(version);
|
||||
s.array(description);
|
||||
|
||||
s.boolean(synchronize);
|
||||
s.boolean(hacks.fastPPU);
|
||||
|
||||
serializeAll(s);
|
||||
serializeSize = s.size();
|
||||
serializeAll(s, synchronize);
|
||||
return s.size();
|
||||
}
|
||||
|
|
|
@ -97,7 +97,8 @@ auto System::load(Emulator::Interface* interface) -> bool {
|
|||
}
|
||||
if(cartridge.has.BSMemorySlot) bsmemory.load();
|
||||
|
||||
serializeInit();
|
||||
information.serializeSize[0] = serializeInit(0);
|
||||
information.serializeSize[1] = serializeInit(1);
|
||||
this->interface = interface;
|
||||
return information.loaded = true;
|
||||
}
|
||||
|
|
|
@ -18,7 +18,7 @@ struct System {
|
|||
auto power(bool reset) -> void;
|
||||
|
||||
//serialization.cpp
|
||||
auto serialize() -> serializer;
|
||||
auto serialize(bool synchronize) -> serializer;
|
||||
auto unserialize(serializer&) -> bool;
|
||||
|
||||
uint frameSkip = 0;
|
||||
|
@ -32,17 +32,15 @@ private:
|
|||
Region region = Region::NTSC;
|
||||
double cpuFrequency = Emulator::Constants::Colorburst::NTSC * 6.0;
|
||||
double apuFrequency = 32040.0 * 768.0;
|
||||
uint serializeSize[2] = {0, 0};
|
||||
} information;
|
||||
|
||||
struct Hacks {
|
||||
bool fastPPU = false;
|
||||
} hacks;
|
||||
|
||||
uint serializeSize = 0;
|
||||
|
||||
auto serialize(serializer&) -> void;
|
||||
auto serializeAll(serializer&) -> void;
|
||||
auto serializeInit() -> void;
|
||||
auto serializeAll(serializer&, bool synchronize) -> void;
|
||||
auto serializeInit(bool synchronize) -> uint;
|
||||
|
||||
friend class Cartridge;
|
||||
};
|
||||
|
|
|
@ -35,7 +35,7 @@ auto Program::rewindRun() -> void {
|
|||
if(rewind.history.size() >= rewind.length) {
|
||||
rewind.history.takeFirst();
|
||||
}
|
||||
auto s = emulator->serialize();
|
||||
auto s = emulator->serialize(settings.emulator.serialization.synchronize);
|
||||
rewind.history.append(s);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -115,6 +115,7 @@ auto Settings::process(bool load) -> void {
|
|||
bind(boolean, "Emulator/AutoSaveStateOnUnload", emulator.autoSaveStateOnUnload);
|
||||
bind(boolean, "Emulator/AutoLoadStateOnLoad", emulator.autoLoadStateOnLoad);
|
||||
bind(text, "Emulator/Serialization/Method", emulator.serialization.method);
|
||||
bind(boolean, "Emulator/Serialization/Synchronize", emulator.serialization.synchronize);
|
||||
bind(boolean, "Emulator/Hack/Hotfixes", emulator.hack.hotfixes);
|
||||
bind(text, "Emulator/Hack/Entropy", emulator.hack.entropy);
|
||||
bind(natural, "Emulator/Hack/CPU/Overclock", emulator.hack.cpu.overclock);
|
||||
|
|
|
@ -96,6 +96,7 @@ struct Settings : Markup::Node {
|
|||
bool autoLoadStateOnLoad = false;
|
||||
struct Serialization {
|
||||
string method = "Fast";
|
||||
bool synchronize = true;
|
||||
} serialization;
|
||||
struct Hack {
|
||||
bool hotfixes = true;
|
||||
|
|
|
@ -4,7 +4,6 @@
|
|||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
|
@ -130,10 +129,11 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
|||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
|
||||
if(handle = (cothread_t)memory) {
|
||||
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
unsigned int offset = (size & ~15) - 32;
|
||||
long long *p = (long long*)((char*)handle + offset); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
@ -146,14 +146,13 @@ cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
|||
co_swap = (void (*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 512; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long long *p = (long long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
unsigned int offset = (size & ~15) - 32;
|
||||
long long *p = (long long*)((char*)handle + offset); /* seek to top of stack */
|
||||
*--p = (long long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long long)entrypoint; /* start of function */
|
||||
*(long long*)handle = (long long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
/*
|
||||
libco v19 (2019-02-18)
|
||||
libco v20 (2019-10-14)
|
||||
author: byuu
|
||||
license: ISC
|
||||
*/
|
||||
|
|
20
libco/x86.c
20
libco/x86.c
|
@ -83,10 +83,11 @@ cothread_t co_derive(void* memory, unsigned int size, void (*entrypoint)(void))
|
|||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
|
||||
if(handle = (cothread_t)memory) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
unsigned int offset = (size & ~15) - 32;
|
||||
long *p = (long*)((char*)handle + offset); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
@ -99,14 +100,13 @@ cothread_t co_create(unsigned int size, void (*entrypoint)(void)) {
|
|||
co_swap = (void (fastcall*)(cothread_t, cothread_t))co_swap_function;
|
||||
}
|
||||
if(!co_active_handle) co_active_handle = &co_active_buffer;
|
||||
size += 256; /* allocate additional space for storage */
|
||||
size &= ~15; /* align stack to 16-byte boundary */
|
||||
|
||||
if(handle = (cothread_t)malloc(size)) {
|
||||
long *p = (long*)((char*)handle + size); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
unsigned int offset = (size & ~15) - 32;
|
||||
long *p = (long*)((char*)handle + offset); /* seek to top of stack */
|
||||
*--p = (long)crash; /* crash if entrypoint returns */
|
||||
*--p = (long)entrypoint; /* start of function */
|
||||
*(long*)handle = (long)p; /* stack pointer */
|
||||
}
|
||||
|
||||
return handle;
|
||||
|
|
Loading…
Reference in New Issue