mirror of
https://github.com/bsnes-emu/bsnes.git
synced 2025-01-18 05:08:55 +01:00
e39987a3e3
byuu says (in the public announcement): Not a large changelog this time, sorry. This release is mostly to fix the SA-1 issue, and to get some real-world testing of the new scheduler model. Most of the work in the past month has gone into writing a 68000 CPU core; yet it's still only about half-way finished. Changelog (since the previous release): - fixed SNES SA-1 IRQ regression (fixes Super Mario RPG level-up screen) - new scheduler for all emulator cores (precision of 2^-127) - icarus database adds nine new SNES games - added Input/Frequency to settings file (allows simulation of latency) byuu says (in the WIP forum): Changelog: - in 32-bit mode, Thread uses uint64\_t with 2^-63 time units (10^-7 precision in the worst case) - nearly ten times the precision of an attosecond - in 64-bit mode, Thread uses uint128\_t with 2^-127 time units (10^-26 precision in the worst case) - far more accurate than yoctoseconds; almost closing in on planck time Note: a quartz crystal is accurate to 10^-4 or 10^-5. A cesium fountain atomic clock is accurate to 10^-15. So ... yeah. 2^-63 was perfectly fine; but there was no speed penalty whatsoever for using uint128\_t in 64-bit mode, so why not?
92 lines
2.2 KiB
C++
92 lines
2.2 KiB
C++
#pragma once
|
|
|
|
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();
|
|
}
|
|
|
|
auto append(Thread& thread) -> bool {
|
|
if(_threads.find(&thread)) return false;
|
|
thread._clock += _threads.size(); //this bias prioritizes threads appended earlier first
|
|
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;
|
|
};
|
|
|
|
}
|