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 {
|
||||
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 License = "GPLv3";
|
||||
static const string Website = "http://byuu.org/";
|
||||
|
@ -20,6 +20,16 @@ namespace Emulator {
|
|||
static const string SerializerVersion = "100";
|
||||
|
||||
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 {
|
||||
static constexpr double NTSC = 315.0 / 88.0 * 1'000'000.0;
|
||||
static constexpr double PAL = 283.75 * 15'625.0 + 25.0;
|
||||
|
|
|
@ -2,6 +2,8 @@
|
|||
|
||||
namespace Emulator {
|
||||
|
||||
struct Thread;
|
||||
|
||||
struct Scheduler {
|
||||
enum class Mode : uint {
|
||||
Run,
|
||||
|
@ -15,29 +17,59 @@ struct Scheduler {
|
|||
Synchronize,
|
||||
};
|
||||
|
||||
auto reset(cothread_t master_) -> void {
|
||||
master = resume = master_;
|
||||
auto active(Thread& thread) const -> bool {
|
||||
return co_active() == thread.handle();
|
||||
}
|
||||
|
||||
auto reset() -> void {
|
||||
threads.reset();
|
||||
}
|
||||
|
||||
auto primary(Thread& thread) -> void {
|
||||
master = _resume = thread.handle();
|
||||
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 {
|
||||
mode = mode_;
|
||||
host = co_active();
|
||||
co_switch(resume);
|
||||
co_switch(_resume);
|
||||
return event;
|
||||
}
|
||||
|
||||
inline auto resume(Thread& thread) -> void {
|
||||
if(mode != Mode::SynchronizeSlave) co_switch(thread.handle());
|
||||
}
|
||||
|
||||
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_;
|
||||
resume = co_active();
|
||||
_resume = co_active();
|
||||
co_switch(host);
|
||||
}
|
||||
|
||||
auto synchronize(cothread_t thread) -> void {
|
||||
if(thread == master) {
|
||||
auto synchronize(Thread& thread) -> void {
|
||||
if(thread.handle() == master) {
|
||||
while(enter(Mode::SynchronizeMaster) != Event::Synchronize);
|
||||
} else {
|
||||
resume = thread;
|
||||
_resume = thread.handle();
|
||||
while(enter(Mode::SynchronizeSlave) != Event::Synchronize);
|
||||
}
|
||||
}
|
||||
|
@ -50,16 +82,13 @@ struct Scheduler {
|
|||
}
|
||||
}
|
||||
|
||||
auto synchronizing() const -> bool {
|
||||
return mode == Mode::SynchronizeSlave;
|
||||
}
|
||||
|
||||
private:
|
||||
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)
|
||||
Mode mode = Mode::Run;
|
||||
Event event = Event::Step;
|
||||
vector<Thread*> threads;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -4,24 +4,47 @@ namespace Emulator {
|
|||
|
||||
struct Thread {
|
||||
virtual ~Thread() {
|
||||
if(thread) co_delete(thread);
|
||||
if(_handle) co_delete(_handle);
|
||||
}
|
||||
|
||||
auto create(auto (*entrypoint)() -> void, double frequency_) -> void {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(64 * 1024 * sizeof(void*), entrypoint);
|
||||
frequency = frequency_ + 0.5; //round to nearest whole number
|
||||
clock = 0;
|
||||
auto handle() const { return _handle; }
|
||||
auto frequency() const { return _frequency; }
|
||||
auto scalar() const { return _scalar; }
|
||||
auto clock() const { return _clock; }
|
||||
|
||||
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 {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
s.integer(_frequency);
|
||||
s.integer(_scalar);
|
||||
s.integer(_clock);
|
||||
}
|
||||
|
||||
cothread_t thread = nullptr;
|
||||
uint frequency = 0;
|
||||
int64 clock = 0;
|
||||
protected:
|
||||
cothread_t _handle = nullptr;
|
||||
uint64 _frequency = 0;
|
||||
uint64 _scalar = 0;
|
||||
uint64 _clock = 0;
|
||||
|
||||
friend class Scheduler;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
@ -63,8 +63,8 @@ auto APU::main() -> void {
|
|||
}
|
||||
|
||||
auto APU::tick() -> void {
|
||||
clock += 12;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(12);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::setIRQ() -> void {
|
||||
|
|
|
@ -109,13 +109,13 @@ auto Board::mirror(uint addr, uint size) -> uint {
|
|||
}
|
||||
|
||||
auto Board::main() -> void {
|
||||
cartridge.clock += 12 * 4095;
|
||||
cartridge.step(12 * 4095);
|
||||
tick();
|
||||
}
|
||||
|
||||
auto Board::tick() -> void {
|
||||
cartridge.clock += 12;
|
||||
if(cartridge.clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
cartridge.step(12);
|
||||
cartridge.synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Board::readCHR(uint addr) -> uint8 {
|
||||
|
|
|
@ -5,22 +5,24 @@ namespace Famicom {
|
|||
#include "gamepad/gamepad.cpp"
|
||||
|
||||
Controller::Controller(bool port) : port(port) {
|
||||
if(!thread) create(Controller::Enter, 1);
|
||||
if(!handle()) create(Controller::Enter, 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
||||
if(scheduler.active(*peripherals.controllerPort1)) peripherals.controllerPort1->main();
|
||||
if(scheduler.active(*peripherals.controllerPort2)) peripherals.controllerPort2->main();
|
||||
}
|
||||
}
|
||||
|
||||
auto Controller::main() -> void {
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -16,7 +16,7 @@
|
|||
// 6: data4 $4016.d4 read $4017.d4 read
|
||||
// 7: gnd
|
||||
|
||||
struct Controller : Cothread {
|
||||
struct Controller : Thread {
|
||||
enum : bool { Port1 = 0, Port2 = 1 };
|
||||
|
||||
Controller(bool port);
|
||||
|
|
|
@ -17,19 +17,11 @@ auto CPU::main() -> void {
|
|||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
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);
|
||||
}
|
||||
Thread::step(clocks);
|
||||
synchronize(apu);
|
||||
synchronize(ppu);
|
||||
synchronize(cartridge);
|
||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||
}
|
||||
|
||||
auto CPU::power() -> void {
|
||||
|
|
|
@ -12,15 +12,14 @@
|
|||
|
||||
namespace Famicom {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
struct Cothread : Thread {
|
||||
auto step(uint clocks) -> void;
|
||||
auto synchronizeCPU() -> void;
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||
auto synchronize(Thread& thread) -> void;
|
||||
};
|
||||
|
||||
#include <fc/controller/controller.hpp>
|
||||
|
@ -31,13 +30,13 @@ namespace Famicom {
|
|||
#include <fc/apu/apu.hpp>
|
||||
#include <fc/ppu/ppu.hpp>
|
||||
|
||||
inline auto Cothread::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
synchronizeCPU();
|
||||
inline auto Thread::create(auto (*entrypoint)() -> void, double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
inline auto Cothread::synchronizeCPU() -> void {
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||
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 == 2) cpu.nmiLine(io.nmiEnable && io.nmiFlag);
|
||||
|
||||
clock += 4;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(4);
|
||||
synchronize(cpu);
|
||||
|
||||
io.lx++;
|
||||
}
|
||||
|
|
|
@ -14,13 +14,11 @@ auto System::run() -> void {
|
|||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(cartridge.thread);
|
||||
for(auto peripheral : cpu.peripherals) {
|
||||
scheduler.synchronize(peripheral->thread);
|
||||
}
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(apu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(cartridge);
|
||||
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
|
@ -65,11 +63,12 @@ auto System::reset() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
ppu.reset();
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
peripherals.reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -51,8 +51,8 @@ auto APU::main() -> void {
|
|||
}
|
||||
cycle++;
|
||||
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
//filter to remove DC bias
|
||||
|
|
|
@ -77,8 +77,8 @@ auto CPU::stop() -> bool {
|
|||
if(status.speedSwitch) {
|
||||
status.speedSwitch = 0;
|
||||
status.speedDouble ^= 1;
|
||||
if(status.speedDouble == 0) frequency = 4 * 1024 * 1024;
|
||||
if(status.speedDouble == 1) frequency = 8 * 1024 * 1024;
|
||||
if(status.speedDouble == 0) setFrequency(4 * 1024 * 1024);
|
||||
if(status.speedDouble == 1) setFrequency(8 * 1024 * 1024);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
|
|
@ -16,11 +16,9 @@ auto CPU::step(uint clocks) -> void {
|
|||
if((status.div & 511) == 0) timer8192hz();
|
||||
if((status.div & 1023) == 0) timer4096hz();
|
||||
|
||||
ppu.clock -= ppu.frequency;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= apu.frequency;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(ppu);
|
||||
synchronize(apu);
|
||||
}
|
||||
|
||||
if(system.sgb()) {
|
||||
|
|
|
@ -12,18 +12,31 @@
|
|||
|
||||
namespace GameBoy {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
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/system/system.hpp>
|
||||
#include <gb/cartridge/cartridge.hpp>
|
||||
#include <gb/cpu/cpu.hpp>
|
||||
#include <gb/ppu/ppu.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>
|
||||
|
|
|
@ -118,9 +118,7 @@ auto PPU::writeIO(uint16 addr, uint8 data) -> void {
|
|||
status.lx = 0;
|
||||
|
||||
//restart cothread to begin new frame
|
||||
auto clock = this->clock;
|
||||
create(Enter, 4 * 1024 * 1024);
|
||||
this->clock = clock;
|
||||
create(Enter, 4 * 1024 * 1024, false);
|
||||
}
|
||||
|
||||
status.displayEnable = data & 0x80;
|
||||
|
|
|
@ -94,8 +94,8 @@ auto PPU::step(uint clocks) -> void {
|
|||
}
|
||||
|
||||
status.lx++;
|
||||
clock += cpu.frequency;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -13,9 +13,9 @@ auto System::run() -> void {
|
|||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
|
@ -68,12 +68,13 @@ auto System::power() -> void {
|
|||
Emulator::audio.setInterface(interface);
|
||||
}
|
||||
|
||||
scheduler.reset();
|
||||
bus.power();
|
||||
cartridge.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
|
||||
_clocksExecuted = 0;
|
||||
}
|
||||
|
|
|
@ -68,8 +68,8 @@ auto APU::main() -> void {
|
|||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
|
|
|
@ -83,11 +83,9 @@ auto CPU::step(uint clocks) -> void {
|
|||
}
|
||||
|
||||
auto CPU::syncStep(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(ppu);
|
||||
synchronize(apu);
|
||||
}
|
||||
|
||||
auto CPU::keypadRun() -> void {
|
||||
|
|
|
@ -11,7 +11,6 @@
|
|||
|
||||
namespace GameBoyAdvance {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
extern Scheduler scheduler;
|
||||
|
||||
|
@ -27,6 +26,12 @@ namespace GameBoyAdvance {
|
|||
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/system/system.hpp>
|
||||
#include <gba/cartridge/cartridge.hpp>
|
||||
|
@ -34,6 +39,19 @@ namespace GameBoyAdvance {
|
|||
#include <gba/cpu/cpu.hpp>
|
||||
#include <gba/ppu/ppu.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>
|
||||
|
|
|
@ -43,8 +43,8 @@ auto PPU::main() -> void {
|
|||
}
|
||||
|
||||
auto PPU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
|
|
|
@ -24,13 +24,14 @@ auto System::power() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
bus.power();
|
||||
player.power();
|
||||
cpu.power();
|
||||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
}
|
||||
|
||||
auto System::load() -> bool {
|
||||
|
@ -66,9 +67,9 @@ auto System::run() -> void {
|
|||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -9,7 +9,7 @@ auto APU::Enter() -> void {
|
|||
}
|
||||
|
||||
auto APU::main() -> void {
|
||||
step(frequency);
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
|
|
|
@ -19,9 +19,10 @@ auto CPU::main() -> void {
|
|||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= frequency / 60) {
|
||||
clock -= frequency / 60;
|
||||
Thread::step(clocks);
|
||||
cycles += clocks;
|
||||
if(cycles >= frequency() / 60) {
|
||||
cycles = 0;
|
||||
scheduler.exit(Scheduler::Event::Frame);
|
||||
}
|
||||
}
|
||||
|
@ -35,6 +36,7 @@ auto CPU::power() -> void {
|
|||
auto CPU::reset() -> void {
|
||||
M68K::reset();
|
||||
create(CPU::Enter, system.colorburst() * 15.0 / 7.0);
|
||||
cycles = 0;
|
||||
}
|
||||
|
||||
auto CPU::read(bool word, uint24 addr) -> uint16 {
|
||||
|
|
|
@ -14,6 +14,8 @@ struct CPU : Processor::M68K, Thread {
|
|||
|
||||
private:
|
||||
uint8 ram[64 * 1024];
|
||||
|
||||
uint cycles = 0;
|
||||
};
|
||||
|
||||
extern CPU cpu;
|
||||
|
|
|
@ -12,10 +12,14 @@
|
|||
|
||||
namespace MegaDrive {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::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/apu/apu.hpp>
|
||||
#include <md/vdp/vdp.hpp>
|
||||
|
@ -24,6 +28,15 @@ namespace MegaDrive {
|
|||
|
||||
#include <md/system/system.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>
|
||||
|
|
|
@ -9,7 +9,7 @@ auto PSG::Enter() -> void {
|
|||
}
|
||||
|
||||
auto PSG::main() -> void {
|
||||
step(frequency);
|
||||
step(system.colorburst());
|
||||
}
|
||||
|
||||
auto PSG::step(uint clocks) -> void {
|
||||
|
|
|
@ -49,13 +49,14 @@ auto System::reset() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
cartridge.reset();
|
||||
cpu.reset();
|
||||
apu.reset();
|
||||
vdp.reset();
|
||||
psg.reset();
|
||||
ym2612.reset();
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -12,7 +12,7 @@ auto VDP::Enter() -> void {
|
|||
}
|
||||
|
||||
auto VDP::main() -> void {
|
||||
step(frequency);
|
||||
step(system.colorburst() * 15.0 / 10.0);
|
||||
}
|
||||
|
||||
auto VDP::step(uint clocks) -> void {
|
||||
|
|
|
@ -9,7 +9,7 @@ auto YM2612::Enter() -> void {
|
|||
}
|
||||
|
||||
auto YM2612::main() -> void {
|
||||
step(frequency);
|
||||
step(system.colorburst() * 15.0 / 7.0);
|
||||
}
|
||||
|
||||
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=Logo
|
||||
binary name=higan file=logo/higan.png
|
||||
namespace name=Sprite
|
||||
binary name=CrosshairRed file=sprite/crosshair-red.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 Logo {
|
||||
extern const nall::vector<uint8_t> higan;
|
||||
}
|
||||
namespace Sprite {
|
||||
extern const nall::vector<uint8_t> CrosshairRed;
|
||||
extern const nall::vector<uint8_t> CrosshairGreen;
|
||||
|
|
|
@ -199,7 +199,7 @@ auto Cartridge::loadHitachiDSP(Markup::Node node, uint roms) -> void {
|
|||
has.HitachiDSP = true;
|
||||
|
||||
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
|
||||
|
||||
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 {
|
||||
has.NECDSP = true;
|
||||
|
||||
necdsp.frequency = node["frequency"].natural();
|
||||
if(necdsp.frequency == 0) necdsp.frequency = 8000000;
|
||||
necdsp.Frequency = node["frequency"].natural();
|
||||
if(necdsp.Frequency == 0) necdsp.Frequency = 8000000;
|
||||
necdsp.revision
|
||||
= node["model"].text() == "uPD7725" ? NECDSP::Revision::uPD7725
|
||||
: node["model"].text() == "uPD96050" ? NECDSP::Revision::uPD96050
|
||||
|
|
|
@ -9,22 +9,24 @@ namespace SuperFamicom {
|
|||
#include "justifier/justifier.cpp"
|
||||
|
||||
Controller::Controller(bool port) : port(port) {
|
||||
if(!thread) create(Controller::Enter, 1);
|
||||
if(!handle()) create(Controller::Enter, 1);
|
||||
}
|
||||
|
||||
Controller::~Controller() {
|
||||
scheduler.remove(*this);
|
||||
}
|
||||
|
||||
auto Controller::Enter() -> void {
|
||||
while(true) {
|
||||
scheduler.synchronize();
|
||||
if(co_active() == peripherals.controllerPort1->thread) peripherals.controllerPort1->main();
|
||||
if(co_active() == peripherals.controllerPort2->thread) peripherals.controllerPort2->main();
|
||||
if(scheduler.active(*peripherals.controllerPort1)) peripherals.controllerPort1->main();
|
||||
if(scheduler.active(*peripherals.controllerPort2)) peripherals.controllerPort2->main();
|
||||
}
|
||||
}
|
||||
|
||||
auto Controller::main() -> void {
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Controller::iobit() -> bool {
|
||||
|
|
|
@ -11,7 +11,7 @@
|
|||
// 6: iobit $4201.d6 write; $4213.d6 read $4201.d7 write; $4213.d7 read
|
||||
// 7: gnd
|
||||
|
||||
struct Controller : Cothread {
|
||||
struct Controller : Thread {
|
||||
enum : bool { Port1 = 0, Port2 = 1 };
|
||||
|
||||
Controller(bool port);
|
||||
|
|
|
@ -76,6 +76,7 @@ auto Justifier::main() -> void {
|
|||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Justifier::data() -> uint2 {
|
||||
|
|
|
@ -66,6 +66,7 @@ auto SuperScope::main() -> void {
|
|||
|
||||
prev = next;
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto SuperScope::data() -> uint2 {
|
||||
|
|
|
@ -42,7 +42,7 @@ auto ArmDSP::main() -> void {
|
|||
print(disassembleRegisters(), "\n");
|
||||
print(disassembleInstructionARM(pipeline.execute.address), "\n");
|
||||
print("Executed: ", instructions, "\n");
|
||||
while(true) step(frequency);
|
||||
while(true) step(21'477'272);
|
||||
}
|
||||
|
||||
stepARM();
|
||||
|
@ -50,8 +50,8 @@ auto ArmDSP::main() -> void {
|
|||
|
||||
auto ArmDSP::step(uint clocks) -> void {
|
||||
if(bridge.timer && --bridge.timer == 0);
|
||||
Cothread::step(clocks);
|
||||
synchronizeCPU();
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
//MMIO: 00-3f,80-bf:3800-38ff
|
||||
|
@ -59,7 +59,7 @@ auto ArmDSP::step(uint clocks) -> void {
|
|||
//a0 ignored
|
||||
|
||||
auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
|
||||
uint8 data = 0x00;
|
||||
addr &= 0xff06;
|
||||
|
@ -83,7 +83,7 @@ auto ArmDSP::read(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
|
||||
auto ArmDSP::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
|
||||
addr &= 0xff06;
|
||||
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//ARMv3 (ARM60)
|
||||
|
||||
struct ArmDSP : Processor::ARM, Cothread {
|
||||
struct ArmDSP : Processor::ARM, Thread {
|
||||
#include "registers.hpp"
|
||||
|
||||
ArmDSP();
|
||||
|
|
|
@ -1,6 +1,3 @@
|
|||
struct Coprocessor : Cothread {
|
||||
};
|
||||
|
||||
#include <sfc/coprocessor/icd2/icd2.hpp>
|
||||
#include <sfc/coprocessor/mcc/mcc.hpp>
|
||||
#include <sfc/coprocessor/nss/nss.hpp>
|
||||
|
|
|
@ -27,7 +27,7 @@ auto EpsonRTC::main() -> void {
|
|||
}
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto EpsonRTC::init() -> void {
|
||||
|
@ -136,7 +136,7 @@ auto EpsonRTC::sync() -> void {
|
|||
}
|
||||
|
||||
auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr &= 3;
|
||||
|
||||
if(addr == 0) {
|
||||
|
@ -161,7 +161,7 @@ auto EpsonRTC::read(uint24 addr, uint8 data) -> uint8 {
|
|||
}
|
||||
|
||||
auto EpsonRTC::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr &= 3, data &= 15;
|
||||
|
||||
if(addr == 0) {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
//Epson RTC-4513 Real-Time Clock
|
||||
|
||||
struct EpsonRTC : Cothread {
|
||||
struct EpsonRTC : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ auto Event::main() -> void {
|
|||
}
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Event::init() -> void {
|
||||
|
@ -47,6 +47,7 @@ auto Event::power() -> void {
|
|||
|
||||
auto Event::reset() -> void {
|
||||
create(Event::Enter, 1);
|
||||
|
||||
for(auto n : range(ram.size())) ram.write(n, 0x00);
|
||||
status = 0x00;
|
||||
select = 0x00;
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
//* Campus Challenge '92
|
||||
//* Powerfest '94
|
||||
|
||||
struct Event : Cothread {
|
||||
struct Event : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto init() -> void;
|
||||
|
|
|
@ -15,14 +15,14 @@ auto HitachiDSP::main() -> void {
|
|||
for(auto n : range(mmio.dmaLength)) {
|
||||
write(mmio.dmaTarget + n, read(mmio.dmaSource + n));
|
||||
step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
mmio.dma = false;
|
||||
}
|
||||
|
||||
exec(mmio.programOffset);
|
||||
step(1);
|
||||
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto HitachiDSP::init() -> void {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct HitachiDSP : Processor::HG51B, Cothread {
|
||||
struct HitachiDSP : Processor::HG51B, Thread {
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
|
||||
|
|
|
@ -36,7 +36,7 @@ auto HitachiDSP::write(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
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());
|
||||
//if(Roms == 2 && mmio.r1f52 == 1 && addr >= (bit::round(rom.size()) >> 1)) return 0x00;
|
||||
return rom.read(addr, data);
|
||||
|
|
|
@ -12,7 +12,7 @@ ICD2 icd2;
|
|||
|
||||
auto ICD2::Enter() -> void {
|
||||
while(true) {
|
||||
if(scheduler.synchronizing()) GameBoy::system.runToSave();
|
||||
//if(scheduler.synchronizing()) GameBoy::system.runToSave();
|
||||
scheduler.synchronize();
|
||||
icd2.main();
|
||||
}
|
||||
|
@ -27,7 +27,7 @@ auto ICD2::main() -> void {
|
|||
stream->sample(0.0, 0.0);
|
||||
step(2); //two clocks per audio sample
|
||||
}
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto ICD2::init() -> void {
|
||||
|
@ -52,8 +52,9 @@ auto ICD2::power() -> void {
|
|||
}
|
||||
|
||||
auto ICD2::reset(bool soft) -> void {
|
||||
create(ICD2::Enter, cpu.frequency / 5);
|
||||
if(!soft) stream = Emulator::audio.createStream(2, cpu.frequency / 10);
|
||||
auto frequency = system.colorburst() * 6.0;
|
||||
create(ICD2::Enter, frequency / 5);
|
||||
if(!soft) stream = Emulator::audio.createStream(2, frequency / 10);
|
||||
|
||||
r6003 = 0x00;
|
||||
r6004 = 0xff;
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
#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;
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
@ -71,7 +71,7 @@ private:
|
|||
|
||||
#else
|
||||
|
||||
struct ICD2 : Coprocessor {
|
||||
struct ICD2 : Thread {
|
||||
auto init() -> void {}
|
||||
auto load() -> void {}
|
||||
auto unload() -> void {}
|
||||
|
|
|
@ -56,11 +56,12 @@ auto ICD2::writeIO(uint24 addr, uint8 data) -> void {
|
|||
if((r6003 & 0x80) == 0x00 && (data & 0x80) == 0x80) {
|
||||
reset(true);
|
||||
}
|
||||
auto frequency = system.colorburst() * 6.0;
|
||||
switch(data & 3) {
|
||||
case 0: frequency = cpu.frequency / 4; break; //fast (glitchy, even on real hardware)
|
||||
case 1: frequency = cpu.frequency / 5; break; //normal
|
||||
case 2: frequency = cpu.frequency / 7; break; //slow
|
||||
case 3: frequency = cpu.frequency / 9; break; //very slow
|
||||
case 0: setFrequency(frequency / 4); break; //fast (glitchy, even on real hardware)
|
||||
case 1: setFrequency(frequency / 5); break; //normal
|
||||
case 2: setFrequency(frequency / 7); break; //slow
|
||||
case 3: setFrequency(frequency / 9); break; //very slow
|
||||
}
|
||||
r6003 = data;
|
||||
return;
|
||||
|
|
|
@ -36,7 +36,7 @@ auto MSU1::main() -> void {
|
|||
|
||||
stream->sample(left, right);
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto MSU1::init() -> void {
|
||||
|
@ -115,7 +115,7 @@ auto MSU1::audioOpen() -> void {
|
|||
}
|
||||
|
||||
auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr = 0x2000 | (addr & 7);
|
||||
|
||||
switch(addr) {
|
||||
|
@ -144,7 +144,7 @@ auto MSU1::readIO(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
|
||||
auto MSU1::writeIO(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr = 0x2000 | (addr & 7);
|
||||
|
||||
switch(addr) {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct MSU1 : Cothread {
|
||||
struct MSU1 : Thread {
|
||||
shared_pointer<Emulator::Stream> stream;
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
|
|
@ -12,11 +12,11 @@ auto NECDSP::Enter() -> void {
|
|||
auto NECDSP::main() -> void {
|
||||
exec();
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto NECDSP::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
if(addr & 1) {
|
||||
return uPD96050::readSR();
|
||||
} else {
|
||||
|
@ -25,7 +25,7 @@ auto NECDSP::read(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
|
||||
auto NECDSP::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
if(addr & 1) {
|
||||
return uPD96050::writeSR(data);
|
||||
} else {
|
||||
|
@ -34,12 +34,12 @@ auto NECDSP::write(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
auto NECDSP::readRAM(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
return uPD96050::readDP(addr);
|
||||
}
|
||||
|
||||
auto NECDSP::writeRAM(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
return uPD96050::writeDP(addr, data);
|
||||
}
|
||||
|
||||
|
@ -56,7 +56,7 @@ auto NECDSP::power() -> void {
|
|||
}
|
||||
|
||||
auto NECDSP::reset() -> void {
|
||||
create(NECDSP::Enter, frequency);
|
||||
create(NECDSP::Enter, Frequency);
|
||||
uPD96050::power();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct NECDSP : Processor::uPD96050, Cothread {
|
||||
struct NECDSP : Processor::uPD96050, Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
|
@ -16,6 +16,8 @@ struct NECDSP : Processor::uPD96050, Cothread {
|
|||
|
||||
auto firmware() const -> vector<uint8>;
|
||||
auto serialize(serializer&) -> void;
|
||||
|
||||
uint Frequency = 0;
|
||||
};
|
||||
|
||||
extern NECDSP necdsp;
|
||||
|
|
|
@ -5,12 +5,12 @@ auto SA1::CPUIRAM::size() const -> uint {
|
|||
}
|
||||
|
||||
auto SA1::CPUIRAM::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
return sa1.iram.read(addr & 0x07ff);
|
||||
}
|
||||
|
||||
auto SA1::CPUIRAM::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
sa1.iram.write(addr & 0x07ff, data);
|
||||
}
|
||||
|
||||
|
@ -19,12 +19,12 @@ auto SA1::CPUBWRAM::size() const -> uint {
|
|||
}
|
||||
|
||||
auto SA1::CPUBWRAM::read(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
if(dma) return sa1.dmaCC1Read(addr);
|
||||
return sa1.bwram.read(addr);
|
||||
}
|
||||
|
||||
auto SA1::CPUBWRAM::write(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
sa1.bwram.write(addr, data);
|
||||
}
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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)) {
|
||||
|
||||
|
@ -91,7 +91,7 @@ auto SA1::readIO(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
|
||||
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)) {
|
||||
|
||||
|
|
|
@ -17,22 +17,22 @@ auto SA1::busRead(uint24 addr, uint8 data) -> uint8 {
|
|||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x000000) { //$00-3f,80-bf:0000-07ff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return iram.read(addr & 2047, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return iram.read(addr & 2047, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return bwram.read(addr & (bwram.size() - 1), data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
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
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return iram.write(addr & 2047, data);
|
||||
}
|
||||
|
||||
if((addr & 0x40f800) == 0x003000) { //$00-3f,80-bf:3000-37ff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return iram.write(addr & 2047, data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x400000) { //$40-4f:0000-ffff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return bwram.write(addr & (bwram.size() - 1), data);
|
||||
}
|
||||
|
||||
if((addr & 0xf00000) == 0x600000) { //$60-6f:0000-ffff
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
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 {
|
||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
return cpubwram.read(addr);
|
||||
}
|
||||
|
@ -185,7 +185,7 @@ auto SA1::mmcbwramRead(uint24 addr, uint8 data) -> uint8 {
|
|||
|
||||
auto SA1::mmcbwramWrite(uint24 addr, uint8 data) -> void {
|
||||
if(addr < 0x2000) { //$00-3f,80-bf:6000-7fff
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(sa1);
|
||||
addr = bus.mirror(mmio.sbm * 0x2000 + (addr & 0x1fff), cpubwram.size());
|
||||
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 {
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
if(mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
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 {
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
if(mmio.sw46 == 0) {
|
||||
//$40-43:0000-ffff x 32 projection
|
||||
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) {
|
||||
//SA-1 co-processor is asleep
|
||||
tick();
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return;
|
||||
}
|
||||
|
||||
|
@ -77,7 +77,7 @@ auto SA1::interruptPending() const -> bool {
|
|||
|
||||
auto SA1::tick() -> void {
|
||||
step(2);
|
||||
if(++status.counter == 0) synchronizeCPU();
|
||||
if(++status.counter == 0) synchronize(cpu);
|
||||
|
||||
//adjust counters:
|
||||
//note that internally, status counters are in clocks;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct SA1 : Processor::R65816, Cothread {
|
||||
struct SA1 : Processor::R65816, Thread {
|
||||
//sa1.cpp
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
|
|
@ -15,7 +15,7 @@ auto SharpRTC::main() -> void {
|
|||
tickSecond();
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto SharpRTC::init() -> void {
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct SharpRTC : Cothread {
|
||||
struct SharpRTC : Thread {
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
|
|
|
@ -29,7 +29,7 @@ auto SPC7110::main() -> void {
|
|||
|
||||
auto SPC7110::addClocks(uint clocks) -> void {
|
||||
step(clocks);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto SPC7110::init() -> void {
|
||||
|
@ -48,7 +48,7 @@ auto SPC7110::power() -> void {
|
|||
}
|
||||
|
||||
auto SPC7110::reset() -> void {
|
||||
create(SPC7110::Enter, 21477272);
|
||||
create(SPC7110::Enter, 21'477'272);
|
||||
|
||||
r4801 = 0x00;
|
||||
r4802 = 0x00;
|
||||
|
@ -105,7 +105,7 @@ auto SPC7110::reset() -> void {
|
|||
}
|
||||
|
||||
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) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||
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 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
if((addr & 0xff0000) == 0x500000) addr = 0x4800; //$50:0000-ffff == $4800
|
||||
if((addr & 0xff0000) == 0x580000) addr = 0x4808; //$58:0000-ffff == $4808
|
||||
addr = 0x4800 | (addr & 0x3f); //$00-3f,80-bf:4800-483f
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct Decompressor;
|
||||
|
||||
struct SPC7110 : Cothread {
|
||||
struct SPC7110 : Thread {
|
||||
SPC7110();
|
||||
~SPC7110();
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr = 0x3000 | addr.bits(0,9);
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
|
@ -51,7 +51,7 @@ auto SuperFX::readIO(uint24 addr, uint8) -> uint8 {
|
|||
}
|
||||
|
||||
auto SuperFX::writeIO(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizeCoprocessors();
|
||||
cpu.synchronize(*this);
|
||||
addr = 0x3000 | addr.bits(0,9);
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
|
|
|
@ -1,24 +1,24 @@
|
|||
auto SuperFX::read(uint24 addr, uint8 data) -> uint8 {
|
||||
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff,:8000-ffff
|
||||
while(!regs.scmr.ron && !scheduler.synchronizing()) {
|
||||
while(!regs.scmr.ron) {
|
||||
step(6);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
return rom.read((((addr & 0x3f0000) >> 1) | (addr & 0x7fff)) & romMask);
|
||||
}
|
||||
|
||||
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
|
||||
while(!regs.scmr.ron && !scheduler.synchronizing()) {
|
||||
while(!regs.scmr.ron) {
|
||||
step(6);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
return rom.read(addr & romMask);
|
||||
}
|
||||
|
||||
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||
while(!regs.scmr.ran && !scheduler.synchronizing()) {
|
||||
while(!regs.scmr.ran) {
|
||||
step(6);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
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 {
|
||||
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
|
||||
while(!regs.scmr.ran && !scheduler.synchronizing()) {
|
||||
while(!regs.scmr.ran) {
|
||||
step(6);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
return ram.write(addr & ramMask, data);
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct SuperFX : Processor::GSU, Cothread {
|
||||
struct SuperFX : Processor::GSU, Thread {
|
||||
MappedRAM rom;
|
||||
MappedRAM ram;
|
||||
|
||||
|
|
|
@ -14,8 +14,8 @@ auto SuperFX::step(uint clocks) -> void {
|
|||
}
|
||||
}
|
||||
|
||||
Cothread::step(clocks);
|
||||
synchronizeCPU();
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto SuperFX::syncROMBuffer() -> void {
|
||||
|
|
|
@ -19,26 +19,6 @@ CPU::CPU() {
|
|||
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 {
|
||||
while(true) scheduler.synchronize(), cpu.main();
|
||||
}
|
||||
|
|
|
@ -5,11 +5,6 @@ struct CPU : Processor::R65816, Thread, PPUcounter {
|
|||
|
||||
CPU();
|
||||
|
||||
auto synchronizeSMP() -> void;
|
||||
auto synchronizePPU() -> void;
|
||||
auto synchronizeCoprocessors() -> void;
|
||||
auto synchronizePeripherals() -> void;
|
||||
|
||||
auto readPort(uint2 port) const -> uint8;
|
||||
auto writePort(uint2 port, uint8 data) -> void;
|
||||
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
auto CPU::readAPU(uint24 addr, uint8 data) -> uint8 {
|
||||
synchronizeSMP();
|
||||
synchronize(smp);
|
||||
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 {
|
||||
synchronizeSMP();
|
||||
synchronize(smp);
|
||||
return writePort(addr.bits(0,1), data);
|
||||
}
|
||||
|
||||
|
|
|
@ -10,15 +10,8 @@ auto CPU::step(uint clocks) -> void {
|
|||
if(hcounter() & 2) pollInterrupts();
|
||||
}
|
||||
|
||||
smp.clock -= clocks * (uint64)smp.frequency;
|
||||
ppu.clock -= clocks;
|
||||
for(auto coprocessor : coprocessors) {
|
||||
coprocessor->clock -= clocks * (uint64)coprocessor->frequency;
|
||||
}
|
||||
for(auto peripheral : peripherals) {
|
||||
peripheral->clock -= clocks * (uint64)peripheral->frequency;
|
||||
}
|
||||
synchronizePeripherals();
|
||||
Thread::step(clocks);
|
||||
for(auto peripheral : peripherals) synchronize(*peripheral);
|
||||
|
||||
status.autoJoypadClock += clocks;
|
||||
if(status.autoJoypadClock >= 256) {
|
||||
|
@ -44,9 +37,9 @@ auto CPU::scanline() -> void {
|
|||
status.lineClocks = lineclocks();
|
||||
|
||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||
synchronizeSMP();
|
||||
synchronizePPU();
|
||||
synchronizeCoprocessors();
|
||||
synchronize(smp);
|
||||
synchronize(ppu);
|
||||
for(auto coprocessor : coprocessors) synchronize(*coprocessor);
|
||||
|
||||
if(vcounter() == 0) {
|
||||
//HDMA init triggers once every frame
|
||||
|
|
|
@ -31,11 +31,7 @@ DSP::DSP() {
|
|||
/* timing */
|
||||
|
||||
auto DSP::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
auto DSP::synchronizeSMP() -> void {
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(smp.thread);
|
||||
Thread::step(clocks);
|
||||
}
|
||||
|
||||
auto DSP::Enter() -> void {
|
||||
|
@ -197,7 +193,7 @@ auto DSP::main() -> void {
|
|||
|
||||
auto DSP::tick() -> void {
|
||||
step(3 * 8);
|
||||
synchronizeSMP();
|
||||
synchronize(smp);
|
||||
}
|
||||
|
||||
/* register interface for S-SMP $00f2,$00f3 */
|
||||
|
|
|
@ -6,7 +6,6 @@ struct DSP : Thread {
|
|||
DSP();
|
||||
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
alwaysinline auto synchronizeSMP() -> void;
|
||||
|
||||
auto mute() const -> bool;
|
||||
auto read(uint8 addr) -> uint8;
|
||||
|
|
|
@ -30,6 +30,7 @@ S21FX::S21FX() {
|
|||
}
|
||||
|
||||
S21FX::~S21FX() {
|
||||
scheduler.remove(*this);
|
||||
bus.unmap("00-3f,80-bf:2184-21ff");
|
||||
bus.unmap("00:fffc-fffd");
|
||||
|
||||
|
@ -54,6 +55,11 @@ auto S21FX::Enter() -> void {
|
|||
while(true) scheduler.synchronize(), peripherals.expansionPort->main();
|
||||
}
|
||||
|
||||
auto S21FX::step(uint clocks) -> void {
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto S21FX::main() -> void {
|
||||
if(linkInit) linkInit(
|
||||
{&S21FX::quit, this},
|
||||
|
|
|
@ -3,6 +3,7 @@ struct S21FX : Expansion {
|
|||
~S21FX();
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto read(uint24 addr, uint8 data) -> uint8;
|
||||
|
|
|
@ -3,7 +3,7 @@
|
|||
namespace SuperFamicom {
|
||||
|
||||
Expansion::Expansion() {
|
||||
if(!thread) create(Expansion::Enter, 1);
|
||||
if(!handle()) create(Expansion::Enter, 1);
|
||||
}
|
||||
|
||||
auto Expansion::Enter() -> void {
|
||||
|
@ -12,6 +12,7 @@ auto Expansion::Enter() -> void {
|
|||
|
||||
auto Expansion::main() -> void {
|
||||
step(1);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
struct Expansion : Cothread {
|
||||
struct Expansion : Thread {
|
||||
Expansion();
|
||||
static auto Enter() -> void;
|
||||
virtual auto main() -> void;
|
||||
|
|
|
@ -20,6 +20,7 @@ SuperDisc::SuperDisc() {
|
|||
}
|
||||
|
||||
SuperDisc::~SuperDisc() {
|
||||
scheduler.remove(*this);
|
||||
bus.unmap("00-3f,80-bf:21e0-21e5");
|
||||
}
|
||||
|
||||
|
@ -41,7 +42,7 @@ auto SuperDisc::main() -> void {
|
|||
}
|
||||
|
||||
step(1);
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
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 {
|
||||
cpu.synchronizePPU();
|
||||
cpu.synchronize(ppu);
|
||||
|
||||
switch((uint16)addr) {
|
||||
|
||||
|
@ -184,7 +184,7 @@ auto PPU::readIO(uint24 addr, uint8 data) -> uint8 {
|
|||
}
|
||||
|
||||
auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
||||
cpu.synchronizePPU();
|
||||
cpu.synchronize(ppu);
|
||||
|
||||
switch((uint16)addr) {
|
||||
|
||||
|
@ -629,7 +629,7 @@ auto PPU::writeIO(uint24 addr, uint8 data) -> void {
|
|||
}
|
||||
|
||||
auto PPU::latchCounters() -> void {
|
||||
cpu.synchronizePPU();
|
||||
cpu.synchronize(ppu);
|
||||
io.hcounter = hdot();
|
||||
io.vcounter = vcounter();
|
||||
latch.counters = 1;
|
||||
|
|
|
@ -32,15 +32,11 @@ auto PPU::step(uint clocks) -> void {
|
|||
clocks >>= 1;
|
||||
while(clocks--) {
|
||||
tick(2);
|
||||
clock += 2;
|
||||
synchronizeCPU();
|
||||
Thread::step(2);
|
||||
synchronize(cpu);
|
||||
}
|
||||
}
|
||||
|
||||
auto PPU::synchronizeCPU() -> void {
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
auto PPU::Enter() -> void {
|
||||
while(true) scheduler.synchronize(), ppu.main();
|
||||
}
|
||||
|
|
|
@ -7,7 +7,6 @@ struct PPU : Thread, PPUcounter {
|
|||
~PPU();
|
||||
|
||||
alwaysinline auto step(uint clocks) -> void;
|
||||
alwaysinline auto synchronizeCPU() -> void;
|
||||
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
|
|
|
@ -21,16 +21,14 @@
|
|||
|
||||
namespace SuperFamicom {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
extern Cheat cheat;
|
||||
|
||||
//dynamic thread bound to CPU (coprocessors and peripherals)
|
||||
struct Cothread : Thread {
|
||||
auto step(uint clocks) -> void;
|
||||
auto synchronizeCPU() -> void;
|
||||
struct Thread : Emulator::Thread {
|
||||
auto create(auto (*entrypoint)() -> void, double frequency) -> void;
|
||||
auto synchronize(Thread& thread) -> void;
|
||||
};
|
||||
|
||||
#include <sfc/memory/memory.hpp>
|
||||
|
@ -51,13 +49,13 @@ namespace SuperFamicom {
|
|||
#include <sfc/memory/memory-inline.hpp>
|
||||
#include <sfc/ppu/counter/counter-inline.hpp>
|
||||
|
||||
inline auto Cothread::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
synchronizeCPU();
|
||||
inline auto Thread::create(auto (*entrypoint)(), double frequency) -> void {
|
||||
Emulator::Thread::create(entrypoint, frequency);
|
||||
scheduler.append(*this);
|
||||
}
|
||||
|
||||
inline auto Cothread::synchronizeCPU() -> void {
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
inline auto Thread::synchronize(Thread& thread) -> void {
|
||||
if(_clock > thread._clock) scheduler.resume(thread);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -38,7 +38,7 @@ auto SMP::readBus(uint16 addr) -> uint8 {
|
|||
case 0xf5: //CPUIO1
|
||||
case 0xf6: //CPUIO2
|
||||
case 0xf7: //CPUIO3
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
return cpu.readPort(addr);
|
||||
|
||||
case 0xf8: //RAM0
|
||||
|
@ -96,7 +96,7 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
|
|||
if(data & 0x30) {
|
||||
//one-time clearing of APU port read registers,
|
||||
//emulated by simulating CPU writes of 0x00
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
if(data & 0x20) {
|
||||
cpu.writePort(2, 0x00);
|
||||
cpu.writePort(3, 0x00);
|
||||
|
@ -140,7 +140,7 @@ auto SMP::writeBus(uint16 addr, uint8 data) -> void {
|
|||
case 0xf5: //CPUIO1
|
||||
case 0xf6: //CPUIO2
|
||||
case 0xf7: //CPUIO3
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
writePort(addr, data);
|
||||
break;
|
||||
|
||||
|
|
|
@ -8,14 +8,6 @@ SMP smp;
|
|||
#include "timing.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 {
|
||||
while(true) scheduler.synchronize(), smp.main();
|
||||
}
|
||||
|
|
|
@ -1,9 +1,6 @@
|
|||
//Sony CXP1100Q-1
|
||||
|
||||
struct SMP : Processor::SPC700, Thread {
|
||||
alwaysinline auto synchronizeCPU() -> void;
|
||||
alwaysinline auto synchronizeDSP() -> void;
|
||||
|
||||
auto readPort(uint2 port) const -> uint8;
|
||||
auto writePort(uint2 port, uint8 data) -> void;
|
||||
|
||||
|
|
|
@ -1,14 +1,13 @@
|
|||
auto SMP::step(uint clocks) -> void {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
dsp.clock -= clocks;
|
||||
synchronizeDSP();
|
||||
Thread::step(clocks);
|
||||
synchronize(dsp);
|
||||
|
||||
#if defined(DEBUGGER)
|
||||
synchronizeCPU();
|
||||
synchronize(cpu);
|
||||
#else
|
||||
//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
|
||||
if(clock > +(768 * 24 * (int64)24000000)) synchronizeCPU();
|
||||
//sync if S-SMP is more than 1ms ahead of S-CPU
|
||||
if(clock() - cpu.clock() > (Emulator::Constants::Time::Attosecond / Emulator::Constants::Time::Millisecond)) synchronize(cpu);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
|
|
@ -15,16 +15,12 @@ auto System::run() -> void {
|
|||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(smp.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(dsp.thread);
|
||||
for(auto coprocessor : cpu.coprocessors) {
|
||||
scheduler.synchronize(coprocessor->thread);
|
||||
}
|
||||
for(auto peripheral : cpu.peripherals) {
|
||||
scheduler.synchronize(peripheral->thread);
|
||||
}
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(smp);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(dsp);
|
||||
for(auto coprocessor : cpu.coprocessors) scheduler.synchronize(*coprocessor);
|
||||
for(auto peripheral : cpu.peripherals) scheduler.synchronize(*peripheral);
|
||||
}
|
||||
|
||||
auto System::init() -> void {
|
||||
|
@ -170,6 +166,7 @@ auto System::reset() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
cpu.reset();
|
||||
smp.reset();
|
||||
dsp.reset();
|
||||
|
@ -205,7 +202,7 @@ auto System::reset() -> void {
|
|||
if(cartridge.has.SPC7110) cpu.coprocessors.append(&spc7110);
|
||||
if(cartridge.has.MSU1) cpu.coprocessors.append(&msu1);
|
||||
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
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 "about.cpp"
|
||||
unique_pointer<AboutWindow> aboutWindow;
|
||||
unique_pointer<Presentation> presentation;
|
||||
|
||||
Presentation::Presentation() {
|
||||
|
@ -122,12 +124,7 @@ Presentation::Presentation() {
|
|||
invoke("http://doc.byuu.org/higan/");
|
||||
});
|
||||
about.setText("About ...").onActivate([&] {
|
||||
MessageDialog().setParent(*this).setTitle("About higan ...").setText({
|
||||
Emulator::Name, " v", Emulator::Version, "\n\n",
|
||||
"Author: ", Emulator::Author, "\n",
|
||||
"License: ", Emulator::License, "\n",
|
||||
"Website: ", Emulator::Website
|
||||
}).information();
|
||||
aboutWindow->setVisible().setFocused();
|
||||
});
|
||||
|
||||
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 {
|
||||
Presentation();
|
||||
auto updateEmulator() -> void;
|
||||
|
@ -70,4 +80,5 @@ struct Presentation : Window {
|
|||
StatusBar statusBar{this};
|
||||
};
|
||||
|
||||
extern unique_pointer<AboutWindow> aboutWindow;
|
||||
extern unique_pointer<Presentation> presentation;
|
||||
|
|
|
@ -49,6 +49,7 @@ Program::Program(string_vector args) {
|
|||
new SettingsManager;
|
||||
new CheatDatabase;
|
||||
new ToolsManager;
|
||||
new AboutWindow;
|
||||
|
||||
presentation->setFocused();
|
||||
|
||||
|
|
|
@ -60,8 +60,8 @@ auto APU::dacRun() -> void {
|
|||
}
|
||||
|
||||
auto APU::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto APU::power() -> void {
|
||||
|
|
|
@ -21,8 +21,8 @@ auto Cartridge::main() -> void {
|
|||
}
|
||||
|
||||
auto Cartridge::step(uint clocks) -> void {
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto Cartridge::power() -> void {
|
||||
|
|
|
@ -18,14 +18,10 @@ auto CPU::main() -> void {
|
|||
}
|
||||
|
||||
auto CPU::step(uint clocks) -> void {
|
||||
ppu.clock -= clocks;
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
|
||||
apu.clock -= clocks;
|
||||
if(apu.clock < 0) co_switch(apu.thread);
|
||||
|
||||
cartridge.clock -= clocks;
|
||||
if(cartridge.clock < 0) co_switch(cartridge.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(ppu);
|
||||
synchronize(apu);
|
||||
synchronize(cartridge);
|
||||
}
|
||||
|
||||
auto CPU::wait(uint clocks) -> void {
|
||||
|
|
|
@ -89,8 +89,8 @@ auto PPU::refresh() -> void {
|
|||
auto PPU::step(uint clocks) -> void {
|
||||
s.hclk += clocks;
|
||||
|
||||
clock += clocks;
|
||||
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
|
||||
Thread::step(clocks);
|
||||
synchronize(cpu);
|
||||
}
|
||||
|
||||
auto PPU::power() -> void {
|
||||
|
|
|
@ -69,6 +69,7 @@ auto System::power() -> void {
|
|||
Emulator::audio.reset();
|
||||
Emulator::audio.setInterface(interface);
|
||||
|
||||
scheduler.reset();
|
||||
bus.power();
|
||||
iram.power();
|
||||
eeprom.power();
|
||||
|
@ -76,7 +77,7 @@ auto System::power() -> void {
|
|||
ppu.power();
|
||||
apu.power();
|
||||
cartridge.power();
|
||||
scheduler.reset(cpu.thread);
|
||||
scheduler.primary(cpu);
|
||||
|
||||
bus.map(this, 0x0060);
|
||||
bus.map(this, 0x00ba, 0x00be);
|
||||
|
@ -93,10 +94,10 @@ auto System::run() -> void {
|
|||
}
|
||||
|
||||
auto System::runToSave() -> void {
|
||||
scheduler.synchronize(cpu.thread);
|
||||
scheduler.synchronize(ppu.thread);
|
||||
scheduler.synchronize(apu.thread);
|
||||
scheduler.synchronize(cartridge.thread);
|
||||
scheduler.synchronize(cpu);
|
||||
scheduler.synchronize(ppu);
|
||||
scheduler.synchronize(apu);
|
||||
scheduler.synchronize(cartridge);
|
||||
}
|
||||
|
||||
auto System::pollKeypad() -> void {
|
||||
|
|
|
@ -12,7 +12,6 @@
|
|||
|
||||
namespace WonderSwan {
|
||||
using File = Emulator::File;
|
||||
using Thread = Emulator::Thread;
|
||||
using Scheduler = Emulator::Scheduler;
|
||||
using Cheat = Emulator::Cheat;
|
||||
extern Scheduler scheduler;
|
||||
|
@ -26,6 +25,12 @@ namespace WonderSwan {
|
|||
|
||||
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/eeprom/eeprom.hpp>
|
||||
#include <ws/system/system.hpp>
|
||||
|
@ -33,6 +38,19 @@ namespace WonderSwan {
|
|||
#include <ws/cpu/cpu.hpp>
|
||||
#include <ws/ppu/ppu.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>
|
||||
|
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue