Skip to content

Commit

Permalink
snes: Fix rewind issue with tales of phantasia. Full details are as f…
Browse files Browse the repository at this point in the history
…ollows: Synchronize events occur right BEFORE the original thread tries to modify the data. That means that if, for instance, the SMP thread switches off to the CPU thread for a synchronize, but then the CPU switches to the PPU, commits a frame, and then in runtosave, finalizes, without having ever advanced past the SMP, when the SMP runs to save, it will do a data modify without being properly synced. The solution is for the CPU to not stop in runtosave until there's enough time for both the PPU and SMP to finish. Because of our stateful design, there's at most one event pending, so that simplifies to (smp.clock < 0 && ppu.clock < 0). This is unfortunately never guaranteed to finish, but with short timing segments on stateful PPU and SMP, and special guards in the PPU and SMP to switch back to the CPU as quickly as possible when in runtosave, it seems to work out.
  • Loading branch information
nattthebear committed Dec 24, 2012
1 parent 74c26d9 commit 587270c
Show file tree
Hide file tree
Showing 4 changed files with 18 additions and 7 deletions.
Binary file modified BizHawk.MultiClient/output/dll/libsneshawk.dll
Binary file not shown.
5 changes: 4 additions & 1 deletion libsnes/bsnes/snes/alt/ppu-compatibility/ppu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@ void PPU::synchronize_cpu() {
void PPU::Enter() { ppu.enter(); }

void PPU::enter() {
while(true) {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
synchronize_cpu(); // when in CPU sync mode, always switch back to CPU as soon as possible
}
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
Expand Down
14 changes: 9 additions & 5 deletions libsnes/bsnes/snes/cpu/cpu.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -55,9 +55,13 @@ void CPU::Enter() { cpu.enter(); }

void CPU::enter() {
while(true) {
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
// we can only stop if there's enough time for at least one more event
// on both the PPU and the SMP
if (smp.clock < 0 && ppu.clock < 0) {
scheduler.sync = Scheduler::SynchronizeMode::All;
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
}

if(status.interrupt_pending) {
Expand Down Expand Up @@ -86,15 +90,15 @@ void CPU::enter() {

void CPU::op_step() {
debugger.op_exec(regs.pc.d);

if (interface->wanttrace)
{
char tmp[512];
disassemble_opcode(tmp, regs.pc.d);
tmp[511] = 0;
interface->cpuTrace(tmp);
}

(this->*opcode_table[op_readpc()])();
}

Expand Down
6 changes: 5 additions & 1 deletion libsnes/bsnes/snes/smp/smp.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,12 @@ void SMP::Enter() { smp.enter(); }
void SMP::enter() {
while(true) {
// see comment in timing.cpp
if(clock > +(768 * 24 * (int64)24000000)) synchronize_cpu();
if(clock > +(768 * 24 * (int64)24000000))
synchronize_cpu();

if(scheduler.sync == Scheduler::SynchronizeMode::CPU) {
synchronize_cpu(); // when in CPU sync mode, always switch back to CPU as soon as possible
}
if(scheduler.sync == Scheduler::SynchronizeMode::All) {
scheduler.exit(Scheduler::ExitReason::SynchronizeEvent);
}
Expand Down

0 comments on commit 587270c

Please sign in to comment.