mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2025-02-27 13:29:32 +00:00
load/saveControlsState() support in recorder
This commit is contained in:
parent
d9c41ca9d7
commit
86823c2c21
@ -54,6 +54,7 @@ TODO:
|
|||||||
- make sure we don't store files in local storage unnecc.
|
- make sure we don't store files in local storage unnecc.
|
||||||
- state buffer/replay
|
- 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)
|
||||||
|
|
||||||
FYI: Image links for the books on http://8bitworkshop.com/ are broken
|
FYI: Image links for the books on http://8bitworkshop.com/ are broken
|
||||||
On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional?
|
On the website the additional grey spacing next to the program line numbers is not dynamically resized when the web browser window size is changed. Intentional?
|
||||||
|
@ -19,8 +19,16 @@ export interface CpuState {
|
|||||||
A:number, X:number, Y:number, SP:number, R:boolean,
|
A:number, X:number, Y:number, SP:number, R:boolean,
|
||||||
N,V,D,Z,C:boolean*/
|
N,V,D,Z,C:boolean*/
|
||||||
};
|
};
|
||||||
export interface EmuState {c:CpuState, b?:number[]};
|
export interface EmuState {
|
||||||
export type DisasmLine = {line:string, nbytes:number};
|
c:CpuState, // CPU state
|
||||||
|
b?:number[] // RAM
|
||||||
|
};
|
||||||
|
export interface EmuControlsState {
|
||||||
|
}
|
||||||
|
export type DisasmLine = {
|
||||||
|
line:string,
|
||||||
|
nbytes:number
|
||||||
|
};
|
||||||
|
|
||||||
export interface Platform {
|
export interface Platform {
|
||||||
start() : void;
|
start() : void;
|
||||||
@ -60,6 +68,8 @@ export interface Platform {
|
|||||||
|
|
||||||
setRecorder?(recorder : EmuRecorder) : void;
|
setRecorder?(recorder : EmuRecorder) : void;
|
||||||
advance?(novideo? : boolean) : void;
|
advance?(novideo? : boolean) : void;
|
||||||
|
loadControlsState?(state : EmuControlsState) : void;
|
||||||
|
saveControlsState?() : EmuControlsState;
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface Preset {
|
export interface Preset {
|
||||||
@ -80,7 +90,7 @@ type BreakpointCallback = (EmuState) => void;
|
|||||||
|
|
||||||
export interface EmuRecorder {
|
export interface EmuRecorder {
|
||||||
frameRequested() : boolean;
|
frameRequested() : boolean;
|
||||||
recordFrame(platform : Platform, state : EmuState);
|
recordFrame(state : EmuState);
|
||||||
}
|
}
|
||||||
|
|
||||||
/////
|
/////
|
||||||
@ -132,7 +142,7 @@ abstract class BaseDebugPlatform {
|
|||||||
updateRecorder() {
|
updateRecorder() {
|
||||||
// are we recording and do we need to save a frame?
|
// are we recording and do we need to save a frame?
|
||||||
if (this.recorder && (<Platform><any>this).isRunning() && this.recorder.frameRequested()) {
|
if (this.recorder && (<Platform><any>this).isRunning() && this.recorder.frameRequested()) {
|
||||||
this.recorder.recordFrame(<Platform><any>this, this.saveState());
|
this.recorder.recordFrame(this.saveState());
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -335,6 +335,14 @@ var Apple2Platform = function(mainElement) {
|
|||||||
lc:{s:auxRAMselected,b:auxRAMbank,w:writeinhibit},
|
lc:{s:auxRAMselected,b:auxRAMbank,w:writeinhibit},
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
this.loadControlsState = function(state) {
|
||||||
|
kbdlatch = state.kbd;
|
||||||
|
}
|
||||||
|
this.saveControlsState = function() {
|
||||||
|
return {
|
||||||
|
kbd:kbdlatch,
|
||||||
|
};
|
||||||
|
}
|
||||||
this.getCPUState = function() {
|
this.getCPUState = function() {
|
||||||
return cpu.saveState();
|
return cpu.saveState();
|
||||||
}
|
}
|
||||||
|
83
src/recorder.ts
Normal file
83
src/recorder.ts
Normal file
@ -0,0 +1,83 @@
|
|||||||
|
|
||||||
|
import { Platform, EmuState, EmuControlsState, EmuRecorder } from "./baseplatform";
|
||||||
|
|
||||||
|
export class StateRecorderImpl implements EmuRecorder {
|
||||||
|
checkpointInterval : number = 60;
|
||||||
|
callbackStateChanged : () => void;
|
||||||
|
maxCheckpoints : number = 120;
|
||||||
|
|
||||||
|
platform : Platform;
|
||||||
|
buffer : EmuState[];
|
||||||
|
controls : EmuControlsState[];
|
||||||
|
frameCount : number;
|
||||||
|
lastSeekFrame : number = -1;
|
||||||
|
|
||||||
|
constructor(platform : Platform) {
|
||||||
|
this.reset();
|
||||||
|
this.platform = platform;
|
||||||
|
}
|
||||||
|
|
||||||
|
reset() {
|
||||||
|
this.buffer = [];
|
||||||
|
this.controls = [];
|
||||||
|
this.frameCount = 0;
|
||||||
|
this.lastSeekFrame = -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
frameRequested() : boolean {
|
||||||
|
// buffer full?
|
||||||
|
if (this.buffer.length >= this.maxCheckpoints) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
// record the control state, if available
|
||||||
|
if (this.platform.saveControlsState) {
|
||||||
|
this.controls.push(this.platform.saveControlsState());
|
||||||
|
}
|
||||||
|
// pick up where we left off, if we used the seek function
|
||||||
|
if (this.lastSeekFrame >= 0) {
|
||||||
|
this.frameCount = this.lastSeekFrame;
|
||||||
|
this.lastSeekFrame = -1;
|
||||||
|
// truncate buffers
|
||||||
|
this.buffer = this.buffer.slice(0, Math.floor((this.frameCount + this.checkpointInterval - 1) / this.checkpointInterval));
|
||||||
|
this.controls = this.controls.slice(0, this.frameCount);
|
||||||
|
}
|
||||||
|
// time to save next frame?
|
||||||
|
this.frameCount++;
|
||||||
|
if (this.callbackStateChanged) {
|
||||||
|
this.callbackStateChanged();
|
||||||
|
}
|
||||||
|
return (this.frameCount % this.checkpointInterval) == 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
numFrames() : number {
|
||||||
|
return this.frameCount;
|
||||||
|
}
|
||||||
|
|
||||||
|
recordFrame(state : EmuState) {
|
||||||
|
this.buffer.push(state);
|
||||||
|
}
|
||||||
|
|
||||||
|
getStateAtOrBefore(frame : number) : {frame : number, state : EmuState} {
|
||||||
|
var bufidx = Math.floor(frame / this.checkpointInterval);
|
||||||
|
var foundidx = bufidx < this.buffer.length ? bufidx : this.buffer.length-1;
|
||||||
|
var foundframe = foundidx * this.checkpointInterval;
|
||||||
|
return {frame:foundframe, state:this.buffer[foundidx]};
|
||||||
|
}
|
||||||
|
|
||||||
|
loadFrame(seekframe : number) {
|
||||||
|
let {frame,state} = this.getStateAtOrBefore(seekframe);
|
||||||
|
if (state) {
|
||||||
|
this.platform.pause();
|
||||||
|
this.platform.loadState(state);
|
||||||
|
while (frame < seekframe) {
|
||||||
|
if (frame < this.controls.length) {
|
||||||
|
this.platform.loadControlsState(this.controls[frame]);
|
||||||
|
}
|
||||||
|
this.platform.advance(true); // TODO: infinite loop?
|
||||||
|
frame++;
|
||||||
|
}
|
||||||
|
this.platform.advance();
|
||||||
|
this.lastSeekFrame = seekframe;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -30,7 +30,7 @@ var current_project : CodeProject; // current CodeProject object
|
|||||||
|
|
||||||
var projectWindows : ProjectWindows; // window manager
|
var projectWindows : ProjectWindows; // window manager
|
||||||
|
|
||||||
var stateRecorder : StateRecorderImpl = new StateRecorderImpl();
|
var stateRecorder : StateRecorderImpl;
|
||||||
|
|
||||||
// TODO: codemirror multiplex support?
|
// TODO: codemirror multiplex support?
|
||||||
var TOOL_TO_SOURCE_STYLE = {
|
var TOOL_TO_SOURCE_STYLE = {
|
||||||
@ -771,7 +771,7 @@ function setupDebugControls(){
|
|||||||
};
|
};
|
||||||
replayslider.on('input', function(e) {
|
replayslider.on('input', function(e) {
|
||||||
_pause();
|
_pause();
|
||||||
stateRecorder.loadFrame(platform, (<any>e.target).value);
|
stateRecorder.loadFrame((<any>e.target).value);
|
||||||
});
|
});
|
||||||
$("#replay_bar").show();
|
$("#replay_bar").show();
|
||||||
}
|
}
|
||||||
@ -908,6 +908,7 @@ function addPageFocusHandlers() {
|
|||||||
function startPlatform() {
|
function startPlatform() {
|
||||||
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
|
if (!PLATFORMS[platform_id]) throw Error("Invalid platform '" + platform_id + "'.");
|
||||||
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
|
platform = new PLATFORMS[platform_id]($("#emulator")[0]);
|
||||||
|
stateRecorder = new StateRecorderImpl(platform);
|
||||||
PRESETS = platform.getPresets();
|
PRESETS = platform.getPresets();
|
||||||
if (qs['file']) {
|
if (qs['file']) {
|
||||||
// start platform and load file
|
// start platform and load file
|
||||||
|
Loading…
x
Reference in New Issue
Block a user