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:
byuu 2010-07-29 16:24:28 +00:00
parent 77375c3c68
commit 53f03be5a2
58 changed files with 494 additions and 379 deletions

BIN
bsnes.exe Normal file

Binary file not shown.

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#ifdef SA1_CPP
void SA1::serialize(serializer &s) {
Processor::serialize(s);
CPUcore::core_serialize(s);
//sa1.hpp

View File

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

View File

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

View File

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

View File

@ -0,0 +1,8 @@
#ifdef SERIAL_CPP
void Serial::serialize(serializer &s) {
Processor::serialize(s);
s.integer((bool&)latch);
}
#endif

View File

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

View File

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

View File

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

View File

@ -1,6 +1,8 @@
#ifdef SUPERFX_CPP
void SuperFX::serialize(serializer &s) {
Processor::serialize(s);
//superfx.hpp
s.integer(clockmode);
s.integer(instruction_counter);

View File

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

View File

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

View File

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

View File

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

View File

@ -1,6 +1,7 @@
#ifdef SUPERGAMEBOY_CPP
void SuperGameBoy::serialize(serializer &s) {
Processor::serialize(s);
s.integer(row);
if(sgb_serialize) sgb_serialize(s);
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -1,4 +1,4 @@
template<unsigned frequency>
template<unsigned timer_frequency>
class sSMPTimer {
public:
uint8 stage0_ticks;

View File

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

View File

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

View File

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

View File

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

View File

@ -30,7 +30,6 @@ public:
private:
Interface *interface;
void coprocessor_enter();
void runthreadtosave();
void serialize(serializer&);