mirror of https://github.com/bsnes-emu/bsnes.git
Update to bsnes v065r05 release.
Fairly major changes here, I'd appreciate some testing if anyone's not busy. Note that the save state version has been bumped, so consider WIP saves unstable until v066 official. Rewrote the entire scheduling system. Instead of having a global class that all of the chips call into, each class now contains its own thread, clock and frequency information. They also implement their own syncing primitives, but with a tiny bit of standardization. void step(unsigned clocks) -- add to/subtract from counter based on master/slave select. void synchronize_chip() -- make sure chip is caught up to our current thread before continuing. So we go from scheduler.sync_copcpu() to sa1.synchronize_cpu(); with the sa1. being omitted inside the SA1 class itself. The S-CPU implementation also adds an array<Processor*> coprocessors; list, and iterates them all. This allows bsnes to have an infinite number of additional coprocessors at the same time. But there is still the limitation of only one of each type. So you can use the XML memory mapping to make a cartridge that contains a SuperFX2 chip, an SA-1 chip, an MSU1 chip and that can be debugged via serial communications. However, you can't make a cart that has two SA-1 chips. That limitation probably could be overcome as well, but it's less important. I mainly wanted to be able to use MSU1 and Serial on special chip games. I implemented the synchronization primitives in the chip base classes, which means that for inlining purposes I had to make it all global, so you'll have src/cpu/synchronization.hpp, etc now. This was to reduce some redundancy of having the same exact code inside both bPPU and sPPU, etc. I'll probably make a Coprocessor : Processor class to automatically implement the step+synchronize_cpu functions for the five coprocessors next. The Scheduler class is actually still around, but it's very trivial now. It simply saves the program thread and last active emulation thread for the purpose of entering and exiting emulation. Because I killed Scheduler::init(), which was responsible for destroying and re- creating threads, I had to create the threads inside ChipName::create(), which I call at power and reset. This means that to load a save state, the system needs to be reset to destroy those threads. This caused some troubles with the SA-1, specifically in Kirby's Dreamland 3, but no other games I tried. I had to move the SA-1 bus initialization to the power-on event, and only reset when loading a save state. It would appear that the SA-1 is leaking bus mapping state information, presumably from the SA-1 MMIO registers that control some of the mapping layout. If I add remapping of those sections into the SA1::serialize() function, it should take care of that problem and I can move sa1bus.init() back into SA1::reset(). All of this results in a 2-3% speed hit for normal games, and a 6-7% speed hit for special chip games. The former should not have any speed hit, so I will have to look into what's going on there. The latter we have no choice on, to allow for more than one coprocessor, the coprocessor synchronization needs to iterate a list, compared to a single hard-coded check in the previous builds. If I can figure out what is happening with the regular game speeds, it should drop the special chip hit to 4%. Worst part is this hit is in addition to the 10-15% speed hit from v065 official where I wasn't syncing the special chips up to the CPU except once per scanline and on MMIO accesses. But that's progress. v065 is definitely going to be the preferred build for playing SuperFX/SA-1 games for a long time to come.
This commit is contained in:
parent
77375c3c68
commit
53f03be5a2
|
@ -8,8 +8,6 @@ MSU1 msu1;
|
|||
#include "serialization.cpp"
|
||||
|
||||
void MSU1::enter() {
|
||||
scheduler.clock.cop_freq = 44100;
|
||||
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
|
@ -36,8 +34,8 @@ void MSU1::enter() {
|
|||
right = sclamp<16>((double)right * (double)mmio.audio_volume / 255.0);
|
||||
|
||||
audio.coprocessor_sample(left, right);
|
||||
scheduler.addclocks_cop(1);
|
||||
scheduler.sync_copcpu();
|
||||
step(1);
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -57,6 +55,7 @@ void MSU1::power() {
|
|||
}
|
||||
|
||||
void MSU1::reset() {
|
||||
create();
|
||||
mmio.data_offset = 0;
|
||||
mmio.audio_offset = 0;
|
||||
mmio.audio_track = 0;
|
||||
|
|
|
@ -1,7 +1,11 @@
|
|||
class MSU1 : public MMIO {
|
||||
class MSU1 : public Processor, public MMIO {
|
||||
public:
|
||||
void enter();
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
void enter();
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifdef MSU1_CPP
|
||||
|
||||
void MSU1::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
|
||||
s.integer(mmio.data_offset);
|
||||
s.integer(mmio.audio_offset);
|
||||
s.integer(mmio.audio_track);
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void msu1_enter() { msu1.enter(); }
|
||||
|
||||
void MSU1::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), msu1_enter);
|
||||
frequency = 44100;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void MSU1::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
}
|
||||
|
||||
void MSU1::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -93,12 +93,12 @@ unsigned SA1IRAM::size() const {
|
|||
}
|
||||
|
||||
uint8 SA1IRAM::read(unsigned addr) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
return memory::iram.read(addr);
|
||||
}
|
||||
|
||||
void SA1IRAM::write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
memory::iram.write(addr, data);
|
||||
}
|
||||
|
||||
|
@ -111,12 +111,12 @@ unsigned CPUIRAM::size() const {
|
|||
}
|
||||
|
||||
uint8 CPUIRAM::read(unsigned addr) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
return memory::iram.read(addr);
|
||||
}
|
||||
|
||||
void CPUIRAM::write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
memory::iram.write(addr, data);
|
||||
}
|
||||
|
||||
|
@ -129,12 +129,12 @@ unsigned SA1BWRAM::size() const {
|
|||
}
|
||||
|
||||
uint8 SA1BWRAM::read(unsigned addr) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
return memory::cartram.read(addr);
|
||||
}
|
||||
|
||||
void SA1BWRAM::write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
memory::cartram.write(addr, data);
|
||||
}
|
||||
|
||||
|
@ -147,13 +147,13 @@ unsigned CC1BWRAM::size() const {
|
|||
}
|
||||
|
||||
uint8 CC1BWRAM::read(unsigned addr) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
if(dma) return sa1.dma_cc1_read(addr);
|
||||
return memory::cartram.read(addr);
|
||||
}
|
||||
|
||||
void CC1BWRAM::write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
memory::cartram.write(addr, data);
|
||||
}
|
||||
|
||||
|
@ -166,7 +166,7 @@ unsigned BitmapRAM::size() const {
|
|||
}
|
||||
|
||||
uint8 BitmapRAM::read(unsigned addr) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
|
||||
if(sa1.mmio.bbf == 0) {
|
||||
//4bpp
|
||||
|
@ -190,7 +190,7 @@ uint8 BitmapRAM::read(unsigned addr) {
|
|||
}
|
||||
|
||||
void BitmapRAM::write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_copcpu();
|
||||
sa1.synchronize_cpu();
|
||||
|
||||
if(sa1.mmio.bbf == 0) {
|
||||
//4bpp
|
||||
|
|
|
@ -521,7 +521,7 @@ uint8 SA1::mmio_r230e() {
|
|||
}
|
||||
|
||||
uint8 SA1::mmio_read(unsigned addr) {
|
||||
(co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu());
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
|
@ -546,7 +546,7 @@ uint8 SA1::mmio_read(unsigned addr) {
|
|||
}
|
||||
|
||||
void SA1::mmio_write(unsigned addr, uint8 data) {
|
||||
(co_active() == scheduler.thread_cpu ? scheduler.sync_cpucop() : scheduler.sync_copcpu());
|
||||
(co_active() == cpu.thread ? cpu.synchronize_coprocessor() : synchronize_cpu());
|
||||
addr &= 0xffff;
|
||||
|
||||
switch(addr) {
|
||||
|
|
|
@ -20,7 +20,7 @@ void SA1::enter() {
|
|||
if(mmio.sa1_rdyb || mmio.sa1_resb) {
|
||||
//SA-1 co-processor is asleep
|
||||
tick();
|
||||
scheduler.sync_copcpu();
|
||||
synchronize_cpu();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -78,8 +78,8 @@ bool SA1::interrupt_pending() {
|
|||
}
|
||||
|
||||
void SA1::tick() {
|
||||
scheduler.addclocks_cop(2);
|
||||
if(++status.tick_counter == 0) scheduler.sync_copcpu();
|
||||
step(2);
|
||||
if(++status.tick_counter == 0) synchronize_cpu();
|
||||
|
||||
//adjust counters:
|
||||
//note that internally, status counters are in clocks;
|
||||
|
@ -122,17 +122,18 @@ void SA1::enable() {
|
|||
void SA1::power() {
|
||||
regs.a = regs.x = regs.y = 0x0000;
|
||||
regs.s = 0x01ff;
|
||||
|
||||
vbrbus.init();
|
||||
sa1bus.init();
|
||||
reset();
|
||||
}
|
||||
|
||||
void SA1::reset() {
|
||||
create();
|
||||
|
||||
memory::cc1bwram.dma = false;
|
||||
for(unsigned addr = 0; addr < memory::iram.size(); addr++) {
|
||||
memory::iram.write(addr, 0x00);
|
||||
}
|
||||
vbrbus.init();
|
||||
sa1bus.init();
|
||||
|
||||
regs.pc.d = 0x000000;
|
||||
regs.x.h = 0x00;
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#include "bus/bus.hpp"
|
||||
|
||||
class SA1 : public CPUcore, public MMIO {
|
||||
class SA1 : public Processor, public CPUcore, public MMIO {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
#include "dma/dma.hpp"
|
||||
#include "memory/memory.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifdef SA1_CPP
|
||||
|
||||
void SA1::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
CPUcore::core_serialize(s);
|
||||
|
||||
//sa1.hpp
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void sa1_enter() { sa1.enter(); }
|
||||
|
||||
void SA1::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), sa1_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.cpu.ntsc_clock_rate : config.cpu.pal_clock_rate;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void SA1::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
}
|
||||
|
||||
void SA1::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -5,21 +5,22 @@ namespace SNES {
|
|||
|
||||
Serial serial;
|
||||
|
||||
#include "serialization.cpp"
|
||||
|
||||
static void snesserial_tick(unsigned clocks) { serial.add_clocks(clocks * 8); }
|
||||
static uint8 snesserial_read() { return serial.read(); }
|
||||
static void snesserial_write(uint8 data) { serial.write(data); }
|
||||
|
||||
void Serial::enter() {
|
||||
scheduler.clock.cop_freq = cartridge.serial_baud_rate() * 8; //over-sample for edge detection
|
||||
latch = 0;
|
||||
add_clocks(256 * 8); //warm-up
|
||||
if(snesserial_main) snesserial_main(snesserial_tick, snesserial_read, snesserial_write);
|
||||
while(true) add_clocks(scheduler.clock.cop_freq); //snesserial_main() fallback
|
||||
while(true) add_clocks(frequency); //snesserial_main() fallback
|
||||
}
|
||||
|
||||
void Serial::add_clocks(unsigned clocks) {
|
||||
scheduler.addclocks_cop(clocks);
|
||||
scheduler.sync_copcpu();
|
||||
step(clocks);
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
uint8 Serial::read() {
|
||||
|
@ -63,9 +64,11 @@ void Serial::enable() {
|
|||
}
|
||||
|
||||
void Serial::power() {
|
||||
reset();
|
||||
}
|
||||
|
||||
void Serial::reset() {
|
||||
create();
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -1,11 +1,16 @@
|
|||
class Serial : property<Serial>, public library {
|
||||
class Serial : public Processor, public library, public property<Serial> {
|
||||
public:
|
||||
void enter();
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
void enter();
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
void serialize(serializer&);
|
||||
|
||||
readonly<bool> latch;
|
||||
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
#ifdef SERIAL_CPP
|
||||
|
||||
void Serial::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
s.integer((bool&)latch);
|
||||
}
|
||||
|
||||
#endif
|
|
@ -0,0 +1,16 @@
|
|||
inline void serial_enter() { serial.enter(); }
|
||||
|
||||
void Serial::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), serial_enter);
|
||||
frequency = cartridge.serial_baud_rate() * 8;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void Serial::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
}
|
||||
|
||||
void Serial::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -27,7 +27,7 @@ unsigned SuperFXGSUROM::size() const {
|
|||
uint8 SuperFXGSUROM::read(unsigned addr) {
|
||||
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
superfx.synchronize_cpu();
|
||||
}
|
||||
return memory::cartrom.read(addr);
|
||||
}
|
||||
|
@ -35,7 +35,7 @@ uint8 SuperFXGSUROM::read(unsigned addr) {
|
|||
void SuperFXGSUROM::write(unsigned addr, uint8 data) {
|
||||
while(!superfx.regs.scmr.ron && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
superfx.synchronize_cpu();
|
||||
}
|
||||
memory::cartrom.write(addr, data);
|
||||
}
|
||||
|
@ -47,7 +47,7 @@ unsigned SuperFXGSURAM::size() const {
|
|||
uint8 SuperFXGSURAM::read(unsigned addr) {
|
||||
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
superfx.synchronize_cpu();
|
||||
}
|
||||
return memory::cartram.read(addr);
|
||||
}
|
||||
|
@ -55,7 +55,7 @@ uint8 SuperFXGSURAM::read(unsigned addr) {
|
|||
void SuperFXGSURAM::write(unsigned addr, uint8 data) {
|
||||
while(!superfx.regs.scmr.ran && scheduler.sync != Scheduler::SynchronizeMode::All) {
|
||||
superfx.add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
superfx.synchronize_cpu();
|
||||
}
|
||||
memory::cartram.write(addr, data);
|
||||
}
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifdef SUPERFX_CPP
|
||||
|
||||
uint8 SuperFX::mmio_read(unsigned addr) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
|
@ -53,7 +53,7 @@ uint8 SuperFX::mmio_read(unsigned addr) {
|
|||
}
|
||||
|
||||
void SuperFX::mmio_write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpucop();
|
||||
cpu.synchronize_coprocessor();
|
||||
addr &= 0xffff;
|
||||
|
||||
if(addr >= 0x3100 && addr <= 0x32ff) {
|
||||
|
|
|
@ -1,6 +1,8 @@
|
|||
#ifdef SUPERFX_CPP
|
||||
|
||||
void SuperFX::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
|
||||
//superfx.hpp
|
||||
s.integer(clockmode);
|
||||
s.integer(instruction_counter);
|
||||
|
|
|
@ -21,7 +21,7 @@ void SuperFX::enter() {
|
|||
|
||||
if(regs.sfr.g == 0) {
|
||||
add_clocks(6);
|
||||
scheduler.sync_copcpu();
|
||||
synchronize_cpu();
|
||||
continue;
|
||||
}
|
||||
|
||||
|
@ -30,7 +30,7 @@ void SuperFX::enter() {
|
|||
|
||||
if(++instruction_counter >= 128) {
|
||||
instruction_counter = 0;
|
||||
scheduler.sync_copcpu();
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -50,6 +50,7 @@ void SuperFX::power() {
|
|||
}
|
||||
|
||||
void SuperFX::reset() {
|
||||
create();
|
||||
superfxbus.init();
|
||||
instruction_counter = 0;
|
||||
|
||||
|
|
|
@ -1,7 +1,12 @@
|
|||
#include "bus/bus.hpp"
|
||||
|
||||
class SuperFX : public MMIO {
|
||||
class SuperFX : public Processor, public MMIO {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
#include "core/core.hpp"
|
||||
#include "memory/memory.hpp"
|
||||
#include "mmio/mmio.hpp"
|
||||
|
@ -9,12 +14,10 @@ public:
|
|||
#include "disasm/disasm.hpp"
|
||||
|
||||
void enter();
|
||||
|
||||
void init();
|
||||
void enable();
|
||||
void power();
|
||||
void reset();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
||||
private:
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void superfx_enter() { superfx.enter(); }
|
||||
|
||||
void SuperFX::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), superfx_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.cpu.ntsc_clock_rate : config.cpu.pal_clock_rate;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void SuperFX::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
}
|
||||
|
||||
void SuperFX::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -16,8 +16,8 @@ void SuperFX::add_clocks(unsigned clocks) {
|
|||
}
|
||||
}
|
||||
|
||||
scheduler.addclocks_cop(clocks);
|
||||
scheduler.sync_copcpu();
|
||||
step(clocks);
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
void SuperFX::rombuffer_sync() {
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
#ifdef SUPERGAMEBOY_CPP
|
||||
|
||||
void SuperGameBoy::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
s.integer(row);
|
||||
if(sgb_serialize) sgb_serialize(s);
|
||||
}
|
||||
|
|
|
@ -8,16 +8,14 @@ SuperGameBoy supergameboy;
|
|||
#include "serialization.cpp"
|
||||
|
||||
void SuperGameBoy::enter() {
|
||||
scheduler.clock.cop_freq = (cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? 2147727 : 2097152);
|
||||
|
||||
if(!sgb_run) while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
audio.coprocessor_sample(0, 0);
|
||||
scheduler.addclocks_cop(1);
|
||||
scheduler.sync_copcpu();
|
||||
step(1);
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
while(true) {
|
||||
|
@ -34,8 +32,8 @@ void SuperGameBoy::enter() {
|
|||
audio.coprocessor_sample(left / 3, right / 3);
|
||||
}
|
||||
|
||||
scheduler.addclocks_cop(samples);
|
||||
scheduler.sync_copcpu();
|
||||
step(samples);
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -113,6 +111,7 @@ void SuperGameBoy::enable() {
|
|||
}
|
||||
|
||||
void SuperGameBoy::power() {
|
||||
create();
|
||||
audio.coprocessor_enable(true);
|
||||
audio.coprocessor_frequency(cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? 2147727.0 : 2097152.0);
|
||||
|
||||
|
@ -126,6 +125,7 @@ void SuperGameBoy::power() {
|
|||
}
|
||||
|
||||
void SuperGameBoy::reset() {
|
||||
create();
|
||||
if(sgb_reset) sgb_reset();
|
||||
}
|
||||
|
||||
|
|
|
@ -1,5 +1,10 @@
|
|||
class SuperGameBoy : public MMIO, public Memory, public library {
|
||||
class SuperGameBoy : public Processor, public MMIO, public Memory, public library {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
void enter();
|
||||
void save();
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void supergameboy_enter() { supergameboy.enter(); }
|
||||
|
||||
void SuperGameBoy::create() {
|
||||
thread = co_create(65536 * sizeof(void*), supergameboy_enter);
|
||||
frequency = cartridge.supergameboy_version() == Cartridge::SuperGameBoyVersion::Version1 ? 2147727 : 2097152;
|
||||
if(frequency == 2147727) frequency = system.region() == System::Region::NTSC ? (config.cpu.ntsc_clock_rate / 10) : (config.cpu.pal_clock_rate / 10);
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void SuperGameBoy::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
}
|
||||
|
||||
void SuperGameBoy::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -8,14 +8,17 @@ namespace SNES {
|
|||
#endif
|
||||
|
||||
void CPU::power() {
|
||||
create();
|
||||
cpu_version = config.cpu.version;
|
||||
}
|
||||
|
||||
void CPU::reset() {
|
||||
create();
|
||||
PPUCounter::reset();
|
||||
}
|
||||
|
||||
void CPU::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
PPUCounter::serialize(s);
|
||||
s.integer(cpu_version);
|
||||
}
|
||||
|
|
|
@ -2,8 +2,16 @@
|
|||
#include "cpu-debugger.hpp"
|
||||
#endif
|
||||
|
||||
class CPU : public PPUCounter, public MMIO {
|
||||
class CPU : public Processor, public PPUCounter, public MMIO {
|
||||
public:
|
||||
//synchronization
|
||||
array<Processor*> coprocessors;
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_smp();
|
||||
alwaysinline void synchronize_ppu();
|
||||
alwaysinline void synchronize_coprocessor();
|
||||
|
||||
virtual void enter() = 0;
|
||||
|
||||
//CPU version number
|
||||
|
|
|
@ -3,8 +3,8 @@
|
|||
void sCPU::dma_add_clocks(unsigned clocks) {
|
||||
status.dma_clocks += clocks;
|
||||
add_clocks(clocks);
|
||||
scheduler.sync_cpucop();
|
||||
scheduler.sync_cpuppu();
|
||||
CPU::synchronize_ppu();
|
||||
CPU::synchronize_coprocessor();
|
||||
}
|
||||
|
||||
//=============
|
||||
|
|
|
@ -429,7 +429,7 @@ uint8 sCPU::mmio_read(unsigned addr) {
|
|||
|
||||
//APU
|
||||
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
|
||||
scheduler.sync_cpusmp();
|
||||
synchronize_smp();
|
||||
return smp.port_read(addr & 3);
|
||||
}
|
||||
|
||||
|
@ -486,7 +486,7 @@ void sCPU::mmio_write(unsigned addr, uint8 data) {
|
|||
|
||||
//APU
|
||||
if((addr & 0xffc0) == 0x2140) { //$2140-$217f
|
||||
scheduler.sync_cpusmp();
|
||||
synchronize_smp();
|
||||
port_write(addr & 3, data);
|
||||
return;
|
||||
}
|
||||
|
|
|
@ -17,8 +17,9 @@ void sCPU::add_clocks(unsigned clocks) {
|
|||
poll_interrupts();
|
||||
}
|
||||
}
|
||||
scheduler.addclocks_cpu(clocks);
|
||||
scheduler.sync_cpucop();
|
||||
|
||||
step(clocks);
|
||||
synchronize_coprocessor();
|
||||
|
||||
if(status.dram_refreshed == false && hcounter() >= status.dram_refresh_position) {
|
||||
status.dram_refreshed = true;
|
||||
|
@ -32,9 +33,9 @@ void sCPU::scanline() {
|
|||
status.line_clocks = lineclocks();
|
||||
|
||||
//forcefully sync S-CPU to other processors, in case chips are not communicating
|
||||
scheduler.sync_cpuppu();
|
||||
scheduler.sync_cpucop();
|
||||
scheduler.sync_cpusmp();
|
||||
synchronize_ppu();
|
||||
synchronize_smp();
|
||||
synchronize_coprocessor();
|
||||
system.scanline();
|
||||
|
||||
if(vcounter() == 0) {
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
inline void cpu_enter() { cpu.enter(); }
|
||||
|
||||
void CPU::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), cpu_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.cpu.ntsc_clock_rate : config.cpu.pal_clock_rate;
|
||||
clock = 0;
|
||||
coprocessors.reset();
|
||||
}
|
||||
|
||||
void CPU::step(unsigned clocks) {
|
||||
smp.clock -= clocks * (uint64)smp.frequency;
|
||||
ppu.clock -= clocks;
|
||||
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
chip.clock -= clocks * (uint64)chip.frequency;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::synchronize_smp() {
|
||||
if(smp.clock < 0) co_switch(smp.thread);
|
||||
}
|
||||
|
||||
void CPU::synchronize_ppu() {
|
||||
if(ppu.clock < 0) co_switch(ppu.thread);
|
||||
}
|
||||
|
||||
void CPU::synchronize_coprocessor() {
|
||||
for(unsigned i = 0; i < coprocessors.size(); i++) {
|
||||
Processor &chip = *coprocessors[i];
|
||||
if(chip.clock < 0) co_switch(chip.thread);
|
||||
}
|
||||
}
|
|
@ -201,6 +201,8 @@ int n = addr & 15;
|
|||
}
|
||||
|
||||
void aDSP::power() {
|
||||
DSP::power();
|
||||
|
||||
spcram = memory::apuram.data();
|
||||
memset(dspram, 0x00, 128);
|
||||
|
||||
|
@ -234,6 +236,8 @@ void aDSP::power() {
|
|||
}
|
||||
|
||||
void aDSP::reset() {
|
||||
DSP::reset();
|
||||
|
||||
status.KON = 0x00;
|
||||
status.KOFF = 0x00;
|
||||
status.FLG |= 0xe0;
|
||||
|
@ -599,8 +603,8 @@ int32 fir_samplel, fir_sampler;
|
|||
}
|
||||
|
||||
audio.sample(msamplel, msampler);
|
||||
scheduler.addclocks_dsp(32 * 3 * 8);
|
||||
scheduler.sync_dspsmp();
|
||||
step(32 * 3 * 8);
|
||||
synchronize_smp();
|
||||
}
|
||||
|
||||
aDSP::aDSP() {}
|
||||
|
|
|
@ -7,4 +7,16 @@ namespace SNES {
|
|||
#include "dsp-debugger.cpp"
|
||||
#endif
|
||||
|
||||
void DSP::power() {
|
||||
create();
|
||||
}
|
||||
|
||||
void DSP::reset() {
|
||||
create();
|
||||
}
|
||||
|
||||
void DSP::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,15 +2,19 @@
|
|||
#include "dsp-debugger.hpp"
|
||||
#endif
|
||||
|
||||
class DSP {
|
||||
class DSP : public Processor {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_smp();
|
||||
virtual void enter() = 0;
|
||||
|
||||
virtual uint8 read(uint8 addr) = 0;
|
||||
virtual void write(uint8 addr, uint8 data) = 0;
|
||||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
|
||||
virtual void serialize(serializer&) {}
|
||||
virtual void serialize(serializer&);
|
||||
};
|
||||
|
|
|
@ -25,12 +25,12 @@ namespace SNES {
|
|||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent); \
|
||||
}
|
||||
#define phase(n)
|
||||
#define tick() scheduler.addclocks_dsp(3 * 8); scheduler.sync_dspsmp()
|
||||
#define tick() step(3 * 8); synchronize_smp()
|
||||
#define phase_end() }
|
||||
#else
|
||||
#define phase_start() switch(phase_index) {
|
||||
#define phase(n) case n:
|
||||
#define tick() scheduler.addclocks_dsp(3 * 8); break
|
||||
#define tick() step(3 * 8); break
|
||||
#define phase_end() } phase_index = (phase_index + 1) & 31;
|
||||
#endif
|
||||
|
||||
|
@ -258,6 +258,8 @@ void sDSP::write(uint8 addr, uint8 data) {
|
|||
/* initialization */
|
||||
|
||||
void sDSP::power() {
|
||||
DSP::power();
|
||||
|
||||
memset(&state.regs, 0, sizeof state.regs);
|
||||
state.echo_hist_pos = 0;
|
||||
state.every_other_sample = false;
|
||||
|
@ -309,6 +311,8 @@ void sDSP::power() {
|
|||
}
|
||||
|
||||
void sDSP::reset() {
|
||||
DSP::reset();
|
||||
|
||||
REG(flg) = 0xe0;
|
||||
|
||||
state.noise = 0x4000;
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void dsp_enter() { dsp.enter(); }
|
||||
|
||||
void DSP::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), dsp_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.smp.ntsc_clock_rate : config.smp.pal_clock_rate;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void DSP::step(unsigned clocks) {
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
void DSP::synchronize_smp() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(smp.thread);
|
||||
}
|
|
@ -59,8 +59,8 @@ void bPPU::enter() {
|
|||
|
||||
void bPPU::add_clocks(unsigned clocks) {
|
||||
tick(clocks);
|
||||
scheduler.addclocks_ppu(clocks);
|
||||
scheduler.sync_ppucpu();
|
||||
step(clocks);
|
||||
synchronize_cpu();
|
||||
}
|
||||
|
||||
void bPPU::scanline() {
|
||||
|
|
|
@ -571,7 +571,7 @@ uint8 r = 0x00;
|
|||
}
|
||||
|
||||
uint8 bPPU::mmio_read(unsigned addr) {
|
||||
scheduler.sync_cpuppu();
|
||||
synchronize_cpu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2104:
|
||||
|
@ -610,7 +610,7 @@ uint8 bPPU::mmio_read(unsigned addr) {
|
|||
}
|
||||
|
||||
void bPPU::mmio_write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpuppu();
|
||||
synchronize_cpu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2100: return mmio_w2100(data); //INIDISP
|
||||
|
|
|
@ -24,11 +24,13 @@ void PPU::frame() {
|
|||
}
|
||||
|
||||
void PPU::power() {
|
||||
create();
|
||||
ppu1_version = config.ppu1.version;
|
||||
ppu2_version = config.ppu2.version;
|
||||
}
|
||||
|
||||
void PPU::reset() {
|
||||
create();
|
||||
PPUCounter::reset();
|
||||
memset(output, 0, 512 * 480 * sizeof(uint16));
|
||||
}
|
||||
|
|
|
@ -52,8 +52,13 @@ private:
|
|||
} history;
|
||||
};
|
||||
|
||||
class PPU : public PPUCounter, public MMIO {
|
||||
class PPU : public Processor, public PPUCounter, public MMIO {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
|
||||
virtual void enter() = 0;
|
||||
|
||||
uint16 *output;
|
||||
|
|
|
@ -13,6 +13,7 @@ void PPUCounter::serialize(serializer &s) {
|
|||
}
|
||||
|
||||
void PPU::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
PPUCounter::serialize(s);
|
||||
|
||||
s.integer(status.frames_updated);
|
||||
|
|
|
@ -1,7 +1,7 @@
|
|||
#ifdef SPPU_CPP
|
||||
|
||||
void sPPU::latch_counters() {
|
||||
scheduler.sync_cpuppu();
|
||||
cpu.synchronize_ppu();
|
||||
regs.hcounter = hdot();
|
||||
regs.vcounter = vcounter();
|
||||
regs.counters_latched = true;
|
||||
|
@ -767,7 +767,7 @@ void sPPU::mmio_reset() {
|
|||
}
|
||||
|
||||
uint8 sPPU::mmio_read(unsigned addr) {
|
||||
scheduler.sync_cpuppu();
|
||||
cpu.synchronize_ppu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2104:
|
||||
|
@ -806,7 +806,7 @@ uint8 sPPU::mmio_read(unsigned addr) {
|
|||
}
|
||||
|
||||
void sPPU::mmio_write(unsigned addr, uint8 data) {
|
||||
scheduler.sync_cpuppu();
|
||||
cpu.synchronize_ppu();
|
||||
|
||||
switch(addr & 0xffff) {
|
||||
case 0x2100: return mmio_w2100(data); //INIDISP
|
||||
|
|
|
@ -55,8 +55,8 @@ void sPPU::add_clocks(unsigned clocks) {
|
|||
clocks >>= 1;
|
||||
while(clocks--) {
|
||||
tick(2);
|
||||
scheduler.addclocks_ppu(2);
|
||||
scheduler.sync_ppucpu();
|
||||
step(2);
|
||||
synchronize_cpu();
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -0,0 +1,16 @@
|
|||
inline void ppu_enter() { ppu.enter(); }
|
||||
|
||||
void PPU::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), ppu_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.cpu.ntsc_clock_rate : config.cpu.pal_clock_rate;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void PPU::step(unsigned clocks) {
|
||||
clock += clocks;
|
||||
}
|
||||
|
||||
void PPU::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
|
@ -2,66 +2,27 @@
|
|||
|
||||
Scheduler scheduler;
|
||||
|
||||
void threadentry_cpu() { cpu.enter(); }
|
||||
void threadentry_cop() { system.coprocessor_enter(); }
|
||||
void threadentry_smp() { smp.enter(); }
|
||||
void threadentry_ppu() { ppu.enter(); }
|
||||
void threadentry_dsp() { dsp.enter(); }
|
||||
|
||||
void Scheduler::enter() {
|
||||
co_switch(thread_active);
|
||||
host_thread = co_active();
|
||||
co_switch(thread);
|
||||
}
|
||||
|
||||
void Scheduler::exit(ExitReason reason) {
|
||||
exit_reason_ = reason;
|
||||
co_switch(thread_snes);
|
||||
}
|
||||
|
||||
Scheduler::ExitReason Scheduler::exit_reason() const {
|
||||
return exit_reason_;
|
||||
exit_reason = reason;
|
||||
thread = co_active();
|
||||
co_switch(host_thread);
|
||||
}
|
||||
|
||||
void Scheduler::init() {
|
||||
clock.cpu_freq = system.region() == System::Region::NTSC
|
||||
? config.cpu.ntsc_clock_rate
|
||||
: config.cpu.pal_clock_rate;
|
||||
clock.smp_freq = system.region() == System::Region::NTSC
|
||||
? config.smp.ntsc_clock_rate
|
||||
: config.smp.pal_clock_rate;
|
||||
clock.cop_freq = clock.cpu_freq;
|
||||
|
||||
clock.cpucop = 0;
|
||||
clock.cpuppu = 0;
|
||||
clock.cpusmp = 0;
|
||||
clock.smpdsp = 0;
|
||||
|
||||
if(thread_cpu) co_delete(thread_cpu);
|
||||
if(thread_cop) co_delete(thread_cop);
|
||||
if(thread_smp) co_delete(thread_smp);
|
||||
if(thread_ppu) co_delete(thread_ppu);
|
||||
if(thread_dsp) co_delete(thread_dsp);
|
||||
|
||||
thread_snes = co_active();
|
||||
thread_cpu = co_create(65536 * sizeof(void*), threadentry_cpu);
|
||||
thread_cop = co_create(65536 * sizeof(void*), threadentry_cop);
|
||||
thread_smp = co_create(65536 * sizeof(void*), threadentry_smp);
|
||||
thread_ppu = co_create(65536 * sizeof(void*), threadentry_ppu);
|
||||
thread_dsp = co_create(65536 * sizeof(void*), threadentry_dsp);
|
||||
|
||||
//start execution with S-CPU after reset
|
||||
thread_active = thread_cpu;
|
||||
host_thread = co_active();
|
||||
thread = cpu.thread;
|
||||
sync = SynchronizeMode::None;
|
||||
}
|
||||
|
||||
Scheduler::Scheduler() {
|
||||
thread_snes = 0;
|
||||
thread_cpu = 0;
|
||||
thread_cop = 0;
|
||||
thread_smp = 0;
|
||||
thread_ppu = 0;
|
||||
thread_dsp = 0;
|
||||
thread_active = 0;
|
||||
|
||||
exit_reason_ = ExitReason::UnknownEvent;
|
||||
host_thread = 0;
|
||||
thread = 0;
|
||||
exit_reason = ExitReason::UnknownEvent;
|
||||
}
|
||||
|
||||
#endif
|
||||
|
|
|
@ -1,141 +1,16 @@
|
|||
//scheduler thread relationships:
|
||||
//S-PPU <-> S-CPU <-> cartridge co-processor
|
||||
// <|>
|
||||
// S-SMP <-> S-DSP
|
||||
|
||||
class Scheduler {
|
||||
public:
|
||||
struct Scheduler : property<Scheduler> {
|
||||
enum class SynchronizeMode : unsigned { None, CPU, All } sync;
|
||||
enum class ExitReason : unsigned { UnknownEvent, FrameEvent, SynchronizeEvent, DebuggerEvent };
|
||||
readonly<ExitReason> exit_reason;
|
||||
|
||||
cothread_t thread_snes;
|
||||
cothread_t thread_cpu; //S-CPU (5a22)
|
||||
cothread_t thread_cop; //cartridge co-processor (SuperFX, SA-1, ...)
|
||||
cothread_t thread_smp; //S-SMP (SPC700)
|
||||
cothread_t thread_ppu; //S-PPU
|
||||
cothread_t thread_dsp; //S-DSP
|
||||
cothread_t thread_active; //reference to active thread
|
||||
|
||||
struct {
|
||||
uint32 cpu_freq;
|
||||
uint32 cop_freq;
|
||||
uint32 smp_freq;
|
||||
|
||||
int64 cpucop;
|
||||
int64 cpuppu;
|
||||
int64 cpusmp;
|
||||
int64 smpdsp;
|
||||
} clock;
|
||||
|
||||
//==========
|
||||
//CPU <> COP
|
||||
//==========
|
||||
|
||||
alwaysinline void sync_cpucop() {
|
||||
if(clock.cpucop < 0) {
|
||||
thread_active = thread_cop;
|
||||
co_switch(thread_cop);
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline void sync_copcpu() {
|
||||
if(clock.cpucop >= 0 && sync != SynchronizeMode::All) {
|
||||
thread_active = thread_cpu;
|
||||
co_switch(thread_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
//==========
|
||||
//CPU <> PPU
|
||||
//==========
|
||||
|
||||
alwaysinline void sync_cpuppu() {
|
||||
if(clock.cpuppu < 0) {
|
||||
thread_active = thread_ppu;
|
||||
co_switch(thread_ppu);
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline void sync_ppucpu() {
|
||||
if(clock.cpuppu >= 0 && sync != SynchronizeMode::All) {
|
||||
thread_active = thread_cpu;
|
||||
co_switch(thread_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
//==========
|
||||
//CPU <> SMP
|
||||
//==========
|
||||
|
||||
alwaysinline void sync_cpusmp() {
|
||||
if(clock.cpusmp < 0) {
|
||||
thread_active = thread_smp;
|
||||
co_switch(thread_smp);
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline void sync_smpcpu() {
|
||||
if(clock.cpusmp >= 0 && sync != SynchronizeMode::All) {
|
||||
thread_active = thread_cpu;
|
||||
co_switch(thread_cpu);
|
||||
}
|
||||
}
|
||||
|
||||
//==========
|
||||
//SMP <> DSP
|
||||
//==========
|
||||
|
||||
alwaysinline void sync_smpdsp() {
|
||||
if(clock.smpdsp < 0 && sync != SynchronizeMode::All) {
|
||||
thread_active = thread_dsp;
|
||||
co_switch(thread_dsp);
|
||||
}
|
||||
}
|
||||
|
||||
alwaysinline void sync_dspsmp() {
|
||||
if(clock.smpdsp >= 0 && sync != SynchronizeMode::All) {
|
||||
thread_active = thread_smp;
|
||||
co_switch(thread_smp);
|
||||
}
|
||||
}
|
||||
|
||||
//==========
|
||||
//add clocks
|
||||
//==========
|
||||
|
||||
alwaysinline void addclocks_cpu(unsigned clocks) {
|
||||
clock.cpucop -= clocks * (uint64)clock.cop_freq;
|
||||
clock.cpuppu -= clocks;
|
||||
clock.cpusmp -= clocks * (uint64)clock.smp_freq;
|
||||
}
|
||||
|
||||
alwaysinline void addclocks_cop(unsigned clocks) {
|
||||
clock.cpucop += clocks * (uint64)clock.cpu_freq;
|
||||
}
|
||||
|
||||
alwaysinline void addclocks_ppu(unsigned clocks) {
|
||||
clock.cpuppu += clocks;
|
||||
}
|
||||
|
||||
alwaysinline void addclocks_smp(unsigned clocks) {
|
||||
clock.cpusmp += clocks * (uint64)clock.cpu_freq;
|
||||
clock.smpdsp -= clocks;
|
||||
}
|
||||
|
||||
alwaysinline void addclocks_dsp(unsigned clocks) {
|
||||
clock.smpdsp += clocks;
|
||||
}
|
||||
cothread_t host_thread; //program thread (used to exit emulation)
|
||||
cothread_t thread; //active emulation thread (used to enter emulation)
|
||||
|
||||
void enter();
|
||||
void exit(ExitReason);
|
||||
ExitReason exit_reason() const;
|
||||
|
||||
void init();
|
||||
|
||||
Scheduler();
|
||||
|
||||
private:
|
||||
ExitReason exit_reason_;
|
||||
};
|
||||
|
||||
extern Scheduler scheduler;
|
||||
|
|
|
@ -7,6 +7,18 @@ namespace SNES {
|
|||
#include "smp-debugger.cpp"
|
||||
#endif
|
||||
|
||||
void SMP::power() {
|
||||
create();
|
||||
}
|
||||
|
||||
void SMP::reset() {
|
||||
create();
|
||||
}
|
||||
|
||||
void SMP::serialize(serializer &s) {
|
||||
Processor::serialize(s);
|
||||
}
|
||||
|
||||
//this is the IPLROM for the S-SMP coprocessor.
|
||||
//the S-SMP does not allow writing to the IPLROM.
|
||||
//all writes are instead mapped to the extended
|
||||
|
|
|
@ -2,9 +2,16 @@
|
|||
#include "smp-debugger.hpp"
|
||||
#endif
|
||||
|
||||
class SMP {
|
||||
class SMP : public Processor {
|
||||
public:
|
||||
//synchronization
|
||||
alwaysinline void create();
|
||||
alwaysinline void step(unsigned clocks);
|
||||
alwaysinline void synchronize_cpu();
|
||||
alwaysinline void synchronize_dsp();
|
||||
|
||||
virtual void enter() = 0;
|
||||
|
||||
static const uint8 iplrom[64];
|
||||
|
||||
virtual uint8 ram_read(uint16 addr) = 0;
|
||||
|
@ -14,10 +21,10 @@ public:
|
|||
virtual uint8 port_read(uint8 port) = 0;
|
||||
virtual void port_write(uint8 port, uint8 value) = 0;
|
||||
|
||||
virtual void power() = 0;
|
||||
virtual void reset() = 0;
|
||||
virtual void power();
|
||||
virtual void reset();
|
||||
|
||||
virtual void serialize(serializer&) {}
|
||||
virtual void serialize(serializer&);
|
||||
SMP() {}
|
||||
virtual ~SMP() {}
|
||||
};
|
||||
|
|
|
@ -44,7 +44,7 @@ alwaysinline uint8 sSMP::op_busread(uint16 addr) {
|
|||
case 0xf5: //CPUIO1
|
||||
case 0xf6: //CPUIO2
|
||||
case 0xf7: { //CPUIO3
|
||||
scheduler.sync_smpcpu();
|
||||
synchronize_cpu();
|
||||
r = cpu.port_read(addr & 3);
|
||||
} break;
|
||||
|
||||
|
@ -110,7 +110,7 @@ alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) {
|
|||
if(data & 0x30) {
|
||||
//one-time clearing of APU port read registers,
|
||||
//emulated by simulating CPU writes of 0x00
|
||||
scheduler.sync_smpcpu();
|
||||
synchronize_cpu();
|
||||
if(data & 0x20) {
|
||||
cpu.port_write(2, 0x00);
|
||||
cpu.port_write(3, 0x00);
|
||||
|
@ -156,7 +156,7 @@ alwaysinline void sSMP::op_buswrite(uint16 addr, uint8 data) {
|
|||
case 0xf5: //CPUIO1
|
||||
case 0xf6: //CPUIO2
|
||||
case 0xf7: { //CPUIO3
|
||||
scheduler.sync_smpcpu();
|
||||
synchronize_cpu();
|
||||
port_write(addr & 3, data);
|
||||
} break;
|
||||
|
||||
|
|
|
@ -29,6 +29,8 @@ void sSMP::op_step() {
|
|||
}
|
||||
|
||||
void sSMP::power() {
|
||||
SMP::power();
|
||||
|
||||
//targets not initialized/changed upon reset
|
||||
t0.target = 0;
|
||||
t1.target = 0;
|
||||
|
@ -38,6 +40,8 @@ void sSMP::power() {
|
|||
}
|
||||
|
||||
void sSMP::reset() {
|
||||
SMP::reset();
|
||||
|
||||
regs.pc = 0xffc0;
|
||||
regs.a = 0x00;
|
||||
regs.x = 0x00;
|
||||
|
|
|
@ -1,16 +1,16 @@
|
|||
#ifdef SSMP_CPP
|
||||
|
||||
void sSMP::add_clocks(unsigned clocks) {
|
||||
scheduler.addclocks_smp(clocks);
|
||||
step(clocks);
|
||||
#if !defined(DSP_STATE_MACHINE)
|
||||
scheduler.sync_smpdsp();
|
||||
synchronize_dsp();
|
||||
#else
|
||||
while(scheduler.clock.smpdsp < 0) dsp.enter();
|
||||
while(dsp.clock < 0) dsp.enter();
|
||||
#endif
|
||||
|
||||
//forcefully sync S-SMP to S-CPU in case chips are not communicating
|
||||
//sync if S-SMP is more than 24 samples ahead of S-CPU
|
||||
if(scheduler.clock.cpusmp > +(768 * 24 * (int64)24000000)) scheduler.sync_smpcpu();
|
||||
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
|
||||
}
|
||||
|
||||
void sSMP::cycle_edge() {
|
||||
|
@ -28,12 +28,12 @@ void sSMP::cycle_edge() {
|
|||
}
|
||||
}
|
||||
|
||||
template<unsigned frequency>
|
||||
void sSMP::sSMPTimer<frequency>::tick() {
|
||||
template<unsigned timer_frequency>
|
||||
void sSMP::sSMPTimer<timer_frequency>::tick() {
|
||||
//stage 0 increment
|
||||
stage0_ticks += smp.status.timer_step;
|
||||
if(stage0_ticks < frequency) return;
|
||||
stage0_ticks -= frequency;
|
||||
if(stage0_ticks < timer_frequency) return;
|
||||
stage0_ticks -= timer_frequency;
|
||||
|
||||
//stage 1 increment
|
||||
stage1_ticks ^= 1;
|
||||
|
|
|
@ -1,4 +1,4 @@
|
|||
template<unsigned frequency>
|
||||
template<unsigned timer_frequency>
|
||||
class sSMPTimer {
|
||||
public:
|
||||
uint8 stage0_ticks;
|
||||
|
|
|
@ -0,0 +1,21 @@
|
|||
inline void smp_enter() { smp.enter(); }
|
||||
|
||||
void SMP::create() {
|
||||
if(thread) co_delete(thread);
|
||||
thread = co_create(65536 * sizeof(void*), smp_enter);
|
||||
frequency = system.region() == System::Region::NTSC ? config.smp.ntsc_clock_rate : config.smp.pal_clock_rate;
|
||||
clock = 0;
|
||||
}
|
||||
|
||||
void SMP::step(unsigned clocks) {
|
||||
clock += clocks * (uint64)cpu.frequency;
|
||||
dsp.clock -= clocks;
|
||||
}
|
||||
|
||||
void SMP::synchronize_cpu() {
|
||||
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
|
||||
}
|
||||
|
||||
void SMP::synchronize_dsp() {
|
||||
if(dsp.clock < 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(dsp.thread);
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
static const char bsnesVersion[] = "065.04";
|
||||
static const char bsnesVersion[] = "065.05";
|
||||
static const char bsnesTitle[] = "bsnes";
|
||||
static const unsigned bsnesSerializerVersion = 10;
|
||||
static const unsigned bsnesSerializerVersion = 11;
|
||||
|
||||
#define CORE_SMEMORY
|
||||
#define CORE_SCPU
|
||||
|
@ -16,7 +16,7 @@ static const unsigned bsnesSerializerVersion = 10;
|
|||
#define CHEAT_SYSTEM
|
||||
|
||||
//enable debugging extensions (~15% speed hit)
|
||||
#define DEBUGGER
|
||||
//#define DEBUGGER
|
||||
|
||||
#include <libco/libco.h>
|
||||
|
||||
|
@ -55,6 +55,17 @@ namespace SNES {
|
|||
typedef uint32_t uint32;
|
||||
typedef uint64_t uint64;
|
||||
|
||||
struct Processor {
|
||||
cothread_t thread;
|
||||
unsigned frequency;
|
||||
int64 clock;
|
||||
inline void serialize(serializer &s) {
|
||||
s.integer(frequency);
|
||||
s.integer(clock);
|
||||
}
|
||||
inline Processor() : thread(0) {}
|
||||
};
|
||||
|
||||
struct ChipDebugger {
|
||||
virtual bool property(unsigned id, string &name, string &value) = 0;
|
||||
};
|
||||
|
@ -95,6 +106,16 @@ namespace SNES {
|
|||
#include <snes/cartridge/cartridge.hpp>
|
||||
#include <snes/cheat/cheat.hpp>
|
||||
|
||||
#include <snes/cpu/synchronization.hpp>
|
||||
#include <snes/smp/synchronization.hpp>
|
||||
#include <snes/dsp/synchronization.hpp>
|
||||
#include <snes/ppu/synchronization.hpp>
|
||||
#include <snes/chip/supergameboy/synchronization.hpp>
|
||||
#include <snes/chip/superfx/synchronization.hpp>
|
||||
#include <snes/chip/sa1/synchronization.hpp>
|
||||
#include <snes/chip/msu1/synchronization.hpp>
|
||||
#include <snes/chip/serial/synchronization.hpp>
|
||||
|
||||
#include <snes/memory/memory-inline.hpp>
|
||||
#include <snes/ppu/ppu-inline.hpp>
|
||||
#include <snes/cheat/cheat-inline.hpp>
|
||||
|
|
|
@ -28,8 +28,8 @@ bool System::unserialize(serializer &s) {
|
|||
if(signature != 0x31545342) return false;
|
||||
if(version != bsnesSerializerVersion) return false;
|
||||
//if(crc32 != cartridge.crc32()) return false;
|
||||
scheduler.init();
|
||||
|
||||
reset();
|
||||
serialize_all(s);
|
||||
return true;
|
||||
}
|
||||
|
@ -41,14 +41,6 @@ bool System::unserialize(serializer &s) {
|
|||
void System::serialize(serializer &s) {
|
||||
s.integer((unsigned&)region);
|
||||
s.integer((unsigned&)expansion);
|
||||
|
||||
s.integer(scheduler.clock.cpu_freq);
|
||||
s.integer(scheduler.clock.smp_freq);
|
||||
|
||||
s.integer(scheduler.clock.cpucop);
|
||||
s.integer(scheduler.clock.cpuppu);
|
||||
s.integer(scheduler.clock.cpusmp);
|
||||
s.integer(scheduler.clock.smpdsp);
|
||||
}
|
||||
|
||||
void System::serialize_all(serializer &s) {
|
||||
|
@ -61,18 +53,18 @@ void System::serialize_all(serializer &s) {
|
|||
dsp.serialize(s);
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.serialize(s);
|
||||
|
||||
if(cartridge.has_superfx()) superfx.serialize(s);
|
||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||
if(cartridge.has_srtc()) srtc.serialize(s);
|
||||
if(cartridge.has_sdd1()) sdd1.serialize(s);
|
||||
if(cartridge.has_sa1()) sa1.serialize(s);
|
||||
if(cartridge.has_srtc()) srtc.serialize(s);
|
||||
if(cartridge.has_sdd1()) sdd1.serialize(s);
|
||||
if(cartridge.has_spc7110()) spc7110.serialize(s);
|
||||
if(cartridge.has_cx4()) cx4.serialize(s);
|
||||
if(cartridge.has_dsp1()) dsp1.serialize(s);
|
||||
if(cartridge.has_dsp2()) dsp2.serialize(s);
|
||||
if(cartridge.has_obc1()) obc1.serialize(s);
|
||||
if(cartridge.has_st0010()) st0010.serialize(s);
|
||||
if(cartridge.has_msu1()) msu1.serialize(s);
|
||||
if(cartridge.has_cx4()) cx4.serialize(s);
|
||||
if(cartridge.has_dsp1()) dsp1.serialize(s);
|
||||
if(cartridge.has_dsp2()) dsp2.serialize(s);
|
||||
if(cartridge.has_obc1()) obc1.serialize(s);
|
||||
if(cartridge.has_st0010()) st0010.serialize(s);
|
||||
if(cartridge.has_msu1()) msu1.serialize(s);
|
||||
if(cartridge.has_serial()) serial.serialize(s);
|
||||
}
|
||||
|
||||
//called once upon cartridge load event: perform dry-run state save.
|
||||
|
|
|
@ -15,25 +15,6 @@ System system;
|
|||
|
||||
#include "serialization.cpp"
|
||||
|
||||
void System::coprocessor_enter() {
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.enter();
|
||||
if(cartridge.has_superfx()) superfx.enter();
|
||||
if(cartridge.has_sa1()) sa1.enter();
|
||||
if(cartridge.has_msu1()) msu1.enter();
|
||||
if(cartridge.has_serial()) serial.enter();
|
||||
|
||||
//coprocessor thread always runs, even if one is not present;
|
||||
//below code implements an empty coprocessor that consumes minimal resources
|
||||
while(true) {
|
||||
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
|
||||
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
|
||||
}
|
||||
|
||||
scheduler.addclocks_cop(64 * 1024 * 1024);
|
||||
scheduler.sync_copcpu();
|
||||
}
|
||||
}
|
||||
|
||||
void System::run() {
|
||||
scheduler.sync = Scheduler::SynchronizeMode::None;
|
||||
|
||||
|
@ -48,19 +29,22 @@ void System::runtosave() {
|
|||
scheduler.sync = Scheduler::SynchronizeMode::CPU;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.thread_active = scheduler.thread_cop;
|
||||
scheduler.thread = smp.thread;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.thread_active = scheduler.thread_smp;
|
||||
runthreadtosave();
|
||||
|
||||
scheduler.thread_active = scheduler.thread_ppu;
|
||||
scheduler.thread = ppu.thread;
|
||||
runthreadtosave();
|
||||
|
||||
#if !defined(DSP_STATE_MACHINE)
|
||||
scheduler.thread_active = scheduler.thread_dsp;
|
||||
scheduler.thread = dsp.thread;
|
||||
runthreadtosave();
|
||||
#endif
|
||||
|
||||
for(unsigned i = 0; i < cpu.coprocessors.size(); i++) {
|
||||
Processor &chip = *cpu.coprocessors[i];
|
||||
scheduler.thread = chip.thread;
|
||||
runthreadtosave();
|
||||
}
|
||||
}
|
||||
|
||||
void System::runthreadtosave() {
|
||||
|
@ -110,16 +94,11 @@ void System::term() {
|
|||
void System::power() {
|
||||
region = config.region;
|
||||
expansion = config.expansion_port;
|
||||
|
||||
if(region == Region::Autodetect) {
|
||||
region = (cartridge.region() == Cartridge::Region::NTSC ? Region::NTSC : Region::PAL);
|
||||
}
|
||||
|
||||
audio.coprocessor_enable(false);
|
||||
|
||||
scheduler.init();
|
||||
bus.power();
|
||||
|
||||
for(unsigned i = 0x2100; i <= 0x213f; i++) memory::mmio.map(i, ppu);
|
||||
for(unsigned i = 0x2140; i <= 0x217f; i++) memory::mmio.map(i, cpu);
|
||||
for(unsigned i = 0x2180; i <= 0x2183; i++) memory::mmio.map(i, cpu);
|
||||
|
@ -127,27 +106,33 @@ void System::power() {
|
|||
for(unsigned i = 0x4200; i <= 0x421f; i++) memory::mmio.map(i, cpu);
|
||||
for(unsigned i = 0x4300; i <= 0x437f; i++) memory::mmio.map(i, cpu);
|
||||
|
||||
audio.coprocessor_enable(false);
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxbase.enable();
|
||||
if(memory::bsxflash.data()) bsxflash.enable();
|
||||
if(cartridge.mode() == Cartridge::Mode::Bsx) bsxcart.enable();
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.enable();
|
||||
|
||||
if(cartridge.has_superfx()) superfx.enable();
|
||||
if(cartridge.has_sa1()) sa1.enable();
|
||||
if(cartridge.has_srtc()) srtc.enable();
|
||||
if(cartridge.has_sdd1()) sdd1.enable();
|
||||
if(cartridge.has_sa1()) sa1.enable();
|
||||
if(cartridge.has_srtc()) srtc.enable();
|
||||
if(cartridge.has_sdd1()) sdd1.enable();
|
||||
if(cartridge.has_spc7110()) spc7110.enable();
|
||||
if(cartridge.has_cx4()) cx4.enable();
|
||||
if(cartridge.has_dsp1()) dsp1.enable();
|
||||
if(cartridge.has_dsp2()) dsp2.enable();
|
||||
if(cartridge.has_dsp3()) dsp3.enable();
|
||||
if(cartridge.has_dsp4()) dsp4.enable();
|
||||
if(cartridge.has_obc1()) obc1.enable();
|
||||
if(cartridge.has_st0010()) st0010.enable();
|
||||
if(cartridge.has_st0011()) st0011.enable();
|
||||
if(cartridge.has_st0018()) st0018.enable();
|
||||
if(cartridge.has_msu1()) msu1.enable();
|
||||
if(cartridge.has_serial()) serial.enable();
|
||||
if(cartridge.has_cx4()) cx4.enable();
|
||||
if(cartridge.has_dsp1()) dsp1.enable();
|
||||
if(cartridge.has_dsp2()) dsp2.enable();
|
||||
if(cartridge.has_dsp3()) dsp3.enable();
|
||||
if(cartridge.has_dsp4()) dsp4.enable();
|
||||
if(cartridge.has_obc1()) obc1.enable();
|
||||
if(cartridge.has_st0010()) st0010.enable();
|
||||
if(cartridge.has_st0011()) st0011.enable();
|
||||
if(cartridge.has_st0018()) st0018.enable();
|
||||
if(cartridge.has_msu1()) msu1.enable();
|
||||
if(cartridge.has_serial()) serial.enable();
|
||||
|
||||
cpu.power();
|
||||
smp.power();
|
||||
dsp.power();
|
||||
ppu.power();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxbase.power();
|
||||
if(memory::bsxflash.data()) bsxflash.power();
|
||||
|
@ -155,41 +140,42 @@ void System::power() {
|
|||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.power();
|
||||
|
||||
if(cartridge.has_superfx()) superfx.power();
|
||||
if(cartridge.has_sa1()) sa1.power();
|
||||
if(cartridge.has_srtc()) srtc.power();
|
||||
if(cartridge.has_sdd1()) sdd1.power();
|
||||
if(cartridge.has_sa1()) sa1.power();
|
||||
if(cartridge.has_srtc()) srtc.power();
|
||||
if(cartridge.has_sdd1()) sdd1.power();
|
||||
if(cartridge.has_spc7110()) spc7110.power();
|
||||
if(cartridge.has_cx4()) cx4.power();
|
||||
if(cartridge.has_dsp1()) dsp1.power();
|
||||
if(cartridge.has_dsp2()) dsp2.power();
|
||||
if(cartridge.has_dsp3()) dsp3.power();
|
||||
if(cartridge.has_dsp4()) dsp4.power();
|
||||
if(cartridge.has_obc1()) obc1.power();
|
||||
if(cartridge.has_st0010()) st0010.power();
|
||||
if(cartridge.has_st0011()) st0011.power();
|
||||
if(cartridge.has_st0018()) st0018.power();
|
||||
if(cartridge.has_msu1()) msu1.power();
|
||||
if(cartridge.has_serial()) serial.power();
|
||||
if(cartridge.has_cx4()) cx4.power();
|
||||
if(cartridge.has_dsp1()) dsp1.power();
|
||||
if(cartridge.has_dsp2()) dsp2.power();
|
||||
if(cartridge.has_dsp3()) dsp3.power();
|
||||
if(cartridge.has_dsp4()) dsp4.power();
|
||||
if(cartridge.has_obc1()) obc1.power();
|
||||
if(cartridge.has_st0010()) st0010.power();
|
||||
if(cartridge.has_st0011()) st0011.power();
|
||||
if(cartridge.has_st0018()) st0018.power();
|
||||
if(cartridge.has_msu1()) msu1.power();
|
||||
if(cartridge.has_serial()) serial.power();
|
||||
|
||||
cpu.power();
|
||||
smp.power();
|
||||
dsp.power();
|
||||
ppu.power();
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&supergameboy);
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
|
||||
|
||||
scheduler.init();
|
||||
|
||||
input.port_set_device(0, config.controller_port1);
|
||||
input.port_set_device(1, config.controller_port2);
|
||||
input.update();
|
||||
video.update();
|
||||
//video.update();
|
||||
}
|
||||
|
||||
void System::reset() {
|
||||
scheduler.init();
|
||||
|
||||
bus.reset();
|
||||
cpu.reset();
|
||||
smp.reset();
|
||||
dsp.reset();
|
||||
ppu.reset();
|
||||
bus.reset();
|
||||
|
||||
if(expansion() == ExpansionPortDevice::BSX) bsxbase.reset();
|
||||
if(memory::bsxflash.data()) bsxflash.reset();
|
||||
|
@ -197,26 +183,34 @@ void System::reset() {
|
|||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) supergameboy.reset();
|
||||
|
||||
if(cartridge.has_superfx()) superfx.reset();
|
||||
if(cartridge.has_sa1()) sa1.reset();
|
||||
if(cartridge.has_srtc()) srtc.reset();
|
||||
if(cartridge.has_sdd1()) sdd1.reset();
|
||||
if(cartridge.has_sa1()) sa1.reset();
|
||||
if(cartridge.has_srtc()) srtc.reset();
|
||||
if(cartridge.has_sdd1()) sdd1.reset();
|
||||
if(cartridge.has_spc7110()) spc7110.reset();
|
||||
if(cartridge.has_cx4()) cx4.reset();
|
||||
if(cartridge.has_dsp1()) dsp1.reset();
|
||||
if(cartridge.has_dsp2()) dsp2.reset();
|
||||
if(cartridge.has_dsp3()) dsp3.reset();
|
||||
if(cartridge.has_dsp4()) dsp4.reset();
|
||||
if(cartridge.has_obc1()) obc1.reset();
|
||||
if(cartridge.has_st0010()) st0010.reset();
|
||||
if(cartridge.has_st0011()) st0011.reset();
|
||||
if(cartridge.has_st0018()) st0018.reset();
|
||||
if(cartridge.has_msu1()) msu1.reset();
|
||||
if(cartridge.has_serial()) serial.reset();
|
||||
if(cartridge.has_cx4()) cx4.reset();
|
||||
if(cartridge.has_dsp1()) dsp1.reset();
|
||||
if(cartridge.has_dsp2()) dsp2.reset();
|
||||
if(cartridge.has_dsp3()) dsp3.reset();
|
||||
if(cartridge.has_dsp4()) dsp4.reset();
|
||||
if(cartridge.has_obc1()) obc1.reset();
|
||||
if(cartridge.has_st0010()) st0010.reset();
|
||||
if(cartridge.has_st0011()) st0011.reset();
|
||||
if(cartridge.has_st0018()) st0018.reset();
|
||||
if(cartridge.has_msu1()) msu1.reset();
|
||||
if(cartridge.has_serial()) serial.reset();
|
||||
|
||||
if(cartridge.mode() == Cartridge::Mode::SuperGameBoy) cpu.coprocessors.append(&supergameboy);
|
||||
if(cartridge.has_superfx()) cpu.coprocessors.append(&superfx);
|
||||
if(cartridge.has_sa1()) cpu.coprocessors.append(&sa1);
|
||||
if(cartridge.has_msu1()) cpu.coprocessors.append(&msu1);
|
||||
if(cartridge.has_serial()) cpu.coprocessors.append(&serial);
|
||||
|
||||
scheduler.init();
|
||||
|
||||
input.port_set_device(0, config.controller_port1);
|
||||
input.port_set_device(1, config.controller_port2);
|
||||
input.update();
|
||||
video.update();
|
||||
//video.update();
|
||||
}
|
||||
|
||||
void System::unload() {
|
||||
|
@ -236,4 +230,4 @@ System::System() : interface(0) {
|
|||
expansion = ExpansionPortDevice::None;
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
|
|
|
@ -30,7 +30,6 @@ public:
|
|||
|
||||
private:
|
||||
Interface *interface;
|
||||
void coprocessor_enter();
|
||||
void runthreadtosave();
|
||||
|
||||
void serialize(serializer&);
|
||||
|
|
Loading…
Reference in New Issue