diff --git a/src/ide/ui.ts b/src/ide/ui.ts index e14e5677..8fbc27c9 100644 --- a/src/ide/ui.ts +++ b/src/ide/ui.ts @@ -297,10 +297,13 @@ function refreshWindowList() { addWindowItem("#symbolprobe", "Symbol Profiler", () => { return new Views.ProbeSymbolView(); }); - /* addWindowItem("#callstack", "Call Stack", () => { return new Views.CallStackView(); }); + /* + addWindowItem("#framecalls", "Frame Profiler", () => { + return new Views.FrameCallsView(); + }); */ } addWindowItem('#asseteditor', 'Asset Editor', () => { diff --git a/src/ide/views.ts b/src/ide/views.ts index 82b7af5a..918383d0 100644 --- a/src/ide/views.ts +++ b/src/ide/views.ts @@ -926,9 +926,13 @@ abstract class ProbeViewBaseBase { abstract tick() : void; - addr2str(addr : number) : string { + addr2symbol(addr : number) : string { var _addr2sym = (platform.debugSymbols && platform.debugSymbols.addr2symbol) || {}; - var sym = _addr2sym[addr]; + return _addr2sym[addr]; + } + + addr2str(addr : number) : string { + var sym = this.addr2symbol(addr); if (typeof sym === 'string') return '$' + hex(addr) + ' (' + sym + ')'; else @@ -1297,6 +1301,8 @@ export class ProbeSymbolView extends ProbeViewBaseBase { const MAX_CHILDREN = 256; const MAX_STRING_LEN = 100; +var TREE_SHOW_DOLLAR_IDENTS = false; + class TreeNode { parent : TreeNode; name : string; @@ -1412,12 +1418,16 @@ class TreeNode { 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); + // hide $xxx idents? + var hidden = !TREE_SHOW_DOLLAR_IDENTS && typeof name === 'string' && name.startsWith("$$"); + if (!hidden) { + let childnode = this.children.get(name); + if (childnode == null) { + childnode = new TreeNode(this, name); + this.children.set(name, childnode); + } + childnode.update(obj[name]); } - childnode.update(obj[name]); orphans.delete(name); }); // remove orphans @@ -1483,8 +1493,10 @@ export class DebugBrowserView extends TreeViewBase implements ProjectView { // TODO? interface CallGraphNode { count : number; - SP : number; - PC : number; + $$SP : number; + $$PC? : number; + startLine? : number; + endLine? : number; calls : {[id:string] : CallGraphNode}; } @@ -1526,22 +1538,22 @@ export class CallStackView extends ProbeViewBaseBase implements ProjectView { case ProbeFlags.SP_PUSH: // need a new root? if (this.stack.length == 0) { - this.graph = {count:0, PC:null, SP:addr, calls:{}}; + this.graph = {count:0, $$SP:addr, calls:{}}; this.stack.unshift(this.graph); - } else if (addr > this.stack[0].SP) { + } else if (addr > this.stack[0].$$SP) { let calls = {}; - if (this.stack[0].PC !== null) calls[this.stack[0].PC] = this.stack[0]; - this.graph = {count:0, PC:null, SP:addr, calls:calls}; + if (this.stack[0].$$PC != null) calls[this.stack[0].$$PC] = this.stack[0]; + this.graph = {count:0, $$SP:addr, calls:calls}; this.stack.unshift(this.graph); } case ProbeFlags.SP_POP: if (this.stack.length) { let top = this.stack[this.stack.length-1]; - if ((this.lastsp - addr) == 2 && addr < top.SP) { // TODO: look for opcode? + if ((this.lastsp - addr) == 2 && addr < top.$$SP) { // TODO: look for opcode? this.jsr = true; } - if ((this.lastsp - addr) == -2 && this.stack.length > 1 && addr > top.SP) { - this.stack.pop(); + if ((this.lastsp - addr) == -2 && this.stack.length > 1 && addr > top.$$SP) { + this.stack.pop().endLine = row; } } this.lastsp = addr; @@ -1551,21 +1563,55 @@ export class CallStackView extends ProbeViewBaseBase implements ProjectView { let top = this.stack[this.stack.length-1]; let sym = this.addr2str(addr); let child = top.calls[sym]; - if (child == null) { child = top.calls[sym] = {count:0, PC:addr, SP:this.lastsp, calls:{}}; } - else if (child.PC === null) child.PC = addr; + if (child == null) { child = top.calls[sym] = {count:0, $$PC:addr, $$SP:this.lastsp, calls:{}}; } + else if (child.$$PC == null) child.$$PC = addr; //this.stack.forEach((node) => node.count++); this.stack.push(child); child.count++; + child.startLine = row; this.jsr = false; } break; } }); - if (this.graph) this.graph['_stack'] = this.stack; + //if (this.graph) this.graph['_stack'] = this.stack; return this.graph; } } +export class FrameCallsView extends ProbeViewBaseBase implements ProjectView { + treeroot : TreeNode; + + createDiv(parent : HTMLElement) : HTMLElement { + this.treeroot = createTreeRootNode(parent, this); + return this.treeroot.getDiv(); + } + + refresh() { + this.tick(); + } + + tick() { + this.treeroot.update(this.getRootObject()); + } + + getRootObject() : Object { + var frame = {}; + this.redraw((op,addr,col,row,clk,value) => { + switch (op) { + case ProbeFlags.EXECUTE: + let sym = this.addr2symbol(addr); + if (sym) { + if (!frame[sym]) { + frame[sym] = row; + } + } + break; + } + }); + return frame; + } +} /// diff --git a/src/platform/nes.ts b/src/platform/nes.ts index 024e1b15..2b2aaa7c 100644 --- a/src/platform/nes.ts +++ b/src/platform/nes.ts @@ -137,7 +137,7 @@ class JSNESPlatform extends Base6502Platform implements Platform, Probeable { // insert debug hook this.nes.cpu._emulate = this.nes.cpu.emulate; this.nes.cpu.emulate = () => { - this.probe.logExecute(this.nes.cpu.REG_PC-1, this.nes.cpu.REG_SP); + this.probe.logExecute(this.nes.cpu.REG_PC+1, this.nes.cpu.REG_SP); var cycles = this.nes.cpu._emulate(); this.evalDebugCondition(); this.probe.logClocks(cycles);