* reverted higan's thread scheduler to olde bsnes scheduler
** this allows CPU overclocking compatibility with coprocessors
This commit is contained in:
byuu 2019-08-01 00:33:28 +09:00
parent f65b7a8528
commit 454b39cb78
52 changed files with 330 additions and 292 deletions

View File

@ -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 {

View File

@ -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<Thread*> _threads;
};
}
#undef uintmax

View File

@ -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

View File

@ -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;

View File

@ -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;

View File

@ -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) {

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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;

View File

@ -1,8 +1,10 @@
struct MSU1 : Thread {
shared_pointer<Emulator::Stream> stream;
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto unload() -> void;
auto power() -> void;

View File

@ -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);
}

View File

@ -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;

View File

@ -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);

View File

@ -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) {

View File

@ -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);
}

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -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

View File

@ -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;

View File

@ -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) {

View File

@ -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);
}

View File

@ -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();
}

View File

@ -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;

View File

@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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);
}

View File

@ -28,9 +28,19 @@ template<uint Clocks, bool Synchronize>
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

View File

@ -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) {

View File

@ -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<uint16>(output, 1024 * 960);

View File

@ -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;

View File

@ -1,5 +1,5 @@
auto PPU::serialize(serializer& s) -> void {
Thread::serialize(s);
ppubase.Thread::serialize(s);
PPUcounter::serialize(s);
latch.serialize(s);

View File

@ -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) {

View File

@ -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<uint16>(output, 512 * 480);
function<auto (uint, uint8) -> uint8> reader{&PPU::readIO, this};
function<auto (uint, uint8) -> void> writer{&PPU::writeIO, this};
function<uint8 (uint, uint8)> reader{&PPU::readIO, this};
function<void (uint, uint8)> 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);
}
}

View File

@ -7,6 +7,7 @@ struct PPU : Thread, PPUcounter {
PPU();
~PPU();
auto synchronizeCPU() -> void;
static auto Enter() -> void;
auto main() -> void;
auto load() -> bool;

View File

@ -4,8 +4,6 @@
//started: 2004-10-14
#include <emulator/emulator.hpp>
#include <emulator/thread.hpp>
#include <emulator/scheduler.hpp>
#include <emulator/random.hpp>
#include <emulator/cheat.hpp>
@ -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 {

View File

@ -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 {

View File

@ -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;

View File

@ -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;

View File

@ -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 {

View File

@ -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;

View File

@ -33,21 +33,21 @@ auto SMP::waitIdle(maybe<uint16> 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;
}

View File

@ -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);

View File

@ -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;