mirror of https://github.com/bsnes-emu/bsnes.git
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:
parent
32a95a9761
commit
6c83329cae
|
@ -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/";
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#include "video.hpp"
|
||||
|
||||
struct PPU : Thread {
|
||||
static auto Main() -> void;
|
||||
static auto Enter() -> void;
|
||||
auto main() -> void;
|
||||
auto tick() -> void;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -3,7 +3,6 @@ struct System {
|
|||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
auto runThreadToSave() -> void;
|
||||
|
||||
auto load() -> void;
|
||||
auto unload() -> void;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
@ -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};
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -23,7 +23,6 @@ struct System {
|
|||
|
||||
auto run() -> void;
|
||||
auto runToSave() -> void;
|
||||
auto runThreadToSave() -> void;
|
||||
|
||||
auto init() -> void;
|
||||
auto load(Revision) -> void;
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -11,7 +11,6 @@ struct CPU : Processor::ARM, Thread, MMIO {
|
|||
~CPU();
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
|
||||
auto step(uint clocks) -> void override;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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/)
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
};
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
|
||||
struct EpsonRTC : Coprocessor {
|
||||
static auto Enter() -> void;
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto init() -> void;
|
||||
auto load() -> void;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
struct SharpRTC : Coprocessor {
|
||||
static auto Enter() -> void;
|
||||
auto enter() -> void;
|
||||
auto main() -> void;
|
||||
|
||||
auto init() -> void;
|
||||
auto load() -> void;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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;
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
struct APU : Thread {
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
auto power() -> void;
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -11,7 +11,6 @@ struct CPU : Processor::V30MZ, Thread, IO {
|
|||
};
|
||||
|
||||
static auto Enter() -> void;
|
||||
|
||||
auto main() -> void;
|
||||
auto step(uint clocks) -> void;
|
||||
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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
Loading…
Reference in New Issue