working on tree view for state, extra debuginfo
This commit is contained in:
parent
741df9f5b8
commit
71fa79cec5
56
css/ui.css
56
css/ui.css
|
@ -594,8 +594,8 @@ div.asset_toolbar {
|
||||||
font-weight: bold;
|
font-weight: bold;
|
||||||
}
|
}
|
||||||
.transcript-style-8 { /* input */
|
.transcript-style-8 { /* input */
|
||||||
font-weight: bold;
|
font-weight: 600;
|
||||||
font-variant: small-caps;
|
text-transform: uppercase;
|
||||||
color: #6666ff;
|
color: #6666ff;
|
||||||
background-color: #eeeeff;
|
background-color: #eeeeff;
|
||||||
padding: 0.25em;
|
padding: 0.25em;
|
||||||
|
@ -607,10 +607,54 @@ div.asset_toolbar {
|
||||||
color: #ddd;
|
color: #ddd;
|
||||||
}
|
}
|
||||||
.transcript-input {
|
.transcript-input {
|
||||||
margin:1%;
|
font-weight: 600;
|
||||||
font-weight: bold;
|
text-transform: uppercase;
|
||||||
font-variant: small-caps;
|
|
||||||
color: #6666ff;
|
color: #6666ff;
|
||||||
background-color: #eeeeff;
|
background-color: #eeeeff;
|
||||||
|
margin:1%;
|
||||||
}
|
}
|
||||||
|
.tree-header {
|
||||||
|
border: 2px solid #555;
|
||||||
|
border-radius:8px;
|
||||||
|
color: #fff;
|
||||||
|
background-color:#666;
|
||||||
|
padding-left:1em;
|
||||||
|
font-family: "Andale Mono", "Menlo", "Lucida Console", monospace;
|
||||||
|
}
|
||||||
|
.tree-content {
|
||||||
|
padding-left:0.75em;
|
||||||
|
padding-right:0.75em;
|
||||||
|
font-size: small;
|
||||||
|
}
|
||||||
|
.tree-value {
|
||||||
|
float:right;
|
||||||
|
font-weight:normal;
|
||||||
|
padding-right:1em;
|
||||||
|
}
|
||||||
|
.tree-expanded::after {
|
||||||
|
float: right;
|
||||||
|
margin-right: 1em;
|
||||||
|
content: '\25b2';
|
||||||
|
}
|
||||||
|
.tree-collapsed::after {
|
||||||
|
float: right;
|
||||||
|
margin-right: 1em;
|
||||||
|
content: '\25bc';
|
||||||
|
}
|
||||||
|
.tree-collapsed:hover, .tree-expanded:hover {
|
||||||
|
border-color: rgba(255,255,255,0.7);
|
||||||
|
}
|
||||||
|
.tree-header:hover {
|
||||||
|
background-color: rgba(255,255,255,0.3);
|
||||||
|
}
|
||||||
|
.tree-level-0 { display:none;}
|
||||||
|
.tree-level-1 { background-color: #638283;}
|
||||||
|
.tree-level-2 { background-color: #636e83;}
|
||||||
|
.tree-level-3 { background-color: #636483;}
|
||||||
|
.tree-level-4 { background-color: #756383;}
|
||||||
|
.tree-level-5 { background-color: #83637e;}
|
||||||
|
.tree-level-6 { background-color: #83636e;}
|
||||||
|
.tree-level-7 { background-color: #836363;}
|
||||||
|
.tree-level-8 { background-color: #837163;}
|
||||||
|
.tree-level-9 { background-color: #7b8363;}
|
||||||
|
.tree-level-10 { background-color: #738363;}
|
||||||
|
|
|
@ -0,0 +1,39 @@
|
||||||
|
|
||||||
|
Constant Story "My Story Name";
|
||||||
|
|
||||||
|
Constant Headline
|
||||||
|
"^This is My Story^
|
||||||
|
By New Writer (2020)^";
|
||||||
|
|
||||||
|
Constant MAX_SCORE 100;
|
||||||
|
|
||||||
|
Release 1;
|
||||||
|
|
||||||
|
Include "Parser";
|
||||||
|
Include "VerbLib";
|
||||||
|
|
||||||
|
!-------------------------------------------------------------------------------
|
||||||
|
! Initialise
|
||||||
|
!-------------------------------------------------------------------------------
|
||||||
|
|
||||||
|
[ Initialise;
|
||||||
|
|
||||||
|
location = Main_Lobby;
|
||||||
|
|
||||||
|
];
|
||||||
|
|
||||||
|
! ----------------------------------------------------------------------------
|
||||||
|
! Locations
|
||||||
|
! ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Object Main_Lobby "Main Lobby"
|
||||||
|
with description
|
||||||
|
"You are in the main lobby.",
|
||||||
|
has light;
|
||||||
|
|
||||||
|
! ----------------------------------------------------------------------------
|
||||||
|
! Grammar
|
||||||
|
! ----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
Include "Grammar";
|
||||||
|
|
|
@ -45,9 +45,11 @@ export type AddrSymbolMap = {[address:number]:string};
|
||||||
export class DebugSymbols {
|
export class DebugSymbols {
|
||||||
symbolmap : SymbolMap; // symbol -> address
|
symbolmap : SymbolMap; // symbol -> address
|
||||||
addr2symbol : AddrSymbolMap; // address -> symbol
|
addr2symbol : AddrSymbolMap; // address -> symbol
|
||||||
|
debuginfo : {}; // extra platform-specific debug info
|
||||||
|
|
||||||
constructor(symbolmap : SymbolMap) {
|
constructor(symbolmap : SymbolMap, debuginfo : {}) {
|
||||||
this.symbolmap = symbolmap;
|
this.symbolmap = symbolmap;
|
||||||
|
this.debuginfo = debuginfo;
|
||||||
this.addr2symbol = invertMap(symbolmap);
|
this.addr2symbol = invertMap(symbolmap);
|
||||||
//// TODO: shouldn't be necc.
|
//// TODO: shouldn't be necc.
|
||||||
if (!this.addr2symbol[0x0]) this.addr2symbol[0x0] = '$00'; // needed for ...
|
if (!this.addr2symbol[0x0]) this.addr2symbol[0x0] = '$00'; // needed for ...
|
||||||
|
@ -123,6 +125,7 @@ export interface Platform {
|
||||||
getCPUState?() : CpuState;
|
getCPUState?() : CpuState;
|
||||||
|
|
||||||
debugSymbols? : DebugSymbols;
|
debugSymbols? : DebugSymbols;
|
||||||
|
getDebugTree?() : {};
|
||||||
|
|
||||||
startProbing?() : ProbeRecorder;
|
startProbing?() : ProbeRecorder;
|
||||||
stopProbing?() : void;
|
stopProbing?() : void;
|
||||||
|
@ -194,6 +197,9 @@ 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() {
|
||||||
|
return this.saveState();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export abstract class BaseDebugPlatform extends BasePlatform {
|
export abstract class BaseDebugPlatform extends BasePlatform {
|
||||||
|
|
|
@ -96,12 +96,13 @@ export type WorkerOutput = Uint8Array | VerilogOutput;
|
||||||
export type Segment = {name:string, start:number, size:number, last?:number, type?:string};
|
export type Segment = {name:string, start:number, size:number, last?:number, type?:string};
|
||||||
|
|
||||||
export interface WorkerResult {
|
export interface WorkerResult {
|
||||||
output:WorkerOutput,
|
output:WorkerOutput
|
||||||
errors:WorkerError[],
|
errors:WorkerError[]
|
||||||
listings:CodeListingMap,
|
listings:CodeListingMap
|
||||||
symbolmap:{[sym:string]:number},
|
symbolmap:{[sym:string]:number}
|
||||||
params:{},
|
params:{}
|
||||||
segments?:Segment[],
|
segments?:Segment[]
|
||||||
unchanged?:boolean,
|
unchanged?:boolean
|
||||||
|
debuginfo?:{} // optional info
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -278,6 +278,11 @@ function refreshWindowList() {
|
||||||
return new Views.VRAMMemoryView();
|
return new Views.VRAMMemoryView();
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
if (platform.getDebugTree) {
|
||||||
|
addWindowItem("#debugview", "Debug Browser", () => {
|
||||||
|
return new Views.DebugBrowserView();
|
||||||
|
});
|
||||||
|
}
|
||||||
if (platform.startProbing) {
|
if (platform.startProbing) {
|
||||||
addWindowItem("#memheatmap", "Memory Probe", () => {
|
addWindowItem("#memheatmap", "Memory Probe", () => {
|
||||||
return new Views.AddressHeatMapView();
|
return new Views.AddressHeatMapView();
|
||||||
|
@ -1031,7 +1036,7 @@ function setCompileOutput(data: WorkerResult) {
|
||||||
showErrorAlert(data.errors);
|
showErrorAlert(data.errors);
|
||||||
} else {
|
} else {
|
||||||
// process symbol map
|
// process symbol map
|
||||||
platform.debugSymbols = new DebugSymbols(data.symbolmap);
|
platform.debugSymbols = new DebugSymbols(data.symbolmap, data.debuginfo);
|
||||||
compparams = data.params;
|
compparams = data.params;
|
||||||
// load ROM
|
// load ROM
|
||||||
var rom = data.output;
|
var rom = data.output;
|
||||||
|
|
246
src/ide/views.ts
246
src/ide/views.ts
|
@ -6,7 +6,7 @@ import { hex, lpad, rpad, safeident, rgb2bgr } from "../common/util";
|
||||||
import { CodeAnalyzer } from "../common/analysis";
|
import { CodeAnalyzer } from "../common/analysis";
|
||||||
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows, runToPC } from "./ui";
|
import { platform, platform_id, compparams, current_project, lastDebugState, projectWindows, runToPC } from "./ui";
|
||||||
import { ProbeRecorder, ProbeFlags } from "../common/recorder";
|
import { ProbeRecorder, ProbeFlags } from "../common/recorder";
|
||||||
import { getMousePos } from "../common/emu";
|
import { getMousePos, dumpRAM } from "../common/emu";
|
||||||
import * as pixed from "./pixeleditor";
|
import * as pixed from "./pixeleditor";
|
||||||
declare var Mousetrap;
|
declare var Mousetrap;
|
||||||
|
|
||||||
|
@ -809,7 +809,7 @@ export class VRAMMemoryView extends MemoryView {
|
||||||
///
|
///
|
||||||
|
|
||||||
export class BinaryFileView implements ProjectView {
|
export class BinaryFileView implements ProjectView {
|
||||||
memorylist;
|
vlist : VirtualTextScroller;
|
||||||
maindiv : HTMLElement;
|
maindiv : HTMLElement;
|
||||||
path:string;
|
path:string;
|
||||||
data:Uint8Array;
|
data:Uint8Array;
|
||||||
|
@ -821,30 +821,12 @@ export class BinaryFileView implements ProjectView {
|
||||||
}
|
}
|
||||||
|
|
||||||
createDiv(parent : HTMLElement) {
|
createDiv(parent : HTMLElement) {
|
||||||
var div = document.createElement('div');
|
this.vlist = new VirtualTextScroller(parent);
|
||||||
div.setAttribute("class", "memdump");
|
this.vlist.create(parent, ((this.data.length+15) >> 4), this.getMemoryLineAt.bind(this));
|
||||||
parent.appendChild(div);
|
return this.vlist.maindiv;
|
||||||
this.showMemoryWindow(parent, div);
|
|
||||||
return this.maindiv = div;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
showMemoryWindow(workspace:HTMLElement, parent:HTMLElement) {
|
getMemoryLineAt(row : number) : VirtualTextLine {
|
||||||
this.memorylist = new VirtualList({
|
|
||||||
w: $(workspace).width(),
|
|
||||||
h: $(workspace).height(),
|
|
||||||
itemHeight: getVisibleEditorLineHeight(),
|
|
||||||
totalRows: ((this.data.length+15) >> 4),
|
|
||||||
generatorFn: (row : number) => {
|
|
||||||
var s = this.getMemoryLineAt(row);
|
|
||||||
var linediv = document.createElement("div");
|
|
||||||
linediv.appendChild(document.createTextNode(s));
|
|
||||||
return linediv;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$(parent).append(this.memorylist.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMemoryLineAt(row : number) : string {
|
|
||||||
var offset = row * 16;
|
var offset = row * 16;
|
||||||
var n1 = 0;
|
var n1 = 0;
|
||||||
var n2 = 16;
|
var n2 = 16;
|
||||||
|
@ -856,12 +838,13 @@ export class BinaryFileView implements ProjectView {
|
||||||
if (i==8) s += ' ';
|
if (i==8) s += ' ';
|
||||||
s += ' ' + (read>=0?hex(read,2):' ');
|
s += ' ' + (read>=0?hex(read,2):' ');
|
||||||
}
|
}
|
||||||
return s;
|
return {text:s};
|
||||||
}
|
}
|
||||||
|
|
||||||
refresh() {
|
refresh() {
|
||||||
|
this.vlist.refresh();
|
||||||
}
|
}
|
||||||
|
|
||||||
getPath() { return this.path; }
|
getPath() { return this.path; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1216,41 +1199,26 @@ export class RasterStackMapView extends ProbeBitmapViewBase implements ProjectVi
|
||||||
}
|
}
|
||||||
|
|
||||||
export class ProbeLogView extends ProbeViewBaseBase {
|
export class ProbeLogView extends ProbeViewBaseBase {
|
||||||
memorylist;
|
vlist : VirtualTextScroller;
|
||||||
maindiv : HTMLElement;
|
maindiv : HTMLElement;
|
||||||
recreateOnResize = true;
|
recreateOnResize = true;
|
||||||
dumplines;
|
dumplines;
|
||||||
|
|
||||||
createDiv(parent : HTMLElement) {
|
createDiv(parent : HTMLElement) {
|
||||||
var div = document.createElement('div');
|
this.vlist = new VirtualTextScroller(parent);
|
||||||
div.setAttribute("class", "memdump");
|
this.vlist.create(parent, 160*262, this.getMemoryLineAt.bind(this));
|
||||||
parent.appendChild(div);
|
return this.vlist.maindiv;
|
||||||
this.showMemoryWindow(parent, div);
|
|
||||||
return this.maindiv = div;
|
|
||||||
}
|
}
|
||||||
|
getMemoryLineAt(row : number) : VirtualTextLine {
|
||||||
showMemoryWindow(workspace:HTMLElement, parent:HTMLElement) {
|
var s : string = "";
|
||||||
this.memorylist = new VirtualList({
|
var c : string = "seg_data";
|
||||||
w: $(workspace).width(),
|
|
||||||
h: $(workspace).height(),
|
|
||||||
itemHeight: getVisibleEditorLineHeight(),
|
|
||||||
totalRows: 160*262, // TODO?
|
|
||||||
generatorFn: (row : number) => {
|
|
||||||
var s = this.getMemoryLineAt(row);
|
|
||||||
var linediv = document.createElement("div");
|
|
||||||
linediv.appendChild(document.createTextNode(s));
|
|
||||||
return linediv;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
$(parent).append(this.memorylist.container);
|
|
||||||
}
|
|
||||||
|
|
||||||
getMemoryLineAt(row : number) : string {
|
|
||||||
var line = this.dumplines && this.dumplines[row];
|
var line = this.dumplines && this.dumplines[row];
|
||||||
if (line != null) {
|
if (line != null) {
|
||||||
var xtra = line.info.join(", ");
|
var xtra : string = line.info.join(", ");
|
||||||
return "(" + lpad(line.row,3) + ", " + lpad(line.col,3) + ") " + rpad(line.asm||"",20) + xtra;
|
s = "(" + lpad(line.row,3) + ", " + lpad(line.col,3) + ") " + rpad(line.asm||"",20) + xtra;
|
||||||
} else return "";
|
if (xtra.indexOf("Write ") >= 0) c = "seg_io";
|
||||||
|
}
|
||||||
|
return {text:s, clas:c};
|
||||||
}
|
}
|
||||||
refresh() {
|
refresh() {
|
||||||
this.tick();
|
this.tick();
|
||||||
|
@ -1279,17 +1247,7 @@ export class ProbeLogView extends ProbeViewBaseBase {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// TODO: refactor with elsewhere
|
this.vlist.refresh();
|
||||||
if (this.memorylist) {
|
|
||||||
$(this.maindiv).find('[data-index]').each( (i,e) => {
|
|
||||||
var div = $(e);
|
|
||||||
var row = parseInt(div.attr('data-index'));
|
|
||||||
var oldtext = div.text();
|
|
||||||
var newtext = this.getMemoryLineAt(row);
|
|
||||||
if (oldtext != newtext)
|
|
||||||
div.text(newtext);
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1362,6 +1320,166 @@ export class ProbeSymbolView extends ProbeViewBaseBase {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
///
|
||||||
|
|
||||||
|
const MAX_CHILDREN = 200;
|
||||||
|
const MAX_STRING_LEN = 100;
|
||||||
|
const MAX_DUMP_BYTES = 256;
|
||||||
|
|
||||||
|
class TreeNode {
|
||||||
|
parent : TreeNode;
|
||||||
|
name : string;
|
||||||
|
_div : HTMLElement;
|
||||||
|
_header : HTMLElement;
|
||||||
|
_inline : HTMLElement;
|
||||||
|
_content : HTMLElement;
|
||||||
|
children : Map<string,TreeNode>;
|
||||||
|
expanded = false;
|
||||||
|
level : number;
|
||||||
|
view : TreeViewBase;
|
||||||
|
|
||||||
|
constructor(parent : TreeNode, name : string) {
|
||||||
|
this.parent = parent;
|
||||||
|
this.name = name;
|
||||||
|
this.children = new Map();
|
||||||
|
this.level = parent ? (parent.level+1) : -1;
|
||||||
|
this.view = parent ? parent.view : null;
|
||||||
|
}
|
||||||
|
getDiv() {
|
||||||
|
if (this._div == null) {
|
||||||
|
this._div = document.createElement("div");
|
||||||
|
this._div.classList.add("vertical-scroll");
|
||||||
|
this._div.classList.add("tree-content");
|
||||||
|
this._header = document.createElement("div");
|
||||||
|
this._header.classList.add("tree-header");
|
||||||
|
this._header.classList.add("tree-level-" + this.level);
|
||||||
|
this._header.append(this.name);
|
||||||
|
this._inline = document.createElement("span");
|
||||||
|
this._inline.classList.add("tree-value");
|
||||||
|
this._header.append(this._inline);
|
||||||
|
this._div.append(this._header);
|
||||||
|
this.parent._content.append(this._div);
|
||||||
|
this._header.onclick = (e) => {
|
||||||
|
this.toggleExpanded();
|
||||||
|
};
|
||||||
|
}
|
||||||
|
if (this.expanded && this._content == null) {
|
||||||
|
this._content = document.createElement("div");
|
||||||
|
this._div.append(this._content);
|
||||||
|
}
|
||||||
|
else if (!this.expanded && this._content != null) {
|
||||||
|
this._content.remove();
|
||||||
|
this._content = null;
|
||||||
|
this.children.clear();
|
||||||
|
}
|
||||||
|
return this._div;
|
||||||
|
}
|
||||||
|
toggleExpanded() {
|
||||||
|
this.expanded = !this.expanded;
|
||||||
|
this.view.tick();
|
||||||
|
}
|
||||||
|
remove() {
|
||||||
|
this._div.remove();
|
||||||
|
this._div = null;
|
||||||
|
}
|
||||||
|
update(obj : any) {
|
||||||
|
this.getDiv();
|
||||||
|
var text = "";
|
||||||
|
// is it a function? call it first, if we are expanded
|
||||||
|
if (typeof obj == 'function' && this._content != null) {
|
||||||
|
obj = obj();
|
||||||
|
}
|
||||||
|
// check null first
|
||||||
|
if (obj == null) {
|
||||||
|
text = obj+"";
|
||||||
|
// primitive types
|
||||||
|
} else if (typeof obj == 'number') {
|
||||||
|
text = obj.toString();
|
||||||
|
} else if (typeof obj == 'boolean') {
|
||||||
|
text = obj.toString();
|
||||||
|
} else if (typeof obj == 'string') {
|
||||||
|
if (obj.length < MAX_STRING_LEN)
|
||||||
|
text = obj;
|
||||||
|
else
|
||||||
|
text = obj.substring(0, MAX_STRING_LEN) + "...";
|
||||||
|
// byte array (TODO: other kinds)
|
||||||
|
} else if (obj instanceof Uint8Array && obj.length <= MAX_DUMP_BYTES) {
|
||||||
|
text = dumpRAM(obj, 0, obj.length);
|
||||||
|
// recurse into object? (or function)
|
||||||
|
} else if (typeof obj == 'object' || typeof obj == 'function') {
|
||||||
|
if (this._content != null) {
|
||||||
|
let names = Object.getOwnPropertyNames(obj);
|
||||||
|
if (names.length < MAX_CHILDREN) { // max # of child objects
|
||||||
|
let orphans = new Set(this.children.keys());
|
||||||
|
// visit all children
|
||||||
|
names.forEach((name) => {
|
||||||
|
let childnode = this.children.get(name);
|
||||||
|
if (childnode == null) {
|
||||||
|
childnode = new TreeNode(this, name);
|
||||||
|
this.children.set(name, childnode);
|
||||||
|
}
|
||||||
|
childnode.update(obj[name]);
|
||||||
|
orphans.delete(name);
|
||||||
|
});
|
||||||
|
// remove orphans
|
||||||
|
orphans.forEach((delname) => {
|
||||||
|
let childnode = this.children.get(delname);
|
||||||
|
childnode.remove();
|
||||||
|
this.children.delete(delname);
|
||||||
|
});
|
||||||
|
this._header.classList.add("tree-expanded");
|
||||||
|
this._header.classList.remove("tree-collapsed");
|
||||||
|
} else {
|
||||||
|
text = names.length + " items"; // too many children
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
this._header.classList.add("tree-collapsed");
|
||||||
|
this._header.classList.remove("tree-expanded");
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
text = typeof obj; // fallthrough
|
||||||
|
}
|
||||||
|
// change DOM object if needed
|
||||||
|
if (this._inline.innerText != text) {
|
||||||
|
this._inline.innerText = text;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export abstract class TreeViewBase implements ProjectView {
|
||||||
|
root : TreeNode;
|
||||||
|
|
||||||
|
createDiv(parent : HTMLElement) : HTMLElement {
|
||||||
|
var mainnode = new TreeNode(null, null);
|
||||||
|
mainnode.view = this;
|
||||||
|
mainnode._content = parent;
|
||||||
|
this.root = new TreeNode(mainnode, "/");
|
||||||
|
this.root.expanded = true;
|
||||||
|
this.root.getDiv(); // create it
|
||||||
|
this.root._div.style.padding = '0px';
|
||||||
|
return this.root.getDiv(); // should be cached
|
||||||
|
}
|
||||||
|
|
||||||
|
refresh() {
|
||||||
|
this.tick();
|
||||||
|
}
|
||||||
|
|
||||||
|
tick() {
|
||||||
|
this.root.update(this.getRootObject());
|
||||||
|
}
|
||||||
|
|
||||||
|
abstract getRootObject() : Object;
|
||||||
|
}
|
||||||
|
|
||||||
|
export class StateBrowserView extends TreeViewBase implements ProjectView {
|
||||||
|
getRootObject() { return platform.saveState(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
export class DebugBrowserView extends TreeViewBase implements ProjectView {
|
||||||
|
getRootObject() { return platform.getDebugTree(); }
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
///
|
///
|
||||||
|
|
||||||
export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
export class AssetEditorView implements ProjectView, pixed.EditorContext {
|
||||||
|
|
|
@ -785,6 +785,10 @@ var VerilogPlatform = function(mainElement, options) {
|
||||||
|
|
||||||
// DEBUGGING
|
// DEBUGGING
|
||||||
|
|
||||||
|
getDebugTree() {
|
||||||
|
return this.saveState().o;
|
||||||
|
}
|
||||||
|
|
||||||
// TODO: bind() a function to avoid depot?
|
// TODO: bind() a function to avoid depot?
|
||||||
saveState() {
|
saveState() {
|
||||||
var state = {
|
var state = {
|
||||||
|
|
|
@ -55,10 +55,9 @@ class GlkImpl {
|
||||||
curline: HTMLElement;
|
curline: HTMLElement;
|
||||||
curstyle: number;
|
curstyle: number;
|
||||||
reverse: boolean;
|
reverse: boolean;
|
||||||
windows: GlkWindow[];
|
|
||||||
wnd: GlkWindow;
|
|
||||||
waitingfor: "line" | "char" | null;
|
waitingfor: "line" | "char" | null;
|
||||||
focused = false;
|
focused = false;
|
||||||
|
exited = false;
|
||||||
|
|
||||||
constructor(page: HTMLElement, input: HTMLInputElement) {
|
constructor(page: HTMLElement, input: HTMLInputElement) {
|
||||||
this.page = page;
|
this.page = page;
|
||||||
|
@ -66,8 +65,7 @@ class GlkImpl {
|
||||||
this.reset();
|
this.reset();
|
||||||
}
|
}
|
||||||
reset() {
|
reset() {
|
||||||
this.windows = [];
|
this.exited = false;
|
||||||
this.wnd = null;
|
|
||||||
this.clear();
|
this.clear();
|
||||||
}
|
}
|
||||||
clear() {
|
clear() {
|
||||||
|
@ -89,6 +87,7 @@ class GlkImpl {
|
||||||
// TODO
|
// TODO
|
||||||
}
|
}
|
||||||
glk_exit() {
|
glk_exit() {
|
||||||
|
this.exited = true;
|
||||||
this.flushline();
|
this.flushline();
|
||||||
this.addtext("** Game exited **", 1);
|
this.addtext("** Game exited **", 1);
|
||||||
}
|
}
|
||||||
|
@ -681,10 +680,11 @@ class ZmachinePlatform implements Platform {
|
||||||
*/
|
*/
|
||||||
|
|
||||||
isRunning(): boolean {
|
isRunning(): boolean {
|
||||||
return this.zvm != null;
|
return this.zvm != null && !this.glk.exited;
|
||||||
}
|
}
|
||||||
|
|
||||||
advance(novideo?: boolean): number {
|
advance(novideo?: boolean): number {
|
||||||
// TODO?
|
// TODO? we should advance 1 step, whatever that is in ZVM
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -696,17 +696,106 @@ class ZmachinePlatform implements Platform {
|
||||||
}
|
}
|
||||||
showHelp(tool:string, ident?:string) {
|
showHelp(tool:string, ident?:string) {
|
||||||
switch (tool) {
|
switch (tool) {
|
||||||
case 'inform6': window.open("https://www.inform-fiction.org/manual/html/"); break;
|
case 'inform6': window.open("https://www.inform-fiction.org/manual/html/contents.html"); break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
getPresets(): Preset[] {
|
getPresets(): Preset[] {
|
||||||
return ZMACHINE_PRESETS;
|
return ZMACHINE_PRESETS;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Z machine is big endian!!
|
||||||
inspect(ident:string) {
|
inspect(ident:string) {
|
||||||
return inspectSymbol(this, ident);
|
return inspectSymbol(this, ident);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
getDebugTree() {
|
||||||
|
var root = {};
|
||||||
|
//root['debuginfo'] = sym.debuginfo;
|
||||||
|
if (this.zvm != null) {
|
||||||
|
root['Objects'] = () => this.getRootObjects();
|
||||||
|
root['Globals'] = () => this.getGlobalVariables();
|
||||||
|
}
|
||||||
|
return root;
|
||||||
|
}
|
||||||
|
getObjectName(node) {
|
||||||
|
var objlookup = this.getDebugLookup('object');
|
||||||
|
var name = objlookup[node] || "";
|
||||||
|
name += " (#" + node + ")";
|
||||||
|
return name;
|
||||||
|
}
|
||||||
|
addObjectToTree(tree, child) {
|
||||||
|
let name = this.getObjectName(child);
|
||||||
|
tree[name] = this.getObjectTree(child);
|
||||||
|
}
|
||||||
|
getRootObjects() {
|
||||||
|
var tree = {};
|
||||||
|
// TODO: better way?
|
||||||
|
try {
|
||||||
|
for (let child=0; child<65536; child++) {
|
||||||
|
if (this.zvm.get_parent(child) == 0) {
|
||||||
|
this.addObjectToTree(tree, child);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
if (!(e instanceof RangeError)) throw e;
|
||||||
|
}
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
getObjectTree(parentobj: number) {
|
||||||
|
var child = this.zvm.get_child(parentobj);
|
||||||
|
var tree = {};
|
||||||
|
while (child) {
|
||||||
|
this.addObjectToTree(tree, child);
|
||||||
|
child = this.zvm.get_sibling(child);
|
||||||
|
}
|
||||||
|
// add attributes
|
||||||
|
var flags = this.getFlagList(parentobj);
|
||||||
|
if (flags.length) {
|
||||||
|
tree["[attributes]"] = flags.join(' ');
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
var props = this.getPropList(parentobj);
|
||||||
|
if (props.length) {
|
||||||
|
tree["[properties]"] = props.join(' ');
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
return tree;
|
||||||
|
}
|
||||||
|
getFlagList(obj:number) {
|
||||||
|
var attrlookup = this.getDebugLookup('attribute');
|
||||||
|
var set_attrs = [];
|
||||||
|
for (var i=0; i<32; i++) {
|
||||||
|
if (this.zvm.test_attr(obj, i)) {
|
||||||
|
set_attrs.push(attrlookup[i] || "#"+i);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return set_attrs;
|
||||||
|
}
|
||||||
|
getPropList(obj:number) {
|
||||||
|
var proplookup = this.getDebugLookup('property');
|
||||||
|
var set_props = [];
|
||||||
|
var addr = 0;
|
||||||
|
for (var i=0; i<50; i++) {
|
||||||
|
addr = this.zvm.find_prop(obj, 0, addr);
|
||||||
|
if (addr == 0) break;
|
||||||
|
set_props.push(proplookup[addr] || "%"+addr);
|
||||||
|
}
|
||||||
|
return set_props;
|
||||||
|
}
|
||||||
|
getDebugLookup(key : 'object'|'property'|'attribute'|'constant'|'global-variable') : {} {
|
||||||
|
var debugsym = (this as Platform).debugSymbols;
|
||||||
|
return (debugsym && debugsym.debuginfo && debugsym.debuginfo[key]) || {};
|
||||||
|
}
|
||||||
|
getGlobalVariables() {
|
||||||
|
var globals = this.getDebugLookup('global-variable');
|
||||||
|
var result = {};
|
||||||
|
Object.entries(globals).forEach((entry) => {
|
||||||
|
var addr = parseInt(entry[0]);
|
||||||
|
var name = entry[1] as string;
|
||||||
|
result[name] = this.zvm.m.getUint16(addr);
|
||||||
|
})
|
||||||
|
return result;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
//
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
"use strict";
|
"use strict";
|
||||||
|
|
||||||
import { WorkerResult, WorkerFileUpdate, WorkerBuildStep, WorkerMessage, WorkerError, Dependency, SourceLine, CodeListing, CodeListingMap, Segment } from "../common/workertypes";
|
import { WorkerResult, WorkerFileUpdate, WorkerBuildStep, WorkerMessage, WorkerError, Dependency, SourceLine, CodeListing, CodeListingMap, Segment, WorkerOutput } from "../common/workertypes";
|
||||||
|
|
||||||
declare var WebAssembly;
|
declare var WebAssembly;
|
||||||
declare function importScripts(path:string);
|
declare function importScripts(path:string);
|
||||||
|
@ -2397,9 +2397,9 @@ interface XMLNode {
|
||||||
|
|
||||||
function parseXMLPoorly(s: string) : XMLNode {
|
function parseXMLPoorly(s: string) : XMLNode {
|
||||||
var re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
|
var re = /[<]([/]?)([?a-z_-]+)([^>]*)[>]+|(\s*[^<]+)/gi;
|
||||||
var m;
|
var m : RegExpMatchArray;
|
||||||
var i=0;
|
//var i=0;
|
||||||
var stack = [];
|
var stack : XMLNode[] = [];
|
||||||
while (m = re.exec(s)) {
|
while (m = re.exec(s)) {
|
||||||
var [_m0,close,ident,attrs,content] = m;
|
var [_m0,close,ident,attrs,content] = m;
|
||||||
//if (i++<100) console.log(close,ident,attrs,content);
|
//if (i++<100) console.log(close,ident,attrs,content);
|
||||||
|
@ -2432,7 +2432,7 @@ function compileInform6(step:BuildStep) {
|
||||||
lstout += "\n";
|
lstout += "\n";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var args = [ '-afnops', '-v5', '-Cu', '-E1', '-k', '+/share/lib', step.path ];
|
var args = [ '-afjnops', '-v5', '-Cu', '-E1', '-k', '+/share/lib', step.path ];
|
||||||
var inform = emglobal.inform({
|
var inform = emglobal.inform({
|
||||||
instantiateWasm: moduleInstFn('inform'),
|
instantiateWasm: moduleInstFn('inform'),
|
||||||
noInitialRun:true,
|
noInitialRun:true,
|
||||||
|
@ -2454,10 +2454,15 @@ function compileInform6(step:BuildStep) {
|
||||||
|
|
||||||
// parse debug XML
|
// parse debug XML
|
||||||
var symbolmap = {};
|
var symbolmap = {};
|
||||||
var entitymap = {'object':{}, 'property':{}, 'constant':{}};
|
var segments : Segment[] = [];
|
||||||
|
var entitymap = {
|
||||||
|
// number -> string
|
||||||
|
'object':{}, 'property':{}, 'attribute':{}, 'constant':{}, 'global-variable':{}, 'routine':{},
|
||||||
|
};
|
||||||
var dbgout = FS.readFile("gameinfo.dbg", {encoding:'utf8'});
|
var dbgout = FS.readFile("gameinfo.dbg", {encoding:'utf8'});
|
||||||
var xmlroot = parseXMLPoorly(dbgout);
|
var xmlroot = parseXMLPoorly(dbgout);
|
||||||
//console.log(xmlroot);
|
//console.log(xmlroot);
|
||||||
|
var segtype = "ram";
|
||||||
xmlroot.children.forEach((node) => {
|
xmlroot.children.forEach((node) => {
|
||||||
switch (node.type) {
|
switch (node.type) {
|
||||||
case 'global-variable':
|
case 'global-variable':
|
||||||
|
@ -2465,34 +2470,26 @@ function compileInform6(step:BuildStep) {
|
||||||
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
||||||
var address = parseInt(node.children.find((c,v) => c.type=='address').text);
|
var address = parseInt(node.children.find((c,v) => c.type=='address').text);
|
||||||
symbolmap[ident] = address;
|
symbolmap[ident] = address;
|
||||||
|
entitymap[node.type][address] = ident;
|
||||||
break;
|
break;
|
||||||
case 'object':
|
case 'object':
|
||||||
case 'property':
|
case 'property':
|
||||||
|
case 'attribute':
|
||||||
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
var ident = node.children.find((c,v) => c.type=='identifier').text;
|
||||||
var value = parseInt(node.children.find((c,v) => c.type=='value').text);
|
var value = parseInt(node.children.find((c,v) => c.type=='value').text);
|
||||||
entitymap[node.type][ident] = value;
|
//entitymap[node.type][ident] = value;
|
||||||
|
entitymap[node.type][value] = ident;
|
||||||
//symbolmap[ident] = address | 0x1000000;
|
//symbolmap[ident] = address | 0x1000000;
|
||||||
break;
|
break;
|
||||||
|
case 'story-file-section':
|
||||||
|
var name = node.children.find((c,v) => c.type=='type').text;
|
||||||
|
var address = parseInt(node.children.find((c,v) => c.type=='address').text);
|
||||||
|
var endAddress = parseInt(node.children.find((c,v) => c.type=='end-address').text);
|
||||||
|
if (name == "grammar table") segtype = "rom";
|
||||||
|
segments.push({name:name, start:address, size:endAddress-address, type:segtype});
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// parse segments
|
// parse listing
|
||||||
var segments : Segment[] = [];
|
|
||||||
var seglst = lstout.split("Offsets in story file:")[1];
|
|
||||||
if (seglst) {
|
|
||||||
let curseg : Segment = {name:'Header',start:0x0,size:0x42,type:'rom'};
|
|
||||||
segments.push(curseg);
|
|
||||||
let curtype = 'ram';
|
|
||||||
let re_seg = /([0-9a-f]{5}) (\w+)/g;
|
|
||||||
let m;
|
|
||||||
while (m = re_seg.exec(seglst)) {
|
|
||||||
var start = parseInt(m[1], 16);
|
|
||||||
var name = m[2];
|
|
||||||
if (name == 'Parse') curtype = 'rom';
|
|
||||||
curseg.size = start - curseg.start;
|
|
||||||
curseg = {name:name, start:start, size:0, type:curtype};
|
|
||||||
segments.push(curseg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
var listings : CodeListingMap = {};
|
var listings : CodeListingMap = {};
|
||||||
// 35 +00015 <*> call_vs long_19 location long_424 -> sp
|
// 35 +00015 <*> call_vs long_19 location long_424 -> sp
|
||||||
var lines = parseListing(lstout, /\s*(\d+)\s+[+]([0-9a-f]+)\s+([<*>]*)\s*(\w+)\s+(.+)/i, -1, 2, 4);
|
var lines = parseListing(lstout, /\s*(\d+)\s+[+]([0-9a-f]+)\s+([<*>]*)\s*(\w+)\s+(.+)/i, -1, 2, 4);
|
||||||
|
@ -2504,10 +2501,11 @@ function compileInform6(step:BuildStep) {
|
||||||
errors:errors,
|
errors:errors,
|
||||||
symbolmap:symbolmap,
|
symbolmap:symbolmap,
|
||||||
segments:segments,
|
segments:segments,
|
||||||
//debuginfo:entitymap,
|
debuginfo:entitymap,
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
////////////////////////////
|
////////////////////////////
|
||||||
|
|
||||||
var TOOLS = {
|
var TOOLS = {
|
||||||
|
|
Loading…
Reference in New Issue