diff --git a/bsnes/emulator/emulator.hpp b/bsnes/emulator/emulator.hpp index ffea014d..3baae31d 100644 --- a/bsnes/emulator/emulator.hpp +++ b/bsnes/emulator/emulator.hpp @@ -31,13 +31,13 @@ using namespace nall; namespace Emulator { static const string Name = "bsnes"; - static const string Version = "108.1"; + static const string Version = "108.2"; static const string Author = "byuu"; static const string License = "GPLv3"; static const string Website = "https://byuu.org"; //incremented only when serialization format changes - static const string SerializerVersion = "108"; + static const string SerializerVersion = "108.2"; namespace Constants { namespace Colorburst { diff --git a/bsnes/emulator/scheduler.hpp b/bsnes/emulator/scheduler.hpp deleted file mode 100644 index da683a56..00000000 --- a/bsnes/emulator/scheduler.hpp +++ /dev/null @@ -1,98 +0,0 @@ -#pragma once -#define uintmax uint64 - -namespace Emulator { - -struct Scheduler { - enum class Mode : uint { - Run, - SynchronizeMaster, - SynchronizeSlave, - }; - - enum class Event : uint { - Step, - Frame, - Synchronize, - }; - - inline auto synchronizing() const -> bool { return _mode == Mode::SynchronizeSlave; } - - auto reset() -> void { - _host = co_active(); - _threads.reset(); - } - - auto primary(Thread& thread) -> void { - _master = _resume = thread.handle(); - uintmax clock = 0; - for(auto& thread : _threads) { - thread->_clock = clock++; //this bias prioritizes threads appended earlier first - } - } - - auto append(Thread& thread) -> bool { - if(_threads.find(&thread)) return false; - thread._clock = _threads.size(); - return _threads.append(&thread), true; - } - - auto remove(Thread& thread) -> bool { - if(auto offset = _threads.find(&thread)) return _threads.remove(*offset), true; - return false; - } - - auto enter(Mode mode = Mode::Run) -> Event { - _mode = mode; - _host = co_active(); - co_switch(_resume); - return _event; - } - - inline auto resume(Thread& thread) -> void { - if(_mode != Mode::SynchronizeSlave) co_switch(thread.handle()); - } - - auto exit(Event event) -> void { - uintmax minimum = -1; - for(auto thread : _threads) { - if(thread->_clock < minimum) minimum = thread->_clock; - } - for(auto thread : _threads) { - thread->_clock -= minimum; - } - - _event = event; - _resume = co_active(); - co_switch(_host); - } - - inline auto synchronize(Thread& thread) -> void { - if(thread.handle() == _master) { - while(enter(Mode::SynchronizeMaster) != Event::Synchronize); - } else { - _resume = thread.handle(); - while(enter(Mode::SynchronizeSlave) != Event::Synchronize); - } - } - - inline auto synchronize() -> void { - if(co_active() == _master) { - if(_mode == Mode::SynchronizeMaster) return exit(Event::Synchronize); - } else { - if(_mode == Mode::SynchronizeSlave) return exit(Event::Synchronize); - } - } - -private: - cothread_t _host = nullptr; //program thread (used to exit scheduler) - cothread_t _resume = nullptr; //resume thread (used to enter scheduler) - cothread_t _master = nullptr; //primary thread (used to synchronize components) - Mode _mode = Mode::Run; - Event _event = Event::Step; - vector _threads; -}; - -} - -#undef uintmax diff --git a/bsnes/emulator/thread.hpp b/bsnes/emulator/thread.hpp deleted file mode 100644 index 59711bf5..00000000 --- a/bsnes/emulator/thread.hpp +++ /dev/null @@ -1,64 +0,0 @@ -#pragma once -#define uintmax uint64 - -namespace Emulator { - -struct Thread { - enum : uintmax { Second = (uintmax)-1 >> 1 }; - - virtual ~Thread() { - if(_handle) co_delete(_handle); - } - - inline auto active() const { return co_active() == _handle; } - inline auto handle() const { return _handle; } - inline auto frequency() const { return _frequency; } - inline auto scalar() const { return _scalar; } - inline auto clock() const { return _clock; } - - auto setHandle(cothread_t handle) -> void { - _handle = handle; - } - - auto setFrequency(double frequency) -> void { - _frequency = frequency + 0.5; - _scalar = Second / _frequency; - } - - auto setScalar(uintmax scalar) -> void { - _scalar = scalar; - } - - auto setClock(uintmax clock) -> void { - _clock = clock; - } - - auto create(auto (*entrypoint)() -> void, double frequency) -> void { - if(_handle) co_delete(_handle); - _handle = co_create(64 * 1024 * sizeof(void*), entrypoint); - setFrequency(frequency); - setClock(0); - } - - inline auto step(uint clocks) -> void { - _clock += _scalar * clocks; - } - - auto serialize(serializer& s) -> void { - s.integer(_frequency); - s.integer(_scalar); - s.integer(_clock); - } - -protected: - cothread_t _handle = nullptr; - uintmax _frequency = 0; - uintmax _scalar = 0; - uintmax _clock = 0; - - friend class Scheduler; -}; - -} - -#undef uintmax diff --git a/bsnes/sfc/coprocessor/armdsp/armdsp.cpp b/bsnes/sfc/coprocessor/armdsp/armdsp.cpp index efb4e2a6..2a2111e7 100644 --- a/bsnes/sfc/coprocessor/armdsp/armdsp.cpp +++ b/bsnes/sfc/coprocessor/armdsp/armdsp.cpp @@ -6,6 +6,10 @@ namespace SuperFamicom { #include "serialization.cpp" ArmDSP armdsp; +auto ArmDSP::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto ArmDSP::Enter() -> void { armdsp.boot(); while(true) scheduler.synchronize(), armdsp.main(); @@ -32,8 +36,8 @@ auto ArmDSP::main() -> void { auto ArmDSP::step(uint clocks) -> void { if(bridge.timer && --bridge.timer == 0); - Thread::step(clocks); - synchronize(cpu); + clock += clocks * (uint64_t)cpu.frequency; + synchronizeCPU(); } //MMIO: 00-3f,80-bf:3800-38ff @@ -41,7 +45,7 @@ auto ArmDSP::step(uint clocks) -> void { //a0 ignored auto ArmDSP::read(uint addr, uint8) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); uint8 data = 0x00; addr &= 0xff06; @@ -65,7 +69,7 @@ auto ArmDSP::read(uint addr, uint8) -> uint8 { } auto ArmDSP::write(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); addr &= 0xff06; diff --git a/bsnes/sfc/coprocessor/armdsp/armdsp.hpp b/bsnes/sfc/coprocessor/armdsp/armdsp.hpp index b500ef2f..732ee899 100644 --- a/bsnes/sfc/coprocessor/armdsp/armdsp.hpp +++ b/bsnes/sfc/coprocessor/armdsp/armdsp.hpp @@ -7,6 +7,7 @@ struct ArmDSP : Processor::ARM7TDMI, Thread { #include "registers.hpp" + auto synchronizeCPU() -> void; static auto Enter() -> void; auto boot() -> void; auto main() -> void; diff --git a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp index 33362678..4ddc20c9 100644 --- a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp +++ b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.cpp @@ -7,6 +7,10 @@ namespace SuperFamicom { #include "serialization.cpp" EpsonRTC epsonrtc; +auto EpsonRTC::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto EpsonRTC::Enter() -> void { while(true) scheduler.synchronize(), epsonrtc.main(); } @@ -27,7 +31,11 @@ auto EpsonRTC::main() -> void { } step(1); - synchronize(cpu); + synchronizeCPU(); +} + +auto EpsonRTC::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto EpsonRTC::initialize() -> void { @@ -127,7 +135,7 @@ auto EpsonRTC::synchronize(uint64 timestamp) -> void { } auto EpsonRTC::read(uint addr, uint8 data) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); addr &= 3; if(addr == 0) { @@ -152,7 +160,7 @@ auto EpsonRTC::read(uint addr, uint8 data) -> uint8 { } auto EpsonRTC::write(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); addr &= 3, data &= 15; if(addr == 0) { diff --git a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.hpp b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.hpp index 991de3cb..62ff70ef 100644 --- a/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.hpp +++ b/bsnes/sfc/coprocessor/epsonrtc/epsonrtc.hpp @@ -1,10 +1,10 @@ //Epson RTC-4513 Real-Time Clock struct EpsonRTC : Thread { - using Thread::synchronize; - + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto initialize() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/event/event.cpp b/bsnes/sfc/coprocessor/event/event.cpp index 968ca1a7..b339defc 100644 --- a/bsnes/sfc/coprocessor/event/event.cpp +++ b/bsnes/sfc/coprocessor/event/event.cpp @@ -5,6 +5,10 @@ namespace SuperFamicom { #include "serialization.cpp" Event event; +auto Event::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto Event::Enter() -> void { while(true) scheduler.synchronize(), event.main(); } @@ -26,7 +30,11 @@ auto Event::main() -> void { } step(1); - synchronize(cpu); + synchronizeCPU(); +} + +auto Event::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto Event::unload() -> void { diff --git a/bsnes/sfc/coprocessor/event/event.hpp b/bsnes/sfc/coprocessor/event/event.hpp index 6161df1f..b2f0de4c 100644 --- a/bsnes/sfc/coprocessor/event/event.hpp +++ b/bsnes/sfc/coprocessor/event/event.hpp @@ -15,8 +15,10 @@ struct Event : Thread { //event.cpp + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto unload() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp index 8aa6b5e9..18cd3236 100644 --- a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp +++ b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.cpp @@ -7,14 +7,18 @@ namespace SuperFamicom { #include "data-rom.cpp" HitachiDSP hitachidsp; +auto HitachiDSP::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto HitachiDSP::Enter() -> void { while(true) scheduler.synchronize(), hitachidsp.main(); } auto HitachiDSP::step(uint clocks) -> void { HG51B::step(clocks); - Thread::step(clocks); - synchronize(cpu); + clock += clocks * (uint64_t)cpu.frequency; + synchronizeCPU(); } auto HitachiDSP::halt() -> void { diff --git a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.hpp b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.hpp index afc0475a..632aca3e 100644 --- a/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.hpp +++ b/bsnes/sfc/coprocessor/hitachidsp/hitachidsp.hpp @@ -3,6 +3,7 @@ struct HitachiDSP : Processor::HG51B, Thread { WritableMemory ram; //hitachidsp.cpp + auto synchronizeCPU() -> void; static auto Enter() -> void; auto step(uint clocks) -> void override; auto halt() -> void override; diff --git a/bsnes/sfc/coprocessor/icd/icd.cpp b/bsnes/sfc/coprocessor/icd/icd.cpp index f10cf859..0b95a86c 100644 --- a/bsnes/sfc/coprocessor/icd/icd.cpp +++ b/bsnes/sfc/coprocessor/icd/icd.cpp @@ -46,6 +46,10 @@ namespace SameBoy { } } +auto ICD::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto ICD::Enter() -> void { while(true) { scheduler.synchronize(); @@ -61,7 +65,11 @@ auto ICD::main() -> void { stream->sample(float(0.0), float(0.0)); step(128); } - synchronize(cpu); + synchronizeCPU(); +} + +auto ICD::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto ICD::load() -> bool { diff --git a/bsnes/sfc/coprocessor/icd/icd.hpp b/bsnes/sfc/coprocessor/icd/icd.hpp index 6e4840e8..b84c2b66 100644 --- a/bsnes/sfc/coprocessor/icd/icd.hpp +++ b/bsnes/sfc/coprocessor/icd/icd.hpp @@ -4,8 +4,10 @@ struct ICD : Emulator::Platform, Thread { inline auto pathID() const -> uint { return information.pathID; } + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto load() -> bool; auto save() -> void; diff --git a/bsnes/sfc/coprocessor/icd/io.cpp b/bsnes/sfc/coprocessor/icd/io.cpp index 069b0fec..8a87257a 100644 --- a/bsnes/sfc/coprocessor/icd/io.cpp +++ b/bsnes/sfc/coprocessor/icd/io.cpp @@ -57,10 +57,10 @@ auto ICD::writeIO(uint addr, uint8 data) -> void { } auto frequency = system.cpuFrequency(); switch(data & 3) { - case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware) - case 1: setFrequency(frequency / 5); break; //normal - case 2: setFrequency(frequency / 7); break; //slow - case 3: setFrequency(frequency / 9); break; //very slow + case 0: this->frequency = frequency / 4; break; //fast (glitchy, even on real hardware) + case 1: this->frequency = frequency / 5; break; //normal + case 2: this->frequency = frequency / 7; break; //slow + case 3: this->frequency = frequency / 9; break; //very slow } r6003 = data; return; diff --git a/bsnes/sfc/coprocessor/msu1/msu1.cpp b/bsnes/sfc/coprocessor/msu1/msu1.cpp index 62875a04..804e7700 100644 --- a/bsnes/sfc/coprocessor/msu1/msu1.cpp +++ b/bsnes/sfc/coprocessor/msu1/msu1.cpp @@ -6,6 +6,11 @@ MSU1 msu1; #include "serialization.cpp" +auto MSU1::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + + auto MSU1::Enter() -> void { while(true) scheduler.synchronize(), msu1.main(); } @@ -36,7 +41,11 @@ auto MSU1::main() -> void { stream->sample(float(left), float(right)); step(1); - synchronize(cpu); + synchronizeCPU(); +} + +auto MSU1::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto MSU1::unload() -> void { @@ -46,7 +55,7 @@ auto MSU1::unload() -> void { auto MSU1::power() -> void { create(MSU1::Enter, 44100); - stream = Emulator::audio.createStream(2, frequency()); + stream = Emulator::audio.createStream(2, frequency); io.dataSeekOffset = 0; io.dataReadOffset = 0; @@ -98,7 +107,7 @@ auto MSU1::audioOpen() -> void { } auto MSU1::readIO(uint addr, uint8) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); switch(0x2000 | (addr & 7)) { case 0x2000: @@ -128,7 +137,7 @@ auto MSU1::readIO(uint addr, uint8) -> uint8 { } auto MSU1::writeIO(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); switch(0x2000 | (addr & 7)) { case 0x2000: bit8(io.dataSeekOffset,0) = data; break; diff --git a/bsnes/sfc/coprocessor/msu1/msu1.hpp b/bsnes/sfc/coprocessor/msu1/msu1.hpp index 3054fc68..3eb6d99b 100644 --- a/bsnes/sfc/coprocessor/msu1/msu1.hpp +++ b/bsnes/sfc/coprocessor/msu1/msu1.hpp @@ -1,8 +1,10 @@ struct MSU1 : Thread { shared_pointer stream; + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto unload() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/necdsp/necdsp.cpp b/bsnes/sfc/coprocessor/necdsp/necdsp.cpp index 53925564..9db99df5 100644 --- a/bsnes/sfc/coprocessor/necdsp/necdsp.cpp +++ b/bsnes/sfc/coprocessor/necdsp/necdsp.cpp @@ -5,6 +5,10 @@ namespace SuperFamicom { #include "serialization.cpp" NECDSP necdsp; +auto NECDSP::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto NECDSP::Enter() -> void { while(true) scheduler.synchronize(), necdsp.main(); } @@ -12,11 +16,15 @@ auto NECDSP::Enter() -> void { auto NECDSP::main() -> void { exec(); step(1); - synchronize(cpu); + synchronizeCPU(); +} + +auto NECDSP::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto NECDSP::read(uint addr, uint8) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); if(addr & 1) { return uPD96050::readSR(); } else { @@ -25,7 +33,7 @@ auto NECDSP::read(uint addr, uint8) -> uint8 { } auto NECDSP::write(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); if(addr & 1) { return uPD96050::writeSR(data); } else { @@ -34,12 +42,12 @@ auto NECDSP::write(uint addr, uint8 data) -> void { } auto NECDSP::readRAM(uint addr, uint8) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); return uPD96050::readDP(addr); } auto NECDSP::writeRAM(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); return uPD96050::writeDP(addr, data); } diff --git a/bsnes/sfc/coprocessor/necdsp/necdsp.hpp b/bsnes/sfc/coprocessor/necdsp/necdsp.hpp index 94b935a5..dd29fbbd 100644 --- a/bsnes/sfc/coprocessor/necdsp/necdsp.hpp +++ b/bsnes/sfc/coprocessor/necdsp/necdsp.hpp @@ -1,6 +1,8 @@ struct NECDSP : Processor::uPD96050, Thread { + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto read(uint addr, uint8 data) -> uint8; auto write(uint addr, uint8 data) -> void; diff --git a/bsnes/sfc/coprocessor/sa1/bwram.cpp b/bsnes/sfc/coprocessor/sa1/bwram.cpp index 0213029f..5de8caf8 100644 --- a/bsnes/sfc/coprocessor/sa1/bwram.cpp +++ b/bsnes/sfc/coprocessor/sa1/bwram.cpp @@ -22,7 +22,7 @@ auto SA1::BWRAM::write(uint address, uint8 data) -> void { //00-3f,80-bf:6000-7fff size=0x2000 => 00:0000-1fff //40-4f:0000-ffff => untranslated auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); if(address < 0x2000) { //$00-3f,80-bf:6000-7fff address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff); @@ -33,7 +33,7 @@ auto SA1::BWRAM::readCPU(uint address, uint8 data) -> uint8 { } auto SA1::BWRAM::writeCPU(uint address, uint8 data) -> void { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); if(address < 0x2000) { //$00-3f,80-bf:6000-7fff address = sa1.mmio.sbm * 0x2000 + (address & 0x1fff); diff --git a/bsnes/sfc/coprocessor/sa1/io.cpp b/bsnes/sfc/coprocessor/sa1/io.cpp index e1d56abb..43f520de 100644 --- a/bsnes/sfc/coprocessor/sa1/io.cpp +++ b/bsnes/sfc/coprocessor/sa1/io.cpp @@ -1,5 +1,5 @@ auto SA1::readIOCPU(uint address, uint8 data) -> uint8 { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); switch(0x2200 | address & 0x1ff) { @@ -25,7 +25,7 @@ auto SA1::readIOCPU(uint address, uint8 data) -> uint8 { } auto SA1::readIOSA1(uint address, uint8) -> uint8 { - synchronize(cpu); + synchronizeCPU(); switch(0x2200 | address & 0x1ff) { @@ -101,7 +101,7 @@ auto SA1::readIOSA1(uint address, uint8) -> uint8 { } auto SA1::writeIOCPU(uint address, uint8 data) -> void { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); switch(0x2200 | address & 0x1ff) { @@ -236,7 +236,7 @@ auto SA1::writeIOCPU(uint address, uint8 data) -> void { } auto SA1::writeIOSA1(uint address, uint8 data) -> void { - synchronize(cpu); + synchronizeCPU(); switch(0x2200 | address & 0x1ff) { diff --git a/bsnes/sfc/coprocessor/sa1/iram.cpp b/bsnes/sfc/coprocessor/sa1/iram.cpp index 9cd6ae81..9e489559 100644 --- a/bsnes/sfc/coprocessor/sa1/iram.cpp +++ b/bsnes/sfc/coprocessor/sa1/iram.cpp @@ -18,12 +18,12 @@ auto SA1::IRAM::write(uint address, uint8 data) -> void { } auto SA1::IRAM::readCPU(uint address, uint8 data) -> uint8 { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); return read(address, data); } auto SA1::IRAM::writeCPU(uint address, uint8 data) -> void { - cpu.synchronize(sa1); + cpu.synchronizeCoprocessors(); return write(address, data); } diff --git a/bsnes/sfc/coprocessor/sa1/sa1.cpp b/bsnes/sfc/coprocessor/sa1/sa1.cpp index fc46c8d9..535b7708 100644 --- a/bsnes/sfc/coprocessor/sa1/sa1.cpp +++ b/bsnes/sfc/coprocessor/sa1/sa1.cpp @@ -11,6 +11,10 @@ namespace SuperFamicom { #include "serialization.cpp" SA1 sa1; +auto SA1::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto SA1::Enter() -> void { while(true) scheduler.synchronize(), sa1.main(); } @@ -78,13 +82,9 @@ auto SA1::interruptPending() const -> bool { return status.interruptPending; } -auto SA1::synchronizing() const -> bool { - return scheduler.synchronizing(); -} - auto SA1::step() -> void { - Thread::step(2); - synchronize(cpu); + clock += (uint64_t)cpu.frequency << 1; + synchronizeCPU(); //adjust counters: //note that internally, status counters are in clocks; diff --git a/bsnes/sfc/coprocessor/sa1/sa1.hpp b/bsnes/sfc/coprocessor/sa1/sa1.hpp index 14eef0aa..f4807c2a 100644 --- a/bsnes/sfc/coprocessor/sa1/sa1.hpp +++ b/bsnes/sfc/coprocessor/sa1/sa1.hpp @@ -1,7 +1,10 @@ //Super Accelerator (SA-1) struct SA1 : Processor::WDC65816, Thread { + inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeAll; } + //sa1.cpp + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; auto step() -> void; @@ -10,7 +13,6 @@ struct SA1 : Processor::WDC65816, Thread { alwaysinline auto triggerIRQ() -> void; alwaysinline auto lastCycle() -> void override; alwaysinline auto interruptPending() const -> bool override; - auto synchronizing() const -> bool override; auto unload() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp b/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp index fcea6dbc..ee8e0326 100644 --- a/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp +++ b/bsnes/sfc/coprocessor/sharprtc/sharprtc.cpp @@ -7,6 +7,10 @@ namespace SuperFamicom { #include "serialization.cpp" SharpRTC sharprtc; +auto SharpRTC::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto SharpRTC::Enter() -> void { while(true) scheduler.synchronize(), sharprtc.main(); } @@ -15,7 +19,11 @@ auto SharpRTC::main() -> void { tickSecond(); step(1); - synchronize(cpu); + synchronizeCPU(); +} + +auto SharpRTC::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; } auto SharpRTC::initialize() -> void { diff --git a/bsnes/sfc/coprocessor/sharprtc/sharprtc.hpp b/bsnes/sfc/coprocessor/sharprtc/sharprtc.hpp index 987de8cd..9ce97b78 100644 --- a/bsnes/sfc/coprocessor/sharprtc/sharprtc.hpp +++ b/bsnes/sfc/coprocessor/sharprtc/sharprtc.hpp @@ -1,8 +1,8 @@ struct SharpRTC : Thread { - using Thread::synchronize; - + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto initialize() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/spc7110/spc7110.cpp b/bsnes/sfc/coprocessor/spc7110/spc7110.cpp index aa57559b..fb253efb 100644 --- a/bsnes/sfc/coprocessor/spc7110/spc7110.cpp +++ b/bsnes/sfc/coprocessor/spc7110/spc7110.cpp @@ -16,6 +16,10 @@ SPC7110::~SPC7110() { delete decompressor; } +auto SPC7110::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto SPC7110::Enter() -> void { while(true) scheduler.synchronize(), spc7110.main(); } @@ -27,9 +31,13 @@ auto SPC7110::main() -> void { addClocks(1); } +auto SPC7110::step(uint clocks) -> void { + clock += clocks * (uint64_t)cpu.frequency; +} + auto SPC7110::addClocks(uint clocks) -> void { step(clocks); - synchronize(cpu); + synchronizeCPU(); } auto SPC7110::unload() -> void { @@ -96,7 +104,7 @@ auto SPC7110::power() -> void { } auto SPC7110::read(uint addr, uint8 data) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800 if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808 addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f @@ -180,7 +188,7 @@ auto SPC7110::read(uint addr, uint8 data) -> uint8 { } auto SPC7110::write(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800 if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808 addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f diff --git a/bsnes/sfc/coprocessor/spc7110/spc7110.hpp b/bsnes/sfc/coprocessor/spc7110/spc7110.hpp index 29aca9a2..ab8d4561 100644 --- a/bsnes/sfc/coprocessor/spc7110/spc7110.hpp +++ b/bsnes/sfc/coprocessor/spc7110/spc7110.hpp @@ -4,8 +4,10 @@ struct SPC7110 : Thread { SPC7110(); ~SPC7110(); + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; + auto step(uint clocks) -> void; auto unload() -> void; auto power() -> void; diff --git a/bsnes/sfc/coprocessor/superfx/io.cpp b/bsnes/sfc/coprocessor/superfx/io.cpp index 670ba6e6..8bdc40cc 100644 --- a/bsnes/sfc/coprocessor/superfx/io.cpp +++ b/bsnes/sfc/coprocessor/superfx/io.cpp @@ -1,5 +1,5 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); addr = 0x3000 | addr & 0x3ff; if(addr >= 0x3100 && addr <= 0x32ff) { @@ -51,7 +51,7 @@ auto SuperFX::readIO(uint addr, uint8) -> uint8 { } auto SuperFX::writeIO(uint addr, uint8 data) -> void { - cpu.synchronize(*this); + cpu.synchronizeCoprocessors(); addr = 0x3000 | addr & 0x3ff; if(addr >= 0x3100 && addr <= 0x32ff) { diff --git a/bsnes/sfc/coprocessor/superfx/memory.cpp b/bsnes/sfc/coprocessor/superfx/memory.cpp index e3c0463e..01a8c821 100644 --- a/bsnes/sfc/coprocessor/superfx/memory.cpp +++ b/bsnes/sfc/coprocessor/superfx/memory.cpp @@ -2,8 +2,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 { if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff while(!regs.scmr.ron) { step(6); - synchronize(cpu); - if(scheduler.synchronizing()) break; + synchronizeCPU(); + if(synchronizing()) break; } return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask); } @@ -11,8 +11,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 { if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff while(!regs.scmr.ron) { step(6); - synchronize(cpu); - if(scheduler.synchronizing()) break; + synchronizeCPU(); + if(synchronizing()) break; } return rom.read(addr & romMask); } @@ -20,8 +20,8 @@ auto SuperFX::read(uint addr, uint8 data) -> uint8 { if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff while(!regs.scmr.ran) { step(6); - synchronize(cpu); - if(scheduler.synchronizing()) break; + synchronizeCPU(); + if(synchronizing()) break; } return ram.read(addr & ramMask); } @@ -33,8 +33,8 @@ auto SuperFX::write(uint addr, uint8 data) -> void { if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff while(!regs.scmr.ran) { step(6); - synchronize(cpu); - if(scheduler.synchronizing()) break; + synchronizeCPU(); + if(synchronizing()) break; } return ram.write(addr & ramMask, data); } diff --git a/bsnes/sfc/coprocessor/superfx/superfx.cpp b/bsnes/sfc/coprocessor/superfx/superfx.cpp index 41f1f55b..c651cf9c 100644 --- a/bsnes/sfc/coprocessor/superfx/superfx.cpp +++ b/bsnes/sfc/coprocessor/superfx/superfx.cpp @@ -10,6 +10,10 @@ namespace SuperFamicom { #include "serialization.cpp" SuperFX superfx; +auto SuperFX::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto SuperFX::Enter() -> void { while(true) scheduler.synchronize(), superfx.main(); } diff --git a/bsnes/sfc/coprocessor/superfx/superfx.hpp b/bsnes/sfc/coprocessor/superfx/superfx.hpp index 99297753..0ef80292 100644 --- a/bsnes/sfc/coprocessor/superfx/superfx.hpp +++ b/bsnes/sfc/coprocessor/superfx/superfx.hpp @@ -2,7 +2,10 @@ struct SuperFX : Processor::GSU, Thread { ReadableMemory rom; WritableMemory ram; + inline auto synchronizing() const -> bool { return scheduler.mode == Scheduler::Mode::SynchronizeAll; } + //superfx.cpp + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; auto unload() -> void; diff --git a/bsnes/sfc/coprocessor/superfx/timing.cpp b/bsnes/sfc/coprocessor/superfx/timing.cpp index 3a427b2b..0b4bf653 100644 --- a/bsnes/sfc/coprocessor/superfx/timing.cpp +++ b/bsnes/sfc/coprocessor/superfx/timing.cpp @@ -14,8 +14,8 @@ auto SuperFX::step(uint clocks) -> void { } } - Thread::step(clocks); - synchronize(cpu); + clock += clocks * (uint64_t)cpu.frequency; + synchronizeCPU(); } auto SuperFX::syncROMBuffer() -> void { diff --git a/bsnes/sfc/cpu/cpu.cpp b/bsnes/sfc/cpu/cpu.cpp index 70d8e8f3..f198da93 100644 --- a/bsnes/sfc/cpu/cpu.cpp +++ b/bsnes/sfc/cpu/cpu.cpp @@ -10,8 +10,27 @@ CPU cpu; #include "irq.cpp" #include "serialization.cpp" +auto CPU::synchronizeSMP() -> void { + if(smp.clock < 0) co_switch(smp.thread); +} + +auto CPU::synchronizePPU() -> void { + if(ppu.clock < 0) co_switch(ppu.thread); +} + +auto CPU::synchronizeCoprocessors() -> void { + for(auto coprocessor : coprocessors) { + if(coprocessor->clock < 0) co_switch(coprocessor->thread); + } +} + auto CPU::Enter() -> void { - while(true) scheduler.synchronize(), cpu.main(); + while(true) { + if(scheduler.mode == Scheduler::Mode::SynchronizeCPU) { + scheduler.leave(Scheduler::Event::Synchronize); + } + cpu.main(); + } } auto CPU::main() -> void { diff --git a/bsnes/sfc/cpu/cpu.hpp b/bsnes/sfc/cpu/cpu.hpp index a304fe1b..5c11202a 100644 --- a/bsnes/sfc/cpu/cpu.hpp +++ b/bsnes/sfc/cpu/cpu.hpp @@ -2,9 +2,12 @@ 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.synchronizing(); } + inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeCPU; } //cpu.cpp + auto synchronizeSMP() -> void; + auto synchronizePPU() -> void; + auto synchronizeCoprocessors() -> void; static auto Enter() -> void; auto main() -> void; auto load() -> bool; diff --git a/bsnes/sfc/cpu/io.cpp b/bsnes/sfc/cpu/io.cpp index a6521946..4a4b758c 100644 --- a/bsnes/sfc/cpu/io.cpp +++ b/bsnes/sfc/cpu/io.cpp @@ -3,7 +3,7 @@ auto CPU::readRAM(uint addr, uint8 data) -> uint8 { } auto CPU::readAPU(uint addr, uint8 data) -> uint8 { - synchronize(smp); + synchronizeSMP(); return smp.portRead(addr & 3); } @@ -100,7 +100,7 @@ auto CPU::writeRAM(uint addr, uint8 data) -> void { } auto CPU::writeAPU(uint addr, uint8 data) -> void { - synchronize(smp); + synchronizeSMP(); return smp.portWrite(addr & 3, data); } diff --git a/bsnes/sfc/cpu/timing.cpp b/bsnes/sfc/cpu/timing.cpp index 13852b1d..08dadd82 100644 --- a/bsnes/sfc/cpu/timing.cpp +++ b/bsnes/sfc/cpu/timing.cpp @@ -28,9 +28,19 @@ template auto CPU::step() -> void { static_assert(Clocks == 2 || Clocks == 4 || Clocks == 6 || Clocks == 8 || Clocks == 10 || Clocks == 12); + for(auto coprocessor : coprocessors) { + coprocessor->clock -= Clocks * (uint64_t)coprocessor->frequency; + } + if(overclocking.target) { overclocking.counter += Clocks; - if(overclocking.counter < overclocking.target) return; + if(overclocking.counter < overclocking.target) { + if constexpr(Synchronize) { + if(configuration.hacks.coprocessors.delayedSync) return; + synchronizeCoprocessors(); + } + return; + } } if constexpr(Clocks >= 2) stepOnce(); @@ -39,7 +49,9 @@ auto CPU::step() -> void { if constexpr(Clocks >= 8) stepOnce(); if constexpr(Clocks >= 10) stepOnce(); if constexpr(Clocks >= 12) stepOnce(); - Thread::step(Clocks); + + smp.clock -= Clocks * (uint64_t)smp.frequency; + ppu.clock -= Clocks; if(!status.dramRefresh && hcounter() >= status.dramRefreshPosition) { //note: pattern should technically be 5-3, 5-3, 5-3, 5-3, 5-3 per logic analyzer @@ -53,7 +65,7 @@ auto CPU::step() -> void { if constexpr(Synchronize) { if(configuration.hacks.coprocessors.delayedSync) return; - for(auto coprocessor : coprocessors) synchronize(*coprocessor); + synchronizeCoprocessors(); } } @@ -73,9 +85,9 @@ auto CPU::scanline() -> void { status.lineClocks = lineclocks(); //forcefully sync S-CPU to other processors, in case chips are not communicating - synchronize(smp); - synchronize(ppu); - for(auto coprocessor : coprocessors) synchronize(*coprocessor); + synchronizeSMP(); + synchronizePPU(); + synchronizeCoprocessors(); if(vcounter() == 0) { //HDMA setup triggers once every frame diff --git a/bsnes/sfc/ppu-fast/io.cpp b/bsnes/sfc/ppu-fast/io.cpp index 23fc6bac..d497c184 100644 --- a/bsnes/sfc/ppu-fast/io.cpp +++ b/bsnes/sfc/ppu-fast/io.cpp @@ -90,7 +90,7 @@ auto PPU::writeCGRAM(uint8_t address, uint15 data) -> void { } auto PPU::readIO(uint address, uint8 data) -> uint8 { - cpu.synchronize(ppu); + cpu.synchronizePPU(); switch(address & 0xffff) { @@ -202,7 +202,7 @@ auto PPU::readIO(uint address, uint8 data) -> uint8 { } auto PPU::writeIO(uint address, uint8 data) -> void { - cpu.synchronize(ppu); + cpu.synchronizePPU(); switch(address & 0xffff) { diff --git a/bsnes/sfc/ppu-fast/ppu.cpp b/bsnes/sfc/ppu-fast/ppu.cpp index 2ae2609f..88c426ed 100644 --- a/bsnes/sfc/ppu-fast/ppu.cpp +++ b/bsnes/sfc/ppu-fast/ppu.cpp @@ -60,14 +60,21 @@ PPU::~PPU() { delete[] tilecache[TileMode::BPP8]; } +auto PPU::synchronizeCPU() -> void { + if(ppubase.clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto PPU::Enter() -> void { - while(true) scheduler.synchronize(), ppu.main(); + while(true) { + scheduler.synchronize(); + ppu.main(); + } } auto PPU::step(uint clocks) -> void { tick(clocks); - Thread::step(clocks); - synchronize(cpu); + ppubase.clock += clocks; + synchronizeCPU(); } auto PPU::main() -> void { @@ -116,7 +123,7 @@ auto PPU::scanline() -> void { if(vcounter() == 240) { Line::flush(); - scheduler.exit(Scheduler::Event::Frame); + scheduler.leave(Scheduler::Event::Frame); } } @@ -160,7 +167,6 @@ auto PPU::load() -> bool { } auto PPU::power(bool reset) -> void { - Thread::create(Enter, system.cpuFrequency()); PPUcounter::reset(); memory::fill(output, 1024 * 960); diff --git a/bsnes/sfc/ppu-fast/ppu.hpp b/bsnes/sfc/ppu-fast/ppu.hpp index 4bfc376f..bb01afa8 100644 --- a/bsnes/sfc/ppu-fast/ppu.hpp +++ b/bsnes/sfc/ppu-fast/ppu.hpp @@ -7,7 +7,7 @@ #define PPU PPUfast -struct PPU : Thread, PPUcounter { +struct PPU : PPUcounter { alwaysinline auto interlace() const -> bool; alwaysinline auto overscan() const -> bool; alwaysinline auto vdisp() const -> uint; @@ -23,6 +23,7 @@ struct PPU : Thread, PPUcounter { PPU(); ~PPU(); + auto synchronizeCPU() -> void; static auto Enter() -> void; alwaysinline auto step(uint clocks) -> void; auto main() -> void; diff --git a/bsnes/sfc/ppu-fast/serialization.cpp b/bsnes/sfc/ppu-fast/serialization.cpp index 69500e87..b7525dd8 100644 --- a/bsnes/sfc/ppu-fast/serialization.cpp +++ b/bsnes/sfc/ppu-fast/serialization.cpp @@ -1,5 +1,5 @@ auto PPU::serialize(serializer& s) -> void { - Thread::serialize(s); + ppubase.Thread::serialize(s); PPUcounter::serialize(s); latch.serialize(s); diff --git a/bsnes/sfc/ppu/io.cpp b/bsnes/sfc/ppu/io.cpp index cf161aa6..b4fc083d 100644 --- a/bsnes/sfc/ppu/io.cpp +++ b/bsnes/sfc/ppu/io.cpp @@ -13,7 +13,7 @@ auto PPU::latchCounters() -> void { return ppufast.latchCounters(); } - cpu.synchronize(ppu); + cpu.synchronizePPU(); io.hcounter = hdot(); io.vcounter = vcounter(); latch.counters = 1; @@ -69,7 +69,7 @@ auto PPU::writeCGRAM(uint8 addr, uint15 data) -> void { } auto PPU::readIO(uint addr, uint8 data) -> uint8 { - cpu.synchronize(ppu); + cpu.synchronizePPU(); switch(addr & 0xffff) { @@ -193,7 +193,7 @@ auto PPU::readIO(uint addr, uint8 data) -> uint8 { } auto PPU::writeIO(uint addr, uint8 data) -> void { - cpu.synchronize(ppu); + cpu.synchronizePPU(); switch(addr & 0xffff) { diff --git a/bsnes/sfc/ppu/ppu.cpp b/bsnes/sfc/ppu/ppu.cpp index 67b958ca..3f5b050a 100644 --- a/bsnes/sfc/ppu/ppu.cpp +++ b/bsnes/sfc/ppu/ppu.cpp @@ -36,22 +36,26 @@ bg4(Background::ID::BG4) { } PPU::~PPU() { - if(system.fastPPU()) { - setHandle(nullptr); - } +} + +auto PPU::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); } auto PPU::step(uint clocks) -> void { clocks >>= 1; while(clocks--) { tick(2); - Thread::step(2); - synchronize(cpu); + clock += 2; + synchronizeCPU(); } } auto PPU::Enter() -> void { - while(true) scheduler.synchronize(), ppu.main(); + while(true) { + scheduler.synchronize(); + ppu.main(); + } } auto PPU::main() -> void { @@ -99,16 +103,17 @@ auto PPU::load() -> bool { auto PPU::power(bool reset) -> void { if(system.fastPPU()) { + create(PPUfast::Enter, system.cpuFrequency()); ppufast.power(reset); - return setHandle(ppufast.handle()); + return; } create(Enter, system.cpuFrequency()); PPUcounter::reset(); memory::fill(output, 512 * 480); - function uint8> reader{&PPU::readIO, this}; - function void> writer{&PPU::writeIO, this}; + function reader{&PPU::readIO, this}; + function writer{&PPU::writeIO, this}; bus.map(reader, writer, "00-3f,80-bf:2100-213f"); if(!reset) random.array((uint8*)vram.data, sizeof(vram.data)); @@ -232,7 +237,7 @@ auto PPU::scanline() -> void { } if(vcounter() == 240) { - scheduler.exit(Scheduler::Event::Frame); + scheduler.leave(Scheduler::Event::Frame); } } diff --git a/bsnes/sfc/ppu/ppu.hpp b/bsnes/sfc/ppu/ppu.hpp index 47b172bd..7a97e5f4 100644 --- a/bsnes/sfc/ppu/ppu.hpp +++ b/bsnes/sfc/ppu/ppu.hpp @@ -7,6 +7,7 @@ struct PPU : Thread, PPUcounter { PPU(); ~PPU(); + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; auto load() -> bool; diff --git a/bsnes/sfc/sfc.hpp b/bsnes/sfc/sfc.hpp index dc317ca6..8412ebae 100644 --- a/bsnes/sfc/sfc.hpp +++ b/bsnes/sfc/sfc.hpp @@ -4,8 +4,6 @@ //started: 2004-10-14 #include -#include -#include #include #include @@ -24,22 +22,55 @@ extern "C" { namespace SuperFamicom { #define platform Emulator::platform namespace File = Emulator::File; - using Scheduler = Emulator::Scheduler; using Random = Emulator::Random; using Cheat = Emulator::Cheat; - extern Scheduler scheduler; extern Random random; extern Cheat cheat; - struct Thread : Emulator::Thread { - auto create(auto (*entrypoint)() -> void, double frequency) -> void { - Emulator::Thread::create(entrypoint, frequency); - scheduler.append(*this); + struct Scheduler { + enum class Mode : uint { Run, SynchronizeCPU, SynchronizeAll } mode; + enum class Event : uint { Frame, Synchronize } event; + + cothread_t host = nullptr; + cothread_t active = nullptr; + + auto enter() -> void { + host = co_active(); + co_switch(active); } - inline auto synchronize(Thread& thread) -> void { - if(clock() >= thread.clock()) scheduler.resume(thread); + auto leave(Event event_) -> void { + event = event_; + active = co_active(); + co_switch(host); } + + auto synchronize() -> void { + if(mode == Mode::SynchronizeAll) leave(Event::Synchronize); + } + }; + extern Scheduler scheduler; + + struct Thread { + auto create(auto (*entrypoint)() -> void, uint frequency_) -> void { + if(thread) co_delete(thread); + thread = co_create(65536 * sizeof(void*), entrypoint); + frequency = frequency_; + clock = 0; + } + + auto active() const -> bool { + return thread == co_active(); + } + + auto serialize(serializer& s) -> void { + s.integer(frequency); + s.integer(clock); + } + + cothread_t thread = nullptr; + uint32_t frequency = 0; + int64_t clock = 0; }; struct Region { diff --git a/bsnes/sfc/slot/bsmemory/bsmemory.cpp b/bsnes/sfc/slot/bsmemory/bsmemory.cpp index 70c98d10..e02f63dd 100644 --- a/bsnes/sfc/slot/bsmemory/bsmemory.cpp +++ b/bsnes/sfc/slot/bsmemory/bsmemory.cpp @@ -12,6 +12,10 @@ BSMemory::BSMemory() { block.self = this; } +auto BSMemory::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + auto BSMemory::Enter() -> void { while(true) scheduler.synchronize(), bsmemory.main(); } @@ -30,8 +34,8 @@ auto BSMemory::main() -> void { } auto BSMemory::step(uint clocks) -> void { - Thread::step(clocks); - synchronize(cpu); + clock += clocks * (uint64_t)cpu.frequency; + synchronizeCPU(); } auto BSMemory::load() -> bool { diff --git a/bsnes/sfc/slot/bsmemory/bsmemory.hpp b/bsnes/sfc/slot/bsmemory/bsmemory.hpp index 3442b65e..efa12731 100644 --- a/bsnes/sfc/slot/bsmemory/bsmemory.hpp +++ b/bsnes/sfc/slot/bsmemory/bsmemory.hpp @@ -24,6 +24,7 @@ struct BSMemory : Thread, Memory { //bsmemory.cpp BSMemory(); + auto synchronizeCPU() -> void; static auto Enter() -> void; auto main() -> void; auto step(uint clocks) -> void; diff --git a/bsnes/sfc/smp/io.cpp b/bsnes/sfc/smp/io.cpp index 36a60fa8..d672a51c 100644 --- a/bsnes/sfc/smp/io.cpp +++ b/bsnes/sfc/smp/io.cpp @@ -31,19 +31,19 @@ auto SMP::readIO(uint16 address) -> uint8 { return dsp.read(io.dspAddr & 0x7f); case 0xf4: //CPUIO0 - synchronize(cpu); + synchronizeCPU(); return io.apu0; case 0xf5: //CPUIO1 - synchronize(cpu); + synchronizeCPU(); return io.apu1; case 0xf6: //CPUIO2 - synchronize(cpu); + synchronizeCPU(); return io.apu2; case 0xf7: //CPUIO3 - synchronize(cpu); + synchronizeCPU(); return io.apu3; case 0xf8: //AUXIO4 @@ -111,13 +111,13 @@ auto SMP::writeIO(uint16 address, uint8 data) -> void { } if(bit1(data,4)) { - synchronize(cpu); + synchronizeCPU(); io.apu0 = 0x00; io.apu1 = 0x00; } if(bit1(data,5)) { - synchronize(cpu); + synchronizeCPU(); io.apu2 = 0x00; io.apu3 = 0x00; } @@ -135,22 +135,22 @@ auto SMP::writeIO(uint16 address, uint8 data) -> void { break; case 0xf4: //CPUIO0 - synchronize(cpu); + synchronizeCPU(); io.cpu0 = data; break; case 0xf5: //CPUIO1 - synchronize(cpu); + synchronizeCPU(); io.cpu1 = data; break; case 0xf6: //CPUIO2 - synchronize(cpu); + synchronizeCPU(); io.cpu2 = data; break; case 0xf7: //CPUIO3 - synchronize(cpu); + synchronizeCPU(); io.cpu3 = data; break; diff --git a/bsnes/sfc/smp/smp.cpp b/bsnes/sfc/smp/smp.cpp index 5ae391ac..abea3a58 100644 --- a/bsnes/sfc/smp/smp.cpp +++ b/bsnes/sfc/smp/smp.cpp @@ -8,8 +8,19 @@ SMP smp; #include "timing.cpp" #include "serialization.cpp" +auto SMP::synchronizeCPU() -> void { + if(clock >= 0 && scheduler.mode != Scheduler::Mode::SynchronizeAll) co_switch(cpu.thread); +} + +auto SMP::synchronizeDSP() -> void { + while(dsp.clock < 0) dsp.main(); +} + auto SMP::Enter() -> void { - while(true) scheduler.synchronize(), smp.main(); + while(true) { + scheduler.synchronize(); + smp.main(); + } } auto SMP::main() -> void { diff --git a/bsnes/sfc/smp/smp.hpp b/bsnes/sfc/smp/smp.hpp index 4e8168a3..ee2aaecf 100644 --- a/bsnes/sfc/smp/smp.hpp +++ b/bsnes/sfc/smp/smp.hpp @@ -1,13 +1,15 @@ //Sony CXP1100Q-1 struct SMP : Processor::SPC700, Thread { - inline auto synchronizing() const -> bool override { return scheduler.synchronizing(); } + inline auto synchronizing() const -> bool override { return scheduler.mode == Scheduler::Mode::SynchronizeAll; } //io.cpp auto portRead(uint2 port) const -> uint8; auto portWrite(uint2 port, uint8 data) -> void; //smp.cpp + auto synchronizeCPU() -> void; + auto synchronizeDSP() -> void; static auto Enter() -> void; auto main() -> void; auto load() -> bool; diff --git a/bsnes/sfc/smp/timing.cpp b/bsnes/sfc/smp/timing.cpp index 4aaad961..00b4f296 100644 --- a/bsnes/sfc/smp/timing.cpp +++ b/bsnes/sfc/smp/timing.cpp @@ -33,21 +33,21 @@ auto SMP::waitIdle(maybe addr) -> void { } auto SMP::step(uint clocks) -> void { - Thread::step(clocks); + clock += clocks * (uint64_t)cpu.frequency; dsp.clock -= clocks; - while(dsp.clock < 0) dsp.main(); + synchronizeDSP(); #if defined(DEBUGGER) - synchronize(cpu); + synchronizeCPU(); #else //forcefully sync S-SMP to S-CPU in case chips are not communicating - //sync if S-SMP is more than 1ms ahead of S-CPU - if(clock() - cpu.clock() > Thread::Second / 1'000) synchronize(cpu); + //sync if S-SMP is more than 24 samples ahead of S-CPU + if(clock > +(768 * 24 * (int64_t)24'000'000)) synchronizeCPU(); #endif } auto SMP::stepIdle(uint clocks) -> void { - Thread::step(clocks); + clock += clocks * (uint64_t)cpu.frequency; dsp.clock -= clocks; } diff --git a/bsnes/sfc/system/system.cpp b/bsnes/sfc/system/system.cpp index 456e42ec..92f8a4a2 100644 --- a/bsnes/sfc/system/system.cpp +++ b/bsnes/sfc/system/system.cpp @@ -9,7 +9,9 @@ Cheat cheat; #include "serialization.cpp" auto System::run() -> void { - if(scheduler.enter() == Scheduler::Event::Frame) { + scheduler.mode = Scheduler::Mode::Run; + scheduler.enter(); + if(scheduler.event == Scheduler::Event::Frame) { ppu.refresh(); //refresh all cheat codes once per frame @@ -24,10 +26,22 @@ auto System::run() -> void { } auto System::runToSave() -> void { - scheduler.synchronize(cpu); - scheduler.synchronize(smp); - scheduler.synchronize(ppu); - for(auto coprocessor : cpu.coprocessors) scheduler.synchronize(*coprocessor); + scheduler.mode = Scheduler::Mode::SynchronizeCPU; + while(true) { scheduler.enter(); if(scheduler.event == Scheduler::Event::Synchronize) break; } + + scheduler.mode = Scheduler::Mode::SynchronizeAll; + scheduler.active = smp.thread; + while(true) { scheduler.enter(); if(scheduler.event == Scheduler::Event::Synchronize) break; } + + scheduler.mode = Scheduler::Mode::SynchronizeAll; + scheduler.active = ppu.thread; + while(true) { scheduler.enter(); if(scheduler.event == Scheduler::Event::Synchronize) break; } + + for(auto coprocessor : cpu.coprocessors) { + scheduler.mode = Scheduler::Mode::SynchronizeAll; + scheduler.active = coprocessor->thread; + while(true) { scheduler.enter(); if(scheduler.event == Scheduler::Event::Synchronize) break; } + } } auto System::load(Emulator::Interface* interface) -> bool { @@ -97,7 +111,6 @@ auto System::power(bool reset) -> void { random.entropy(Random::Entropy::Low); - scheduler.reset(); cpu.power(reset); smp.power(reset); dsp.power(reset); @@ -140,7 +153,7 @@ auto System::power(bool reset) -> void { if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1); if(cartridge.has.BSMemorySlot) cpu.coprocessors.append(&bsmemory); - scheduler.primary(cpu); + scheduler.active = cpu.thread; controllerPort1.power(ID::Port::Controller1); controllerPort2.power(ID::Port::Controller2); diff --git a/bsnes/target-bsnes/input/input.hpp b/bsnes/target-bsnes/input/input.hpp index 342c0810..d93d078f 100644 --- a/bsnes/target-bsnes/input/input.hpp +++ b/bsnes/target-bsnes/input/input.hpp @@ -1,4 +1,9 @@ +#if defined(PLATFORM_WINDOWS) || defined(PLATFORM_MACOS) +//TODO: hiro support for TableView::activate(TableViewCell) is required for > 1 binding +enum : uint { BindingLimit = 1 }; +#else enum : uint { BindingLimit = 4 }; +#endif struct InputMapping { auto bind() -> void;