libsnes: implement "stateful" smp that can restore its state at more points, theoretically eliminating incorrectness from smp misbehavior. no real speed loss. doesn't fix anything at the moment; i might back out the change if it turns out to never fix anything. breaks savestates.

This commit is contained in:
goyuken 2012-12-24 18:07:13 +00:00
parent 5f854466ab
commit 5e3d6555b0
10 changed files with 3211 additions and 18 deletions

View File

@ -4,10 +4,13 @@
namespace SNES {
#include "algorithms.cpp"
#include "opcodes.cpp"
//#include "opcodes.cpp"
#include "disassembler.cpp"
#include "serialization.cpp"
#include "uop.cpp"
/*
void SMPcore::op_step() {
switch(opcode = op_readpc()) {
case 0x00: return op_nop();
@ -268,5 +271,5 @@ void SMPcore::op_step() {
case 0xff: return op_wait();
}
}
*/
}

View File

@ -9,7 +9,9 @@ struct SMPcore {
regs_t regs;
word_t dp, sp, rd, wr, bit, ya;
uint8 opcode;
uint8 opcode;
int uindex;
void core_serialize(serializer&);
string disassemble_opcode(uint16 addr);

View File

@ -16,4 +16,10 @@ alwaysinline uint8 op_readdp(uint8 addr) {
alwaysinline void op_writedp(uint8 addr, uint8 data) {
return op_write((regs.p.p << 8) + addr, data);
}
}
alwaysinline void op_next() {
opcode = op_readpc();
uindex = -1;
}

View File

@ -21,7 +21,9 @@ void SMPcore::core_serialize(serializer &s) {
s.integer(rd.w);
s.integer(wr.w);
s.integer(bit.w);
s.integer(ya.w);
s.integer(ya.w);
s.integer(uindex);
}
#endif

File diff suppressed because it is too large Load Diff

View File

@ -40,7 +40,7 @@ uint8 SMP::op_busread(uint16 addr) {
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
synchronize_cpu();
synchronize_cpu_force();
return cpu.port_read(addr);
case 0xf8: //RAM0
@ -98,7 +98,7 @@ void SMP::op_buswrite(uint16 addr, uint8 data) {
if(data & 0x30) {
//one-time clearing of APU port read registers,
//emulated by simulating CPU writes of 0x00
synchronize_cpu();
synchronize_cpu_force();
if(data & 0x20) {
cpu.port_write(2, 0x00);
cpu.port_write(3, 0x00);
@ -142,7 +142,7 @@ void SMP::op_buswrite(uint16 addr, uint8 data) {
case 0xf5: //CPUIO1
case 0xf6: //CPUIO2
case 0xf7: //CPUIO3
synchronize_cpu();
synchronize_cpu_force();
port_write(addr, data);
break;

View File

@ -16,12 +16,23 @@ void SMP::step(unsigned clocks) {
}
void SMP::synchronize_cpu() {
if(CPU::Threaded == true) {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All) co_switch(cpu.thread);
} else {
while(clock >= 0) cpu.enter();
}
}
}
void SMP::synchronize_cpu_force() {
if(CPU::Threaded == true) {
if(clock >= 0 && scheduler.sync != Scheduler::SynchronizeMode::All)
co_switch(cpu.thread);
else if(clock >= 0 && scheduler.sync == Scheduler::SynchronizeMode::All)
interface->message("SMP had to advance nondeterministically!");
} else {
while(clock >= 0) cpu.enter();
}
}
void SMP::synchronize_dsp() {
if(DSP::Threaded == true) {
@ -34,10 +45,13 @@ void SMP::synchronize_dsp() {
void SMP::Enter() { smp.enter(); }
void SMP::enter() {
while(true) {
while(true) {
// see comment in timing.cpp
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
}
debugger.op_exec(regs.pc);
op_step();
@ -58,7 +72,11 @@ void SMP::power() {
void SMP::reset() {
create(Enter, system.apu_frequency());
regs.pc = 0xffc0;
regs.pc = 0xffc0;
// exact value doesn't matter much, so long as "fetch" is next
opcode = 0; // NOP
uindex = 1; // fetch phase
regs.a = 0x00;
regs.x = 0x00;
regs.y = 0x00;

View File

@ -4,7 +4,8 @@ struct SMP : public Processor, public SMPcore {
enum : bool { Threaded = true };
alwaysinline void step(unsigned clocks);
alwaysinline void synchronize_cpu();
alwaysinline void synchronize_cpu();
alwaysinline void synchronize_cpu_force();
alwaysinline void synchronize_dsp();
uint8 port_read(uint2 port) const;

View File

@ -4,12 +4,22 @@ void SMP::add_clocks(unsigned clocks) {
step(clocks);
synchronize_dsp();
#if defined(DEBUGGER)
#if defined(DEBUGGER)
#error -DDEBUGGER SMP runtosave() correctness not checked
synchronize_cpu();
#else
//forcefully sync S-SMP to S-CPU in case chips are not communicating
//sync if S-SMP is more than 24 samples ahead of S-CPU
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
//sync if S-SMP is more than 24 samples ahead of S-CPU
/*
our new smp design guarantees that there is at most one required synchronize_cpu() per uop,
inside an op_busread() or op_buswrite(). this extra synchronize can cause problems if we
swap out of the SMP at the beginning of a uop with an add_clocks() call when there is an
important op_busread() / op_buswrite() later on. the SMP will need to finish that uop in
order to reach a savable state, but it might never get to do so until it's too late (ie,
scheduler.sync == Scheduler.SynchronizeMode::All). so we remove this call and instead
do catchup sync in the main Enter() loop.
*/
//if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
#endif
}