From e66802addb627a8567d72957920e4f75437d2c31 Mon Sep 17 00:00:00 2001
From: Steven Hugg <hugg@fasterlight.com>
Date: Sat, 11 Jul 2020 13:46:47 -0500
Subject: [PATCH] emerged the call stack view

---
 src/ide/ui.ts       |  5 ++-
 src/ide/views.ts    | 84 +++++++++++++++++++++++++++++++++++----------
 src/platform/nes.ts |  2 +-
 3 files changed, 70 insertions(+), 21 deletions(-)

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