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