mirror of https://github.com/bsnes-emu/bsnes.git
v108.2
* reverted higan's thread scheduler to olde bsnes scheduler ** this allows CPU overclocking compatibility with coprocessors
This commit is contained in:
parent
f65b7a8528
commit
454b39cb78
|
@ -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 {
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
auto PPU::serialize(serializer& s) -> void {
|
||||
Thread::serialize(s);
|
||||
ppubase.Thread::serialize(s);
|
||||
PPUcounter::serialize(s);
|
||||
|
||||
latch.serialize(s);
|
||||
|
|
|
@ -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) {
|
||||
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -7,6 +7,7 @@ struct PPU : Thread, PPUcounter {
|
|||
PPU();
|
||||
~PPU();
|
||||
|
||||
auto synchronizeCPU() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto load() -> bool;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Reference in New Issue