Update to v097r13 release.

byuu says:

I refactored my schedulers. Added about ten lines to each scheduler, and
removed about 100 lines of calling into internal state in the scheduler
for the FC,SFC cores and about 30-40 lines for the other cores. All of
its state is now private.

Also reworked all of the entry points to static auto Enter() and auto
main(). Where Enter() handles all the synchronization stuff, and main()
doesn't need the while(true); loop forcing another layer of indentation
everywhere.

Took a few hours to do, but totally worth it. I'm surprised I didn't do
this sooner.

Also updated icarus gmake install rule to copy over the database.
This commit is contained in:
Tim Allen 2016-02-09 22:51:12 +11:00
parent 32a95a9761
commit 6c83329cae
105 changed files with 1021 additions and 1189 deletions

View File

@ -6,7 +6,7 @@ using namespace nall;
namespace Emulator {
static const string Name = "higan";
static const string Version = "097.12";
static const string Version = "097.13";
static const string Author = "byuu";
static const string License = "GPLv3";
static const string Website = "http://byuu.org/";

View File

@ -34,43 +34,37 @@ APU::APU() {
}
}
auto APU::Main() -> void {
apu.main();
auto APU::Enter() -> void {
while(true) scheduler.synchronize(), apu.main();
}
auto APU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
uint pulse_output, triangle_output, noise_output, dmc_output;
uint pulse_output, triangle_output, noise_output, dmc_output;
pulse_output = pulse[0].clock();
pulse_output += pulse[1].clock();
triangle_output = triangle.clock();
noise_output = noise.clock();
dmc_output = dmc.clock();
pulse_output = pulse[0].clock();
pulse_output += pulse[1].clock();
triangle_output = triangle.clock();
noise_output = noise.clock();
dmc_output = dmc.clock();
clock_frame_counter_divider();
clock_frame_counter_divider();
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
int output = pulse_dac[pulse_output] + dmc_triangle_noise_dac[dmc_output][triangle_output][noise_output];
output = filter.run_hipass_strong(output);
output += cartridge_sample;
output = filter.run_hipass_weak(output);
//output = filter.run_lopass(output);
output = sclamp<16>(output);
output = filter.run_hipass_strong(output);
output += cartridge_sample;
output = filter.run_hipass_weak(output);
//output = filter.run_lopass(output);
output = sclamp<16>(output);
interface->audioSample(output, output);
interface->audioSample(output, output);
tick();
}
tick();
}
auto APU::tick() -> void {
clock += 12;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto APU::set_irq_line() -> void {
@ -94,7 +88,7 @@ auto APU::power() -> void {
}
auto APU::reset() -> void {
create(APU::Main, 21477272);
create(APU::Enter, 21'477'272);
pulse[0].reset();
pulse[1].reset();

View File

@ -1,7 +1,7 @@
struct APU : Thread {
APU();
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto tick() -> void;
auto set_irq_line() -> void;

View File

@ -5,20 +5,14 @@ struct BandaiFCG : Board {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
irq_counter_enable = false;
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(1);
irq_counter_enable = false;
}
}
tick();
}
tick();
}
auto ciram_addr(uint addr) const -> uint {

View File

@ -80,19 +80,13 @@ auto Board::mirror(uint addr, uint size) -> uint {
}
auto Board::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cartridge.clock += 12 * 4095;
tick();
}
cartridge.clock += 12 * 4095;
tick();
}
auto Board::tick() -> void {
cartridge.clock += 12;
if(cartridge.clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(cartridge.clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto Board::chr_read(uint addr) -> uint8 {

View File

@ -44,25 +44,19 @@ struct Sunsoft5B : Board {
} pulse[3];
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(irq_enable);
}
if(irq_counter_enable) {
if(--irq_counter == 0xffff) {
cpu.set_irq_line(irq_enable);
}
}
pulse[0].clock();
pulse[1].clock();
pulse[2].clock();
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
apu.set_sample(-output);
tick();
}
pulse[0].clock();
pulse[1].clock();
pulse[2].clock();
int16 output = dac[pulse[0].output] + dac[pulse[1].output] + dac[pulse[2].output];
apu.set_sample(-output);
tick();
}
auto prg_read(uint addr) -> uint8 {

View File

@ -18,8 +18,8 @@ auto Cartridge::title() const -> string {
return information.title;
}
auto Cartridge::Main() -> void {
cartridge.main();
auto Cartridge::Enter() -> void {
while(true) scheduler.synchronize(), cartridge.main();
}
auto Cartridge::main() -> void {
@ -47,7 +47,7 @@ auto Cartridge::power() -> void {
}
auto Cartridge::reset() -> void {
create(Cartridge::Main, 21477272);
create(Cartridge::Enter, 21'477'272);
board->reset();
}

View File

@ -2,7 +2,7 @@
#include "board/board.hpp"
struct Cartridge : Thread {
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto sha256() const -> string;

View File

@ -4,14 +4,8 @@ struct MMC1 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(writedelay) writedelay--;
tick();
}
if(writedelay) writedelay--;
tick();
}
auto prg_addr(uint addr) -> uint {

View File

@ -3,15 +3,9 @@ struct MMC3 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
auto irq_test(uint addr) -> void {

View File

@ -4,17 +4,11 @@ struct MMC5 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
//scanline() resets this; if no scanlines detected, enter video blanking period
if(++cpu_cycle_counter >= 200) blank(); //113-114 normal; ~2500 across Vblank period
cpu.set_irq_line(irq_enable && irq_pending);
tick();
}
cpu.set_irq_line(irq_enable && irq_pending);
tick();
}
auto scanline(uint y) -> void {

View File

@ -3,15 +3,9 @@ struct MMC6 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
if(irq_delay) irq_delay--;
cpu.set_irq_line(irq_line);
tick();
}
auto irq_test(uint addr) -> void {

View File

@ -3,31 +3,25 @@ struct VRC3 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
if(irq_enable) {
if(irq_mode == 0) { //16-bit
if(++irq_counter.w == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.w = irq_latch;
}
}
if(irq_mode == 1) { //8-bit
if(++irq_counter.l == 0) {
irq_line = 1;
irq_enable = irq_acknowledge;
irq_counter.l = irq_latch;
}
}
cpu.set_irq_line(irq_line);
tick();
}
cpu.set_irq_line(irq_line);
tick();
}
auto prg_addr(uint addr) const -> uint {

View File

@ -3,26 +3,11 @@ struct VRC4 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
@ -32,9 +17,18 @@ struct VRC4 : Chip {
}
}
cpu.set_irq_line(irq_line);
tick();
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
auto prg_addr(uint addr) const -> uint {

View File

@ -77,26 +77,11 @@ struct VRC6 : Chip {
} sawtooth;
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
@ -105,16 +90,25 @@ struct VRC6 : Chip {
}
}
}
cpu.set_irq_line(irq_line);
pulse1.clock();
pulse2.clock();
sawtooth.clock();
int output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
apu.set_sample(-output);
tick();
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
pulse1.clock();
pulse2.clock();
sawtooth.clock();
int output = (pulse1.output + pulse2.output + sawtooth.output) << 7;
apu.set_sample(-output);
tick();
}
auto prg_addr(uint addr) const -> uint {

View File

@ -6,26 +6,11 @@ struct VRC7 : Chip {
}
auto main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
if(irq_mode == 1) {
if(irq_enable) {
if(irq_mode == 0) {
irq_scalar -= 3;
if(irq_scalar <= 0) {
irq_scalar += 341;
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
@ -34,10 +19,19 @@ struct VRC7 : Chip {
}
}
}
cpu.set_irq_line(irq_line);
tick();
if(irq_mode == 1) {
if(irq_counter == 0xff) {
irq_counter = irq_latch;
irq_line = 1;
} else {
irq_counter++;
}
}
}
cpu.set_irq_line(irq_line);
tick();
}
auto reg_write(uint addr, uint8 data) -> void {

View File

@ -7,33 +7,23 @@ namespace Famicom {
CPU cpu;
auto CPU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cpu.main();
}
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {
if(status.interrupt_pending) {
interrupt();
return;
}
if(status.interrupt_pending) return interrupt();
exec();
}
auto CPU::add_clocks(uint clocks) -> void {
apu.clock -= clocks;
if(apu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(apu.thread);
if(apu.clock < 0 && !scheduler.synchronizing()) co_switch(apu.thread);
ppu.clock -= clocks;
if(ppu.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(ppu.thread);
if(ppu.clock < 0 && !scheduler.synchronizing()) co_switch(ppu.thread);
cartridge.clock -= clocks;
if(cartridge.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cartridge.thread);
if(cartridge.clock < 0 && !scheduler.synchronizing()) co_switch(cartridge.thread);
}
auto CPU::power() -> void {
@ -48,7 +38,7 @@ auto CPU::power() -> void {
auto CPU::reset() -> void {
R6502::reset();
create(CPU::Enter, 21477272);
create(CPU::Enter, 21'477'272);
regs.pc = bus.read(0xfffc) << 0;
regs.pc |= bus.read(0xfffd) << 8;

View File

@ -7,18 +7,12 @@ PPU ppu;
#include "serialization.cpp"
auto PPU::Main() -> void {
ppu.main();
auto PPU::Enter() -> void {
while(true) scheduler.synchronize(), ppu.main();
}
auto PPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::PPU) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
raster_scanline();
}
raster_scanline();
}
auto PPU::tick() -> void {
@ -49,14 +43,15 @@ auto PPU::scanline() -> void {
auto PPU::frame() -> void {
status.field ^= 1;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
auto PPU::power() -> void {
}
auto PPU::reset() -> void {
create(PPU::Main, 21477272);
create(PPU::Enter, 21'477'272);
status.mdr = 0x00;
status.field = 0;

View File

@ -1,7 +1,7 @@
#include "video.hpp"
struct PPU : Thread {
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto tick() -> void;

View File

@ -4,25 +4,40 @@ namespace Famicom {
Scheduler scheduler;
auto Scheduler::enter() -> void {
host_thread = co_active();
co_switch(thread);
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
thread = co_active();
co_switch(host_thread);
}
auto Scheduler::power() -> void {
}
auto Scheduler::reset() -> void {
host_thread = co_active();
thread = cpu.thread;
sync = SynchronizeMode::None;
exit_reason = ExitReason::UnknownEvent;
host = co_active();
resume = cpu.thread;
}
auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
return event;
}
auto Scheduler::exit(Event event_) -> void {
event = event_;
resume = co_active();
co_switch(host);
}
auto Scheduler::synchronize(cothread_t thread) -> void {
if(thread == ppu.thread) {
while(enter(Mode::SynchronizePPU) != Event::Synchronize);
} else {
resume = thread;
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
}
}
auto Scheduler::synchronize() -> void {
if(co_active() == ppu.thread && mode == Mode::SynchronizePPU) return exit(Event::Synchronize);
if(co_active() != ppu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
}
auto Scheduler::synchronizing() const -> bool {
return mode == Mode::SynchronizeAll;
}
}

View File

@ -1,16 +1,28 @@
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : uint { None, PPU, All } sync;
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
struct Scheduler {
enum class Mode : uint {
Run,
SynchronizePPU,
SynchronizeAll,
};
auto enter() -> void;
auto exit(ExitReason) -> void;
enum class Event : uint {
Unknown,
Frame,
Synchronize,
};
auto power() -> void;
auto reset() -> void;
auto enter(Mode = Mode::Run) -> Event;
auto exit(Event) -> void;
auto synchronize(cothread_t) -> void;
auto synchronize() -> void;
auto synchronizing() const -> bool;
cothread_t host_thread; //program thread (used to exit emulation)
cothread_t thread; //active emulation thread (used to enter emulation)
readonly<ExitReason> exit_reason;
private:
cothread_t host = nullptr;
cothread_t resume = nullptr;
Mode mode = Mode::Run;
Event event = Event::Unknown;
};
extern Scheduler scheduler;

View File

@ -9,38 +9,13 @@ auto System::loaded() const -> bool { return _loaded; }
auto System::run() -> void {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
auto System::runToSave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::PPU;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = cpu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = apu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.thread = cartridge.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
auto System::runThreadToSave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
scheduler.synchronize(ppu.thread);
scheduler.synchronize(cpu.thread);
scheduler.synchronize(apu.thread);
scheduler.synchronize(cartridge.thread);
}
auto System::load() -> void {
@ -63,7 +38,6 @@ auto System::power() -> void {
apu.power();
ppu.power();
input.reset();
scheduler.power();
reset();
}
@ -78,7 +52,7 @@ auto System::reset() -> void {
}
auto System::init() -> void {
assert(interface != 0);
assert(interface != nullptr);
input.connect(0, Input::Device::Joypad);
input.connect(1, Input::Device::None);
}

View File

@ -3,7 +3,6 @@ struct System {
auto run() -> void;
auto runToSave() -> void;
auto runThreadToSave() -> void;
auto load() -> void;
auto unload() -> void;

View File

@ -10,52 +10,44 @@ namespace GameBoy {
#include "serialization.cpp"
APU apu;
auto APU::Main() -> void {
apu.main();
auto APU::Enter() -> void {
while(true) scheduler.synchronize(), apu.main();
}
auto APU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
square1.run();
square2.run();
wave.run();
noise.run();
sequencer.run();
hipass(sequencer.center, sequencer.centerBias);
hipass(sequencer.left, sequencer.leftBias);
hipass(sequencer.right, sequencer.rightBias);
interface->audioSample(sequencer.left, sequencer.right);
if(cycle == 0) { //512hz
if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
square1.clockLength();
square2.clockLength();
wave.clockLength();
noise.clockLength();
}
square1.run();
square2.run();
wave.run();
noise.run();
sequencer.run();
hipass(sequencer.center, sequencer.centerBias);
hipass(sequencer.left, sequencer.leftBias);
hipass(sequencer.right, sequencer.rightBias);
interface->audioSample(sequencer.left, sequencer.right);
if(cycle == 0) { //512hz
if(phase == 0 || phase == 2 || phase == 4 || phase == 6) { //256hz
square1.clockLength();
square2.clockLength();
wave.clockLength();
noise.clockLength();
}
if(phase == 2 || phase == 6) { //128hz
square1.clockSweep();
}
if(phase == 7) { //64hz
square1.clockEnvelope();
square2.clockEnvelope();
noise.clockEnvelope();
}
phase++;
if(phase == 2 || phase == 6) { //128hz
square1.clockSweep();
}
cycle++;
clock += cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
if(phase == 7) { //64hz
square1.clockEnvelope();
square2.clockEnvelope();
noise.clockEnvelope();
}
phase++;
}
cycle++;
clock += cpu.frequency;
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
//filter to remove DC bias
@ -65,7 +57,7 @@ auto APU::hipass(int16& sample, int64& bias) -> void {
}
auto APU::power() -> void {
create(Main, 2 * 1024 * 1024);
create(Enter, 2 * 1024 * 1024);
for(uint n = 0xff10; n <= 0xff3f; n++) bus.mmio[n] = this;
square1.power();

View File

@ -1,5 +1,5 @@
struct APU : Thread, MMIO {
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto hipass(int16& sample, int64& bias) -> void;
auto power() -> void;

View File

@ -8,20 +8,13 @@ namespace GameBoy {
#include "serialization.cpp"
CPU cpu;
auto CPU::Main() -> void {
cpu.main();
auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
interrupt_test();
exec();
}
interrupt_test();
exec();
}
auto CPU::interrupt_raise(CPU::Interrupt id) -> void {
@ -102,7 +95,7 @@ auto CPU::stop() -> bool {
}
auto CPU::power() -> void {
create(Main, 4 * 1024 * 1024);
create(Enter, 4 * 1024 * 1024);
LR35902::power();
for(uint n = 0xc000; n <= 0xdfff; n++) bus.mmio[n] = this; //WRAM

View File

@ -1,7 +1,7 @@
struct CPU : Processor::LR35902, Thread, MMIO {
enum class Interrupt : uint { Vblank, Stat, Timer, Serial, Joypad };
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto interrupt_raise(Interrupt id) -> void;
auto interrupt_test() -> void;

View File

@ -19,13 +19,13 @@ auto CPU::add_clocks(uint clocks) -> void {
if((status.div & 1023) == 0) timer_4096hz();
ppu.clock -= ppu.frequency;
if(ppu.clock < 0) co_switch(scheduler.active_thread = ppu.thread);
if(ppu.clock < 0) co_switch(ppu.thread);
apu.clock -= apu.frequency;
if(apu.clock < 0) co_switch(scheduler.active_thread = apu.thread);
if(apu.clock < 0) co_switch(apu.thread);
}
if(system.sgb()) scheduler.exit(Scheduler::ExitReason::StepEvent);
if(system.sgb()) scheduler.exit(Scheduler::Event::Step);
}
auto CPU::timer_262144hz() -> void {

View File

@ -125,7 +125,7 @@ auto PPU::mmio_write(uint16 addr, uint8 data) -> void {
//restart cothread to begin new frame
auto clock = this->clock;
create(Main, 4 * 1024 * 1024);
create(Enter, 4 * 1024 * 1024);
this->clock = clock;
}

View File

@ -16,60 +16,55 @@ PPU ppu;
#include "cgb.cpp"
#include "serialization.cpp"
auto PPU::Main() -> void {
ppu.main();
auto PPU::Enter() -> void {
while(true) scheduler.synchronize(), ppu.main();
}
auto PPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification
if(status.display_enable) {
//LYC of zero triggers on LY==153
if((status.lyc && status.ly == status.lyc) || (!status.lyc && status.ly == 153)) {
if(status.interrupt_lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
status.lx = 0;
interface->lcdScanline(); //Super Game Boy notification
if(status.display_enable) {
//LYC of zero triggers on LY==153
if((status.lyc && status.ly == status.lyc) || (!status.lyc && status.ly == 153)) {
if(status.interrupt_lyc) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly <= 143) {
scanline();
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
if(status.ly == 144) {
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
else if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); //hardware quirk
cpu.interrupt_raise(CPU::Interrupt::Vblank);
}
}
add_clocks(92);
if(status.ly <= 143) {
for(auto n : range(160)) {
if(status.display_enable) run();
add_clocks(1);
}
if(status.display_enable) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
} else {
add_clocks(160);
scanline();
if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat);
}
add_clocks(204);
if(++status.ly == 154) {
status.ly = 0;
scheduler.exit(Scheduler::ExitReason::FrameEvent);
if(status.ly == 144) {
if(status.interrupt_vblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
else if(status.interrupt_oam) cpu.interrupt_raise(CPU::Interrupt::Stat); //hardware quirk
cpu.interrupt_raise(CPU::Interrupt::Vblank);
}
}
add_clocks(92);
if(status.ly <= 143) {
for(auto n : range(160)) {
if(status.display_enable) run();
add_clocks(1);
}
if(status.display_enable) {
if(status.interrupt_hblank) cpu.interrupt_raise(CPU::Interrupt::Stat);
cpu.hblank();
}
} else {
add_clocks(160);
}
add_clocks(204);
if(++status.ly == 154) {
status.ly = 0;
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
}
auto PPU::add_clocks(uint clocks) -> void {
@ -92,9 +87,7 @@ auto PPU::add_clocks(uint clocks) -> void {
status.lx++;
clock += cpu.frequency;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) {
co_switch(scheduler.active_thread = cpu.thread);
}
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
}
@ -106,7 +99,7 @@ auto PPU::hflip(uint data) const -> uint {
}
auto PPU::power() -> void {
create(Main, 4 * 1024 * 1024);
create(Enter, 4 * 1024 * 1024);
if(system.cgb()) {
scanline = {&PPU::cgb_scanline, this};

View File

@ -1,7 +1,7 @@
#include "video.hpp"
struct PPU : Thread, MMIO {
static auto Main() -> void;
static auto Enter() -> void;
auto main() -> void;
auto add_clocks(uint clocks) -> void;

View File

@ -4,20 +4,40 @@ namespace GameBoy {
Scheduler scheduler;
auto Scheduler::init() -> void {
host_thread = co_active();
active_thread = cpu.thread;
auto Scheduler::power() -> void {
host = co_active();
resume = cpu.thread;
}
auto Scheduler::enter() -> void {
host_thread = co_active();
co_switch(active_thread);
auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
return event;
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
active_thread = co_active();
co_switch(host_thread);
auto Scheduler::exit(Event event_) -> void {
event = event_;
resume = co_active();
co_switch(host);
}
auto Scheduler::synchronize(cothread_t thread) -> void {
if(thread == cpu.thread) {
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
} else {
resume = thread;
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
}
}
auto Scheduler::synchronize() -> void {
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
}
auto Scheduler::synchronizing() const -> bool {
return mode == Mode::SynchronizeAll;
}
}

View File

@ -1,14 +1,29 @@
struct Scheduler {
enum class SynchronizeMode : uint { None, CPU, All } sync;
enum class ExitReason : uint { UnknownEvent, StepEvent, FrameEvent, SynchronizeEvent };
enum class Mode : uint {
Run,
SynchronizeCPU,
SynchronizeAll,
};
auto init() -> void;
auto enter() -> void;
auto exit(ExitReason) -> void;
enum class Event : uint {
Unknown,
Step,
Frame,
Synchronize,
};
cothread_t host_thread = nullptr;
cothread_t active_thread = nullptr;
ExitReason exit_reason = ExitReason::UnknownEvent;
auto power() -> void;
auto enter(Mode = Mode::Run) -> Event;
auto exit(Event) -> void;
auto synchronize(cothread_t) -> void;
auto synchronize() -> void;
auto synchronizing() const -> bool;
private:
cothread_t host = nullptr;
cothread_t resume = nullptr;
Mode mode = Mode::Run;
Event event = Event::Unknown;
};
extern Scheduler scheduler;

View File

@ -16,37 +16,13 @@ System::System() {
}
auto System::run() -> void {
scheduler.sync = Scheduler::SynchronizeMode::None;
scheduler.enter();
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
auto System::runToSave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active_thread = ppu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active_thread = apu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
auto System::runThreadToSave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
scheduler.synchronize(cpu.thread);
scheduler.synchronize(ppu.thread);
scheduler.synchronize(apu.thread);
}
auto System::init() -> void {
@ -89,7 +65,7 @@ auto System::power() -> void {
ppu.power();
apu.power();
video.power();
scheduler.init();
scheduler.power();
_clocksExecuted = 0;
}

View File

@ -23,7 +23,6 @@ struct System {
auto run() -> void;
auto runToSave() -> void;
auto runThreadToSave() -> void;
auto init() -> void;
auto load(Revision) -> void;

View File

@ -15,13 +15,7 @@ namespace GameBoyAdvance {
APU apu;
auto APU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
apu.main();
}
while(true) scheduler.synchronize(), apu.main();
}
auto APU::main() -> void {
@ -76,11 +70,11 @@ auto APU::main() -> void {
auto APU::step(uint clocks) -> void {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto APU::power() -> void {
create(APU::Enter, 16777216);
create(APU::Enter, 16'777'216);
square1.power();
square2.power();

View File

@ -39,14 +39,7 @@ CPU::~CPU() {
}
auto CPU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
cpu.main();
}
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {

View File

@ -11,7 +11,6 @@ struct CPU : Processor::ARM, Thread, MMIO {
~CPU();
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void override;

View File

@ -38,13 +38,7 @@ PPU::~PPU() {
}
auto PPU::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
ppu.main();
}
while(true) scheduler.synchronize(), ppu.main();
}
auto PPU::main() -> void {
@ -53,11 +47,11 @@ auto PPU::main() -> void {
auto PPU::step(uint clocks) -> void {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto PPU::power() -> void {
create(PPU::Enter, 16777216);
create(PPU::Enter, 16'777'216);
for(uint n = 0; n < 240 * 160; n++) output[n] = 0;
@ -165,7 +159,8 @@ auto PPU::scanline() -> void {
auto PPU::frame() -> void {
player.frame();
scheduler.exit(Scheduler::ExitReason::FrameEvent);
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
}

View File

@ -4,27 +4,40 @@ namespace GameBoyAdvance {
Scheduler scheduler;
Scheduler::Scheduler() {
sync = SynchronizeMode::None;
exit_reason = ExitReason::UnknownEvent;
host = nullptr;
active = nullptr;
}
auto Scheduler::enter() -> void {
auto Scheduler::power() -> void {
host = co_active();
co_switch(active);
resume = cpu.thread;
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
active = co_active();
auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
return event;
}
auto Scheduler::exit(Event event_) -> void {
event = event_;
resume = co_active();
co_switch(host);
}
auto Scheduler::power() -> void {
host = co_active();
active = cpu.thread;
auto Scheduler::synchronize(cothread_t thread) -> void {
if(thread == cpu.thread) {
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
} else {
resume = thread;
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
}
}
auto Scheduler::synchronize() -> void {
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
}
auto Scheduler::synchronizing() const -> bool {
return mode == Mode::SynchronizeAll;
}
}

View File

@ -1,15 +1,28 @@
struct Scheduler : property<Scheduler> {
enum class SynchronizeMode : uint { None, CPU, All } sync;
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent };
readonly<ExitReason> exit_reason;
struct Scheduler {
enum class Mode : uint {
Run,
SynchronizeCPU,
SynchronizeAll,
};
cothread_t host;
cothread_t active;
enum class Event : uint {
Unknown,
Frame,
Synchronize,
};
Scheduler();
auto enter() -> void;
auto exit(ExitReason) -> void;
auto power() -> void;
auto enter(Mode = Mode::Run) -> Event;
auto exit(Event) -> void;
auto synchronize(cothread_t) -> void;
auto synchronize() -> void;
auto synchronizing() const -> bool;
private:
cothread_t host = nullptr;
cothread_t resume = nullptr;
Mode mode = Mode::Run;
Event event = Event::Unknown;
};
extern Scheduler scheduler;

View File

@ -46,36 +46,13 @@ auto System::unload() -> void {
}
auto System::run() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) break;
}
video.refresh();
while(scheduler.enter() != Scheduler::Event::Frame);
}
auto System::runToSave() -> void {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active = ppu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.active = apu.thread;
runThreadToSave();
scheduler.sync = Scheduler::SynchronizeMode::None;
}
auto System::runThreadToSave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason() == Scheduler::ExitReason::SynchronizeEvent) break;
if(scheduler.exit_reason() == Scheduler::ExitReason::FrameEvent) {
video.refresh();
}
}
scheduler.synchronize(cpu.thread);
scheduler.synchronize(ppu.thread);
scheduler.synchronize(apu.thread);
}
}

View File

@ -24,7 +24,6 @@ struct System {
auto power() -> void;
auto run() -> void;
auto runToSave() -> void;
auto runThreadToSave() -> void;
auto serialize() -> serializer;
auto unserialize(serializer&) -> bool;

View File

@ -1,4 +1,4 @@
sfc_objects := sfc-interface sfc-system sfc-controller
sfc_objects := sfc-interface sfc-system sfc-scheduler sfc-controller
sfc_objects += sfc-cartridge sfc-cheat
sfc_objects += sfc-memory sfc-cpu sfc-smp sfc-dsp sfc-ppu
sfc_objects += sfc-satellaview sfc-eboot
@ -35,6 +35,7 @@ endif
obj/sfc-interface.o: $(sfc)/interface/interface.cpp $(call rwildcard,$(sfc)/interface)
obj/sfc-system.o: $(sfc)/system/system.cpp $(call rwildcard,$(sfc)/system/)
obj/sfc-scheduler.o: $(sfc)/scheduler/scheduler.cpp $(call rwildcard,$(sfc)/scheduler/)
obj/sfc-controller.o: $(sfc)/controller/controller.cpp $(call rwildcard,$(sfc)/controller/)
obj/sfc-cartridge.o: $(sfc)/cartridge/cartridge.cpp $(call rwildcard,$(sfc)/cartridge/)
obj/sfc-cheat.o: $(sfc)/cheat/cheat.cpp $(call rwildcard,$(sfc)/cheat/)

View File

@ -85,7 +85,7 @@ private:
auto parseMarkup(const string&) -> void;
auto parseMarkupMap(Markup::Node, SuperFamicom::Memory&) -> void;
auto parseMarkupMap(Markup::Node, const function<uint8 (uint, uint8)>&, const function<void (uint, uint8)>&) -> void;
auto parseMarkupMemory(MappedRAM&, Markup::Node, unsigned id, bool writable) -> void;
auto parseMarkupMemory(MappedRAM&, Markup::Node, uint id, bool writable) -> void;
auto parseMarkupROM(Markup::Node) -> void;
auto parseMarkupRAM(Markup::Node) -> void;

View File

@ -8,15 +8,15 @@ auto Cheat::reset() -> void {
codes.reset();
}
auto Cheat::append(unsigned addr, unsigned data) -> void {
auto Cheat::append(uint addr, uint data) -> void {
codes.append({addr, Unused, data});
}
auto Cheat::append(unsigned addr, unsigned comp, unsigned data) -> void {
auto Cheat::append(uint addr, uint comp, uint data) -> void {
codes.append({addr, comp, data});
}
auto Cheat::find(unsigned addr, unsigned comp) -> maybe<unsigned> {
auto Cheat::find(uint addr, uint comp) -> maybe<unsigned> {
//WRAM mirroring: $00-3f,80-bf:0000-1fff -> $7e:0000-1fff
if((addr & 0x40e000) == 0x000000) addr = 0x7e0000 | (addr & 0x1fff);

View File

@ -1,17 +1,17 @@
struct Cheat {
enum : unsigned { Unused = ~0u };
enum : uint { Unused = ~0u };
alwaysinline auto enable() const -> bool { return codes.size() > 0; }
auto reset() -> void;
auto append(unsigned addr, unsigned data) -> void;
auto append(unsigned addr, unsigned comp, unsigned data) -> void;
auto find(unsigned addr, unsigned comp) -> maybe<unsigned>;
auto append(uint addr, uint data) -> void;
auto append(uint addr, uint comp, uint data) -> void;
auto find(uint addr, uint comp) -> maybe<uint>;
struct Code {
unsigned addr;
unsigned comp;
unsigned data;
uint addr;
uint comp;
uint data;
};
vector<Code> codes;
};

View File

@ -14,24 +14,26 @@ Controller::Controller(bool port) : port(port) {
}
auto Controller::Enter() -> void {
if(co_active() == device.controllerPort1->thread) device.controllerPort1->enter();
if(co_active() == device.controllerPort2->thread) device.controllerPort2->enter();
while(true) {
if(co_active() == device.controllerPort1->thread) device.controllerPort1->main();
if(co_active() == device.controllerPort2->thread) device.controllerPort2->main();
}
}
auto Controller::enter() -> void {
while(true) step(1);
auto Controller::main() -> void {
step(1);
}
auto Controller::step(unsigned clocks) -> void {
auto Controller::step(uint clocks) -> void {
clock += clocks * (uint64)cpu.frequency;
synchronizeCPU();
}
auto Controller::synchronizeCPU() -> void {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(CPU::Threaded) {
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
while(clock >= 0) cpu.main();
}
}

View File

@ -17,9 +17,9 @@ struct Controller : Thread {
Controller(bool port);
static auto Enter() -> void;
virtual auto enter() -> void;
virtual auto main() -> void;
auto step(unsigned clocks) -> void;
auto step(uint clocks) -> void;
auto synchronizeCPU() -> void;
auto iobit() -> bool;

View File

@ -7,6 +7,7 @@ device(chained == false ? (unsigned)Device::ID::Justifier : (unsigned)Device::ID
latched = 0;
counter = 0;
active = 0;
prev = 0;
player1.x = 256 / 2;
player1.y = 240 / 2;
@ -27,44 +28,41 @@ device(chained == false ? (unsigned)Device::ID::Justifier : (unsigned)Device::ID
}
}
auto Justifier::enter() -> void {
unsigned prev = 0;
while(true) {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
auto Justifier::main() -> void {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
signed x = (active == 0 ? player1.x : player2.x), y = (active == 0 ? player1.y : player2.y);
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
signed x = (active == 0 ? player1.x : player2.x), y = (active == 0 ? player1.y : player2.y);
bool offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
if(next < prev) {
int nx1 = interface->inputPoll(port, device, 0 + X);
int ny1 = interface->inputPoll(port, device, 0 + Y);
nx1 += player1.x;
ny1 += player1.y;
player1.x = max(-16, min(256 + 16, nx1));
player1.y = max(-16, min(240 + 16, ny1));
}
if(next < prev && chained) {
int nx2 = interface->inputPoll(port, device, 4 + X);
int ny2 = interface->inputPoll(port, device, 4 + Y);
nx2 += player2.x;
ny2 += player2.y;
player2.x = max(-16, min(256 + 16, nx2));
player2.y = max(-16, min(240 + 16, ny2));
}
prev = next;
step(2);
}
if(next < prev) {
int nx1 = interface->inputPoll(port, device, 0 + X);
int ny1 = interface->inputPoll(port, device, 0 + Y);
nx1 += player1.x;
ny1 += player1.y;
player1.x = max(-16, min(256 + 16, nx1));
player1.y = max(-16, min(240 + 16, ny1));
}
if(next < prev && chained) {
int nx2 = interface->inputPoll(port, device, 4 + X);
int ny2 = interface->inputPoll(port, device, 4 + Y);
nx2 += player2.x;
ny2 += player2.y;
player2.x = max(-16, min(256 + 16, nx2));
player2.y = max(-16, min(240 + 16, ny2));
}
prev = next;
step(2);
}
auto Justifier::data() -> uint2 {

View File

@ -5,7 +5,7 @@ struct Justifier : Controller {
Justifier(bool port, bool chained);
auto enter() -> void;
auto main() -> void;
auto data() -> uint2;
auto latch(bool data) -> void;
@ -14,6 +14,7 @@ struct Justifier : Controller {
const unsigned device;
bool latched;
unsigned counter;
unsigned prev;
bool active;
struct Player {

View File

@ -28,36 +28,35 @@ SuperScope::SuperScope(bool port) : Controller(port) {
turbolock = false;
triggerlock = false;
pauselock = false;
prev = 0;
}
auto SuperScope::enter() -> void {
unsigned prev = 0;
while(true) {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
auto SuperScope::main() -> void {
unsigned next = cpu.vcounter() * 1364 + cpu.hcounter();
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
if(offscreen == false) {
unsigned target = y * 1364 + (x + 24) * 4;
if(next >= target && prev < target) {
//CRT raster detected, toggle iobit to latch counters
iobit(0);
iobit(1);
}
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, X);
int ny = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
y = max(-16, min(240 + 16, ny));
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
}
prev = next;
step(2);
}
if(next < prev) {
//Vcounter wrapped back to zero; update cursor coordinates for start of new frame
int nx = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, X);
int ny = interface->inputPoll(port, (unsigned)Device::ID::SuperScope, Y);
nx += x;
ny += y;
x = max(-16, min(256 + 16, nx));
y = max(-16, min(240 + 16, ny));
offscreen = (x < 0 || y < 0 || x >= 256 || y >= (ppu.overscan() ? 240 : 225));
}
prev = next;
step(2);
}
auto SuperScope::data() -> uint2 {

View File

@ -5,7 +5,7 @@ struct SuperScope : Controller {
SuperScope(bool port);
auto enter() -> void;
auto main() -> void;
auto data() -> uint2;
auto latch(bool data) -> void;
@ -24,4 +24,6 @@ struct SuperScope : Controller {
bool turbolock;
bool triggerlock;
bool pauselock;
unsigned prev;
};

View File

@ -15,9 +15,9 @@
USART::USART(bool port) : Controller(port) {
string filename{interface->path(ID::SuperFamicom), "usart.so"};
if(openAbsolute(filename)) {
init = sym("usart_init");
main = sym("usart_main");
if(init && main) create(Controller::Enter, 10'000'000);
usart_init = sym("usart_init");
usart_main = sym("usart_main");
if(usart_init && usart_main) create(Controller::Enter, 10'000'000);
}
}
@ -25,9 +25,9 @@ USART::~USART() {
if(open()) close();
}
auto USART::enter() -> void {
if(init && main) {
init(
auto USART::main() -> void {
if(usart_init && usart_main) {
usart_init(
{&USART::quit, this},
{&USART::usleep, this},
{&USART::readable, this},
@ -35,7 +35,7 @@ auto USART::enter() -> void {
{&USART::writable, this},
{&USART::write, this}
);
main({});
usart_main({});
}
while(true) step(10'000'000);
}

View File

@ -2,7 +2,7 @@ struct USART : Controller, public library {
USART(bool port);
~USART();
auto enter() -> void;
auto main() -> void;
auto quit() -> bool;
auto usleep(uint microseconds) -> void;
@ -35,6 +35,6 @@ private:
function<uint8 ()>, //read
function<bool ()>, //writable
function<void (uint8)> //write
)> init;
function<void (lstring)> main;
)> usart_init;
function<void (lstring)> usart_main;
};

View File

@ -18,9 +18,12 @@ ArmDSP::~ArmDSP() {
delete[] programRAM;
}
auto ArmDSP::Enter() -> void { armdsp.enter(); }
auto ArmDSP::Enter() -> void {
armdsp.boot();
while(true) scheduler.synchronize(), armdsp.main();
}
auto ArmDSP::enter() -> void {
auto ArmDSP::boot() -> void {
//reset hold delay
while(bridge.reset) {
step(1);
@ -32,21 +35,17 @@ auto ArmDSP::enter() -> void {
step(65536);
bridge.ready = true;
}
}
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(crash) {
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
print(disassemble_registers(), "\n");
print("Executed: ", instructions, "\n");
while(true) step(frequency);
}
arm_step();
auto ArmDSP::main() -> void {
if(crash) {
print(disassemble_arm_instruction(pipeline.execute.address), "\n");
print(disassemble_registers(), "\n");
print("Executed: ", instructions, "\n");
while(true) step(frequency);
}
arm_step();
}
auto ArmDSP::step(uint clocks) -> void {
@ -55,7 +54,7 @@ auto ArmDSP::step(uint clocks) -> void {
synchronizeCPU();
}
//MMIO: $00-3f|80-bf:3800-38ff
//MMIO: 00-3f,80-bf:3800-38ff
//3800-3807 mirrored throughout
//a0 ignored
@ -119,7 +118,7 @@ auto ArmDSP::reset() -> void {
}
auto ArmDSP::resetARM() -> void {
create(ArmDSP::Enter, 21477272);
create(ArmDSP::Enter, 21'477'272);
ARM::power();
bridge.ready = false;

View File

@ -7,7 +7,8 @@ struct ArmDSP : Processor::ARM, Coprocessor {
~ArmDSP();
static auto Enter() -> void;
auto enter() -> void;
auto boot() -> void;
auto main() -> void;
auto step(uint clocks) -> void override;
auto bus_idle() -> void override;

View File

@ -29,5 +29,5 @@ auto Coprocessor::step(uint clocks) -> void {
}
auto Coprocessor::synchronizeCPU() -> void {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}

View File

@ -8,32 +8,26 @@ namespace SuperFamicom {
EpsonRTC epsonrtc;
auto EpsonRTC::Enter() -> void {
epsonrtc.enter();
while(true) scheduler.synchronize(), epsonrtc.main();
}
auto EpsonRTC::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto EpsonRTC::main() -> void {
if(wait) { if(--wait == 0) ready = 1; }
if(wait) { if(--wait == 0) ready = 1; }
clocks++;
if((clocks & ~0x00ff) == 0) round_seconds(); //125 microseconds
if((clocks & ~0x3fff) == 0) duty(); //1/128th second
if((clocks & ~0x7fff) == 0) irq(0); //1/64th second
if(clocks == 0) { //1 second
seconds++;
irq(1);
if(seconds % 60 == 0) irq(2); //1 minute
if(seconds % 1440 == 0) irq(3), seconds = 0; //1 hour
tick();
}
step(1);
synchronizeCPU();
clocks++;
if((clocks & ~0x00ff) == 0) round_seconds(); //125 microseconds
if((clocks & ~0x3fff) == 0) duty(); //1/128th second
if((clocks & ~0x7fff) == 0) irq(0); //1/64th second
if(clocks == 0) { //1 second
seconds++;
irq(1);
if(seconds % 60 == 0) irq(2); //1 minute
if(seconds % 1440 == 0) irq(3), seconds = 0; //1 hour
tick();
}
step(1);
synchronizeCPU();
}
auto EpsonRTC::init() -> void {

View File

@ -2,7 +2,7 @@
struct EpsonRTC : Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;

View File

@ -5,33 +5,27 @@ namespace SuperFamicom {
Event event;
auto Event::Enter() -> void {
event.enter();
while(true) scheduler.synchronize(), event.main();
}
auto Event::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
auto Event::main() -> void {
if(scoreActive && scoreSecondsRemaining) {
if(--scoreSecondsRemaining == 0) {
scoreActive = false;
}
if(scoreActive && scoreSecondsRemaining) {
if(--scoreSecondsRemaining == 0) {
scoreActive = false;
}
}
if(timerActive && timerSecondsRemaining) {
if(--timerSecondsRemaining == 0) {
timerActive = false;
status |= 0x02; //time over
scoreActive = true;
scoreSecondsRemaining = 5;
}
}
step(1);
synchronizeCPU();
}
if(timerActive && timerSecondsRemaining) {
if(--timerSecondsRemaining == 0) {
timerActive = false;
status |= 0x02; //time over
scoreActive = true;
scoreSecondsRemaining = 5;
}
}
step(1);
synchronizeCPU();
}
auto Event::init() -> void {

View File

@ -4,7 +4,7 @@
struct Event : Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;

View File

@ -6,27 +6,23 @@ namespace SuperFamicom {
#include "serialization.cpp"
HitachiDSP hitachidsp;
auto HitachiDSP::Enter() -> void { hitachidsp.enter(); }
auto HitachiDSP::Enter() -> void {
while(true) scheduler.synchronize(), hitachidsp.main();
}
auto HitachiDSP::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
auto HitachiDSP::main() -> void {
if(mmio.dma) {
for(auto n : range(mmio.dma_length)) {
bus_write(mmio.dma_target + n, bus_read(mmio.dma_source + n));
step(2);
}
if(mmio.dma) {
for(auto n : range(mmio.dma_length)) {
bus_write(mmio.dma_target + n, bus_read(mmio.dma_source + n));
step(2);
}
mmio.dma = false;
}
exec(mmio.program_offset);
step(1);
synchronizeCPU();
mmio.dma = false;
}
exec(mmio.program_offset);
step(1);
synchronizeCPU();
}
auto HitachiDSP::init() -> void {

View File

@ -5,7 +5,7 @@ struct HitachiDSP : Processor::HG51B, Coprocessor {
#include "mmio.hpp"
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;

View File

@ -7,27 +7,26 @@ namespace SuperFamicom {
#include "serialization.cpp"
ICD2 icd2;
auto ICD2::Enter() -> void { icd2.enter(); }
auto ICD2::enter() -> void {
auto ICD2::Enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
GameBoy::system.runToSave();
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(r6003 & 0x80) {
GameBoy::system.run();
step(GameBoy::system._clocksExecuted);
GameBoy::system._clocksExecuted = 0;
} else { //DMG halted
audio.coprocessorSample(0, 0);
step(1);
}
synchronizeCPU();
if(scheduler.synchronizing()) GameBoy::system.runToSave();
scheduler.synchronize();
icd2.main();
}
}
auto ICD2::main() -> void {
if(r6003 & 0x80) {
GameBoy::system.run();
step(GameBoy::system._clocksExecuted);
GameBoy::system._clocksExecuted = 0;
} else { //DMG halted
audio.coprocessorSample(0, 0);
step(1);
}
synchronizeCPU();
}
auto ICD2::init() -> void {
}

View File

@ -1,6 +1,6 @@
struct ICD2 : Emulator::Interface::Bind, GameBoy::Interface::Hook, Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;

View File

@ -6,45 +6,41 @@ MSU1 msu1;
#include "serialization.cpp"
auto MSU1::Enter() -> void { msu1.enter(); }
auto MSU1::Enter() -> void {
while(true) scheduler.synchronize(), msu1.main();
}
auto MSU1::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto MSU1::main() -> void {
int16 left = 0, right = 0;
int16 left = 0, right = 0;
if(mmio.audioPlay) {
if(audioFile.open()) {
if(audioFile.end()) {
if(!mmio.audioRepeat) {
mmio.audioPlay = false;
audioFile.seek(mmio.audioPlayOffset = 8);
} else {
audioFile.seek(mmio.audioPlayOffset = mmio.audioLoopOffset);
}
if(mmio.audioPlay) {
if(audioFile.open()) {
if(audioFile.end()) {
if(!mmio.audioRepeat) {
mmio.audioPlay = false;
audioFile.seek(mmio.audioPlayOffset = 8);
} else {
mmio.audioPlayOffset += 4;
left = audioFile.readl(2);
right = audioFile.readl(2);
audioFile.seek(mmio.audioPlayOffset = mmio.audioLoopOffset);
}
} else {
mmio.audioPlay = false;
mmio.audioPlayOffset += 4;
left = audioFile.readl(2);
right = audioFile.readl(2);
}
} else {
mmio.audioPlay = false;
}
int lchannel = (double)left * (double)mmio.audioVolume / 255.0;
int rchannel = (double)right * (double)mmio.audioVolume / 255.0;
left = sclamp<16>(lchannel);
right = sclamp<16>(rchannel);
if(dsp.mute()) left = 0, right = 0;
audio.coprocessorSample(left, right);
step(1);
synchronizeCPU();
}
int lchannel = (double)left * (double)mmio.audioVolume / 255.0;
int rchannel = (double)right * (double)mmio.audioVolume / 255.0;
left = sclamp<16>(lchannel);
right = sclamp<16>(rchannel);
if(dsp.mute()) left = 0, right = 0;
audio.coprocessorSample(left, right);
step(1);
synchronizeCPU();
}
auto MSU1::init() -> void {

View File

@ -1,7 +1,6 @@
struct MSU1 : Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;

View File

@ -5,18 +5,14 @@ namespace SuperFamicom {
#include "serialization.cpp"
NECDSP necdsp;
auto NECDSP::Enter() -> void { necdsp.enter(); }
auto NECDSP::Enter() -> void {
while(true) scheduler.synchronize(), necdsp.main();
}
auto NECDSP::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
exec();
step(1);
synchronizeCPU();
}
auto NECDSP::main() -> void {
exec();
step(1);
synchronizeCPU();
}
auto NECDSP::read(uint addr, uint8) -> uint8 {

View File

@ -1,6 +1,6 @@
struct NECDSP : Processor::uPD96050, Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto read(uint addr, uint8 data) -> uint8;
auto write(uint addr, uint8 data) -> void;

View File

@ -10,29 +10,25 @@ SA1 sa1;
#include "memory/memory.cpp"
#include "mmio/mmio.cpp"
auto SA1::Enter() -> void { sa1.enter(); }
auto SA1::Enter() -> void {
while(true) scheduler.synchronize(), sa1.main();
}
auto SA1::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(mmio.sa1_rdyb || mmio.sa1_resb) {
//SA-1 co-processor is asleep
tick();
synchronizeCPU();
continue;
}
if(status.interrupt_pending) {
status.interrupt_pending = false;
op_irq();
continue;
}
op_exec();
auto SA1::main() -> void {
if(mmio.sa1_rdyb || mmio.sa1_resb) {
//SA-1 co-processor is asleep
tick();
synchronizeCPU();
return;
}
if(status.interrupt_pending) {
status.interrupt_pending = false;
op_irq();
return;
}
op_exec();
}
auto SA1::op_irq() -> void {

View File

@ -15,7 +15,7 @@ struct SA1 : Processor::R65816, public Coprocessor {
} status;
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto tick() -> void;
auto op_irq() -> void;

View File

@ -8,20 +8,14 @@ namespace SuperFamicom {
SharpRTC sharprtc;
auto SharpRTC::Enter() -> void {
sharprtc.enter();
while(true) scheduler.synchronize(), sharprtc.main();
}
auto SharpRTC::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto SharpRTC::main() -> void {
tick_second();
tick_second();
step(1);
synchronizeCPU();
}
step(1);
synchronizeCPU();
}
auto SharpRTC::init() -> void {

View File

@ -1,6 +1,6 @@
struct SharpRTC : Coprocessor {
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;

View File

@ -16,20 +16,15 @@ SPC7110::~SPC7110() {
delete decompressor;
}
auto SPC7110::Enter() -> void { spc7110.enter(); }
auto SPC7110::Enter() -> void {
while(true) scheduler.synchronize(), spc7110.main();
}
auto SPC7110::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
if(dcu_pending) { dcu_pending = 0; dcu_begin_transfer(); }
if(mul_pending) { mul_pending = 0; alu_multiply(); }
if(div_pending) { div_pending = 0; alu_divide(); }
add_clocks(1);
}
auto SPC7110::main() -> void {
if(dcu_pending) { dcu_pending = 0; dcu_begin_transfer(); }
if(mul_pending) { mul_pending = 0; alu_multiply(); }
if(div_pending) { div_pending = 0; alu_divide(); }
add_clocks(1);
}
auto SPC7110::add_clocks(uint clocks) -> void {

View File

@ -5,7 +5,7 @@ struct SPC7110 : Coprocessor {
~SPC7110();
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;

View File

@ -1,6 +1,6 @@
auto SuperFX::bus_read(unsigned addr) -> uint8 {
if((addr & 0xc00000) == 0x000000) { //$00-3f:0000-7fff, $00-3f:8000-ffff
while(!regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) {
while(!regs.scmr.ron && !scheduler.synchronizing()) {
step(6);
synchronizeCPU();
}
@ -8,7 +8,7 @@ auto SuperFX::bus_read(unsigned addr) -> uint8 {
}
if((addr & 0xe00000) == 0x400000) { //$40-5f:0000-ffff
while(!regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) {
while(!regs.scmr.ron && !scheduler.synchronizing()) {
step(6);
synchronizeCPU();
}
@ -16,7 +16,7 @@ auto SuperFX::bus_read(unsigned addr) -> uint8 {
}
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
while(!regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) {
while(!regs.scmr.ran && !scheduler.synchronizing()) {
step(6);
synchronizeCPU();
}
@ -26,7 +26,7 @@ auto SuperFX::bus_read(unsigned addr) -> uint8 {
auto SuperFX::bus_write(unsigned addr, uint8 data) -> void {
if((addr & 0xe00000) == 0x600000) { //$60-7f:0000-ffff
while(!regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) {
while(!regs.scmr.ran && !scheduler.synchronizing()) {
step(6);
synchronizeCPU();
}

View File

@ -13,24 +13,15 @@ namespace SuperFamicom {
SuperFX superfx;
auto SuperFX::Enter() -> void {
superfx.enter();
while(true) scheduler.synchronize(), superfx.main();
}
auto SuperFX::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto SuperFX::main() -> void {
if(regs.sfr.g == 0) return step(6);
if(regs.sfr.g == 0) {
step(6);
continue;
}
unsigned opcode = regs.sfr.alt2 << 9 | regs.sfr.alt1 << 8 | peekpipe();
(this->*opcode_table[opcode])();
if(!r15_modified) regs.r[15]++;
}
uint opcode = regs.sfr.alt2 << 9 | regs.sfr.alt1 << 8 | peekpipe();
(this->*opcode_table[opcode])();
if(!r15_modified) regs.r[15]++;
}
auto SuperFX::init() -> void {

View File

@ -11,8 +11,7 @@ struct SuperFX : Processor::GSU, Coprocessor {
//superfx.cpp
static auto Enter() -> void;
auto enter() -> void;
auto main() -> void;
auto init() -> void;
auto load() -> void;
auto unload() -> void;

View File

@ -17,9 +17,8 @@ CPU::CPU() {
auto CPU::step(uint clocks) -> void {
smp.clock -= clocks * (uint64)smp.frequency;
ppu.clock -= clocks;
for(unsigned i = 0; i < coprocessors.size(); i++) {
auto& chip = *coprocessors[i];
chip.clock -= clocks * (uint64)chip.frequency;
for(auto chip : coprocessors) {
chip->clock -= clocks * (uint64)chip->frequency;
}
device.controllerPort1->clock -= clocks * (uint64)device.controllerPort1->frequency;
device.controllerPort2->clock -= clocks * (uint64)device.controllerPort2->frequency;
@ -27,25 +26,24 @@ auto CPU::step(uint clocks) -> void {
}
auto CPU::synchronizeSMP() -> void {
if(SMP::Threaded == true) {
if(SMP::Threaded) {
if(smp.clock < 0) co_switch(smp.thread);
} else {
while(smp.clock < 0) smp.enter();
while(smp.clock < 0) smp.main();
}
}
auto CPU::synchronizePPU() -> void {
if(PPU::Threaded == true) {
if(PPU::Threaded) {
if(ppu.clock < 0) co_switch(ppu.thread);
} else {
while(ppu.clock < 0) ppu.enter();
while(ppu.clock < 0) ppu.main();
}
}
auto CPU::synchronizeCoprocessors() -> void {
for(unsigned i = 0; i < coprocessors.size(); i++) {
auto& chip = *coprocessors[i];
if(chip.clock < 0) co_switch(chip.thread);
for(auto chip : coprocessors) {
if(chip->clock < 0) co_switch(chip->thread);
}
}
@ -54,37 +52,32 @@ auto CPU::synchronizeDevices() -> void {
if(device.controllerPort2->clock < 0) co_switch(device.controllerPort2->thread);
}
auto CPU::Enter() -> void { cpu.enter(); }
auto CPU::Enter() -> void {
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
auto CPU::main() -> void {
if(status.interrupt_pending) {
status.interrupt_pending = false;
if(status.nmi_pending) {
status.nmi_pending = false;
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
op_irq();
debugger.op_nmi();
} else if(status.irq_pending) {
status.irq_pending = false;
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
op_irq();
debugger.op_irq();
} else if(status.reset_pending) {
status.reset_pending = false;
add_clocks(186);
regs.pc.l = bus.read(0xfffc, regs.mdr);
regs.pc.h = bus.read(0xfffd, regs.mdr);
}
if(status.interrupt_pending) {
status.interrupt_pending = false;
if(status.nmi_pending) {
status.nmi_pending = false;
regs.vector = (regs.e == false ? 0xffea : 0xfffa);
op_irq();
debugger.op_nmi();
} else if(status.irq_pending) {
status.irq_pending = false;
regs.vector = (regs.e == false ? 0xffee : 0xfffe);
op_irq();
debugger.op_irq();
} else if(status.reset_pending) {
status.reset_pending = false;
add_clocks(186);
regs.pc.l = bus.read(0xfffc, regs.mdr);
regs.pc.h = bus.read(0xfffd, regs.mdr);
}
}
op_step();
}
op_step();
}
auto CPU::op_step() -> void {

View File

@ -16,7 +16,7 @@ struct CPU : Processor::R65816, Thread, public PPUcounter {
auto joylatch() -> bool;
alwaysinline auto interrupt_pending() -> bool { return status.interrupt_pending; }
auto enter() -> void;
auto main() -> void;
auto enable() -> void;
auto power() -> void;
auto reset() -> void;

View File

@ -31,177 +31,173 @@ DSP::DSP() {
/* timing */
auto DSP::step(unsigned clocks) -> void {
auto DSP::step(uint clocks) -> void {
clock += clocks;
}
auto DSP::synchronizeSMP() -> void {
if(SMP::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
if(SMP::Threaded) {
if(clock >= 0 && !scheduler.synchronizing()) co_switch(smp.thread);
} else {
while(clock >= 0) smp.enter();
while(clock >= 0) smp.main();
}
}
auto DSP::Enter() -> void { dsp.enter(); }
auto DSP::Enter() -> void {
while(true) scheduler.synchronize(), dsp.main();
}
auto DSP::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto DSP::main() -> void {
voice5(voice[0]);
voice2(voice[1]);
tick();
voice5(voice[0]);
voice2(voice[1]);
tick();
voice6(voice[0]);
voice3(voice[1]);
tick();
voice6(voice[0]);
voice3(voice[1]);
tick();
voice7(voice[0]);
voice4(voice[1]);
voice1(voice[3]);
tick();
voice7(voice[0]);
voice4(voice[1]);
voice1(voice[3]);
tick();
voice8(voice[0]);
voice5(voice[1]);
voice2(voice[2]);
tick();
voice8(voice[0]);
voice5(voice[1]);
voice2(voice[2]);
tick();
voice9(voice[0]);
voice6(voice[1]);
voice3(voice[2]);
tick();
voice9(voice[0]);
voice6(voice[1]);
voice3(voice[2]);
tick();
voice7(voice[1]);
voice4(voice[2]);
voice1(voice[4]);
tick();
voice7(voice[1]);
voice4(voice[2]);
voice1(voice[4]);
tick();
voice8(voice[1]);
voice5(voice[2]);
voice2(voice[3]);
tick();
voice8(voice[1]);
voice5(voice[2]);
voice2(voice[3]);
tick();
voice9(voice[1]);
voice6(voice[2]);
voice3(voice[3]);
tick();
voice9(voice[1]);
voice6(voice[2]);
voice3(voice[3]);
tick();
voice7(voice[2]);
voice4(voice[3]);
voice1(voice[5]);
tick();
voice7(voice[2]);
voice4(voice[3]);
voice1(voice[5]);
tick();
voice8(voice[2]);
voice5(voice[3]);
voice2(voice[4]);
tick();
voice8(voice[2]);
voice5(voice[3]);
voice2(voice[4]);
tick();
voice9(voice[2]);
voice6(voice[3]);
voice3(voice[4]);
tick();
voice9(voice[2]);
voice6(voice[3]);
voice3(voice[4]);
tick();
voice7(voice[3]);
voice4(voice[4]);
voice1(voice[6]);
tick();
voice7(voice[3]);
voice4(voice[4]);
voice1(voice[6]);
tick();
voice8(voice[3]);
voice5(voice[4]);
voice2(voice[5]);
tick();
voice8(voice[3]);
voice5(voice[4]);
voice2(voice[5]);
tick();
voice9(voice[3]);
voice6(voice[4]);
voice3(voice[5]);
tick();
voice9(voice[3]);
voice6(voice[4]);
voice3(voice[5]);
tick();
voice7(voice[4]);
voice4(voice[5]);
voice1(voice[7]);
tick();
voice7(voice[4]);
voice4(voice[5]);
voice1(voice[7]);
tick();
voice8(voice[4]);
voice5(voice[5]);
voice2(voice[6]);
tick();
voice8(voice[4]);
voice5(voice[5]);
voice2(voice[6]);
tick();
voice9(voice[4]);
voice6(voice[5]);
voice3(voice[6]);
tick();
voice9(voice[4]);
voice6(voice[5]);
voice3(voice[6]);
tick();
voice1(voice[0]);
voice7(voice[5]);
voice4(voice[6]);
tick();
voice1(voice[0]);
voice7(voice[5]);
voice4(voice[6]);
tick();
voice8(voice[5]);
voice5(voice[6]);
voice2(voice[7]);
tick();
voice8(voice[5]);
voice5(voice[6]);
voice2(voice[7]);
tick();
voice9(voice[5]);
voice6(voice[6]);
voice3(voice[7]);
tick();
voice9(voice[5]);
voice6(voice[6]);
voice3(voice[7]);
tick();
voice1(voice[1]);
voice7(voice[6]);
voice4(voice[7]);
tick();
voice1(voice[1]);
voice7(voice[6]);
voice4(voice[7]);
tick();
voice8(voice[6]);
voice5(voice[7]);
voice2(voice[0]);
tick();
voice8(voice[6]);
voice5(voice[7]);
voice2(voice[0]);
tick();
voice3a(voice[0]);
voice9(voice[6]);
voice6(voice[7]);
echo22();
tick();
voice3a(voice[0]);
voice9(voice[6]);
voice6(voice[7]);
echo22();
tick();
voice7(voice[7]);
echo23();
tick();
voice7(voice[7]);
echo23();
tick();
voice8(voice[7]);
echo24();
tick();
voice8(voice[7]);
echo24();
tick();
voice3b(voice[0]);
voice9(voice[7]);
echo25();
tick();
voice3b(voice[0]);
voice9(voice[7]);
echo25();
tick();
echo26();
tick();
echo26();
tick();
misc27();
echo27();
tick();
misc27();
echo27();
tick();
misc28();
echo28();
tick();
misc28();
echo28();
tick();
misc29();
echo29();
tick();
misc29();
echo29();
tick();
misc30();
voice3c(voice[0]);
echo30();
tick();
misc30();
voice3c(voice[0]);
echo30();
tick();
voice4(voice[0]);
voice1(voice[2]);
tick();
}
voice4(voice[0]);
voice1(voice[2]);
tick();
}
auto DSP::tick() -> void {

View File

@ -14,7 +14,7 @@ struct DSP : Thread {
auto read(uint8 addr) -> uint8;
auto write(uint8 addr, uint8 data) -> void;
auto enter() -> void;
auto main() -> void;
auto power() -> void;
auto reset() -> void;

View File

@ -11,7 +11,7 @@ auto eBoot::load() -> void {
resetVector = bus.read(0xfffc, 0x00) << 0;
resetVector |= bus.read(0xfffd, 0x00) << 8;
for(auto& byte : ram) byte = 0xdb;
for(auto& byte : ram) byte = 0xdb; //stp
ram[0] = 0x6c; //jmp ($fffc)
ram[1] = 0xfc;
ram[2] = 0xff;

View File

@ -32,56 +32,52 @@ auto PPU::step(uint clocks) -> void {
}
auto PPU::synchronizeCPU() -> void {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(CPU::Threaded) {
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
while(clock >= 0) cpu.main();
}
}
auto PPU::Enter() -> void { ppu.enter(); }
auto PPU::Enter() -> void {
while(true) scheduler.synchronize(), ppu.main();
}
auto PPU::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
auto PPU::main() -> void {
scanline();
add_clocks(28);
bg1.begin();
bg2.begin();
bg3.begin();
bg4.begin();
scanline();
add_clocks(28);
bg1.begin();
bg2.begin();
bg3.begin();
bg4.begin();
if(vcounter() <= 239) {
for(int pixel = -7; pixel <= 255; pixel++) {
bg1.run(1);
bg2.run(1);
bg3.run(1);
bg4.run(1);
add_clocks(2);
if(vcounter() <= 239) {
for(signed pixel = -7; pixel <= 255; pixel++) {
bg1.run(1);
bg2.run(1);
bg3.run(1);
bg4.run(1);
add_clocks(2);
bg1.run(0);
bg2.run(0);
bg3.run(0);
bg4.run(0);
if(pixel >= 0) {
sprite.run();
window.run();
screen.run();
}
add_clocks(2);
bg1.run(0);
bg2.run(0);
bg3.run(0);
bg4.run(0);
if(pixel >= 0) {
sprite.run();
window.run();
screen.run();
}
add_clocks(14);
sprite.tilefetch();
} else {
add_clocks(1052 + 14 + 136);
add_clocks(2);
}
add_clocks(lineclocks() - 28 - 1052 - 14 - 136);
add_clocks(14);
sprite.tilefetch();
} else {
add_clocks(1052 + 14 + 136);
}
add_clocks(lineclocks() - 28 - 1052 - 14 - 136);
}
auto PPU::add_clocks(uint clocks) -> void {
@ -144,6 +140,7 @@ auto PPU::scanline() -> void {
if(vcounter() == 241) {
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
}

View File

@ -13,7 +13,7 @@ struct PPU : Thread, public PPUcounter {
auto interlace() const -> bool;
auto overscan() const -> bool;
auto enter() -> void;
auto main() -> void;
auto enable() -> void;
auto power() -> void;
auto reset() -> void;

View File

@ -102,7 +102,6 @@ auto Video::refresh() -> void {
drawCursors();
interface->videoRefresh(output - (ppu.overscan() ? 0 : 7 * 1024), 512 * sizeof(uint32), 512, 480);
scheduler.exit(Scheduler::ExitReason::FrameEvent);
}
auto Video::drawCursor(uint32 color, int x, int y) -> void {

View File

@ -1,22 +1,43 @@
#include <sfc/sfc.hpp>
namespace SuperFamicom {
Scheduler scheduler;
auto Scheduler::init() -> void {
host_thread = co_active();
thread = cpu.thread;
sync = SynchronizeMode::None;
auto Scheduler::reset() -> void {
host = co_active();
resume = cpu.thread;
}
auto Scheduler::enter() -> void {
host_thread = co_active();
co_switch(thread);
auto Scheduler::enter(Mode mode_) -> Event {
mode = mode_;
host = co_active();
co_switch(resume);
return event;
}
auto Scheduler::exit(ExitReason reason) -> void {
exit_reason = reason;
thread = co_active();
co_switch(host_thread);
auto Scheduler::exit(Event event_) -> void {
event = event_;
resume = co_active();
co_switch(host);
}
auto Scheduler::synchronize(cothread_t thread) -> void {
if(thread == cpu.thread) {
while(enter(Mode::SynchronizeCPU) != Event::Synchronize);
} else {
resume = thread;
while(enter(Mode::SynchronizeAll) != Event::Synchronize);
}
}
auto Scheduler::synchronize() -> void {
if(co_active() == cpu.thread && mode == Mode::SynchronizeCPU) return exit(Event::Synchronize);
if(co_active() != cpu.thread && mode == Mode::SynchronizeAll) return exit(Event::Synchronize);
}
auto Scheduler::synchronizing() const -> bool {
return mode == Mode::SynchronizeAll;
}
auto Scheduler::debug() -> void {
exit(ExitReason::DebuggerEvent);
}

View File

@ -1,15 +1,29 @@
struct Scheduler {
enum class SynchronizeMode : uint { None, CPU, All } sync;
enum class ExitReason : uint { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
enum class Mode : uint {
Run,
SynchronizeCPU,
SynchronizeAll,
};
auto init() -> void;
auto enter() -> void;
auto exit(ExitReason) -> void;
auto debug() -> void;
enum class Event : uint {
Unknown,
Frame,
Synchronize,
Debugger,
};
cothread_t host_thread = nullptr; //program thread (used to exit emulation)
cothread_t thread = nullptr; //active emulation thread (used to enter emulation)
ExitReason exit_reason = ExitReason::UnknownEvent;
auto reset() -> void;
auto enter(Mode = Mode::Run) -> Event;
auto exit(Event) -> void;
auto synchronize(cothread_t) -> void;
auto synchronize() -> void;
auto synchronizing() const -> bool;
private:
cothread_t host = nullptr; //program thread (used to exit emulation)
cothread_t resume = nullptr; //resume thread (used to re-enter emulation)
Mode mode = Mode::Run; //determines when to exit emulation thread
Event event = Event::Unknown; //set by exit(), returned by enter()
};
extern Scheduler scheduler;

View File

@ -65,6 +65,7 @@ namespace SuperFamicom {
#include <sfc/controller/controller.hpp>
#include <sfc/system/system.hpp>
#include <sfc/scheduler/scheduler.hpp>
#include <sfc/expansion/expansion.hpp>
#include <sfc/coprocessor/coprocessor.hpp>
#include <sfc/slot/slot.hpp>

View File

@ -15,31 +15,27 @@ auto SMP::step(uint clocks) -> void {
auto SMP::synchronizeCPU() -> void {
if(CPU::Threaded) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
while(clock >= 0) cpu.main();
}
}
auto SMP::synchronizeDSP() -> void {
if(DSP::Threaded) {
if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread);
if(dsp.clock < 0 && !scheduler.synchronizing()) co_switch(dsp.thread);
} else {
while(dsp.clock < 0) dsp.enter();
while(dsp.clock < 0) dsp.main();
}
}
auto SMP::Enter() -> void { smp.enter(); }
auto SMP::Enter() -> void {
while(true) scheduler.synchronize(), smp.main();
}
auto SMP::enter() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
debugger.op_exec(regs.pc);
op_step();
}
auto SMP::main() -> void {
debugger.op_exec(regs.pc);
op_step();
}
auto SMP::power() -> void {

View File

@ -10,7 +10,7 @@ struct SMP : Processor::SPC700, Thread {
auto portRead(uint2 port) const -> uint8;
auto portWrite(uint2 port, uint8 data) -> void;
auto enter() -> void;
auto main() -> void;
auto power() -> void;
auto reset() -> void;

View File

@ -8,8 +8,6 @@ System system;
#include "random.cpp"
#include "serialization.cpp"
#include <sfc/scheduler/scheduler.cpp>
auto System::loaded() const -> bool { return _loaded; }
auto System::region() const -> Region { return _region; }
auto System::expansionPort() const -> Device::ID { return _expansionPort; }
@ -17,42 +15,16 @@ auto System::cpuFrequency() const -> uint { return _cpuFrequency; }
auto System::apuFrequency() const -> uint { return _apuFrequency; }
auto System::run() -> void {
scheduler.sync = Scheduler::SynchronizeMode::None;
scheduler.enter();
}
auto System::runToSave() -> void {
if(CPU::Threaded) {
scheduler.sync = Scheduler::SynchronizeMode::CPU;
runThreadToSave();
}
if(SMP::Threaded) {
scheduler.thread = smp.thread;
runThreadToSave();
}
if(PPU::Threaded) {
scheduler.thread = ppu.thread;
runThreadToSave();
}
if(DSP::Threaded) {
scheduler.thread = dsp.thread;
runThreadToSave();
}
for(uint n = 0; n < cpu.coprocessors.size(); n++) {
auto& chip = *cpu.coprocessors[n];
scheduler.thread = chip.thread;
runThreadToSave();
}
}
auto System::runThreadToSave() -> void {
while(true) {
scheduler.enter();
if(scheduler.exit_reason == Scheduler::ExitReason::SynchronizeEvent) break;
if(CPU::Threaded) scheduler.synchronize(cpu.thread);
if(SMP::Threaded) scheduler.synchronize(smp.thread);
if(PPU::Threaded) scheduler.synchronize(ppu.thread);
if(DSP::Threaded) scheduler.synchronize(dsp.thread);
for(auto chip : cpu.coprocessors) {
scheduler.synchronize(chip->thread);
}
}
@ -99,8 +71,8 @@ auto System::load() -> void {
cartridge.load();
_region = cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL;
_expansionPort = (Device::ID)settings.expansionPort;
_cpuFrequency = region() == Region::NTSC ? 21477272 : 21281370;
_apuFrequency = 24606720;
_cpuFrequency = region() == Region::NTSC ? 21'477'272 : 21'281'370;
_apuFrequency = 24'606'720;
bus.reset();
bus.map();
@ -233,7 +205,7 @@ auto System::reset() -> void {
if(cartridge.hasSPC7110()) cpu.coprocessors.append(&spc7110);
if(cartridge.hasMSU1()) cpu.coprocessors.append(&msu1);
scheduler.init();
scheduler.reset();
device.connect(0, (Device::ID)settings.controllerPort1);
device.connect(1, (Device::ID)settings.controllerPort2);
device.connect(2, (Device::ID)settings.expansionPort);

View File

@ -29,8 +29,6 @@ struct System {
} information;
private:
auto runThreadToSave() -> void;
auto serialize(serializer&) -> void;
auto serializeAll(serializer&) -> void;
auto serializeInit() -> void;
@ -48,8 +46,6 @@ private:
extern System system;
#include <sfc/scheduler/scheduler.hpp>
struct Random {
auto seed(uint seed) -> void;
auto operator()(uint result) -> uint;

View File

@ -5,27 +5,21 @@ namespace WonderSwan {
APU apu;
auto APU::Enter() -> void {
apu.main();
while(true) scheduler.synchronize(), apu.main();
}
auto APU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
step(128);
interface->audioSample(0, 0);
}
step(128);
interface->audioSample(0, 0);
}
auto APU::step(uint clocks) -> void {
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto APU::power() -> void {
create(APU::Enter, 3072000);
create(APU::Enter, 3'072'000);
}
}

View File

@ -1,6 +1,5 @@
struct APU : Thread {
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;
auto power() -> void;

View File

@ -9,19 +9,12 @@ CPU cpu;
#include "dma.cpp"
auto CPU::Enter() -> void {
cpu.main();
while(true) scheduler.synchronize(), cpu.main();
}
auto CPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
poll();
exec();
}
poll();
exec();
}
auto CPU::step(uint clocks) -> void {
@ -54,7 +47,7 @@ auto CPU::out(uint16 port, uint8 data) -> void {
auto CPU::power() -> void {
V30MZ::power();
create(CPU::Enter, 3072000);
create(CPU::Enter, 3'072'000);
iomap[0x00a0] = this;
iomap[0x00b0] = this;

View File

@ -11,7 +11,6 @@ struct CPU : Processor::V30MZ, Thread, IO {
};
static auto Enter() -> void;
auto main() -> void;
auto step(uint clocks) -> void;

View File

@ -7,18 +7,12 @@ PPU ppu;
#include "video.cpp"
auto PPU::Enter() -> void {
ppu.main();
while(true) scheduler.synchronize(), ppu.main();
}
auto PPU::main() -> void {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
step(256);
scanline();
}
step(256);
scanline();
}
auto PPU::scanline() -> void {
@ -33,17 +27,18 @@ auto PPU::scanline() -> void {
auto PPU::frame() -> void {
status.vclk = 0;
video.refresh();
scheduler.exit(Scheduler::Event::Frame);
}
auto PPU::step(uint clocks) -> void {
status.hclk += clocks;
clock += clocks;
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
if(clock >= 0 && !scheduler.synchronizing()) co_switch(cpu.thread);
}
auto PPU::power() -> void {
create(PPU::Enter, 3072000);
create(PPU::Enter, 3'072'000);
for(uint n = 0x0000; n <= 0x0001; n++) iomap[n] = this;
for(uint n = 0x0004; n <= 0x0007; n++) iomap[n] = this;

View File

@ -2,7 +2,6 @@
struct PPU : Thread, IO {
static auto Enter() -> void;
auto main() -> void;
auto scanline() -> void;
auto frame() -> void;

Some files were not shown because too many files have changed in this diff Show More