1
0
mirror of https://github.com/sehugg/8bitworkshop.git synced 2024-06-03 04:29:33 +00:00

stepOver(), rearranged dbg buttons, fixed basic debugging and saveState()

This commit is contained in:
Steven Hugg 2020-08-13 12:32:47 -05:00
parent 45ab88611e
commit e96f7e8b49
5 changed files with 87 additions and 58 deletions

View File

@ -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();
} }
} }

View File

@ -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",

View File

@ -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);
} }

View File

@ -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});

View File

@ -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"; }
@ -156,19 +156,18 @@ 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();
}
} }
// //