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:
Tim Allen 2016-07-30 13:56:12 +10:00
parent 306cac2b54
commit ca277cd5e8
104 changed files with 3032 additions and 325 deletions

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
struct HitachiDSP : Processor::HG51B, Cothread { struct HitachiDSP : Processor::HG51B, Thread {
MappedRAM rom; MappedRAM rom;
MappedRAM ram; MappedRAM ram;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
struct SharpRTC : Cothread { struct SharpRTC : Thread {
static auto Enter() -> void; static auto Enter() -> void;
auto main() -> void; auto main() -> void;

View File

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

View File

@ -1,6 +1,6 @@
struct Decompressor; struct Decompressor;
struct SPC7110 : Cothread { struct SPC7110 : Thread {
SPC7110(); SPC7110();
~SPC7110(); ~SPC7110();

View File

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

View File

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

View File

@ -1,4 +1,4 @@
struct SuperFX : Processor::GSU, Cothread { struct SuperFX : Processor::GSU, Thread {
MappedRAM rom; MappedRAM rom;
MappedRAM ram; MappedRAM ram;

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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