mirror of
https://github.com/sehugg/8bitworkshop.git
synced 2024-12-28 20:29:22 +00:00
stepOver(), rearranged dbg buttons, fixed basic debugging and saveState()
This commit is contained in:
parent
45ab88611e
commit
e96f7e8b49
@ -104,6 +104,7 @@ export interface Platform {
|
||||
stepBack?() : void;
|
||||
runEval?(evalfunc : DebugEvalCondition) : void;
|
||||
runToFrameClock?(clock : number) : void;
|
||||
stepOver?() : void;
|
||||
|
||||
getOpcodeMetadata?(opcode:number, offset:number) : OpcodeMetadata; //TODO
|
||||
getSP?() : number;
|
||||
@ -199,7 +200,7 @@ export abstract class BasePlatform {
|
||||
inspect(sym: string) : string {
|
||||
return inspectSymbol((this as any) as Platform, sym);
|
||||
}
|
||||
getDebugTree() {
|
||||
getDebugTree() : {} {
|
||||
return this.saveState();
|
||||
}
|
||||
}
|
||||
|
@ -955,7 +955,7 @@ export const ECMA55_MINIMAL : BASICOptions = {
|
||||
export const BASICODE : BASICOptions = {
|
||||
dialectName: "BASICODE",
|
||||
asciiOnly : true,
|
||||
uppercaseOnly : true,
|
||||
uppercaseOnly : false,
|
||||
optionalLabels : false,
|
||||
optionalWhitespace : true,
|
||||
varNaming : "AA",
|
||||
|
@ -80,9 +80,9 @@ export class BASICRuntime {
|
||||
vars : {};
|
||||
arrays : {};
|
||||
defs : {};
|
||||
forLoops : { [varname:string] : { $next:(name:string) => void, inner:string } };
|
||||
forLoops : { [varname:string] : { $next:(name:string) => void } };
|
||||
forLoopStack: string[];
|
||||
whileLoops : number[];
|
||||
topForLoopName : string;
|
||||
returnStack : number[];
|
||||
column : number;
|
||||
rng : RNG;
|
||||
@ -145,7 +145,7 @@ export class BASICRuntime {
|
||||
this.arrays = {};
|
||||
this.defs = {}; // TODO? only in interpreters
|
||||
this.forLoops = {};
|
||||
this.topForLoopName = null;
|
||||
this.forLoopStack = [];
|
||||
this.whileLoops = [];
|
||||
this.rng = new RNG();
|
||||
// initialize arrays?
|
||||
@ -157,6 +157,13 @@ export class BASICRuntime {
|
||||
}
|
||||
|
||||
// TODO: saveState(), loadState()
|
||||
saveState() {
|
||||
// TODO: linked list loop?
|
||||
return $.extend(true, {}, this);
|
||||
}
|
||||
loadState(state) {
|
||||
$.extend(true, this, state);
|
||||
}
|
||||
|
||||
getBuiltinFunctions() {
|
||||
var fnames = this.program && this.opts.validFunctions;
|
||||
@ -455,20 +462,20 @@ export class BASICRuntime {
|
||||
// skip entire for loop before first iteration? (Minimal BASIC)
|
||||
if (this.opts.testInitialFor && loopdone())
|
||||
return this.skipToAfterNext(forname);
|
||||
// save for var name on top of linked-list stack
|
||||
var inner = this.topForLoopName;
|
||||
this.topForLoopName = forname;
|
||||
// save for var name on stack, remove existing entry
|
||||
if (this.forLoopStack[forname] != null)
|
||||
this.forLoopStack = this.forLoopStack.filter((n) => n == forname);
|
||||
this.forLoopStack.push(forname);
|
||||
// create for loop record
|
||||
this.forLoops[forname] = {
|
||||
inner: inner,
|
||||
$next: (nextname:string) => {
|
||||
if (nextname && forname != nextname)
|
||||
this.runtimeError(`I executed NEXT "${nextname}", but the last FOR was for "${forname}".`)
|
||||
this.vars[forname] += step;
|
||||
var done = loopdone();
|
||||
if (done) {
|
||||
// pop FOR off the stack and continue
|
||||
this.topForLoopName = inner;
|
||||
// delete entry, pop FOR off the stack and continue
|
||||
this.forLoopStack.pop();
|
||||
delete this.forLoops[forname];
|
||||
} else {
|
||||
this.curpc = pc; // go back to FOR location
|
||||
@ -479,7 +486,8 @@ export class BASICRuntime {
|
||||
}
|
||||
|
||||
nextForLoop(name) {
|
||||
var fl = this.forLoops[name || (this.opts.optionalNextVar && this.topForLoopName)];
|
||||
// get FOR loop entry, or get top of stack if NEXT var is optional
|
||||
var fl = this.forLoops[name || (this.opts.optionalNextVar && this.forLoopStack[this.forLoopStack.length-1])];
|
||||
if (!fl) this.runtimeError(`I couldn't find a matching FOR for this NEXT.`)
|
||||
fl.$next(name);
|
||||
}
|
||||
|
@ -1316,6 +1316,12 @@ function singleStep() {
|
||||
platform.step();
|
||||
}
|
||||
|
||||
function stepOver() {
|
||||
if (!checkRunReady()) return;
|
||||
setupBreakpoint("stepover");
|
||||
platform.stepOver();
|
||||
}
|
||||
|
||||
function singleFrameStep() {
|
||||
if (!checkRunReady()) return;
|
||||
setupBreakpoint("tovsync");
|
||||
@ -1374,17 +1380,15 @@ function resetAndDebug() {
|
||||
if (!checkRunReady()) return;
|
||||
var wasRecording = recorderActive;
|
||||
_disableRecording();
|
||||
if (platform.setupDebug && platform.readAddress) { // TODO??
|
||||
if (platform.setupDebug && platform.runEval) { // TODO??
|
||||
clearBreakpoint();
|
||||
_resume();
|
||||
platform.reset();
|
||||
setupBreakpoint("restart");
|
||||
if (platform.runEval)
|
||||
platform.runEval((c) => { return true; }); // break immediately
|
||||
else
|
||||
; // TODO???
|
||||
} else {
|
||||
platform.reset();
|
||||
_resume();
|
||||
}
|
||||
if (wasRecording) _enableRecording();
|
||||
}
|
||||
@ -1423,19 +1427,6 @@ function breakExpression(exprs : string) {
|
||||
lastBreakExpr = exprs;
|
||||
}
|
||||
|
||||
function getSymbolAtAddress(a : number) {
|
||||
var addr2symbol = platform.debugSymbols && platform.debugSymbols.addr2symbol;
|
||||
if (addr2symbol) {
|
||||
if (addr2symbol[a]) return addr2symbol[a];
|
||||
var i=0;
|
||||
while (--a >= 0) {
|
||||
i++;
|
||||
if (addr2symbol[a]) return addr2symbol[a] + '+' + i;
|
||||
}
|
||||
}
|
||||
return '';
|
||||
}
|
||||
|
||||
function updateDebugWindows() {
|
||||
if (platform.isRunning()) {
|
||||
projectWindows.tick();
|
||||
@ -1657,23 +1648,26 @@ function setupDebugControls() {
|
||||
uitoolbar.newGroup();
|
||||
uitoolbar.grp.prop('id','debug_bar');
|
||||
if (platform.runEval) {
|
||||
uitoolbar.add('ctrl+alt+e', 'Restart Debugging', 'glyphicon-fast-backward', resetAndDebug).prop('id','dbg_restart');
|
||||
uitoolbar.add('ctrl+alt+e', 'Restart Debugging', 'glyphicon-repeat', resetAndDebug).prop('id','dbg_restart');
|
||||
}
|
||||
if (platform.stepBack) {
|
||||
uitoolbar.add('ctrl+alt+b', 'Step Backwards', 'glyphicon-step-backward', runStepBackwards).prop('id','dbg_stepback');
|
||||
}
|
||||
if (platform.step) {
|
||||
uitoolbar.add('ctrl+alt+s', 'Single Step', 'glyphicon-step-forward', singleStep).prop('id','dbg_step');
|
||||
}
|
||||
if (platform.stepOver) {
|
||||
uitoolbar.add('ctrl+alt+t', 'Step Over', 'glyphicon-hand-right', stepOver).prop('id','dbg_stepover');
|
||||
}
|
||||
if (platform.runUntilReturn) {
|
||||
uitoolbar.add('ctrl+alt+o', 'Step Out of Subroutine', 'glyphicon-hand-up', runUntilReturn).prop('id','dbg_stepout');
|
||||
}
|
||||
if (platform.runToVsync) {
|
||||
uitoolbar.add('ctrl+alt+n', 'Next Frame/Interrupt', 'glyphicon-forward', singleFrameStep).prop('id','dbg_tovsync');
|
||||
}
|
||||
if ((platform.runEval || platform.runToPC) && !platform_id.startsWith('verilog')) {
|
||||
uitoolbar.add('ctrl+alt+l', 'Run To Line', 'glyphicon-save', runToCursor).prop('id','dbg_toline');
|
||||
}
|
||||
if (platform.runUntilReturn) {
|
||||
uitoolbar.add('ctrl+alt+o', 'Step Out of Subroutine', 'glyphicon-hand-up', runUntilReturn).prop('id','dbg_stepout');
|
||||
}
|
||||
if (platform.stepBack) {
|
||||
uitoolbar.add('ctrl+alt+b', 'Step Backwards', 'glyphicon-step-backward', runStepBackwards).prop('id','dbg_stepback');
|
||||
}
|
||||
uitoolbar.newGroup();
|
||||
// add menu clicks
|
||||
$(".dropdown-menu").collapse({toggle: false});
|
||||
|
@ -1,5 +1,5 @@
|
||||
|
||||
import { Platform, BreakpointCallback } from "../common/baseplatform";
|
||||
import { Platform, BreakpointCallback, DebugCondition, DebugEvalCondition } from "../common/baseplatform";
|
||||
import { PLATFORMS, AnimationTimer, EmuHalt } from "../common/emu";
|
||||
import { loadScript } from "../ide/ui";
|
||||
import * as views from "../ide/views";
|
||||
@ -19,11 +19,11 @@ class BASICPlatform implements Platform {
|
||||
mainElement: HTMLElement;
|
||||
program: BASICProgram;
|
||||
runtime: BASICRuntime;
|
||||
clock: number = 0;
|
||||
timer: AnimationTimer;
|
||||
tty: TeleTypeWithKeyboard;
|
||||
clock: number = 0;
|
||||
hotReload: boolean = false;
|
||||
debugstop: boolean = false; // TODO: should be higher-level support
|
||||
animcount: number = 0;
|
||||
|
||||
constructor(mainElement: HTMLElement) {
|
||||
//super();
|
||||
@ -69,7 +69,7 @@ class BASICPlatform implements Platform {
|
||||
this.resize();
|
||||
this.runtime.print = (s:string) => {
|
||||
// TODO: why null sometimes?
|
||||
this.clock = 0; // exit advance loop when printing
|
||||
this.animcount = 0; // exit advance loop when printing
|
||||
this.tty.print(s);
|
||||
}
|
||||
this.runtime.resume = this.resume.bind(this);
|
||||
@ -78,8 +78,8 @@ class BASICPlatform implements Platform {
|
||||
animate() {
|
||||
if (this.tty.isBusy()) return;
|
||||
var ips = this.program.opts.commandsPerSec || 1000;
|
||||
this.clock += ips / 60;
|
||||
while (!this.runtime.exited && this.clock-- > 0) {
|
||||
this.animcount += ips / 60;
|
||||
while (!this.runtime.exited && this.animcount-- > 0) {
|
||||
this.advance();
|
||||
}
|
||||
}
|
||||
@ -87,6 +87,8 @@ class BASICPlatform implements Platform {
|
||||
// should not depend on tty state
|
||||
advance(novideo?: boolean) : number {
|
||||
if (this.runtime.running) {
|
||||
if (this.checkDebugTrap())
|
||||
return 0;
|
||||
var more = this.runtime.step();
|
||||
if (!more) {
|
||||
this.pause();
|
||||
@ -94,7 +96,7 @@ class BASICPlatform implements Platform {
|
||||
this.exitmsg();
|
||||
}
|
||||
}
|
||||
// TODO: break() when EmuHalt at location?
|
||||
this.clock++;
|
||||
return 1;
|
||||
} else {
|
||||
return 0;
|
||||
@ -127,10 +129,7 @@ class BASICPlatform implements Platform {
|
||||
reset(): void {
|
||||
this.tty.clear();
|
||||
this.runtime.reset();
|
||||
if (this.debugstop)
|
||||
this.break();
|
||||
else
|
||||
this.resume();
|
||||
this.clock = 0;
|
||||
}
|
||||
|
||||
pause(): void {
|
||||
@ -138,11 +137,12 @@ class BASICPlatform implements Platform {
|
||||
}
|
||||
|
||||
resume(): void {
|
||||
this.clock = 0;
|
||||
this.debugstop = false;
|
||||
if (this.isBlocked()) return;
|
||||
this.animcount = 0;
|
||||
this.timer.start();
|
||||
}
|
||||
|
||||
isBlocked() { return this.tty.waitingfor != null; } // is blocked for input?
|
||||
isRunning() { return this.timer.isRunning(); }
|
||||
getDefaultExtension() { return ".bas"; }
|
||||
getToolForFilename() { return "basic"; }
|
||||
@ -157,18 +157,17 @@ class BASICPlatform implements Platform {
|
||||
isStable() {
|
||||
return true;
|
||||
}
|
||||
|
||||
getCPUState() {
|
||||
return { PC: this.getPC(), SP: this.getSP() }
|
||||
}
|
||||
saveState() {
|
||||
return {
|
||||
c: this.getCPUState(),
|
||||
rt: $.extend(true, {}, this.runtime) // TODO: don't take all
|
||||
rt: this.runtime.saveState(),
|
||||
}
|
||||
}
|
||||
loadState(state) {
|
||||
$.extend(true, this.runtime, state);
|
||||
this.runtime.loadState(state);
|
||||
}
|
||||
getDebugTree() {
|
||||
return {
|
||||
@ -180,6 +179,7 @@ class BASICPlatform implements Platform {
|
||||
WhileLoops: this.runtime.whileLoops,
|
||||
ReturnStack: this.runtime.returnStack,
|
||||
NextDatum: this.runtime.datums[this.runtime.dataptr],
|
||||
Clock: this.clock,
|
||||
Options: this.runtime.opts,
|
||||
Internals: this.runtime,
|
||||
}
|
||||
@ -194,27 +194,53 @@ class BASICPlatform implements Platform {
|
||||
// TODO: debugging (get running state, etc)
|
||||
|
||||
onBreakpointHit : BreakpointCallback;
|
||||
debugTrap : DebugCondition;
|
||||
|
||||
setupDebug(callback : BreakpointCallback) : void {
|
||||
this.onBreakpointHit = callback;
|
||||
}
|
||||
clearDebug() {
|
||||
this.onBreakpointHit = null;
|
||||
this.debugTrap = null;
|
||||
}
|
||||
step() {
|
||||
if (this.tty.waitingfor == null) {
|
||||
checkDebugTrap() : boolean {
|
||||
if (this.debugTrap && this.debugTrap()) {
|
||||
this.pause();
|
||||
this.advance();
|
||||
this.break();
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
break() {
|
||||
// TODO: why doesn't highlight go away on resume?
|
||||
if (this.onBreakpointHit) {
|
||||
this.onBreakpointHit(this.saveState());
|
||||
this.debugstop = true;
|
||||
}
|
||||
}
|
||||
step() {
|
||||
var prevClock = this.clock;
|
||||
this.debugTrap = () => {
|
||||
return this.clock > prevClock;
|
||||
};
|
||||
this.resume();
|
||||
}
|
||||
stepOver() {
|
||||
var stmt = this.runtime.getStatement();
|
||||
if (stmt && (stmt.command == 'GOSUB' || stmt.command == 'ONGOSUB')) {
|
||||
var nextPC = this.getPC() + 1;
|
||||
this.runEval(() => this.getPC() == nextPC);
|
||||
} else {
|
||||
this.step();
|
||||
}
|
||||
}
|
||||
runUntilReturn() {
|
||||
var prevSP = this.getSP();
|
||||
this.runEval(() => this.getSP() > prevSP);
|
||||
}
|
||||
runEval(evalfunc : DebugEvalCondition) {
|
||||
this.debugTrap = () => evalfunc(this.getCPUState());
|
||||
this.resume();
|
||||
}
|
||||
}
|
||||
|
||||
//
|
||||
|
Loading…
Reference in New Issue
Block a user