From d8bc2050bef1c9b313a7cec13b14b8504fe9a6f9 Mon Sep 17 00:00:00 2001 From: byuu <2107894+byuu@users.noreply.github.com> Date: Wed, 9 Oct 2019 00:45:57 +0900 Subject: [PATCH] v111.4 Serialization improvements. --- bsnes/emulator/emulator.hpp | 2 +- bsnes/sfc/coprocessor/armdsp/armdsp.cpp | 5 +- bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp | 5 +- bsnes/sfc/coprocessor/event/event.cpp | 5 +- .../sfc/coprocessor/hitachidsp/hitachidsp.cpp | 5 +- bsnes/sfc/coprocessor/icd/icd.cpp | 8 ++- bsnes/sfc/coprocessor/msu1/msu1.cpp | 5 +- bsnes/sfc/coprocessor/necdsp/necdsp.cpp | 5 +- bsnes/sfc/coprocessor/sa1/sa1.cpp | 8 ++- bsnes/sfc/coprocessor/sa1/sa1.hpp | 2 +- bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp | 5 +- bsnes/sfc/coprocessor/spc7110/spc7110.cpp | 5 +- bsnes/sfc/coprocessor/superfx/superfx.cpp | 5 +- bsnes/sfc/coprocessor/superfx/superfx.hpp | 2 +- bsnes/sfc/cpu/cpu.cpp | 12 ++-- bsnes/sfc/cpu/cpu.hpp | 2 +- bsnes/sfc/ppu-fast/ppu.cpp | 5 +- bsnes/sfc/ppu/ppu.cpp | 5 +- bsnes/sfc/sfc.hpp | 29 ++++++---- bsnes/sfc/slot/bsmemory/bsmemory.cpp | 5 +- bsnes/sfc/smp/smp.cpp | 8 ++- bsnes/sfc/smp/smp.hpp | 2 +- bsnes/sfc/system/system.cpp | 55 ++++++++++--------- bsnes/sfc/system/system.hpp | 3 +- libco/amd64.c | 15 +++++ libco/libco.h | 3 + 26 files changed, 115 insertions(+), 96 deletions(-) diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index aa60c8b8..607b0fa7 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -29,7 +29,7 @@ using namespace nall; namespace Emulator { 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 License = "GPLv3"; static const string Website = "https://byuu.org"; diff --git a/bsnes/sfc/coprocessor/armdsp/armdsp.cpp b/bsnes/sfc/coprocessor/armdsp/armdsp.cpp index 29095b11..8a7d08d0 100644 --- a/bsnes/sfc/coprocessor/armdsp/armdsp.cpp +++ b/bsnes/sfc/coprocessor/armdsp/armdsp.cpp @@ -7,14 +7,13 @@ namespace SuperFamicom { ArmDSP armdsp; auto ArmDSP::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto ArmDSP::Enter() -> void { armdsp.boot(); while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); armdsp.main(); } } diff --git a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp index de700ce9..74e0c2fe 100644 --- a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp +++ b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp @@ -8,13 +8,12 @@ namespace SuperFamicom { EpsonRTC epsonrtc; auto EpsonRTC::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto EpsonRTC::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); epsonrtc.main(); } } diff --git a/bsnes/sfc/coprocessor/event/event.cpp b/bsnes/sfc/coprocessor/event/event.cpp index 2316c3e2..08c053a3 100644 --- a/bsnes/sfc/coprocessor/event/event.cpp +++ b/bsnes/sfc/coprocessor/event/event.cpp @@ -6,13 +6,12 @@ namespace SuperFamicom { Event event; auto Event::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto Event::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); event.main(); } } diff --git a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp index 5b07b634..c4d2a513 100644 --- a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp +++ b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp @@ -9,13 +9,12 @@ namespace SuperFamicom { HitachiDSP hitachidsp; auto HitachiDSP::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto HitachiDSP::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); hitachidsp.main(); } } diff --git a/bsnes/sfc/coprocessor/icd/icd.cpp b/bsnes/sfc/coprocessor/icd/icd.cpp index 788cd8a5..d5a7f12d 100644 --- a/bsnes/sfc/coprocessor/icd/icd.cpp +++ b/bsnes/sfc/coprocessor/icd/icd.cpp @@ -48,13 +48,15 @@ namespace SameBoy { } auto ICD::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) { + scheduler.desynchronize(); + co_switch(cpu.thread); + } } auto ICD::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); icd.main(); } } diff --git a/bsnes/sfc/coprocessor/msu1/msu1.cpp b/bsnes/sfc/coprocessor/msu1/msu1.cpp index b0b891bb..046e7101 100644 --- a/bsnes/sfc/coprocessor/msu1/msu1.cpp +++ b/bsnes/sfc/coprocessor/msu1/msu1.cpp @@ -7,14 +7,13 @@ MSU1 msu1; #include "serialization.cpp" auto MSU1::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto MSU1::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); msu1.main(); } } diff --git a/bsnes/sfc/coprocessor/necdsp/necdsp.cpp b/bsnes/sfc/coprocessor/necdsp/necdsp.cpp index fbbdb3d6..3942d98a 100644 --- a/bsnes/sfc/coprocessor/necdsp/necdsp.cpp +++ b/bsnes/sfc/coprocessor/necdsp/necdsp.cpp @@ -7,13 +7,12 @@ namespace SuperFamicom { NECDSP necdsp; auto NECDSP::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto NECDSP::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); necdsp.main(); } } diff --git a/bsnes/sfc/coprocessor/sa1/sa1.cpp b/bsnes/sfc/coprocessor/sa1/sa1.cpp index 0a87173c..5e487f7a 100644 --- a/bsnes/sfc/coprocessor/sa1/sa1.cpp +++ b/bsnes/sfc/coprocessor/sa1/sa1.cpp @@ -12,13 +12,15 @@ namespace SuperFamicom { SA1 sa1; auto SA1::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) { + scheduler.desynchronize(); + co_switch(cpu.thread); + } } auto SA1::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); sa1.main(); } } diff --git a/bsnes/sfc/coprocessor/sa1/sa1.hpp b/bsnes/sfc/coprocessor/sa1/sa1.hpp index 21c46656..66dc6c4c 100644 --- a/bsnes/sfc/coprocessor/sa1/sa1.hpp +++ b/bsnes/sfc/coprocessor/sa1/sa1.hpp @@ -1,7 +1,7 @@ //Super Accelerator (SA-1) 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 auto synchronizeCPU() -> void; diff --git a/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp b/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp index 372ffd56..70782657 100644 --- a/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp +++ b/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp @@ -8,13 +8,12 @@ namespace SuperFamicom { SharpRTC sharprtc; auto SharpRTC::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto SharpRTC::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); sharprtc.main(); } } diff --git a/bsnes/sfc/coprocessor/spc7110/spc7110.cpp b/bsnes/sfc/coprocessor/spc7110/spc7110.cpp index 85c7354c..a2981245 100644 --- a/bsnes/sfc/coprocessor/spc7110/spc7110.cpp +++ b/bsnes/sfc/coprocessor/spc7110/spc7110.cpp @@ -17,13 +17,12 @@ SPC7110::~SPC7110() { } auto SPC7110::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto SPC7110::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); spc7110.main(); } } diff --git a/bsnes/sfc/coprocessor/superfx/superfx.cpp b/bsnes/sfc/coprocessor/superfx/superfx.cpp index b9ddf09d..847f6a06 100644 --- a/bsnes/sfc/coprocessor/superfx/superfx.cpp +++ b/bsnes/sfc/coprocessor/superfx/superfx.cpp @@ -12,13 +12,12 @@ namespace SuperFamicom { SuperFX superfx; auto SuperFX::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto SuperFX::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); superfx.main(); } } diff --git a/bsnes/sfc/coprocessor/superfx/superfx.hpp b/bsnes/sfc/coprocessor/superfx/superfx.hpp index 680e9b1e..97df3dfc 100644 --- a/bsnes/sfc/coprocessor/superfx/superfx.hpp +++ b/bsnes/sfc/coprocessor/superfx/superfx.hpp @@ -2,7 +2,7 @@ struct SuperFX : Processor::GSU, Thread { ReadableMemory rom; WritableMemory ram; - inline auto synchronizing() const -> bool { return scheduler.synchronizingAll(); } + inline auto synchronizing() const -> bool { return scheduler.synchronizing(); } //superfx.cpp auto synchronizeCPU() -> void; diff --git a/bsnes/sfc/cpu/cpu.cpp b/bsnes/sfc/cpu/cpu.cpp index 2505dc92..45566eb1 100644 --- a/bsnes/sfc/cpu/cpu.cpp +++ b/bsnes/sfc/cpu/cpu.cpp @@ -11,26 +11,22 @@ CPU cpu; #include "serialization.cpp" auto CPU::synchronizeSMP() -> void { - if(scheduler.synchronizingAll()) return; - if(smp.clock < 0) co_switch(smp.thread); + if(smp.clock < 0) scheduler.resume(smp.thread); } auto CPU::synchronizePPU() -> void { - if(scheduler.synchronizingAll()) return; - if(ppu.clock < 0) co_switch(ppu.thread); + if(ppu.clock < 0) scheduler.resume(ppu.thread); } auto CPU::synchronizeCoprocessors() -> void { - if(scheduler.synchronizingAll()) return; for(auto coprocessor : coprocessors) { - if(coprocessor->clock < 0) co_switch(coprocessor->thread); + if(coprocessor->clock < 0) scheduler.resume(coprocessor->thread); } } auto CPU::Enter() -> void { while(true) { - scheduler.synchronizeCPU(); - scheduler.synchronizeAll(); + scheduler.synchronize(); cpu.main(); } } diff --git a/bsnes/sfc/cpu/cpu.hpp b/bsnes/sfc/cpu/cpu.hpp index bac32b3e..154270c6 100644 --- a/bsnes/sfc/cpu/cpu.hpp +++ b/bsnes/sfc/cpu/cpu.hpp @@ -2,7 +2,7 @@ struct CPU : Processor::WDC65816, Thread, PPUcounter { inline auto interruptPending() const -> bool override { return status.interruptPending; } inline auto pio() const -> uint8 { return io.pio; } 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 auto synchronizeSMP() -> void; diff --git a/bsnes/sfc/ppu-fast/ppu.cpp b/bsnes/sfc/ppu-fast/ppu.cpp index 6dd09b6c..0d1d8eb4 100644 --- a/bsnes/sfc/ppu-fast/ppu.cpp +++ b/bsnes/sfc/ppu-fast/ppu.cpp @@ -69,13 +69,12 @@ PPU::~PPU() { } auto PPU::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(ppubase.clock >= 0) co_switch(cpu.thread); + if(ppubase.clock >= 0) scheduler.resume(cpu.thread); } auto PPU::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); ppu.main(); } } diff --git a/bsnes/sfc/ppu/ppu.cpp b/bsnes/sfc/ppu/ppu.cpp index a3de2dca..f5ede96d 100644 --- a/bsnes/sfc/ppu/ppu.cpp +++ b/bsnes/sfc/ppu/ppu.cpp @@ -39,8 +39,7 @@ PPU::~PPU() { } auto PPU::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto PPU::step() -> void { @@ -60,7 +59,7 @@ auto PPU::step(uint clocks) -> void { auto PPU::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); ppu.main(); } } diff --git a/bsnes/sfc/sfc.hpp b/bsnes/sfc/sfc.hpp index 0f209dcf..7171b29b 100644 --- a/bsnes/sfc/sfc.hpp +++ b/bsnes/sfc/sfc.hpp @@ -28,11 +28,12 @@ namespace SuperFamicom { extern Cheat cheat; struct Scheduler { - enum class Mode : uint { Run, SynchronizeCPU, SynchronizeAll } mode; - enum class Event : uint { Frame, Synchronize } event; + enum class Mode : uint { Run, Synchronize } mode; + enum class Event : uint { Frame, Synchronized, Desynchronized } event; cothread_t host = nullptr; cothread_t active = nullptr; + bool desynchronized = false; auto enter() -> void { host = co_active(); @@ -45,20 +46,28 @@ namespace SuperFamicom { co_switch(host); } - inline auto synchronizingCPU() const -> bool { - return mode == Mode::SynchronizeCPU; + auto resume(cothread_t thread) -> void { + if(mode == Mode::Synchronize) desynchronized = true; + co_switch(thread); } - inline auto synchronizingAll() const -> bool { - return mode == Mode::SynchronizeAll; + inline auto synchronizing() const -> bool { + return mode == Mode::Synchronize; } - inline auto synchronizeCPU() -> void { - if(mode == Mode::SynchronizeCPU) leave(Event::Synchronize); + inline auto synchronize() -> void { + if(mode == Mode::Synchronize) { + if(desynchronized) { + desynchronized = false; + leave(Event::Desynchronized); + } else { + leave(Event::Synchronized); + } + } } - inline auto synchronizeAll() -> void { - if(mode == Mode::SynchronizeAll) leave(Event::Synchronize); + inline auto desynchronize() -> void { + desynchronized = true; } }; extern Scheduler scheduler; diff --git a/bsnes/sfc/slot/bsmemory/bsmemory.cpp b/bsnes/sfc/slot/bsmemory/bsmemory.cpp index 17f51a48..3adf0750 100644 --- a/bsnes/sfc/slot/bsmemory/bsmemory.cpp +++ b/bsnes/sfc/slot/bsmemory/bsmemory.cpp @@ -13,13 +13,12 @@ BSMemory::BSMemory() { } auto BSMemory::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) scheduler.resume(cpu.thread); } auto BSMemory::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); bsmemory.main(); } } diff --git a/bsnes/sfc/smp/smp.cpp b/bsnes/sfc/smp/smp.cpp index 7d9ae842..9a746b9a 100644 --- a/bsnes/sfc/smp/smp.cpp +++ b/bsnes/sfc/smp/smp.cpp @@ -9,8 +9,10 @@ SMP smp; #include "serialization.cpp" auto SMP::synchronizeCPU() -> void { - if(scheduler.synchronizingAll()) return; - if(clock >= 0) co_switch(cpu.thread); + if(clock >= 0) { + scheduler.desynchronize(); + co_switch(cpu.thread); + } } auto SMP::synchronizeDSP() -> void { @@ -19,7 +21,7 @@ auto SMP::synchronizeDSP() -> void { auto SMP::Enter() -> void { while(true) { - scheduler.synchronizeAll(); + scheduler.synchronize(); smp.main(); } } diff --git a/bsnes/sfc/smp/smp.hpp b/bsnes/sfc/smp/smp.hpp index c973c079..b23bfaae 100644 --- a/bsnes/sfc/smp/smp.hpp +++ b/bsnes/sfc/smp/smp.hpp @@ -1,7 +1,7 @@ //Sony CXP1100Q-1 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 auto portRead(uint2 port) const -> uint8; diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp index d83689cd..c2e179ee 100644 --- a/bsnes/sfc/system/system.cpp +++ b/bsnes/sfc/system/system.cpp @@ -15,44 +15,47 @@ auto System::run() -> void { } auto System::runToSave() -> void { - //run the CPU thread normally, exiting once we reach the next synchronization point. - //the CPU thread may run in the pathological case for up to 10 frames for a long 8-channel x 64KB DMA transfer. - //in practice, this will typically be at most one DMA transfer. - scheduler.mode = Scheduler::Mode::SynchronizeCPU; - runToSynchronize(); + while(true) { + //SMP thread is synchronized twice to ensure the CPU and SMP are closely aligned: + //this is extremely critical for Tales of Phantasia and Star Ocean. + scheduler.mode = Scheduler::Mode::Synchronize; + scheduler.active = smp.thread; + if(!runToSynchronize()) continue; - //now synchronize every other thread to their synchronization points. - //stop after each thread is very slightly ahead of the CPU thread. - scheduler.mode = Scheduler::Mode::SynchronizeAll; - runToThread(smp); - runToThread(ppu); - for(auto coprocessor : cpu.coprocessors) runToThread(*coprocessor); + scheduler.mode = Scheduler::Mode::Synchronize; + scheduler.active = cpu.thread; + if(!runToSynchronize()) continue; + + scheduler.mode = Scheduler::Mode::Synchronize; + 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.active = cpu.thread; } -auto System::runToSynchronize() -> void { +auto System::runToSynchronize() -> bool { while(true) { scheduler.enter(); 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 { ppu.refresh(); diff --git a/bsnes/sfc/system/system.hpp b/bsnes/sfc/system/system.hpp index 3e717dc7..eae0532d 100644 --- a/bsnes/sfc/system/system.hpp +++ b/bsnes/sfc/system/system.hpp @@ -10,8 +10,7 @@ struct System { auto run() -> void; auto runToSave() -> void; - auto runToSynchronize() -> void; - auto runToThread(Thread&) -> void; + auto runToSynchronize() -> bool; auto frameEvent() -> void; auto load(Emulator::Interface*) -> bool; diff --git a/libco/amd64.c b/libco/amd64.c index ab62bd92..686b8565 100755 --- a/libco/amd64.c +++ b/libco/amd64.c @@ -4,6 +4,7 @@ #include #include +#include #ifdef __cplusplus extern "C" { @@ -167,6 +168,20 @@ void co_switch(cothread_t 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 } #endif diff --git a/libco/libco.h b/libco/libco.h index b7f60852..0a5db70a 100755 --- a/libco/libco.h +++ b/libco/libco.h @@ -18,6 +18,9 @@ cothread_t co_derive(void*, unsigned int, void (*)(void)); cothread_t co_create(unsigned int, void (*)(void)); void co_delete(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 }