mirror of https://github.com/bsnes-emu/bsnes.git
Update to v100r14 release.
byuu says: (Windows: compile with -fpermissive to silence an annoying error. I'll fix it in the next WIP.) I completely replaced the time management system in higan and overhauled the scheduler. Before, processor threads would have "int64 clock"; and there would be a 1:1 relationship between two threads. When thread A ran for X cycles, it'd subtract X * B.Frequency from clock; and when thread B ran for Y cycles, it'd add Y * A.Frequency from clock. This worked well and allowed perfect precision; but it doesn't work when you have more complicated relationships: eg the 68K can sync to the Z80 and PSG; the Z80 to the 68K and PSG; so the PSG needs two counters. The new system instead uses a "uint64 clock" variable that represents time in attoseconds. Every time the scheduler exits, it subtracts the smallest clock count from all threads, to prevent an overflow scenario. The only real downside is that rounding errors mean that roughly every 20 minutes, we have a rounding error of one clock cycle (one 20,000,000th of a second.) However, this only applies to systems with multiple oscillators, like the SNES. And when you're in that situation ... there's no such thing as a perfect oscillator anyway. A real SNES will be thousands of times less out of spec than 1hz per 20 minutes. The advantages are pretty immense. First, we obviously can now support more complex relationships between threads. Second, we can build a much more abstracted scheduler. All of libco is now abstracted away completely, which may permit a state-machine / coroutine version of Thread in the future. We've basically gone from this: auto SMP::step(uint clocks) -> void { clock += clocks * (uint64)cpu.frequency; dsp.clock -= clocks; if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread); if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread); } To this: auto SMP::step(uint clocks) -> void { Thread::step(clocks); synchronize(dsp); synchronize(cpu); } As you can see, we don't have to do multiple clock adjustments anymore. This is a huge win for the SNES CPU that had to update the SMP, DSP, all peripherals and all coprocessors. Likewise, we don't have to synchronize all coprocessors when one runs, now we can just synchronize the active one to the CPU. Third, when changing the frequencies of threads (think SGB speed setting modes, GBC double-speed mode, etc), it no longer causes the "int64 clock" value to be erroneous. Fourth, this results in a fairly decent speedup, mostly across the board. Aside from the GBA being mostly a wash (for unknown reasons), it's about an 8% - 12% speedup in every other emulation core. Now, all of this said ... this was an unbelievably massive change, so ... you know what that means >_> If anyone can help test all types of SNES coprocessors, and some other system games, it'd be appreciated. ---- Lastly, we have a bitchin' new about screen. It unfortunately adds ~200KiB onto the binary size, because the PNG->C++ header file transformation doesn't compress very well, and I want to keep the original resource files in with the higan archive. I might try some things to work around this file size increase in the future, but for now ... yeah, slightly larger archive sizes, sorry. The logo's a bit busted on Windows (the Label control's background transparency and alignment settings aren't working), but works well on GTK. I'll have to fix Windows before the next official release. For now, look on my Twitter feed if you want to see what it's supposed to look like. ---- EDIT: forgot about ICD2::Enter. It's doing some weird inverse run-to-save thing that I need to implement support for somehow. So, save states on the SGB core probably won't work with this WIP.
This commit is contained in:
parent
306cac2b54
commit
ca277cd5e8
|
@ -11,7 +11,7 @@ using namespace nall;
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
static const string Name = "higan";
|
static const string Name = "higan";
|
||||||
static const string Version = "100.13";
|
static const string Version = "100.14";
|
||||||
static const string Author = "byuu";
|
static const string Author = "byuu";
|
||||||
static const string License = "GPLv3";
|
static const string License = "GPLv3";
|
||||||
static const string Website = "http://byuu.org/";
|
static const string Website = "http://byuu.org/";
|
||||||
|
@ -20,6 +20,16 @@ namespace Emulator {
|
||||||
static const string SerializerVersion = "100";
|
static const string SerializerVersion = "100";
|
||||||
|
|
||||||
namespace Constants {
|
namespace Constants {
|
||||||
|
namespace Time {
|
||||||
|
static constexpr double Second = 1.0;
|
||||||
|
static constexpr double Millisecond = 1'000.0;
|
||||||
|
static constexpr double Microsecond = 1'000'000.0;
|
||||||
|
static constexpr double Nanosecond = 1'000'000'000.0;
|
||||||
|
static constexpr double Picosecond = 1'000'000'000'000.0;
|
||||||
|
static constexpr double Femtosecond = 1'000'000'000'000'000.0;
|
||||||
|
static constexpr double Attosecond = 1'000'000'000'000'000'000.0;
|
||||||
|
}
|
||||||
|
|
||||||
namespace Colorburst {
|
namespace Colorburst {
|
||||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||||
|
|
|
@ -2,6 +2,8 @@
|
||||||
|
|
||||||
namespace Emulator {
|
namespace Emulator {
|
||||||
|
|
||||||
|
struct Thread;
|
||||||
|
|
||||||
struct Scheduler {
|
struct Scheduler {
|
||||||
enum class Mode : uint {
|
enum class Mode : uint {
|
||||||
Run,
|
Run,
|
||||||
|
@ -15,29 +17,59 @@ struct Scheduler {
|
||||||
Synchronize,
|
Synchronize,
|
||||||
};
|
};
|
||||||
|
|
||||||
auto reset(cothread_t master_) -> void {
|
auto active(Thread& thread) const -> bool {
|
||||||
master = resume = master_;
|
return co_active() == thread.handle();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto reset() -> void {
|
||||||
|
threads.reset();
|
||||||
|
}
|
||||||
|
|
||||||
|
auto primary(Thread& thread) -> void {
|
||||||
|
master = _resume = thread.handle();
|
||||||
host = co_active();
|
host = co_active();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto append(Thread& thread) -> bool {
|
||||||
|
if(threads.find(&thread)) return false;
|
||||||
|
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 {
|
auto enter(Mode mode_ = Mode::Run) -> Event {
|
||||||
mode = mode_;
|
mode = mode_;
|
||||||
host = co_active();
|
host = co_active();
|
||||||
co_switch(resume);
|
co_switch(_resume);
|
||||||
return event;
|
return event;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline auto resume(Thread& thread) -> void {
|
||||||
|
if(mode != Mode::SynchronizeSlave) co_switch(thread.handle());
|
||||||
|
}
|
||||||
|
|
||||||
auto exit(Event event_) -> void {
|
auto exit(Event event_) -> void {
|
||||||
|
uint64 minimum = ~0ull >> 1;
|
||||||
|
for(auto thread : threads) {
|
||||||
|
if(thread->_clock < minimum) minimum = thread->_clock;
|
||||||
|
}
|
||||||
|
for(auto thread : threads) {
|
||||||
|
thread->_clock -= minimum;
|
||||||
|
}
|
||||||
|
|
||||||
event = event_;
|
event = event_;
|
||||||
resume = co_active();
|
_resume = co_active();
|
||||||
co_switch(host);
|
co_switch(host);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto synchronize(cothread_t thread) -> void {
|
auto synchronize(Thread& thread) -> void {
|
||||||
if(thread == master) {
|
if(thread.handle() == master) {
|
||||||
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
||||||
} else {
|
} else {
|
||||||
resume = thread;
|
_resume = thread.handle();
|
||||||
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -50,16 +82,13 @@ struct Scheduler {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto synchronizing() const -> bool {
|
|
||||||
return mode == Mode::SynchronizeSlave;
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
cothread_t host = nullptr; //program thread (used to exit scheduler)
|
cothread_t host = nullptr; //program thread (used to exit scheduler)
|
||||||
cothread_t resume = nullptr; //resume thread (used to enter scheduler)
|
cothread_t _resume = nullptr; //resume thread (used to enter scheduler)
|
||||||
cothread_t master = nullptr; //primary thread (used to synchronize components)
|
cothread_t master = nullptr; //primary thread (used to synchronize components)
|
||||||
Mode mode = Mode::Run;
|
Mode mode = Mode::Run;
|
||||||
Event event = Event::Step;
|
Event event = Event::Step;
|
||||||
|
vector<Thread*> threads;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -4,24 +4,47 @@ namespace Emulator {
|
||||||
|
|
||||||
struct Thread {
|
struct Thread {
|
||||||
virtual ~Thread() {
|
virtual ~Thread() {
|
||||||
if(thread) co_delete(thread);
|
if(_handle) co_delete(_handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto create(auto (*entrypoint)() -> void, double frequency_) -> void {
|
auto handle() const { return _handle; }
|
||||||
if(thread) co_delete(thread);
|
auto frequency() const { return _frequency; }
|
||||||
thread = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
auto scalar() const { return _scalar; }
|
||||||
frequency = frequency_ + 0.5; //round to nearest whole number
|
auto clock() const { return _clock; }
|
||||||
clock = 0;
|
|
||||||
|
auto create(auto (*entrypoint)() -> void, double frequency, bool resetClock = true) -> void {
|
||||||
|
if(_handle) co_delete(_handle);
|
||||||
|
_handle = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
||||||
|
if(resetClock) _clock = 0;
|
||||||
|
setFrequency(frequency);
|
||||||
|
}
|
||||||
|
|
||||||
|
auto setFrequency(double frequency) -> void {
|
||||||
|
_frequency = frequency;
|
||||||
|
_scalar = 1.0L / frequency * Constants::Time::Attosecond + 0.5L;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto step(uint clocks) -> void {
|
||||||
|
_clock += _scalar * clocks;
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto synchronize(Thread& thread) -> void {
|
||||||
|
if(_clock > thread._clock) co_switch(thread._handle);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto serialize(serializer& s) -> void {
|
auto serialize(serializer& s) -> void {
|
||||||
s.integer(frequency);
|
s.integer(_frequency);
|
||||||
s.integer(clock);
|
s.integer(_scalar);
|
||||||
|
s.integer(_clock);
|
||||||
}
|
}
|
||||||
|
|
||||||
cothread_t thread = nullptr;
|
protected:
|
||||||
uint frequency = 0;
|
cothread_t _handle = nullptr;
|
||||||
int64 clock = 0;
|
uint64 _frequency = 0;
|
||||||
|
uint64 _scalar = 0;
|
||||||
|
uint64 _clock = 0;
|
||||||
|
|
||||||
|
friend class Scheduler;
|
||||||
};
|
};
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -63,8 +63,8 @@ auto APU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::tick() -> void {
|
auto APU::tick() -> void {
|
||||||
clock += 12;
|
Thread::step(12);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::setIRQ() -> void {
|
auto APU::setIRQ() -> void {
|
||||||
|
|
|
@ -109,13 +109,13 @@ auto Board::mirror(uint addr, uint size) -> uint {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Board::main() -> void {
|
auto Board::main() -> void {
|
||||||
cartridge.clock += 12 * 4095;
|
cartridge.step(12 * 4095);
|
||||||
tick();
|
tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Board::tick() -> void {
|
auto Board::tick() -> void {
|
||||||
cartridge.clock += 12;
|
cartridge.step(12);
|
||||||
if(cartridge.clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
cartridge.synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Board::readCHR(uint addr) -> uint8 {
|
auto Board::readCHR(uint addr) -> uint8 {
|
||||||
|
|
|
@ -5,22 +5,24 @@ namespace Famicom {
|
||||||
#include "gamepad/gamepad.cpp"
|
#include "gamepad/gamepad.cpp"
|
||||||
|
|
||||||
Controller::Controller(bool port) : port(port) {
|
Controller::Controller(bool port) : port(port) {
|
||||||
if(!thread) create(Controller::Enter, 1);
|
if(!handle()) create(Controller::Enter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::~Controller() {
|
Controller::~Controller() {
|
||||||
|
scheduler.remove(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Controller::Enter() -> void {
|
auto Controller::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.synchronize();
|
scheduler.synchronize();
|
||||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
if(scheduler.active(*peripherals.controllerPort1)) peripherals.controllerPort1->main();
|
||||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
if(scheduler.active(*peripherals.controllerPort2)) peripherals.controllerPort2->main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Controller::main() -> void {
|
auto Controller::main() -> void {
|
||||||
step(1);
|
step(1);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -16,7 +16,7 @@
|
||||||
// 6: data4 $4016.d4 read $4017.d4 read
|
// 6: data4 $4016.d4 read $4017.d4 read
|
||||||
// 7: gnd
|
// 7: gnd
|
||||||
|
|
||||||
struct Controller : Cothread {
|
struct Controller : Thread {
|
||||||
enum : bool { Port1 = 0, Port2 = 1 };
|
enum : bool { Port1 = 0, Port2 = 1 };
|
||||||
|
|
||||||
Controller(bool port);
|
Controller(bool port);
|
||||||
|
|
|
@ -17,19 +17,11 @@ auto CPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::step(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
apu.clock -= clocks;
|
Thread::step(clocks);
|
||||||
if(apu.clock < 0) co_switch(apu.thread);
|
synchronize(apu);
|
||||||
|
synchronize(ppu);
|
||||||
ppu.clock -= clocks;
|
synchronize(cartridge);
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||||
|
|
||||||
cartridge.clock -= clocks;
|
|
||||||
if(cartridge.clock < 0) co_switch(cartridge.thread);
|
|
||||||
|
|
||||||
for(auto peripheral : peripherals) {
|
|
||||||
peripheral->clock -= clocks * (uint64)peripheral->frequency;
|
|
||||||
if(peripheral->clock < 0) co_switch(peripheral->thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::power() -> void {
|
auto CPU::power() -> void {
|
||||||
|
|
|
@ -12,15 +12,14 @@
|
||||||
|
|
||||||
namespace Famicom {
|
namespace Famicom {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
using Cheat = Emulator::Cheat;
|
using Cheat = Emulator::Cheat;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
||||||
struct Cothread : Thread {
|
struct Thread : Emulator::Thread {
|
||||||
auto step(uint clocks) -> void;
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||||
auto synchronizeCPU() -> void;
|
auto synchronize(Thread& thread) -> void;
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <fc/controller/controller.hpp>
|
#include <fc/controller/controller.hpp>
|
||||||
|
@ -31,13 +30,13 @@ namespace Famicom {
|
||||||
#include <fc/apu/apu.hpp>
|
#include <fc/apu/apu.hpp>
|
||||||
#include <fc/ppu/ppu.hpp>
|
#include <fc/ppu/ppu.hpp>
|
||||||
|
|
||||||
inline auto Cothread::step(uint clocks) -> void {
|
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||||
clock += clocks * (uint64)cpu.frequency;
|
Emulator::Thread::create(entrypoint, frequency);
|
||||||
synchronizeCPU();
|
scheduler.append(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto Cothread::synchronizeCPU() -> void {
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -27,8 +27,8 @@ auto PPU::step(uint clocks) -> void {
|
||||||
if(io.ly == 261 && io.lx == 0) io.nmiFlag = io.nmiHold;
|
if(io.ly == 261 && io.lx == 0) io.nmiFlag = io.nmiHold;
|
||||||
if(io.ly == 261 && io.lx == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
if(io.ly == 261 && io.lx == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
||||||
|
|
||||||
clock += 4;
|
Thread::step(4);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
|
|
||||||
io.lx++;
|
io.lx++;
|
||||||
}
|
}
|
||||||
|
|
|
@ -14,13 +14,11 @@ auto System::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu);
|
||||||
scheduler.synchronize(apu.thread);
|
scheduler.synchronize(apu);
|
||||||
scheduler.synchronize(ppu.thread);
|
scheduler.synchronize(ppu);
|
||||||
scheduler.synchronize(cartridge.thread);
|
scheduler.synchronize(cartridge);
|
||||||
for(auto peripheral : cpu.peripherals) {
|
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
|
||||||
scheduler.synchronize(peripheral->thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::load() -> bool {
|
auto System::load() -> bool {
|
||||||
|
@ -65,11 +63,12 @@ auto System::reset() -> void {
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset();
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
cartridge.reset();
|
cartridge.reset();
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
apu.reset();
|
apu.reset();
|
||||||
ppu.reset();
|
ppu.reset();
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
peripherals.reset();
|
peripherals.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -51,8 +51,8 @@ auto APU::main() -> void {
|
||||||
}
|
}
|
||||||
cycle++;
|
cycle++;
|
||||||
|
|
||||||
clock += cpu.frequency;
|
Thread::step(1);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
//filter to remove DC bias
|
//filter to remove DC bias
|
||||||
|
|
|
@ -77,8 +77,8 @@ auto CPU::stop() -> bool {
|
||||||
if(status.speedSwitch) {
|
if(status.speedSwitch) {
|
||||||
status.speedSwitch = 0;
|
status.speedSwitch = 0;
|
||||||
status.speedDouble ^= 1;
|
status.speedDouble ^= 1;
|
||||||
if(status.speedDouble == 0) frequency = 4 * 1024 * 1024;
|
if(status.speedDouble == 0) setFrequency(4 * 1024 * 1024);
|
||||||
if(status.speedDouble == 1) frequency = 8 * 1024 * 1024;
|
if(status.speedDouble == 1) setFrequency(8 * 1024 * 1024);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
|
|
@ -16,11 +16,9 @@ auto CPU::step(uint clocks) -> void {
|
||||||
if((status.div & 511) == 0) timer8192hz();
|
if((status.div & 511) == 0) timer8192hz();
|
||||||
if((status.div & 1023) == 0) timer4096hz();
|
if((status.div & 1023) == 0) timer4096hz();
|
||||||
|
|
||||||
ppu.clock -= ppu.frequency;
|
Thread::step(1);
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
synchronize(ppu);
|
||||||
|
synchronize(apu);
|
||||||
apu.clock -= apu.frequency;
|
|
||||||
if(apu.clock < 0) co_switch(apu.thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(system.sgb()) {
|
if(system.sgb()) {
|
||||||
|
|
|
@ -12,18 +12,31 @@
|
||||||
|
|
||||||
namespace GameBoy {
|
namespace GameBoy {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
using Cheat = Emulator::Cheat;
|
using Cheat = Emulator::Cheat;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
||||||
|
struct Thread : Emulator::Thread {
|
||||||
|
auto create(auto (*entrypoint)() -> void, double frequency, bool resetClock) -> void;
|
||||||
|
auto synchronize(Thread& thread) -> void;
|
||||||
|
};
|
||||||
|
|
||||||
#include <gb/memory/memory.hpp>
|
#include <gb/memory/memory.hpp>
|
||||||
#include <gb/system/system.hpp>
|
#include <gb/system/system.hpp>
|
||||||
#include <gb/cartridge/cartridge.hpp>
|
#include <gb/cartridge/cartridge.hpp>
|
||||||
#include <gb/cpu/cpu.hpp>
|
#include <gb/cpu/cpu.hpp>
|
||||||
#include <gb/ppu/ppu.hpp>
|
#include <gb/ppu/ppu.hpp>
|
||||||
#include <gb/apu/apu.hpp>
|
#include <gb/apu/apu.hpp>
|
||||||
|
|
||||||
|
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency, bool resetClock = true) -> void {
|
||||||
|
Emulator::Thread::create(entrypoint, frequency, resetClock);
|
||||||
|
scheduler.append(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <gb/interface/interface.hpp>
|
#include <gb/interface/interface.hpp>
|
||||||
|
|
|
@ -118,9 +118,7 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
||||||
status.lx = 0;
|
status.lx = 0;
|
||||||
|
|
||||||
//restart cothread to begin new frame
|
//restart cothread to begin new frame
|
||||||
auto clock = this->clock;
|
create(Enter, 4 * 1024 * 1024, false);
|
||||||
create(Enter, 4 * 1024 * 1024);
|
|
||||||
this->clock = clock;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
status.displayEnable = data & 0x80;
|
status.displayEnable = data & 0x80;
|
||||||
|
|
|
@ -94,8 +94,8 @@ auto PPU::step(uint clocks) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
status.lx++;
|
status.lx++;
|
||||||
clock += cpu.frequency;
|
Thread::step(1);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -13,9 +13,9 @@ auto System::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu);
|
||||||
scheduler.synchronize(ppu.thread);
|
scheduler.synchronize(ppu);
|
||||||
scheduler.synchronize(apu.thread);
|
scheduler.synchronize(apu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::init() -> void {
|
auto System::init() -> void {
|
||||||
|
@ -68,12 +68,13 @@ auto System::power() -> void {
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
bus.power();
|
bus.power();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
cpu.power();
|
cpu.power();
|
||||||
ppu.power();
|
ppu.power();
|
||||||
apu.power();
|
apu.power();
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
|
|
||||||
_clocksExecuted = 0;
|
_clocksExecuted = 0;
|
||||||
}
|
}
|
||||||
|
|
|
@ -68,8 +68,8 @@ auto APU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::step(uint clocks) -> void {
|
auto APU::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::power() -> void {
|
auto APU::power() -> void {
|
||||||
|
|
|
@ -83,11 +83,9 @@ auto CPU::step(uint clocks) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::syncStep(uint clocks) -> void {
|
auto CPU::syncStep(uint clocks) -> void {
|
||||||
ppu.clock -= clocks;
|
Thread::step(clocks);
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
synchronize(ppu);
|
||||||
|
synchronize(apu);
|
||||||
apu.clock -= clocks;
|
|
||||||
if(apu.clock < 0) co_switch(apu.thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::keypadRun() -> void {
|
auto CPU::keypadRun() -> void {
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
|
|
||||||
namespace GameBoyAdvance {
|
namespace GameBoyAdvance {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
|
||||||
|
@ -27,6 +26,12 @@ namespace GameBoyAdvance {
|
||||||
Signed = 256, //sign extended
|
Signed = 256, //sign extended
|
||||||
};
|
};
|
||||||
|
|
||||||
|
struct Thread : Emulator::Thread {
|
||||||
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||||
|
auto synchronize(Thread& thread) -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
};
|
||||||
|
|
||||||
#include <gba/memory/memory.hpp>
|
#include <gba/memory/memory.hpp>
|
||||||
#include <gba/system/system.hpp>
|
#include <gba/system/system.hpp>
|
||||||
#include <gba/cartridge/cartridge.hpp>
|
#include <gba/cartridge/cartridge.hpp>
|
||||||
|
@ -34,6 +39,19 @@ namespace GameBoyAdvance {
|
||||||
#include <gba/cpu/cpu.hpp>
|
#include <gba/cpu/cpu.hpp>
|
||||||
#include <gba/ppu/ppu.hpp>
|
#include <gba/ppu/ppu.hpp>
|
||||||
#include <gba/apu/apu.hpp>
|
#include <gba/apu/apu.hpp>
|
||||||
|
|
||||||
|
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||||
|
Emulator::Thread::create(entrypoint, frequency);
|
||||||
|
scheduler.append(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::step(uint clocks) -> void {
|
||||||
|
_clock += clocks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <gba/interface/interface.hpp>
|
#include <gba/interface/interface.hpp>
|
||||||
|
|
|
@ -43,8 +43,8 @@ auto PPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::step(uint clocks) -> void {
|
auto PPU::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::power() -> void {
|
auto PPU::power() -> void {
|
||||||
|
|
|
@ -24,13 +24,14 @@ auto System::power() -> void {
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset();
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
bus.power();
|
bus.power();
|
||||||
player.power();
|
player.power();
|
||||||
cpu.power();
|
cpu.power();
|
||||||
ppu.power();
|
ppu.power();
|
||||||
apu.power();
|
apu.power();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::load() -> bool {
|
auto System::load() -> bool {
|
||||||
|
@ -66,9 +67,9 @@ auto System::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu);
|
||||||
scheduler.synchronize(ppu.thread);
|
scheduler.synchronize(ppu);
|
||||||
scheduler.synchronize(apu.thread);
|
scheduler.synchronize(apu);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -9,7 +9,7 @@ auto APU::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::main() -> void {
|
auto APU::main() -> void {
|
||||||
step(frequency);
|
step(system.colorburst());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::step(uint clocks) -> void {
|
auto APU::step(uint clocks) -> void {
|
||||||
|
|
|
@ -19,9 +19,10 @@ auto CPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::step(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= frequency / 60) {
|
cycles += clocks;
|
||||||
clock -= frequency / 60;
|
if(cycles >= frequency() / 60) {
|
||||||
|
cycles = 0;
|
||||||
scheduler.exit(Scheduler::Event::Frame);
|
scheduler.exit(Scheduler::Event::Frame);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -35,6 +36,7 @@ auto CPU::power() -> void {
|
||||||
auto CPU::reset() -> void {
|
auto CPU::reset() -> void {
|
||||||
M68K::reset();
|
M68K::reset();
|
||||||
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
|
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
|
||||||
|
cycles = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::read(bool word, uint24 addr) -> uint16 {
|
auto CPU::read(bool word, uint24 addr) -> uint16 {
|
||||||
|
|
|
@ -14,6 +14,8 @@ struct CPU : Processor::M68K, Thread {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
uint8 ram[64 * 1024];
|
uint8 ram[64 * 1024];
|
||||||
|
|
||||||
|
uint cycles = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern CPU cpu;
|
extern CPU cpu;
|
||||||
|
|
|
@ -12,10 +12,14 @@
|
||||||
|
|
||||||
namespace MegaDrive {
|
namespace MegaDrive {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
|
||||||
|
struct Thread : Emulator::Thread {
|
||||||
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||||
|
auto synchronize(Thread& thread) -> void;
|
||||||
|
};
|
||||||
|
|
||||||
#include <md/cpu/cpu.hpp>
|
#include <md/cpu/cpu.hpp>
|
||||||
#include <md/apu/apu.hpp>
|
#include <md/apu/apu.hpp>
|
||||||
#include <md/vdp/vdp.hpp>
|
#include <md/vdp/vdp.hpp>
|
||||||
|
@ -24,6 +28,15 @@ namespace MegaDrive {
|
||||||
|
|
||||||
#include <md/system/system.hpp>
|
#include <md/system/system.hpp>
|
||||||
#include <md/cartridge/cartridge.hpp>
|
#include <md/cartridge/cartridge.hpp>
|
||||||
|
|
||||||
|
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||||
|
Emulator::Thread::create(entrypoint, frequency);
|
||||||
|
scheduler.append(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <md/interface/interface.hpp>
|
#include <md/interface/interface.hpp>
|
||||||
|
|
|
@ -9,7 +9,7 @@ auto PSG::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PSG::main() -> void {
|
auto PSG::main() -> void {
|
||||||
step(frequency);
|
step(system.colorburst());
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PSG::step(uint clocks) -> void {
|
auto PSG::step(uint clocks) -> void {
|
||||||
|
|
|
@ -49,13 +49,14 @@ auto System::reset() -> void {
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset();
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
cartridge.reset();
|
cartridge.reset();
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
apu.reset();
|
apu.reset();
|
||||||
vdp.reset();
|
vdp.reset();
|
||||||
psg.reset();
|
psg.reset();
|
||||||
ym2612.reset();
|
ym2612.reset();
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -12,7 +12,7 @@ auto VDP::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::main() -> void {
|
auto VDP::main() -> void {
|
||||||
step(frequency);
|
step(system.colorburst() * 15.0 / 10.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto VDP::step(uint clocks) -> void {
|
auto VDP::step(uint clocks) -> void {
|
||||||
|
|
|
@ -9,7 +9,7 @@ auto YM2612::Enter() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto YM2612::main() -> void {
|
auto YM2612::main() -> void {
|
||||||
step(frequency);
|
step(system.colorburst() * 15.0 / 7.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto YM2612::step(uint clocks) -> void {
|
auto YM2612::step(uint clocks) -> void {
|
||||||
|
|
Binary file not shown.
After Width: | Height: | Size: 79 KiB |
|
@ -1,4 +1,6 @@
|
||||||
namespace name=Resource
|
namespace name=Resource
|
||||||
|
namespace name=Logo
|
||||||
|
binary name=higan file=logo/higan.png
|
||||||
namespace name=Sprite
|
namespace name=Sprite
|
||||||
binary name=CrosshairRed file=sprite/crosshair-red.png
|
binary name=CrosshairRed file=sprite/crosshair-red.png
|
||||||
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
binary name=CrosshairGreen file=sprite/crosshair-green.png
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -1,4 +1,7 @@
|
||||||
namespace Resource {
|
namespace Resource {
|
||||||
|
namespace Logo {
|
||||||
|
extern const nall::vector<uint8_t> higan;
|
||||||
|
}
|
||||||
namespace Sprite {
|
namespace Sprite {
|
||||||
extern const nall::vector<uint8_t> CrosshairRed;
|
extern const nall::vector<uint8_t> CrosshairRed;
|
||||||
extern const nall::vector<uint8_t> CrosshairGreen;
|
extern const nall::vector<uint8_t> CrosshairGreen;
|
||||||
|
|
|
@ -199,7 +199,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
||||||
has.HitachiDSP = true;
|
has.HitachiDSP = true;
|
||||||
|
|
||||||
hitachidsp.Frequency = node["frequency"].natural();
|
hitachidsp.Frequency = node["frequency"].natural();
|
||||||
if(hitachidsp.Frequency == 0) hitachidsp.frequency = 20000000;
|
if(hitachidsp.Frequency == 0) hitachidsp.Frequency = 20'000'000;
|
||||||
hitachidsp.Roms = roms; //1 or 2
|
hitachidsp.Roms = roms; //1 or 2
|
||||||
|
|
||||||
loadMemory(hitachidsp.rom, node["rom"], File::Required);
|
loadMemory(hitachidsp.rom, node["rom"], File::Required);
|
||||||
|
@ -224,8 +224,8 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
||||||
auto Cartridge::loadNECDSP(Markup::Node node) -> void {
|
auto Cartridge::loadNECDSP(Markup::Node node) -> void {
|
||||||
has.NECDSP = true;
|
has.NECDSP = true;
|
||||||
|
|
||||||
necdsp.frequency = node["frequency"].natural();
|
necdsp.Frequency = node["frequency"].natural();
|
||||||
if(necdsp.frequency == 0) necdsp.frequency = 8000000;
|
if(necdsp.Frequency == 0) necdsp.Frequency = 8000000;
|
||||||
necdsp.revision
|
necdsp.revision
|
||||||
= node["model"].text() == "uPD7725" ? NECDSP::Revision::uPD7725
|
= node["model"].text() == "uPD7725" ? NECDSP::Revision::uPD7725
|
||||||
: node["model"].text() == "uPD96050" ? NECDSP::Revision::uPD96050
|
: node["model"].text() == "uPD96050" ? NECDSP::Revision::uPD96050
|
||||||
|
|
|
@ -9,22 +9,24 @@ namespace SuperFamicom {
|
||||||
#include "justifier/justifier.cpp"
|
#include "justifier/justifier.cpp"
|
||||||
|
|
||||||
Controller::Controller(bool port) : port(port) {
|
Controller::Controller(bool port) : port(port) {
|
||||||
if(!thread) create(Controller::Enter, 1);
|
if(!handle()) create(Controller::Enter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
Controller::~Controller() {
|
Controller::~Controller() {
|
||||||
|
scheduler.remove(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Controller::Enter() -> void {
|
auto Controller::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
scheduler.synchronize();
|
scheduler.synchronize();
|
||||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
if(scheduler.active(*peripherals.controllerPort1)) peripherals.controllerPort1->main();
|
||||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
if(scheduler.active(*peripherals.controllerPort2)) peripherals.controllerPort2->main();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Controller::main() -> void {
|
auto Controller::main() -> void {
|
||||||
step(1);
|
step(1);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Controller::iobit() -> bool {
|
auto Controller::iobit() -> bool {
|
||||||
|
|
|
@ -11,7 +11,7 @@
|
||||||
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
|
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
|
||||||
// 7: gnd
|
// 7: gnd
|
||||||
|
|
||||||
struct Controller : Cothread {
|
struct Controller : Thread {
|
||||||
enum : bool { Port1 = 0, Port2 = 1 };
|
enum : bool { Port1 = 0, Port2 = 1 };
|
||||||
|
|
||||||
Controller(bool port);
|
Controller(bool port);
|
||||||
|
|
|
@ -76,6 +76,7 @@ auto Justifier::main() -> void {
|
||||||
|
|
||||||
prev = next;
|
prev = next;
|
||||||
step(2);
|
step(2);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Justifier::data() -> uint2 {
|
auto Justifier::data() -> uint2 {
|
||||||
|
|
|
@ -66,6 +66,7 @@ auto SuperScope::main() -> void {
|
||||||
|
|
||||||
prev = next;
|
prev = next;
|
||||||
step(2);
|
step(2);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperScope::data() -> uint2 {
|
auto SuperScope::data() -> uint2 {
|
||||||
|
|
|
@ -42,7 +42,7 @@ auto ArmDSP::main() -> void {
|
||||||
print(disassembleRegisters(), "\n");
|
print(disassembleRegisters(), "\n");
|
||||||
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
||||||
print("Executed: ", instructions, "\n");
|
print("Executed: ", instructions, "\n");
|
||||||
while(true) step(frequency);
|
while(true) step(21'477'272);
|
||||||
}
|
}
|
||||||
|
|
||||||
stepARM();
|
stepARM();
|
||||||
|
@ -50,8 +50,8 @@ auto ArmDSP::main() -> void {
|
||||||
|
|
||||||
auto ArmDSP::step(uint clocks) -> void {
|
auto ArmDSP::step(uint clocks) -> void {
|
||||||
if(bridge.timer && --bridge.timer == 0);
|
if(bridge.timer && --bridge.timer == 0);
|
||||||
Cothread::step(clocks);
|
Thread::step(clocks);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
//MMIO: 00-3f,80-bf:3800-38ff
|
//MMIO: 00-3f,80-bf:3800-38ff
|
||||||
|
@ -59,7 +59,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
||||||
//a0 ignored
|
//a0 ignored
|
||||||
|
|
||||||
auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
|
|
||||||
uint8 data = 0x00;
|
uint8 data = 0x00;
|
||||||
addr &= 0xff06;
|
addr &= 0xff06;
|
||||||
|
@ -83,7 +83,7 @@ auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ArmDSP::write(uint24 addr, uint8 data) -> void {
|
auto ArmDSP::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
|
|
||||||
addr &= 0xff06;
|
addr &= 0xff06;
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//ARMv3 (ARM60)
|
//ARMv3 (ARM60)
|
||||||
|
|
||||||
struct ArmDSP : Processor::ARM, Cothread {
|
struct ArmDSP : Processor::ARM, Thread {
|
||||||
#include "registers.hpp"
|
#include "registers.hpp"
|
||||||
|
|
||||||
ArmDSP();
|
ArmDSP();
|
||||||
|
|
|
@ -1,6 +1,3 @@
|
||||||
struct Coprocessor : Cothread {
|
|
||||||
};
|
|
||||||
|
|
||||||
#include <sfc/coprocessor/icd2/icd2.hpp>
|
#include <sfc/coprocessor/icd2/icd2.hpp>
|
||||||
#include <sfc/coprocessor/mcc/mcc.hpp>
|
#include <sfc/coprocessor/mcc/mcc.hpp>
|
||||||
#include <sfc/coprocessor/nss/nss.hpp>
|
#include <sfc/coprocessor/nss/nss.hpp>
|
||||||
|
|
|
@ -27,7 +27,7 @@ auto EpsonRTC::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::init() -> void {
|
auto EpsonRTC::init() -> void {
|
||||||
|
@ -136,7 +136,7 @@ auto EpsonRTC::sync() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr &= 3;
|
addr &= 3;
|
||||||
|
|
||||||
if(addr == 0) {
|
if(addr == 0) {
|
||||||
|
@ -161,7 +161,7 @@ auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr &= 3, data &= 15;
|
addr &= 3, data &= 15;
|
||||||
|
|
||||||
if(addr == 0) {
|
if(addr == 0) {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
//Epson RTC-4513 Real-Time Clock
|
//Epson RTC-4513 Real-Time Clock
|
||||||
|
|
||||||
struct EpsonRTC : Cothread {
|
struct EpsonRTC : Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
||||||
|
|
|
@ -25,7 +25,7 @@ auto Event::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Event::init() -> void {
|
auto Event::init() -> void {
|
||||||
|
@ -47,6 +47,7 @@ auto Event::power() -> void {
|
||||||
|
|
||||||
auto Event::reset() -> void {
|
auto Event::reset() -> void {
|
||||||
create(Event::Enter, 1);
|
create(Event::Enter, 1);
|
||||||
|
|
||||||
for(auto n : range(ram.size())) ram.write(n, 0x00);
|
for(auto n : range(ram.size())) ram.write(n, 0x00);
|
||||||
status = 0x00;
|
status = 0x00;
|
||||||
select = 0x00;
|
select = 0x00;
|
||||||
|
|
|
@ -2,7 +2,7 @@
|
||||||
//* Campus Challenge '92
|
//* Campus Challenge '92
|
||||||
//* Powerfest '94
|
//* Powerfest '94
|
||||||
|
|
||||||
struct Event : Cothread {
|
struct Event : Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
auto init() -> void;
|
auto init() -> void;
|
||||||
|
|
|
@ -15,14 +15,14 @@ auto HitachiDSP::main() -> void {
|
||||||
for(auto n : range(mmio.dmaLength)) {
|
for(auto n : range(mmio.dmaLength)) {
|
||||||
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
|
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
|
||||||
step(2);
|
step(2);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
mmio.dma = false;
|
mmio.dma = false;
|
||||||
}
|
}
|
||||||
|
|
||||||
exec(mmio.programOffset);
|
exec(mmio.programOffset);
|
||||||
step(1);
|
step(1);
|
||||||
|
synchronize(cpu);
|
||||||
synchronizeCPU();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HitachiDSP::init() -> void {
|
auto HitachiDSP::init() -> void {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct HitachiDSP : Processor::HG51B, Cothread {
|
struct HitachiDSP : Processor::HG51B, Thread {
|
||||||
MappedRAM rom;
|
MappedRAM rom;
|
||||||
MappedRAM ram;
|
MappedRAM ram;
|
||||||
|
|
||||||
|
|
|
@ -36,7 +36,7 @@ auto HitachiDSP::write(uint24 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 {
|
auto HitachiDSP::romRead(uint24 addr, uint8 data) -> uint8 {
|
||||||
if(co_active() == hitachidsp.thread || regs.halt) {
|
if(scheduler.active(hitachidsp) || regs.halt) {
|
||||||
addr = Bus::mirror(addr, rom.size());
|
addr = Bus::mirror(addr, rom.size());
|
||||||
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00;
|
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00;
|
||||||
return rom.read(addr, data);
|
return rom.read(addr, data);
|
||||||
|
|
|
@ -12,7 +12,7 @@ ICD2 icd2;
|
||||||
|
|
||||||
auto ICD2::Enter() -> void {
|
auto ICD2::Enter() -> void {
|
||||||
while(true) {
|
while(true) {
|
||||||
if(scheduler.synchronizing()) GameBoy::system.runToSave();
|
//if(scheduler.synchronizing()) GameBoy::system.runToSave();
|
||||||
scheduler.synchronize();
|
scheduler.synchronize();
|
||||||
icd2.main();
|
icd2.main();
|
||||||
}
|
}
|
||||||
|
@ -27,7 +27,7 @@ auto ICD2::main() -> void {
|
||||||
stream->sample(0.0, 0.0);
|
stream->sample(0.0, 0.0);
|
||||||
step(2); //two clocks per audio sample
|
step(2); //two clocks per audio sample
|
||||||
}
|
}
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ICD2::init() -> void {
|
auto ICD2::init() -> void {
|
||||||
|
@ -52,8 +52,9 @@ auto ICD2::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto ICD2::reset(bool soft) -> void {
|
auto ICD2::reset(bool soft) -> void {
|
||||||
create(ICD2::Enter, cpu.frequency / 5);
|
auto frequency = system.colorburst() * 6.0;
|
||||||
if(!soft) stream = Emulator::audio.createStream(2, cpu.frequency / 10);
|
create(ICD2::Enter, frequency / 5);
|
||||||
|
if(!soft) stream = Emulator::audio.createStream(2, frequency / 10);
|
||||||
|
|
||||||
r6003 = 0x00;
|
r6003 = 0x00;
|
||||||
r6004 = 0xff;
|
r6004 = 0xff;
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
#if defined(SFC_SUPERGAMEBOY)
|
#if defined(SFC_SUPERGAMEBOY)
|
||||||
|
|
||||||
struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Cothread {
|
struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Thread {
|
||||||
shared_pointer<Emulator::Stream> stream;
|
shared_pointer<Emulator::Stream> stream;
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
@ -71,7 +71,7 @@ private:
|
||||||
|
|
||||||
#else
|
#else
|
||||||
|
|
||||||
struct ICD2 : Coprocessor {
|
struct ICD2 : Thread {
|
||||||
auto init() -> void {}
|
auto init() -> void {}
|
||||||
auto load() -> void {}
|
auto load() -> void {}
|
||||||
auto unload() -> void {}
|
auto unload() -> void {}
|
||||||
|
|
|
@ -56,11 +56,12 @@ auto ICD2::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
||||||
reset(true);
|
reset(true);
|
||||||
}
|
}
|
||||||
|
auto frequency = system.colorburst() * 6.0;
|
||||||
switch(data & 3) {
|
switch(data & 3) {
|
||||||
case 0: frequency = cpu.frequency / 4; break; //fast (glitchy, even on real hardware)
|
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
|
||||||
case 1: frequency = cpu.frequency / 5; break; //normal
|
case 1: setFrequency(frequency / 5); break; //normal
|
||||||
case 2: frequency = cpu.frequency / 7; break; //slow
|
case 2: setFrequency(frequency / 7); break; //slow
|
||||||
case 3: frequency = cpu.frequency / 9; break; //very slow
|
case 3: setFrequency(frequency / 9); break; //very slow
|
||||||
}
|
}
|
||||||
r6003 = data;
|
r6003 = data;
|
||||||
return;
|
return;
|
||||||
|
|
|
@ -36,7 +36,7 @@ auto MSU1::main() -> void {
|
||||||
|
|
||||||
stream->sample(left, right);
|
stream->sample(left, right);
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::init() -> void {
|
auto MSU1::init() -> void {
|
||||||
|
@ -115,7 +115,7 @@ auto MSU1::audioOpen() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
|
auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr = 0x2000 | (addr & 7);
|
addr = 0x2000 | (addr & 7);
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
|
@ -144,7 +144,7 @@ auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto MSU1::writeIO(uint24 addr, uint8 data) -> void {
|
auto MSU1::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr = 0x2000 | (addr & 7);
|
addr = 0x2000 | (addr & 7);
|
||||||
|
|
||||||
switch(addr) {
|
switch(addr) {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct MSU1 : Cothread {
|
struct MSU1 : Thread {
|
||||||
shared_pointer<Emulator::Stream> stream;
|
shared_pointer<Emulator::Stream> stream;
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
|
|
@ -12,11 +12,11 @@ auto NECDSP::Enter() -> void {
|
||||||
auto NECDSP::main() -> void {
|
auto NECDSP::main() -> void {
|
||||||
exec();
|
exec();
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::read(uint24 addr, uint8) -> uint8 {
|
auto NECDSP::read(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
if(addr & 1) {
|
if(addr & 1) {
|
||||||
return uPD96050::readSR();
|
return uPD96050::readSR();
|
||||||
} else {
|
} else {
|
||||||
|
@ -25,7 +25,7 @@ auto NECDSP::read(uint24 addr, uint8) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::write(uint24 addr, uint8 data) -> void {
|
auto NECDSP::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
if(addr & 1) {
|
if(addr & 1) {
|
||||||
return uPD96050::writeSR(data);
|
return uPD96050::writeSR(data);
|
||||||
} else {
|
} else {
|
||||||
|
@ -34,12 +34,12 @@ auto NECDSP::write(uint24 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::readRAM(uint24 addr, uint8) -> uint8 {
|
auto NECDSP::readRAM(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
return uPD96050::readDP(addr);
|
return uPD96050::readDP(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::writeRAM(uint24 addr, uint8 data) -> void {
|
auto NECDSP::writeRAM(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
return uPD96050::writeDP(addr, data);
|
return uPD96050::writeDP(addr, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ auto NECDSP::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto NECDSP::reset() -> void {
|
auto NECDSP::reset() -> void {
|
||||||
create(NECDSP::Enter, frequency);
|
create(NECDSP::Enter, Frequency);
|
||||||
uPD96050::power();
|
uPD96050::power();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct NECDSP : Processor::uPD96050, Cothread {
|
struct NECDSP : Processor::uPD96050, Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
||||||
|
@ -16,6 +16,8 @@ struct NECDSP : Processor::uPD96050, Cothread {
|
||||||
|
|
||||||
auto firmware() const -> vector<uint8>;
|
auto firmware() const -> vector<uint8>;
|
||||||
auto serialize(serializer&) -> void;
|
auto serialize(serializer&) -> void;
|
||||||
|
|
||||||
|
uint Frequency = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
extern NECDSP necdsp;
|
extern NECDSP necdsp;
|
||||||
|
|
|
@ -5,12 +5,12 @@ auto SA1::CPUIRAM::size() const -> uint {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::CPUIRAM::read(uint24 addr, uint8) -> uint8 {
|
auto SA1::CPUIRAM::read(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
return sa1.iram.read(addr & 0x07ff);
|
return sa1.iram.read(addr & 0x07ff);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::CPUIRAM::write(uint24 addr, uint8 data) -> void {
|
auto SA1::CPUIRAM::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
sa1.iram.write(addr & 0x07ff, data);
|
sa1.iram.write(addr & 0x07ff, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -19,12 +19,12 @@ auto SA1::CPUBWRAM::size() const -> uint {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::CPUBWRAM::read(uint24 addr, uint8) -> uint8 {
|
auto SA1::CPUBWRAM::read(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
if(dma) return sa1.dmaCC1Read(addr);
|
if(dma) return sa1.dmaCC1Read(addr);
|
||||||
return sa1.bwram.read(addr);
|
return sa1.bwram.read(addr);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::CPUBWRAM::write(uint24 addr, uint8 data) -> void {
|
auto SA1::CPUBWRAM::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
sa1.bwram.write(addr, data);
|
sa1.bwram.write(addr, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
(co_active() == cpu.thread ? cpu.synchronizeCoprocessors() : synchronizeCPU());
|
scheduler.active(cpu) ? cpu.synchronize(sa1) : synchronize(cpu);
|
||||||
|
|
||||||
switch(0x2300 | addr.bits(0,7)) {
|
switch(0x2300 | addr.bits(0,7)) {
|
||||||
|
|
||||||
|
@ -91,7 +91,7 @@ auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
auto SA1::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
(co_active() == cpu.thread ? cpu.synchronizeCoprocessors() : synchronizeCPU());
|
scheduler.active(cpu) ? cpu.synchronize(sa1) : synchronize(cpu);
|
||||||
|
|
||||||
switch(0x2200 | addr.bits(0,7)) {
|
switch(0x2200 | addr.bits(0,7)) {
|
||||||
|
|
||||||
|
|
|
@ -17,22 +17,22 @@ auto SA1::busRead(uint24 addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return iram.read(addr & 2047, data);
|
return iram.read(addr & 2047, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return iram.read(addr & 2047, data);
|
return iram.read(addr & 2047, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return bwram.read(addr & (bwram.size() - 1), data);
|
return bwram.read(addr & (bwram.size() - 1), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return bitmapRead(addr & 0x0fffff, data);
|
return bitmapRead(addr & 0x0fffff, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -50,22 +50,22 @@ auto SA1::busWrite(uint24 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return iram.write(addr & 2047, data);
|
return iram.write(addr & 2047, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return iram.write(addr & 2047, data);
|
return iram.write(addr & 2047, data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return bwram.write(addr & (bwram.size() - 1), data);
|
return bwram.write(addr & (bwram.size() - 1), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return bitmapWrite(addr & 0x0fffff, data);
|
return bitmapWrite(addr & 0x0fffff, data);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -171,7 +171,7 @@ auto SA1::mmcromWrite(uint24 addr, uint8 data) -> void {
|
||||||
|
|
||||||
auto SA1::mmcbwramRead(uint24 addr, uint8 data) -> uint8 {
|
auto SA1::mmcbwramRead(uint24 addr, uint8 data) -> uint8 {
|
||||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||||
return cpubwram.read(addr);
|
return cpubwram.read(addr);
|
||||||
}
|
}
|
||||||
|
@ -185,7 +185,7 @@ auto SA1::mmcbwramRead(uint24 addr, uint8 data) -> uint8 {
|
||||||
|
|
||||||
auto SA1::mmcbwramWrite(uint24 addr, uint8 data) -> void {
|
auto SA1::mmcbwramWrite(uint24 addr, uint8 data) -> void {
|
||||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(sa1);
|
||||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||||
return cpubwram.write(addr, data);
|
return cpubwram.write(addr, data);
|
||||||
}
|
}
|
||||||
|
@ -196,7 +196,7 @@ auto SA1::mmcbwramWrite(uint24 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::mmcSA1Read(uint addr, uint8 data) -> uint8 {
|
auto SA1::mmcSA1Read(uint addr, uint8 data) -> uint8 {
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
if(mmio.sw46 == 0) {
|
if(mmio.sw46 == 0) {
|
||||||
//$40-43:0000-ffff x 32 projection
|
//$40-43:0000-ffff x 32 projection
|
||||||
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
||||||
|
@ -209,7 +209,7 @@ auto SA1::mmcSA1Read(uint addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SA1::mmcSA1Write(uint addr, uint8 data) -> void {
|
auto SA1::mmcSA1Write(uint addr, uint8 data) -> void {
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
if(mmio.sw46 == 0) {
|
if(mmio.sw46 == 0) {
|
||||||
//$40-43:0000-ffff x 32 projection
|
//$40-43:0000-ffff x 32 projection
|
||||||
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
addr = bus.mirror((mmio.cbm & 0x1f) * 0x2000 + (addr & 0x1fff), bwram.size());
|
||||||
|
|
|
@ -17,7 +17,7 @@ auto SA1::main() -> void {
|
||||||
if(mmio.sa1_rdyb || mmio.sa1_resb) {
|
if(mmio.sa1_rdyb || mmio.sa1_resb) {
|
||||||
//SA-1 co-processor is asleep
|
//SA-1 co-processor is asleep
|
||||||
tick();
|
tick();
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -77,7 +77,7 @@ auto SA1::interruptPending() const -> bool {
|
||||||
|
|
||||||
auto SA1::tick() -> void {
|
auto SA1::tick() -> void {
|
||||||
step(2);
|
step(2);
|
||||||
if(++status.counter == 0) synchronizeCPU();
|
if(++status.counter == 0) synchronize(cpu);
|
||||||
|
|
||||||
//adjust counters:
|
//adjust counters:
|
||||||
//note that internally, status counters are in clocks;
|
//note that internally, status counters are in clocks;
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct SA1 : Processor::R65816, Cothread {
|
struct SA1 : Processor::R65816, Thread {
|
||||||
//sa1.cpp
|
//sa1.cpp
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
|
@ -15,7 +15,7 @@ auto SharpRTC::main() -> void {
|
||||||
tickSecond();
|
tickSecond();
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SharpRTC::init() -> void {
|
auto SharpRTC::init() -> void {
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct SharpRTC : Cothread {
|
struct SharpRTC : Thread {
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,7 @@ auto SPC7110::main() -> void {
|
||||||
|
|
||||||
auto SPC7110::addClocks(uint clocks) -> void {
|
auto SPC7110::addClocks(uint clocks) -> void {
|
||||||
step(clocks);
|
step(clocks);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::init() -> void {
|
auto SPC7110::init() -> void {
|
||||||
|
@ -48,7 +48,7 @@ auto SPC7110::power() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::reset() -> void {
|
auto SPC7110::reset() -> void {
|
||||||
create(SPC7110::Enter, 21477272);
|
create(SPC7110::Enter, 21'477'272);
|
||||||
|
|
||||||
r4801 = 0x00;
|
r4801 = 0x00;
|
||||||
r4802 = 0x00;
|
r4802 = 0x00;
|
||||||
|
@ -105,7 +105,7 @@ auto SPC7110::reset() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::read(uint24 addr, uint8 data) -> uint8 {
|
auto SPC7110::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||||
|
@ -189,7 +189,7 @@ auto SPC7110::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SPC7110::write(uint24 addr, uint8 data) -> void {
|
auto SPC7110::write(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
struct Decompressor;
|
struct Decompressor;
|
||||||
|
|
||||||
struct SPC7110 : Cothread {
|
struct SPC7110 : Thread {
|
||||||
SPC7110();
|
SPC7110();
|
||||||
~SPC7110();
|
~SPC7110();
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr = 0x3000 | addr.bits(0,9);
|
addr = 0x3000 | addr.bits(0,9);
|
||||||
|
|
||||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||||
|
@ -51,7 +51,7 @@ auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::writeIO(uint24 addr, uint8 data) -> void {
|
auto SuperFX::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizeCoprocessors();
|
cpu.synchronize(*this);
|
||||||
addr = 0x3000 | addr.bits(0,9);
|
addr = 0x3000 | addr.bits(0,9);
|
||||||
|
|
||||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||||
|
|
|
@ -1,24 +1,24 @@
|
||||||
auto SuperFX::read(uint24 addr, uint8 data) -> uint8 {
|
auto SuperFX::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
|
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
|
||||||
while(!regs.scmr.ron && !scheduler.synchronizing()) {
|
while(!regs.scmr.ron) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask);
|
return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
|
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
|
||||||
while(!regs.scmr.ron && !scheduler.synchronizing()) {
|
while(!regs.scmr.ron) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
return rom.read(addr & romMask);
|
return rom.read(addr & romMask);
|
||||||
}
|
}
|
||||||
|
|
||||||
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||||
while(!regs.scmr.ran && !scheduler.synchronizing()) {
|
while(!regs.scmr.ran) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
return ram.read(addr & ramMask);
|
return ram.read(addr & ramMask);
|
||||||
}
|
}
|
||||||
|
@ -28,9 +28,9 @@ auto SuperFX::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
|
|
||||||
auto SuperFX::write(uint24 addr, uint8 data) -> void {
|
auto SuperFX::write(uint24 addr, uint8 data) -> void {
|
||||||
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||||
while(!regs.scmr.ran && !scheduler.synchronizing()) {
|
while(!regs.scmr.ran) {
|
||||||
step(6);
|
step(6);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
return ram.write(addr & ramMask, data);
|
return ram.write(addr & ramMask, data);
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct SuperFX : Processor::GSU, Cothread {
|
struct SuperFX : Processor::GSU, Thread {
|
||||||
MappedRAM rom;
|
MappedRAM rom;
|
||||||
MappedRAM ram;
|
MappedRAM ram;
|
||||||
|
|
||||||
|
|
|
@ -14,8 +14,8 @@ auto SuperFX::step(uint clocks) -> void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Cothread::step(clocks);
|
Thread::step(clocks);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperFX::syncROMBuffer() -> void {
|
auto SuperFX::syncROMBuffer() -> void {
|
||||||
|
|
|
@ -19,26 +19,6 @@ CPU::CPU() {
|
||||||
PPUcounter::scanline = {&CPU::scanline, this};
|
PPUcounter::scanline = {&CPU::scanline, this};
|
||||||
}
|
}
|
||||||
|
|
||||||
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::synchronizePeripherals() -> void {
|
|
||||||
for(auto peripheral : peripherals) {
|
|
||||||
if(peripheral->clock < 0) co_switch(peripheral->thread);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
auto CPU::Enter() -> void {
|
auto CPU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), cpu.main();
|
while(true) scheduler.synchronize(), cpu.main();
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,11 +5,6 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
||||||
|
|
||||||
CPU();
|
CPU();
|
||||||
|
|
||||||
auto synchronizeSMP() -> void;
|
|
||||||
auto synchronizePPU() -> void;
|
|
||||||
auto synchronizeCoprocessors() -> void;
|
|
||||||
auto synchronizePeripherals() -> void;
|
|
||||||
|
|
||||||
auto readPort(uint2 port) const -> uint8;
|
auto readPort(uint2 port) const -> uint8;
|
||||||
auto writePort(uint2 port, uint8 data) -> void;
|
auto writePort(uint2 port, uint8 data) -> void;
|
||||||
|
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
|
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
|
||||||
synchronizeSMP();
|
synchronize(smp);
|
||||||
return smp.readPort(addr.bits(0,1));
|
return smp.readPort(addr.bits(0,1));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -157,7 +157,7 @@ auto CPU::readDMA(uint24 addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
|
auto CPU::writeAPU(uint24 addr, uint8 data) -> void {
|
||||||
synchronizeSMP();
|
synchronize(smp);
|
||||||
return writePort(addr.bits(0,1), data);
|
return writePort(addr.bits(0,1), data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -10,15 +10,8 @@ auto CPU::step(uint clocks) -> void {
|
||||||
if(hcounter() & 2) pollInterrupts();
|
if(hcounter() & 2) pollInterrupts();
|
||||||
}
|
}
|
||||||
|
|
||||||
smp.clock -= clocks * (uint64)smp.frequency;
|
Thread::step(clocks);
|
||||||
ppu.clock -= clocks;
|
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||||
for(auto coprocessor : coprocessors) {
|
|
||||||
coprocessor->clock -= clocks * (uint64)coprocessor->frequency;
|
|
||||||
}
|
|
||||||
for(auto peripheral : peripherals) {
|
|
||||||
peripheral->clock -= clocks * (uint64)peripheral->frequency;
|
|
||||||
}
|
|
||||||
synchronizePeripherals();
|
|
||||||
|
|
||||||
status.autoJoypadClock += clocks;
|
status.autoJoypadClock += clocks;
|
||||||
if(status.autoJoypadClock >= 256) {
|
if(status.autoJoypadClock >= 256) {
|
||||||
|
@ -44,9 +37,9 @@ auto CPU::scanline() -> void {
|
||||||
status.lineClocks = lineclocks();
|
status.lineClocks = lineclocks();
|
||||||
|
|
||||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||||
synchronizeSMP();
|
synchronize(smp);
|
||||||
synchronizePPU();
|
synchronize(ppu);
|
||||||
synchronizeCoprocessors();
|
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
||||||
|
|
||||||
if(vcounter() == 0) {
|
if(vcounter() == 0) {
|
||||||
//HDMA init triggers once every frame
|
//HDMA init triggers once every frame
|
||||||
|
|
|
@ -31,11 +31,7 @@ DSP::DSP() {
|
||||||
/* timing */
|
/* timing */
|
||||||
|
|
||||||
auto DSP::step(uint clocks) -> void {
|
auto DSP::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
}
|
|
||||||
|
|
||||||
auto DSP::synchronizeSMP() -> void {
|
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(smp.thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto DSP::Enter() -> void {
|
auto DSP::Enter() -> void {
|
||||||
|
@ -197,7 +193,7 @@ auto DSP::main() -> void {
|
||||||
|
|
||||||
auto DSP::tick() -> void {
|
auto DSP::tick() -> void {
|
||||||
step(3 * 8);
|
step(3 * 8);
|
||||||
synchronizeSMP();
|
synchronize(smp);
|
||||||
}
|
}
|
||||||
|
|
||||||
/* register interface for S-SMP $00f2,$00f3 */
|
/* register interface for S-SMP $00f2,$00f3 */
|
||||||
|
|
|
@ -6,7 +6,6 @@ struct DSP : Thread {
|
||||||
DSP();
|
DSP();
|
||||||
|
|
||||||
alwaysinline auto step(uint clocks) -> void;
|
alwaysinline auto step(uint clocks) -> void;
|
||||||
alwaysinline auto synchronizeSMP() -> void;
|
|
||||||
|
|
||||||
auto mute() const -> bool;
|
auto mute() const -> bool;
|
||||||
auto read(uint8 addr) -> uint8;
|
auto read(uint8 addr) -> uint8;
|
||||||
|
|
|
@ -30,6 +30,7 @@ S21FX::S21FX() {
|
||||||
}
|
}
|
||||||
|
|
||||||
S21FX::~S21FX() {
|
S21FX::~S21FX() {
|
||||||
|
scheduler.remove(*this);
|
||||||
bus.unmap("00-3f,80-bf:2184-21ff");
|
bus.unmap("00-3f,80-bf:2184-21ff");
|
||||||
bus.unmap("00:fffc-fffd");
|
bus.unmap("00:fffc-fffd");
|
||||||
|
|
||||||
|
@ -54,6 +55,11 @@ auto S21FX::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
|
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
auto S21FX::step(uint clocks) -> void {
|
||||||
|
Thread::step(clocks);
|
||||||
|
synchronize(cpu);
|
||||||
|
}
|
||||||
|
|
||||||
auto S21FX::main() -> void {
|
auto S21FX::main() -> void {
|
||||||
if(linkInit) linkInit(
|
if(linkInit) linkInit(
|
||||||
{&S21FX::quit, this},
|
{&S21FX::quit, this},
|
||||||
|
|
|
@ -3,6 +3,7 @@ struct S21FX : Expansion {
|
||||||
~S21FX();
|
~S21FX();
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
||||||
auto read(uint24 addr, uint8 data) -> uint8;
|
auto read(uint24 addr, uint8 data) -> uint8;
|
||||||
|
|
|
@ -3,7 +3,7 @@
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
|
|
||||||
Expansion::Expansion() {
|
Expansion::Expansion() {
|
||||||
if(!thread) create(Expansion::Enter, 1);
|
if(!handle()) create(Expansion::Enter, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Expansion::Enter() -> void {
|
auto Expansion::Enter() -> void {
|
||||||
|
@ -12,6 +12,7 @@ auto Expansion::Enter() -> void {
|
||||||
|
|
||||||
auto Expansion::main() -> void {
|
auto Expansion::main() -> void {
|
||||||
step(1);
|
step(1);
|
||||||
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,4 @@
|
||||||
struct Expansion : Cothread {
|
struct Expansion : Thread {
|
||||||
Expansion();
|
Expansion();
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
virtual auto main() -> void;
|
virtual auto main() -> void;
|
||||||
|
|
|
@ -20,6 +20,7 @@ SuperDisc::SuperDisc() {
|
||||||
}
|
}
|
||||||
|
|
||||||
SuperDisc::~SuperDisc() {
|
SuperDisc::~SuperDisc() {
|
||||||
|
scheduler.remove(*this);
|
||||||
bus.unmap("00-3f,80-bf:21e0-21e5");
|
bus.unmap("00-3f,80-bf:21e0-21e5");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -41,7 +42,7 @@ auto SuperDisc::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
step(1);
|
step(1);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto SuperDisc::read(uint24 addr, uint8 data) -> uint8 {
|
auto SuperDisc::read(uint24 addr, uint8 data) -> uint8 {
|
||||||
|
|
|
@ -59,7 +59,7 @@ auto PPU::writeCGRAM(uint8 addr, uint15 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
|
auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
|
||||||
cpu.synchronizePPU();
|
cpu.synchronize(ppu);
|
||||||
|
|
||||||
switch((uint16)addr) {
|
switch((uint16)addr) {
|
||||||
|
|
||||||
|
@ -184,7 +184,7 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
cpu.synchronizePPU();
|
cpu.synchronize(ppu);
|
||||||
|
|
||||||
switch((uint16)addr) {
|
switch((uint16)addr) {
|
||||||
|
|
||||||
|
@ -629,7 +629,7 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::latchCounters() -> void {
|
auto PPU::latchCounters() -> void {
|
||||||
cpu.synchronizePPU();
|
cpu.synchronize(ppu);
|
||||||
io.hcounter = hdot();
|
io.hcounter = hdot();
|
||||||
io.vcounter = vcounter();
|
io.vcounter = vcounter();
|
||||||
latch.counters = 1;
|
latch.counters = 1;
|
||||||
|
|
|
@ -32,15 +32,11 @@ auto PPU::step(uint clocks) -> void {
|
||||||
clocks >>= 1;
|
clocks >>= 1;
|
||||||
while(clocks--) {
|
while(clocks--) {
|
||||||
tick(2);
|
tick(2);
|
||||||
clock += 2;
|
Thread::step(2);
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::synchronizeCPU() -> void {
|
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto PPU::Enter() -> void {
|
auto PPU::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), ppu.main();
|
while(true) scheduler.synchronize(), ppu.main();
|
||||||
}
|
}
|
||||||
|
|
|
@ -7,7 +7,6 @@ struct PPU : Thread, PPUcounter {
|
||||||
~PPU();
|
~PPU();
|
||||||
|
|
||||||
alwaysinline auto step(uint clocks) -> void;
|
alwaysinline auto step(uint clocks) -> void;
|
||||||
alwaysinline auto synchronizeCPU() -> void;
|
|
||||||
|
|
||||||
static auto Enter() -> void;
|
static auto Enter() -> void;
|
||||||
auto main() -> void;
|
auto main() -> void;
|
||||||
|
|
|
@ -21,16 +21,14 @@
|
||||||
|
|
||||||
namespace SuperFamicom {
|
namespace SuperFamicom {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
using Cheat = Emulator::Cheat;
|
using Cheat = Emulator::Cheat;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
extern Cheat cheat;
|
extern Cheat cheat;
|
||||||
|
|
||||||
//dynamic thread bound to CPU (coprocessors and peripherals)
|
struct Thread : Emulator::Thread {
|
||||||
struct Cothread : Thread {
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||||
auto step(uint clocks) -> void;
|
auto synchronize(Thread& thread) -> void;
|
||||||
auto synchronizeCPU() -> void;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
#include <sfc/memory/memory.hpp>
|
#include <sfc/memory/memory.hpp>
|
||||||
|
@ -51,13 +49,13 @@ namespace SuperFamicom {
|
||||||
#include <sfc/memory/memory-inline.hpp>
|
#include <sfc/memory/memory-inline.hpp>
|
||||||
#include <sfc/ppu/counter/counter-inline.hpp>
|
#include <sfc/ppu/counter/counter-inline.hpp>
|
||||||
|
|
||||||
inline auto Cothread::step(uint clocks) -> void {
|
inline auto Thread::create(auto (*entrypoint)(), double frequency) -> void {
|
||||||
clock += clocks * (uint64)cpu.frequency;
|
Emulator::Thread::create(entrypoint, frequency);
|
||||||
synchronizeCPU();
|
scheduler.append(*this);
|
||||||
}
|
}
|
||||||
|
|
||||||
inline auto Cothread::synchronizeCPU() -> void {
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,7 @@ auto SMP::readBus(uint16 addr) -> uint8 {
|
||||||
case 0xf5: //CPUIO1
|
case 0xf5: //CPUIO1
|
||||||
case 0xf6: //CPUIO2
|
case 0xf6: //CPUIO2
|
||||||
case 0xf7: //CPUIO3
|
case 0xf7: //CPUIO3
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
return cpu.readPort(addr);
|
return cpu.readPort(addr);
|
||||||
|
|
||||||
case 0xf8: //RAM0
|
case 0xf8: //RAM0
|
||||||
|
@ -96,7 +96,7 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
|
||||||
if(data & 0x30) {
|
if(data & 0x30) {
|
||||||
//one-time clearing of APU port read registers,
|
//one-time clearing of APU port read registers,
|
||||||
//emulated by simulating CPU writes of 0x00
|
//emulated by simulating CPU writes of 0x00
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
if(data & 0x20) {
|
if(data & 0x20) {
|
||||||
cpu.writePort(2, 0x00);
|
cpu.writePort(2, 0x00);
|
||||||
cpu.writePort(3, 0x00);
|
cpu.writePort(3, 0x00);
|
||||||
|
@ -140,7 +140,7 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
|
||||||
case 0xf5: //CPUIO1
|
case 0xf5: //CPUIO1
|
||||||
case 0xf6: //CPUIO2
|
case 0xf6: //CPUIO2
|
||||||
case 0xf7: //CPUIO3
|
case 0xf7: //CPUIO3
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
writePort(addr, data);
|
writePort(addr, data);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
|
|
@ -8,14 +8,6 @@ SMP smp;
|
||||||
#include "timing.cpp"
|
#include "timing.cpp"
|
||||||
#include "serialization.cpp"
|
#include "serialization.cpp"
|
||||||
|
|
||||||
auto SMP::synchronizeCPU() -> void {
|
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SMP::synchronizeDSP() -> void {
|
|
||||||
if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
|
|
||||||
}
|
|
||||||
|
|
||||||
auto SMP::Enter() -> void {
|
auto SMP::Enter() -> void {
|
||||||
while(true) scheduler.synchronize(), smp.main();
|
while(true) scheduler.synchronize(), smp.main();
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,9 +1,6 @@
|
||||||
//Sony CXP1100Q-1
|
//Sony CXP1100Q-1
|
||||||
|
|
||||||
struct SMP : Processor::SPC700, Thread {
|
struct SMP : Processor::SPC700, Thread {
|
||||||
alwaysinline auto synchronizeCPU() -> void;
|
|
||||||
alwaysinline auto synchronizeDSP() -> void;
|
|
||||||
|
|
||||||
auto readPort(uint2 port) const -> uint8;
|
auto readPort(uint2 port) const -> uint8;
|
||||||
auto writePort(uint2 port, uint8 data) -> void;
|
auto writePort(uint2 port, uint8 data) -> void;
|
||||||
|
|
||||||
|
|
|
@ -1,14 +1,13 @@
|
||||||
auto SMP::step(uint clocks) -> void {
|
auto SMP::step(uint clocks) -> void {
|
||||||
clock += clocks * (uint64)cpu.frequency;
|
Thread::step(clocks);
|
||||||
dsp.clock -= clocks;
|
synchronize(dsp);
|
||||||
synchronizeDSP();
|
|
||||||
|
|
||||||
#if defined(DEBUGGER)
|
#if defined(DEBUGGER)
|
||||||
synchronizeCPU();
|
synchronize(cpu);
|
||||||
#else
|
#else
|
||||||
//forcefully sync S-SMP to S-CPU in case chips are not communicating
|
//forcefully sync S-SMP to S-CPU in case chips are not communicating
|
||||||
//sync if S-SMP is more than 24 samples ahead of S-CPU
|
//sync if S-SMP is more than 1ms ahead of S-CPU
|
||||||
if(clock > +(768 * 24 * (int64)24000000)) synchronizeCPU();
|
if(clock() - cpu.clock() > (Emulator::Constants::Time::Attosecond / Emulator::Constants::Time::Millisecond)) synchronize(cpu);
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -15,16 +15,12 @@ auto System::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu);
|
||||||
scheduler.synchronize(smp.thread);
|
scheduler.synchronize(smp);
|
||||||
scheduler.synchronize(ppu.thread);
|
scheduler.synchronize(ppu);
|
||||||
scheduler.synchronize(dsp.thread);
|
scheduler.synchronize(dsp);
|
||||||
for(auto coprocessor : cpu.coprocessors) {
|
for(auto coprocessor : cpu.coprocessors) scheduler.synchronize(*coprocessor);
|
||||||
scheduler.synchronize(coprocessor->thread);
|
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
|
||||||
}
|
|
||||||
for(auto peripheral : cpu.peripherals) {
|
|
||||||
scheduler.synchronize(peripheral->thread);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::init() -> void {
|
auto System::init() -> void {
|
||||||
|
@ -170,6 +166,7 @@ auto System::reset() -> void {
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset();
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
smp.reset();
|
smp.reset();
|
||||||
dsp.reset();
|
dsp.reset();
|
||||||
|
@ -205,7 +202,7 @@ auto System::reset() -> void {
|
||||||
if(cartridge.has.SPC7110) cpu.coprocessors.append(&spc7110);
|
if(cartridge.has.SPC7110) cpu.coprocessors.append(&spc7110);
|
||||||
if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1);
|
if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1);
|
||||||
|
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
peripherals.reset();
|
peripherals.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,26 @@
|
||||||
|
AboutWindow::AboutWindow() {
|
||||||
|
aboutWindow = this;
|
||||||
|
|
||||||
|
setTitle("About higan ...");
|
||||||
|
setBackgroundColor({255, 255, 240});
|
||||||
|
layout.setMargin(10);
|
||||||
|
auto logo = image{Resource::Logo::higan};
|
||||||
|
logo.alphaBlend(0xfffff0);
|
||||||
|
canvas.setIcon(logo);
|
||||||
|
informationLeft.setFont(Font().setBold()).setAlignment(1.0).setText({
|
||||||
|
"Version:\n",
|
||||||
|
"Author:\n",
|
||||||
|
"License:\n",
|
||||||
|
"Website:"
|
||||||
|
});
|
||||||
|
informationRight.setFont(Font().setBold()).setAlignment(0.0).setText({
|
||||||
|
string{Emulator::Version}.replace("v", ""), "\n",
|
||||||
|
Emulator::Author, "\n",
|
||||||
|
Emulator::License, "\n",
|
||||||
|
Emulator::Website
|
||||||
|
});
|
||||||
|
|
||||||
|
setResizable(false);
|
||||||
|
setSize(layout.minimumSize());
|
||||||
|
setCentered();
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
#include "../tomoko.hpp"
|
#include "../tomoko.hpp"
|
||||||
|
#include "about.cpp"
|
||||||
|
unique_pointer<AboutWindow> aboutWindow;
|
||||||
unique_pointer<Presentation> presentation;
|
unique_pointer<Presentation> presentation;
|
||||||
|
|
||||||
Presentation::Presentation() {
|
Presentation::Presentation() {
|
||||||
|
@ -122,12 +124,7 @@ Presentation::Presentation() {
|
||||||
invoke("http://doc.byuu.org/higan/");
|
invoke("http://doc.byuu.org/higan/");
|
||||||
});
|
});
|
||||||
about.setText("About ...").onActivate([&] {
|
about.setText("About ...").onActivate([&] {
|
||||||
MessageDialog().setParent(*this).setTitle("About higan ...").setText({
|
aboutWindow->setVisible().setFocused();
|
||||||
Emulator::Name, " v", Emulator::Version, "\n\n",
|
|
||||||
"Author: ", Emulator::Author, "\n",
|
|
||||||
"License: ", Emulator::License, "\n",
|
|
||||||
"Website: ", Emulator::Website
|
|
||||||
}).information();
|
|
||||||
});
|
});
|
||||||
|
|
||||||
statusBar.setFont(Font().setBold());
|
statusBar.setFont(Font().setBold());
|
||||||
|
|
|
@ -1,3 +1,13 @@
|
||||||
|
struct AboutWindow : Window {
|
||||||
|
AboutWindow();
|
||||||
|
|
||||||
|
VerticalLayout layout{this};
|
||||||
|
Canvas canvas{&layout, Size{399, 95}, 15};
|
||||||
|
HorizontalLayout informationLayout{&layout, Size{~0, 0}};
|
||||||
|
Label informationLeft{&informationLayout, Size{~0, 0}, 3};
|
||||||
|
Label informationRight{&informationLayout, Size{~0, 0}};
|
||||||
|
};
|
||||||
|
|
||||||
struct Presentation : Window {
|
struct Presentation : Window {
|
||||||
Presentation();
|
Presentation();
|
||||||
auto updateEmulator() -> void;
|
auto updateEmulator() -> void;
|
||||||
|
@ -70,4 +80,5 @@ struct Presentation : Window {
|
||||||
StatusBar statusBar{this};
|
StatusBar statusBar{this};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
extern unique_pointer<AboutWindow> aboutWindow;
|
||||||
extern unique_pointer<Presentation> presentation;
|
extern unique_pointer<Presentation> presentation;
|
||||||
|
|
|
@ -49,6 +49,7 @@ Program::Program(string_vector args) {
|
||||||
new SettingsManager;
|
new SettingsManager;
|
||||||
new CheatDatabase;
|
new CheatDatabase;
|
||||||
new ToolsManager;
|
new ToolsManager;
|
||||||
|
new AboutWindow;
|
||||||
|
|
||||||
presentation->setFocused();
|
presentation->setFocused();
|
||||||
|
|
||||||
|
|
|
@ -60,8 +60,8 @@ auto APU::dacRun() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::step(uint clocks) -> void {
|
auto APU::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto APU::power() -> void {
|
auto APU::power() -> void {
|
||||||
|
|
|
@ -21,8 +21,8 @@ auto Cartridge::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::step(uint clocks) -> void {
|
auto Cartridge::step(uint clocks) -> void {
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto Cartridge::power() -> void {
|
auto Cartridge::power() -> void {
|
||||||
|
|
|
@ -18,14 +18,10 @@ auto CPU::main() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::step(uint clocks) -> void {
|
auto CPU::step(uint clocks) -> void {
|
||||||
ppu.clock -= clocks;
|
Thread::step(clocks);
|
||||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
synchronize(ppu);
|
||||||
|
synchronize(apu);
|
||||||
apu.clock -= clocks;
|
synchronize(cartridge);
|
||||||
if(apu.clock < 0) co_switch(apu.thread);
|
|
||||||
|
|
||||||
cartridge.clock -= clocks;
|
|
||||||
if(cartridge.clock < 0) co_switch(cartridge.thread);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
auto CPU::wait(uint clocks) -> void {
|
auto CPU::wait(uint clocks) -> void {
|
||||||
|
|
|
@ -89,8 +89,8 @@ auto PPU::refresh() -> void {
|
||||||
auto PPU::step(uint clocks) -> void {
|
auto PPU::step(uint clocks) -> void {
|
||||||
s.hclk += clocks;
|
s.hclk += clocks;
|
||||||
|
|
||||||
clock += clocks;
|
Thread::step(clocks);
|
||||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
synchronize(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto PPU::power() -> void {
|
auto PPU::power() -> void {
|
||||||
|
|
|
@ -69,6 +69,7 @@ auto System::power() -> void {
|
||||||
Emulator::audio.reset();
|
Emulator::audio.reset();
|
||||||
Emulator::audio.setInterface(interface);
|
Emulator::audio.setInterface(interface);
|
||||||
|
|
||||||
|
scheduler.reset();
|
||||||
bus.power();
|
bus.power();
|
||||||
iram.power();
|
iram.power();
|
||||||
eeprom.power();
|
eeprom.power();
|
||||||
|
@ -76,7 +77,7 @@ auto System::power() -> void {
|
||||||
ppu.power();
|
ppu.power();
|
||||||
apu.power();
|
apu.power();
|
||||||
cartridge.power();
|
cartridge.power();
|
||||||
scheduler.reset(cpu.thread);
|
scheduler.primary(cpu);
|
||||||
|
|
||||||
bus.map(this, 0x0060);
|
bus.map(this, 0x0060);
|
||||||
bus.map(this, 0x00ba, 0x00be);
|
bus.map(this, 0x00ba, 0x00be);
|
||||||
|
@ -93,10 +94,10 @@ auto System::run() -> void {
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::runToSave() -> void {
|
auto System::runToSave() -> void {
|
||||||
scheduler.synchronize(cpu.thread);
|
scheduler.synchronize(cpu);
|
||||||
scheduler.synchronize(ppu.thread);
|
scheduler.synchronize(ppu);
|
||||||
scheduler.synchronize(apu.thread);
|
scheduler.synchronize(apu);
|
||||||
scheduler.synchronize(cartridge.thread);
|
scheduler.synchronize(cartridge);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto System::pollKeypad() -> void {
|
auto System::pollKeypad() -> void {
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
|
|
||||||
namespace WonderSwan {
|
namespace WonderSwan {
|
||||||
using File = Emulator::File;
|
using File = Emulator::File;
|
||||||
using Thread = Emulator::Thread;
|
|
||||||
using Scheduler = Emulator::Scheduler;
|
using Scheduler = Emulator::Scheduler;
|
||||||
using Cheat = Emulator::Cheat;
|
using Cheat = Emulator::Cheat;
|
||||||
extern Scheduler scheduler;
|
extern Scheduler scheduler;
|
||||||
|
@ -26,6 +25,12 @@ namespace WonderSwan {
|
||||||
|
|
||||||
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
enum : uint { Byte = 1, Word = 2, Long = 4 };
|
||||||
|
|
||||||
|
struct Thread : Emulator::Thread {
|
||||||
|
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||||
|
auto synchronize(Thread& thread) -> void;
|
||||||
|
auto step(uint clocks) -> void;
|
||||||
|
};
|
||||||
|
|
||||||
#include <ws/memory/memory.hpp>
|
#include <ws/memory/memory.hpp>
|
||||||
#include <ws/eeprom/eeprom.hpp>
|
#include <ws/eeprom/eeprom.hpp>
|
||||||
#include <ws/system/system.hpp>
|
#include <ws/system/system.hpp>
|
||||||
|
@ -33,6 +38,19 @@ namespace WonderSwan {
|
||||||
#include <ws/cpu/cpu.hpp>
|
#include <ws/cpu/cpu.hpp>
|
||||||
#include <ws/ppu/ppu.hpp>
|
#include <ws/ppu/ppu.hpp>
|
||||||
#include <ws/apu/apu.hpp>
|
#include <ws/apu/apu.hpp>
|
||||||
|
|
||||||
|
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||||
|
Emulator::Thread::create(entrypoint, frequency);
|
||||||
|
scheduler.append(*this);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||||
|
if(_clock > thread._clock) scheduler.resume(thread);
|
||||||
|
}
|
||||||
|
|
||||||
|
inline auto Thread::step(uint clocks) -> void {
|
||||||
|
_clock += clocks;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#include <ws/interface/interface.hpp>
|
#include <ws/interface/interface.hpp>
|
||||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue