Skip to content

Commit

Permalink
Switch from ticks -> secs in Wasm build
Browse files Browse the repository at this point in the history
This is needed now that the number of ticks per frame will be different
on NTSC vs. PAL games. It's a more convenient API anyway.
  • Loading branch information
binji committed Jun 19, 2024
1 parent 99e9dda commit 8865347
Show file tree
Hide file tree
Showing 8 changed files with 91 additions and 87 deletions.
2 changes: 1 addition & 1 deletion docs/binjnes-simd.js

Large diffs are not rendered by default.

Binary file modified docs/binjnes-simd.wasm
Binary file not shown.
2 changes: 1 addition & 1 deletion docs/binjnes.js

Large diffs are not rendered by default.

Binary file modified docs/binjnes.wasm
Binary file not shown.
73 changes: 36 additions & 37 deletions docs/demo.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ const SCREEN_WIDTH = 256;
const SCREEN_HEIGHT = 240;
const AUDIO_FRAMES = 4096;
const AUDIO_LATENCY_SEC = 0.1;
const PPU_TICKS_PER_SECOND = 5369318;
const REWIND_FACTOR = 1.5;
const REWIND_UPDATE_MS = 16;
const FAST_FORWARD_FACTOR = 2.0;
Expand Down Expand Up @@ -42,7 +41,7 @@ function clamp(x, min, max) { return Math.min(Math.max(x, min), max); }

let data = {
msPerFrame: 0,
ticks: 0,
secs: 0,
loaded: false,
loadedFile: null,
paused: false,
Expand All @@ -52,8 +51,8 @@ let data = {
scale: 2,
},
rewind: {
minTicks: 0,
maxTicks: 0,
minSecs: 0,
maxSecs: 0,
},
files: {
show: true,
Expand Down Expand Up @@ -140,11 +139,11 @@ let vm = new Vue({
},
rewindTime: function() {
const zeroPadLeft = (num, width) => ('' + (num | 0)).padStart(width, '0');
const ticks = this.ticks;
const hr = (ticks / (60 * 60 * PPU_TICKS_PER_SECOND)) | 0;
const min = zeroPadLeft((ticks / (60 * PPU_TICKS_PER_SECOND)) % 60, 2);
const sec = zeroPadLeft((ticks / PPU_TICKS_PER_SECOND) % 60, 2);
const ms = zeroPadLeft((ticks / (PPU_TICKS_PER_SECOND / 1000)) % 1000, 3);
const secs = this.secs;
const hr = (secs / (60 * 60)) | 0;
const min = zeroPadLeft((secs / 60) % 60, 2);
const sec = zeroPadLeft(secs % 60, 2);
const ms = zeroPadLeft((secs / 1000) % 1000, 3);
return `${hr}:${min}:${sec}.${ms}`;
},
pauseLabel: function() {
Expand Down Expand Up @@ -208,8 +207,8 @@ let vm = new Vue({
}
this.paused = newPaused;
},
updateTicks: function() {
this.ticks = emulator.ticks;
updateSecs: function() {
this.secs = emulator.secs;
},
togglePause: function() {
if (!this.loaded) return;
Expand All @@ -222,7 +221,7 @@ let vm = new Vue({
},
rewindTo: function(event) {
if (!emulator) return;
emulator.rewindToTicks(+event.target.value);
emulator.rewindToSecs(+event.target.value);
},
selectFile: function(index) {
this.files.selected = index;
Expand Down Expand Up @@ -478,16 +477,16 @@ class Emulator {
this.state = EMULATOR_STATE_RUN;
this.lastState = EMULATOR_STATE_RUN;
this.intervalId = 0;
this.isRewindingToTicks = false;
this.ticks = 0;
this.isRewindingToSecs = false;
this.secs = 0;
this.msPerFrame = 0;
this.gpId = -1;
this.gpPrev = new Array(vm.input.length);
this.gpIntervalId = 0;
this.rewind = {
oldestTicks: 0,
newestTicks: 0,
newestTicks: 0,
oldestSecs: 0,
newestSecs: 0,
newestSecs: 0,
};
this.getPrgRamPromise = null;
this.resolveGetPrgRam = null;
Expand Down Expand Up @@ -626,28 +625,28 @@ class Emulator {
}

autoRewindInterval() {
const oldest = this.rewind.oldestTicks;
const start = this.ticks;
const delta = REWIND_FACTOR * REWIND_UPDATE_MS / 1000 * PPU_TICKS_PER_SECOND;
const oldest = this.rewind.oldestSecs;
const start = this.secs;
const delta = REWIND_FACTOR * REWIND_UPDATE_MS / 1000;
const rewindTo = Math.max(oldest, start - delta);
this.rewindToTicks(rewindTo);
this.rewindToSecs(rewindTo);
}

autoFastForwardInterval() {
const delta = FAST_FORWARD_FACTOR * FAST_FORWARD_UPDATE_MS / 1000 * PPU_TICKS_PER_SECOND;
this.runUntil(this.ticks + delta);
const delta = FAST_FORWARD_FACTOR * FAST_FORWARD_UPDATE_MS / 1000;
this.runUntil(this.secs + delta);
}

rewindToTicks(ticks) {
rewindToSecs(secs) {
if (!this.isPaused && !this.isAutoRewinding) return;
if (!this.isRewindingToTicks) {
emulatorWorker.postMessage({msg: 'rewindToTicks', ticks});
this.isRewindingToTicks = true;
if (!this.isRewindingToSecs) {
emulatorWorker.postMessage({msg: 'rewindToSecs', secs});
this.isRewindingToSecs = true;
}
}

runUntil(ticks) {
emulatorWorker.postMessage({msg: 'runUntil', ticks})
runUntil(secs) {
emulatorWorker.postMessage({msg: 'runUntil', secs})
}

setReset(active) {
Expand Down Expand Up @@ -690,19 +689,19 @@ class Emulator {
this.getPrgRamPromise = null;
break;
case 'runUntil:result':
this.ticks = e.data.ticks;
vm.updateTicks();
this.secs = e.data.secs;
vm.updateSecs();
vm.prgRamUpdated ||= e.data.prgRamUpdated;
this.video.renderTexture();
this.msPerFrame = e.data.msPerFrame;
vm.rewind.minTicks = this.rewind.oldestTicks = e.data.oldestTicks;
vm.rewind.maxTicks = this.rewind.newestTicks = e.data.newestTicks;
vm.rewind.minSecs = this.rewind.oldestSecs = e.data.oldestSecs;
vm.rewind.maxSecs = this.rewind.newestSecs = e.data.newestSecs;
break;
case 'rewindToTicks:result':
this.ticks = e.data.ticks;
vm.updateTicks();
case 'rewindToSecs:result':
this.secs = e.data.secs;
vm.updateSecs();
this.video.renderTexture();
this.isRewindingToTicks = false;
this.isRewindingToSecs = false;
break;
}
}
Expand Down
63 changes: 30 additions & 33 deletions docs/worker.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@ const REWIND_BUFFER_CAPACITY = 4 * 1024 * 1024;
const CONTROLLER_JOYPAD = 0
const CONTROLLER_ZAPPER = 1
const CONTROLLER_SNES_MOUSE = 2
const PPU_TICKS_PER_SECOND = 5369318;
const MAX_UPDATE_SEC = 5 / 60;

self.emulator = null;
Expand All @@ -41,16 +40,16 @@ onmessage = async function(e) {
if (emulator) { emulator.cancelAnimationFrame(); }
break;
case 'runUntil':
if (emulator) { emulator.runUntil(e.data.ticks); }
if (emulator) { emulator.runUntil(e.data.secs); }
break;
case 'setReset':
if (emulator) { emulator.setReset(e.data.ticks); }
if (emulator) { emulator.setReset(e.data.secs); }
break;
case 'beginRewind':
if (emulator) { emulator.beginRewind(); }
break;
case 'rewindToTicks':
if (emulator) { emulator.rewindToTicks(e.data.ticks); }
case 'rewindToSecs':
if (emulator) { emulator.rewindToSecs(e.data.secs); }
break;
case 'endRewind':
if (emulator) { emulator.endRewind(); }
Expand Down Expand Up @@ -114,7 +113,7 @@ class Emulator {
this.msPerFrame = 0;
this.rafCancelToken = null;
this.lastRafSec = 0;
this.leftoverTicks = 0;
this.leftoverSecs = 0;
this.rewind = new Rewind(module, this.e);
this.mouseFracX = 0;
this.mouseFracY = 0;
Expand Down Expand Up @@ -203,27 +202,27 @@ class Emulator {
this.rewind.beginRewind();
}

rewindToTicks(ticks) {
ticks = Math.min(Math.max(ticks, this.rewind.oldestTicks), this.rewind.newestTicks);
if (this.rewind.rewindToTicks(ticks)) {
this.runUntil(ticks);
rewindToSecs(secs) {
secs = Math.min(Math.max(secs, this.rewind.oldestSecs), this.rewind.newestSecs);
if (this.rewind.rewindToSecs(secs)) {
this.runUntil(secs);
}
self.postMessage({msg: 'rewindToTicks:result', ticks: this.ticks});
self.postMessage({msg: 'rewindToSecs:result', secs: this.secs});
}

endRewind() {
this.rewind.endRewind();
this.lastRafSec = 0;
this.leftoverTicks = 0;
this.leftoverSecs = 0;
}

get ticks() {
return this.module._emulator_get_ticks_f64(this.e);
get secs() {
return this.module._emulator_get_secs(this.e);
}

runUntil(runUntilTicks) {
runUntil(runUntilSecs) {
while (true) {
const event = this.module._emulator_run_until_f64(this.e, runUntilTicks);
const event = this.module._emulator_run_until_secs(this.e, runUntilSecs);
if (event & EVENT_NEW_FRAME) {
this.rewind.pushBuffer();
const buffer = this.videoBuffer.slice();
Expand All @@ -237,17 +236,17 @@ class Emulator {
break;
}
}
const ticks = this.ticks;
const secs = this.secs;
const msPerFrame = this.msPerFrame;
const prgRamUpdated = this.module._emulator_was_prg_ram_updated(this.e);
this.leftoverTicks = (ticks - runUntilTicks) | 0;
this.leftoverSecs = (secs - runUntilSecs) | 0;
self.postMessage({
msg: 'runUntil:result',
ticks,
secs,
prgRamUpdated,
msPerFrame,
oldestTicks: this.rewind.oldestTicks,
newestTicks: this.rewind.newestTicks,
oldestSecs: this.rewind.oldestSecs,
newestSecs: this.rewind.newestSecs,
});
}

Expand All @@ -257,12 +256,11 @@ class Emulator {
const startSec = rafStartMs / 1000;
deltaSec = Math.max(startSec - (this.lastRafSec || startSec), 0);
this.lastRafSec = startSec;
const startTicks = this.ticks;
const deltaTicks =
Math.min(deltaSec, MAX_UPDATE_SEC) * PPU_TICKS_PER_SECOND;
const runUntilTicks = startTicks + deltaTicks - this.leftoverTicks;
const startSecs = this.secs;
const deltaSecs = Math.min(deltaSec, MAX_UPDATE_SEC);
const runUntilSecs = startSecs + deltaSecs - this.leftoverSecs;
const startMs = performance.now();
this.runUntil(runUntilTicks);
this.runUntil(runUntilSecs);
const endMs = performance.now();
const lerp = (from, to, alpha) => alpha * from + (1 - alpha) * to;
// this.msPerFrame = lerp(this.msPerFrame, deltaSec * 1000, 0.3);
Expand Down Expand Up @@ -326,12 +324,12 @@ class Rewind {
this.module._joypad_delete(this.joypadPtr);
}

get oldestTicks() {
return this.module._rewind_get_oldest_ticks_f64(this.bufferPtr);
get oldestSecs() {
return this.module._rewind_get_oldest_secs(this.bufferPtr);
}

get newestTicks() {
return this.module._rewind_get_newest_ticks_f64(this.bufferPtr);
get newestSecs() {
return this.module._rewind_get_newest_secs(this.bufferPtr);
}

pushBuffer() {
Expand All @@ -351,10 +349,9 @@ class Rewind {
this.module._joypad_begin_rewind_playback(this.joypadPtr);
}

rewindToTicks(ticks) {
rewindToSecs(secs) {
if (!this.isRewinding) return;
return this.module._rewind_to_ticks_wrapper(this.statePtr, ticks) ===
RESULT_OK;
return this.module._rewind_to_secs(this.statePtr, secs) === RESULT_OK;
}

endRewind() {
Expand Down
10 changes: 5 additions & 5 deletions src/emscripten/exported.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,11 @@
"_add_snesmouse_delta",
"_emulator_convert_frame_buffer_simple",
"_emulator_delete",
"_emulator_get_ticks_f64",
"_emulator_get_secs",
"_emulator_new_simple",
"_emulator_read_prg_ram",
"_emulator_read_state",
"_emulator_run_until_f64",
"_emulator_run_until_secs",
"_emulator_set_reset",
"_emulator_was_prg_ram_updated",
"_emulator_write_prg_ram",
Expand Down Expand Up @@ -34,10 +34,10 @@
"_rewind_begin",
"_rewind_delete",
"_rewind_end",
"_rewind_get_newest_ticks_f64",
"_rewind_get_oldest_ticks_f64",
"_rewind_get_newest_secs",
"_rewind_get_oldest_secs",
"_rewind_new_simple",
"_rewind_to_ticks_wrapper",
"_rewind_to_secs",
"_set_controller_type",
"_set_file_data_ptr",
"_set_file_data_size",
Expand Down
28 changes: 18 additions & 10 deletions src/emscripten/wrapper.c
Original file line number Diff line number Diff line change
Expand Up @@ -40,20 +40,28 @@ Emulator* emulator_new_simple(void* rom_data, size_t rom_size,
return e;
}

f64 emulator_get_ticks_f64(Emulator* e) {
return (f64)emulator_get_ticks(e);
static f64 ticks_to_secs(Ticks ticks) {
return (f64)ticks / e->master_ticks_per_second;
}

EmulatorEvent emulator_run_until_f64(Emulator* e, f64 until_ticks_f64) {
return emulator_run_until(e, (Ticks)until_ticks_f64);
static Ticks secs_to_ticks(f64 secs) {
return (Ticks)(secs * e->master_ticks_per_second);
}

f64 rewind_get_newest_ticks_f64(RewindBuffer* buf) {
return (f64)rewind_get_newest_ticks(buf);
f64 emulator_get_secs(Emulator* e) {
return ticks_to_secs(emulator_get_ticks(e));
}

f64 rewind_get_oldest_ticks_f64(RewindBuffer* buf) {
return (f64)rewind_get_oldest_ticks(buf);
EmulatorEvent emulator_run_until_secs(Emulator* e, f64 until_secs) {
return emulator_run_until(e, secs_to_ticks(until_secs));
}

f64 rewind_get_newest_secs(RewindBuffer* buf) {
return ticks_to_secs(rewind_get_newest_ticks(buf));
}

f64 rewind_get_oldest_secs(RewindBuffer* buf) {
return ticks_to_secs(rewind_get_oldest_ticks(buf));
}

static void default_joypad_callback(SystemInput *input, void *user_data,
Expand Down Expand Up @@ -211,8 +219,8 @@ RewindState *rewind_begin(Emulator *e, RewindBuffer *rewind_buffer,
return &s_rewind_state;
}

Result rewind_to_ticks_wrapper(RewindState* state, f64 ticks_f64) {
Ticks ticks = (Ticks)ticks_f64;
Result rewind_to_secs(RewindState* state, f64 secs) {
Ticks ticks = secs_to_ticks(secs);
CHECK(SUCCESS(
rewind_to_ticks(state->rewind_buffer, ticks, &state->rewind_result)));
CHECK(SUCCESS(emulator_read_state(e, &state->rewind_result.file_data)));
Expand Down

0 comments on commit 8865347

Please sign in to comment.