mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-01-11 08:30:02 +00:00
now we can live replay
This commit is contained in:
parent
caf56e14e3
commit
a071cd80db
@ -51,10 +51,7 @@ TODO:
|
|||||||
- tools (memory, disasm) use debugging state
|
- tools (memory, disasm) use debugging state
|
||||||
- text log debugging script
|
- text log debugging script
|
||||||
- NES crt should mark raster pos when debugging
|
- NES crt should mark raster pos when debugging
|
||||||
- make sure we don't store files in local storage unnecc.
|
|
||||||
- state buffer/replay
|
|
||||||
- intro/help text for each platform
|
- intro/help text for each platform
|
||||||
- make sure controls work with replay feature (we'll have to save control state every frame)
|
|
||||||
- vscode/atom extension?
|
- vscode/atom extension?
|
||||||
- navigator.getGamepads
|
- navigator.getGamepads
|
||||||
|
|
||||||
|
@ -24,7 +24,8 @@ export class StateRecorderImpl implements EmuRecorder {
|
|||||||
this.checkpoints = [];
|
this.checkpoints = [];
|
||||||
this.framerecs = [];
|
this.framerecs = [];
|
||||||
this.frameCount = 0;
|
this.frameCount = 0;
|
||||||
this.lastSeekFrame = -1;
|
this.lastSeekFrame = 0;
|
||||||
|
if (this.callbackStateChanged) this.callbackStateChanged();
|
||||||
}
|
}
|
||||||
|
|
||||||
frameRequested() : boolean {
|
frameRequested() : boolean {
|
||||||
@ -32,32 +33,35 @@ export class StateRecorderImpl implements EmuRecorder {
|
|||||||
if (this.checkpoints.length >= this.maxCheckpoints) {
|
if (this.checkpoints.length >= this.maxCheckpoints) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
// record the control state, if available
|
var controls = {
|
||||||
if (this.platform.saveControlsState) {
|
|
||||||
this.framerecs.push({
|
|
||||||
controls:this.platform.saveControlsState(),
|
controls:this.platform.saveControlsState(),
|
||||||
seed:getNoiseSeed()
|
seed:getNoiseSeed()
|
||||||
});
|
};
|
||||||
}
|
var requested = false;
|
||||||
// pick up where we left off, if we used the seek function
|
// are we replaying? then we don't need to save a frame, just replace controls
|
||||||
if (this.lastSeekFrame >= 0) {
|
if (this.lastSeekFrame < this.frameCount) {
|
||||||
this.frameCount = this.lastSeekFrame;
|
this.loadControls(this.lastSeekFrame);
|
||||||
this.lastSeekFrame = -1;
|
} else {
|
||||||
// truncate buffers
|
// record the control state, if available
|
||||||
this.checkpoints = this.checkpoints.slice(0, Math.floor((this.frameCount + this.checkpointInterval - 1) / this.checkpointInterval));
|
if (this.platform.saveControlsState) {
|
||||||
this.framerecs = this.framerecs.slice(0, this.frameCount);
|
this.framerecs.push(controls);
|
||||||
}
|
}
|
||||||
// time to save next frame?
|
// time to save next frame?
|
||||||
if (this.callbackStateChanged) {
|
requested = (this.frameCount++ % this.checkpointInterval) == 0;
|
||||||
this.callbackStateChanged();
|
|
||||||
}
|
}
|
||||||
return (this.frameCount++ % this.checkpointInterval) == 0;
|
this.lastSeekFrame++;
|
||||||
|
if (this.callbackStateChanged) this.callbackStateChanged();
|
||||||
|
return requested;
|
||||||
}
|
}
|
||||||
|
|
||||||
numFrames() : number {
|
numFrames() : number {
|
||||||
return this.frameCount;
|
return this.frameCount;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
currentFrame() : number {
|
||||||
|
return this.lastSeekFrame;
|
||||||
|
}
|
||||||
|
|
||||||
recordFrame(state : EmuState) {
|
recordFrame(state : EmuState) {
|
||||||
this.checkpoints.push(state);
|
this.checkpoints.push(state);
|
||||||
}
|
}
|
||||||
@ -79,8 +83,7 @@ export class StateRecorderImpl implements EmuRecorder {
|
|||||||
this.platform.loadState(state);
|
this.platform.loadState(state);
|
||||||
while (frame < seekframe) {
|
while (frame < seekframe) {
|
||||||
if (frame < this.framerecs.length) {
|
if (frame < this.framerecs.length) {
|
||||||
this.platform.loadControlsState(this.framerecs[frame].controls);
|
this.loadControls(frame);
|
||||||
setNoiseSeed(this.framerecs[frame].seed);
|
|
||||||
}
|
}
|
||||||
frame++;
|
frame++;
|
||||||
this.platform.advance(frame < seekframe); // TODO: infinite loop?
|
this.platform.advance(frame < seekframe); // TODO: infinite loop?
|
||||||
@ -91,4 +94,10 @@ export class StateRecorderImpl implements EmuRecorder {
|
|||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
loadControls(frame : number) {
|
||||||
|
if (this.platform.loadControlsState)
|
||||||
|
this.platform.loadControlsState(this.framerecs[frame].controls);
|
||||||
|
setNoiseSeed(this.framerecs[frame].seed);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
12
src/ui.ts
12
src/ui.ts
@ -406,6 +406,7 @@ function setCompileOutput(data: WorkerResult) {
|
|||||||
if (rom) { // TODO instanceof Uint8Array) {
|
if (rom) { // TODO instanceof Uint8Array) {
|
||||||
try {
|
try {
|
||||||
clearBreakpoint(); // so we can replace memory (TODO: change toolbar btn)
|
clearBreakpoint(); // so we can replace memory (TODO: change toolbar btn)
|
||||||
|
_resetRecording();
|
||||||
platform.loadROM(getCurrentPresetTitle(), rom);
|
platform.loadROM(getCurrentPresetTitle(), rom);
|
||||||
if (!userPaused) _resume();
|
if (!userPaused) _resume();
|
||||||
current_output = rom;
|
current_output = rom;
|
||||||
@ -550,6 +551,7 @@ function clearBreakpoint() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function resetAndDebug() {
|
function resetAndDebug() {
|
||||||
|
_disableRecording();
|
||||||
if (platform.setupDebug && platform.readAddress) { // TODO??
|
if (platform.setupDebug && platform.readAddress) { // TODO??
|
||||||
clearBreakpoint();
|
clearBreakpoint();
|
||||||
_resume();
|
_resume();
|
||||||
@ -693,6 +695,12 @@ function _disableRecording() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function _resetRecording() {
|
||||||
|
if (recorderActive) {
|
||||||
|
stateRecorder.reset();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
function _enableRecording() {
|
function _enableRecording() {
|
||||||
stateRecorder.reset();
|
stateRecorder.reset();
|
||||||
platform.setRecorder(stateRecorder);
|
platform.setRecorder(stateRecorder);
|
||||||
@ -790,8 +798,8 @@ function setupReplaySlider() {
|
|||||||
stateRecorder.callbackStateChanged = () => {
|
stateRecorder.callbackStateChanged = () => {
|
||||||
replayslider.attr('min', 1);
|
replayslider.attr('min', 1);
|
||||||
replayslider.attr('max', stateRecorder.numFrames());
|
replayslider.attr('max', stateRecorder.numFrames());
|
||||||
replayslider.val(stateRecorder.numFrames());
|
replayslider.val(stateRecorder.currentFrame());
|
||||||
updateFrameNo(stateRecorder.numFrames());
|
updateFrameNo(stateRecorder.currentFrame());
|
||||||
};
|
};
|
||||||
replayslider.on('input', sliderChanged);
|
replayslider.on('input', sliderChanged);
|
||||||
replayslider.on('change', sliderChanged);
|
replayslider.on('change', sliderChanged);
|
||||||
|
@ -24,15 +24,16 @@ global.Log = require('tss/js/Log.js').Log;
|
|||||||
includeInThisContext('tss/js/tss/PsgDeviceChannel.js');
|
includeInThisContext('tss/js/tss/PsgDeviceChannel.js');
|
||||||
includeInThisContext('tss/js/tss/MasterChannel.js');
|
includeInThisContext('tss/js/tss/MasterChannel.js');
|
||||||
includeInThisContext('tss/js/tss/AudioLooper.js');
|
includeInThisContext('tss/js/tss/AudioLooper.js');
|
||||||
|
includeInThisContext("jsnes/jsnes.min.js");
|
||||||
var jsnes = require("jsnes/jsnes.min.js");
|
|
||||||
|
|
||||||
var emu = require('gen/emu.js');
|
var emu = require('gen/emu.js');
|
||||||
|
var Keys = emu.Keys;
|
||||||
var audio = require('gen/audio.js');
|
var audio = require('gen/audio.js');
|
||||||
var recorder = require('gen/recorder.js');
|
var recorder = require('gen/recorder.js');
|
||||||
var _vicdual = require('gen/platform/vicdual.js');
|
var _vicdual = require('gen/platform/vicdual.js');
|
||||||
var _apple2 = require('gen/platform/apple2.js');
|
var _apple2 = require('gen/platform/apple2.js');
|
||||||
var _vcs = require('gen/platform/vcs.js');
|
var _vcs = require('gen/platform/vcs.js');
|
||||||
|
var _nes = require('gen/platform/nes.js');
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
||||||
@ -108,5 +109,14 @@ describe('Platform Replay', () => {
|
|||||||
assert.equal(platform.saveState().p.SA, 0xff ^ 0x40);
|
assert.equal(platform.saveState().p.SA, 0xff ^ 0x40);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Should run nes', () => {
|
||||||
|
var platform = testPlatform('nes', 'shoot2.c.rom', 70, (platform, frameno) => {
|
||||||
|
if (frameno == 60) {
|
||||||
|
keycallback(Keys.VK_Z.c, Keys.VK_Z.c, 1);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
assert.equal(65, platform.saveControlsState().c1[0]);
|
||||||
|
});
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
|
BIN
test/roms/nes/shoot2.c.rom
Normal file
BIN
test/roms/nes/shoot2.c.rom
Normal file
Binary file not shown.
Loading…
x
Reference in New Issue
Block a user