Serialization improvements.
This commit is contained in:
byuu
2019-10-09 00:45:57 +09:00
parent e71da4d8c8
commit d8bc2050be
26 changed files with 115 additions and 96 deletions

View File

@@ -29,7 +29,7 @@ using namespace nall;
namespace Emulator { namespace Emulator {
static const string Name = "bsnes"; static const string Name = "bsnes";
static const string Version = "111.3"; static const string Version = "111.4";
static const string Author = "byuu"; static const string Author = "byuu";
static const string License = "GPLv3"; static const string License = "GPLv3";
static const string Website = "https://byuu.org"; static const string Website = "https://byuu.org";

View File

@@ -7,14 +7,13 @@ namespace SuperFamicom {
ArmDSP armdsp; ArmDSP armdsp;
auto ArmDSP::synchronizeCPU() -> void { auto ArmDSP::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto ArmDSP::Enter() -> void { auto ArmDSP::Enter() -> void {
armdsp.boot(); armdsp.boot();
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
armdsp.main(); armdsp.main();
} }
} }

View File

@@ -8,13 +8,12 @@ namespace SuperFamicom {
EpsonRTC epsonrtc; EpsonRTC epsonrtc;
auto EpsonRTC::synchronizeCPU() -> void { auto EpsonRTC::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto EpsonRTC::Enter() -> void { auto EpsonRTC::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
epsonrtc.main(); epsonrtc.main();
} }
} }

View File

@@ -6,13 +6,12 @@ namespace SuperFamicom {
Event event; Event event;
auto Event::synchronizeCPU() -> void { auto Event::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto Event::Enter() -> void { auto Event::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
event.main(); event.main();
} }
} }

View File

@@ -9,13 +9,12 @@ namespace SuperFamicom {
HitachiDSP hitachidsp; HitachiDSP hitachidsp;
auto HitachiDSP::synchronizeCPU() -> void { auto HitachiDSP::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto HitachiDSP::Enter() -> void { auto HitachiDSP::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
hitachidsp.main(); hitachidsp.main();
} }
} }

View File

@@ -48,13 +48,15 @@ namespace SameBoy {
} }
auto ICD::synchronizeCPU() -> void { auto ICD::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) {
if(clock >= 0) co_switch(cpu.thread); scheduler.desynchronize();
co_switch(cpu.thread);
}
} }
auto ICD::Enter() -> void { auto ICD::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
icd.main(); icd.main();
} }
} }

View File

@@ -7,14 +7,13 @@ MSU1 msu1;
#include "serialization.cpp" #include "serialization.cpp"
auto MSU1::synchronizeCPU() -> void { auto MSU1::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto MSU1::Enter() -> void { auto MSU1::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
msu1.main(); msu1.main();
} }
} }

View File

@@ -7,13 +7,12 @@ namespace SuperFamicom {
NECDSP necdsp; NECDSP necdsp;
auto NECDSP::synchronizeCPU() -> void { auto NECDSP::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto NECDSP::Enter() -> void { auto NECDSP::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
necdsp.main(); necdsp.main();
} }
} }

View File

@@ -12,13 +12,15 @@ namespace SuperFamicom {
SA1 sa1; SA1 sa1;
auto SA1::synchronizeCPU() -> void { auto SA1::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) {
if(clock >= 0) co_switch(cpu.thread); scheduler.desynchronize();
co_switch(cpu.thread);
}
} }
auto SA1::Enter() -> void { auto SA1::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
sa1.main(); sa1.main();
} }
} }

View File

@@ -1,7 +1,7 @@
//Super Accelerator (SA-1) //Super Accelerator (SA-1)
struct SA1 : Processor::WDC65816, Thread { struct SA1 : Processor::WDC65816, Thread {
inline auto synchronizing() const -> bool override { return scheduler.synchronizingAll(); } inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
//sa1.cpp //sa1.cpp
auto synchronizeCPU() -> void; auto synchronizeCPU() -> void;

View File

@@ -8,13 +8,12 @@ namespace SuperFamicom {
SharpRTC sharprtc; SharpRTC sharprtc;
auto SharpRTC::synchronizeCPU() -> void { auto SharpRTC::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto SharpRTC::Enter() -> void { auto SharpRTC::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
sharprtc.main(); sharprtc.main();
} }
} }

View File

@@ -17,13 +17,12 @@ SPC7110::~SPC7110() {
} }
auto SPC7110::synchronizeCPU() -> void { auto SPC7110::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto SPC7110::Enter() -> void { auto SPC7110::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
spc7110.main(); spc7110.main();
} }
} }

View File

@@ -12,13 +12,12 @@ namespace SuperFamicom {
SuperFX superfx; SuperFX superfx;
auto SuperFX::synchronizeCPU() -> void { auto SuperFX::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto SuperFX::Enter() -> void { auto SuperFX::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
superfx.main(); superfx.main();
} }
} }

View File

@@ -2,7 +2,7 @@ struct SuperFX : Processor::GSU, Thread {
ReadableMemory rom; ReadableMemory rom;
WritableMemory ram; WritableMemory ram;
inline auto synchronizing() const -> bool { return scheduler.synchronizingAll(); } inline auto synchronizing() const -> bool { return scheduler.synchronizing(); }
//superfx.cpp //superfx.cpp
auto synchronizeCPU() -> void; auto synchronizeCPU() -> void;

View File

@@ -11,26 +11,22 @@ CPU cpu;
#include "serialization.cpp" #include "serialization.cpp"
auto CPU::synchronizeSMP() -> void { auto CPU::synchronizeSMP() -> void {
if(scheduler.synchronizingAll()) return; if(smp.clock < 0) scheduler.resume(smp.thread);
if(smp.clock < 0) co_switch(smp.thread);
} }
auto CPU::synchronizePPU() -> void { auto CPU::synchronizePPU() -> void {
if(scheduler.synchronizingAll()) return; if(ppu.clock < 0) scheduler.resume(ppu.thread);
if(ppu.clock < 0) co_switch(ppu.thread);
} }
auto CPU::synchronizeCoprocessors() -> void { auto CPU::synchronizeCoprocessors() -> void {
if(scheduler.synchronizingAll()) return;
for(auto coprocessor : coprocessors) { for(auto coprocessor : coprocessors) {
if(coprocessor->clock < 0) co_switch(coprocessor->thread); if(coprocessor->clock < 0) scheduler.resume(coprocessor->thread);
} }
} }
auto CPU::Enter() -> void { auto CPU::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeCPU(); scheduler.synchronize();
scheduler.synchronizeAll();
cpu.main(); cpu.main();
} }
} }

View File

@@ -2,7 +2,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter {
inline auto interruptPending() const -> bool override { return status.interruptPending; } inline auto interruptPending() const -> bool override { return status.interruptPending; }
inline auto pio() const -> uint8 { return io.pio; } inline auto pio() const -> uint8 { return io.pio; }
inline auto refresh() const -> bool { return status.dramRefresh == 1; } inline auto refresh() const -> bool { return status.dramRefresh == 1; }
inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeCPU; } inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
//cpu.cpp //cpu.cpp
auto synchronizeSMP() -> void; auto synchronizeSMP() -> void;

View File

@@ -69,13 +69,12 @@ PPU::~PPU() {
} }
auto PPU::synchronizeCPU() -> void { auto PPU::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(ppubase.clock >= 0) scheduler.resume(cpu.thread);
if(ppubase.clock >= 0) co_switch(cpu.thread);
} }
auto PPU::Enter() -> void { auto PPU::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
ppu.main(); ppu.main();
} }
} }

View File

@@ -39,8 +39,7 @@ PPU::~PPU() {
} }
auto PPU::synchronizeCPU() -> void { auto PPU::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto PPU::step() -> void { auto PPU::step() -> void {
@@ -60,7 +59,7 @@ auto PPU::step(uint clocks) -> void {
auto PPU::Enter() -> void { auto PPU::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
ppu.main(); ppu.main();
} }
} }

View File

@@ -28,11 +28,12 @@ namespace SuperFamicom {
extern Cheat cheat; extern Cheat cheat;
struct Scheduler { struct Scheduler {
enum class Mode : uint { Run, SynchronizeCPU, SynchronizeAll } mode; enum class Mode : uint { Run, Synchronize } mode;
enum class Event : uint { Frame, Synchronize } event; enum class Event : uint { Frame, Synchronized, Desynchronized } event;
cothread_t host = nullptr; cothread_t host = nullptr;
cothread_t active = nullptr; cothread_t active = nullptr;
bool desynchronized = false;
auto enter() -> void { auto enter() -> void {
host = co_active(); host = co_active();
@@ -45,20 +46,28 @@ namespace SuperFamicom {
co_switch(host); co_switch(host);
} }
inline auto synchronizingCPU() const -> bool { auto resume(cothread_t thread) -> void {
return mode == Mode::SynchronizeCPU; if(mode == Mode::Synchronize) desynchronized = true;
co_switch(thread);
} }
inline auto synchronizingAll() const -> bool { inline auto synchronizing() const -> bool {
return mode == Mode::SynchronizeAll; return mode == Mode::Synchronize;
} }
inline auto synchronizeCPU() -> void { inline auto synchronize() -> void {
if(mode == Mode::SynchronizeCPU) leave(Event::Synchronize); if(mode == Mode::Synchronize) {
if(desynchronized) {
desynchronized = false;
leave(Event::Desynchronized);
} else {
leave(Event::Synchronized);
}
}
} }
inline auto synchronizeAll() -> void { inline auto desynchronize() -> void {
if(mode == Mode::SynchronizeAll) leave(Event::Synchronize); desynchronized = true;
} }
}; };
extern Scheduler scheduler; extern Scheduler scheduler;

View File

@@ -13,13 +13,12 @@ BSMemory::BSMemory() {
} }
auto BSMemory::synchronizeCPU() -> void { auto BSMemory::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) scheduler.resume(cpu.thread);
if(clock >= 0) co_switch(cpu.thread);
} }
auto BSMemory::Enter() -> void { auto BSMemory::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
bsmemory.main(); bsmemory.main();
} }
} }

View File

@@ -9,8 +9,10 @@ SMP smp;
#include "serialization.cpp" #include "serialization.cpp"
auto SMP::synchronizeCPU() -> void { auto SMP::synchronizeCPU() -> void {
if(scheduler.synchronizingAll()) return; if(clock >= 0) {
if(clock >= 0) co_switch(cpu.thread); scheduler.desynchronize();
co_switch(cpu.thread);
}
} }
auto SMP::synchronizeDSP() -> void { auto SMP::synchronizeDSP() -> void {
@@ -19,7 +21,7 @@ auto SMP::synchronizeDSP() -> void {
auto SMP::Enter() -> void { auto SMP::Enter() -> void {
while(true) { while(true) {
scheduler.synchronizeAll(); scheduler.synchronize();
smp.main(); smp.main();
} }
} }

View File

@@ -1,7 +1,7 @@
//Sony CXP1100Q-1 //Sony CXP1100Q-1
struct SMP : Processor::SPC700, Thread { struct SMP : Processor::SPC700, Thread {
inline auto synchronizing() const -> bool override { return scheduler.synchronizingAll(); } inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); }
//io.cpp //io.cpp
auto portRead(uint2 port) const -> uint8; auto portRead(uint2 port) const -> uint8;

View File

@@ -15,44 +15,47 @@ auto System::run() -> void {
} }
auto System::runToSave() -> void { auto System::runToSave() -> void {
//run the CPU thread normally, exiting once we reach the next synchronization point. while(true) {
//the CPU thread may run in the pathological case for up to 10 frames for a long 8-channel x 64KB DMA transfer. //SMP thread is synchronized twice to ensure the CPU and SMP are closely aligned:
//in practice, this will typically be at most one DMA transfer. //this is extremely critical for Tales of Phantasia and Star Ocean.
scheduler.mode = Scheduler::Mode::SynchronizeCPU; scheduler.mode = Scheduler::Mode::Synchronize;
runToSynchronize(); scheduler.active = smp.thread;
if(!runToSynchronize()) continue;
//now synchronize every other thread to their synchronization points. scheduler.mode = Scheduler::Mode::Synchronize;
//stop after each thread is very slightly ahead of the CPU thread. scheduler.active = cpu.thread;
scheduler.mode = Scheduler::Mode::SynchronizeAll; if(!runToSynchronize()) continue;
runToThread(smp);
runToThread(ppu); scheduler.mode = Scheduler::Mode::Synchronize;
for(auto coprocessor : cpu.coprocessors) runToThread(*coprocessor); scheduler.active = smp.thread;
if(!runToSynchronize()) continue;
scheduler.mode = Scheduler::Mode::Synchronize;
scheduler.active = ppu.thread;
if(!runToSynchronize()) continue;
for(auto coprocessor : cpu.coprocessors) {
scheduler.mode = Scheduler::Mode::Synchronize;
scheduler.active = coprocessor->thread;
if(!runToSynchronize()) continue;
}
break;
}
//at this point, the CPU thread is the furthest behind in time.
//all threads are now at their synchronization points and can be serialized safely.
scheduler.mode = Scheduler::Mode::Run; scheduler.mode = Scheduler::Mode::Run;
scheduler.active = cpu.thread; scheduler.active = cpu.thread;
} }
auto System::runToSynchronize() -> void { auto System::runToSynchronize() -> bool {
while(true) { while(true) {
scheduler.enter(); scheduler.enter();
if(scheduler.event == Scheduler::Event::Frame) frameEvent(); if(scheduler.event == Scheduler::Event::Frame) frameEvent();
if(scheduler.event == Scheduler::Event::Synchronize) break; if(scheduler.event == Scheduler::Event::Synchronized) return true;
if(scheduler.event == Scheduler::Event::Desynchronized) return false;
} }
} }
auto System::runToThread(Thread& aux) -> void {
//first, ensure that the CPU is ahead of the thread we want to synchronize to.
//because if it isn't, and the other thread is ahead, it will run even more ahead to synchronize itself.
scheduler.active = cpu.thread;
while(aux.clock >= 0) runToSynchronize();
//now that it is, run the other thread until it's just barely surpassed the CPU thread.
scheduler.active = aux.thread;
do runToSynchronize(); while(aux.clock < 0);
}
auto System::frameEvent() -> void { auto System::frameEvent() -> void {
ppu.refresh(); ppu.refresh();

View File

@@ -10,8 +10,7 @@ struct System {
auto run() -> void; auto run() -> void;
auto runToSave() -> void; auto runToSave() -> void;
auto runToSynchronize() -> void; auto runToSynchronize() -> bool;
auto runToThread(Thread&) -> void;
auto frameEvent() -> void; auto frameEvent() -> void;
auto load(Emulator::Interface*) -> bool; auto load(Emulator::Interface*) -> bool;

View File

@@ -4,6 +4,7 @@
#include <assert.h> #include <assert.h>
#include <stdlib.h> #include <stdlib.h>
#include <string.h>
#ifdef __cplusplus #ifdef __cplusplus
extern "C" { extern "C" {
@@ -167,6 +168,20 @@ void co_switch(cothread_t handle) {
co_swap(co_active_handle = handle, co_previous_handle); co_swap(co_active_handle = handle, co_previous_handle);
} }
unsigned int co_size(unsigned int size) {
size += 512;
size &= ~15;
return size;
}
void co_save(cothread_t handle, void* memory, unsigned int size) {
memcpy(memory, handle, size);
}
void co_load(cothread_t handle, const void* memory, unsigned int size) {
memcpy(handle, memory, size);
}
#ifdef __cplusplus #ifdef __cplusplus
} }
#endif #endif

View File

@@ -18,6 +18,9 @@ cothread_t co_derive(void*, unsigned int, void (*)(void));
cothread_t co_create(unsigned int, void (*)(void)); cothread_t co_create(unsigned int, void (*)(void));
void co_delete(cothread_t); void co_delete(cothread_t);
void co_switch(cothread_t); void co_switch(cothread_t);
unsigned int co_size(unsigned int);
void co_save(cothread_t, void*, unsigned int);
void co_load(cothread_t, const void*, unsigned int);
#ifdef __cplusplus #ifdef __cplusplus
} }