diff --git a/.gitignore b/.gitignore
index 13b73eb6..378ef9af 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,6 @@
*~
node_modules
scripts
+blog
+local
+release
diff --git a/src/platform/vcs.js b/src/platform/vcs.js
index 5963eac0..013b5c36 100644
--- a/src/platform/vcs.js
+++ b/src/platform/vcs.js
@@ -127,3 +127,145 @@ var VCSPlatform = function() {
};
PLATFORMS['vcs'] = VCSPlatform;
+
+/// VCS TIMING ANALYSIS
+
+var pc2minclocks = {};
+var pc2maxclocks = {};
+var jsrresult = {};
+var MAX_CLOCKS = 76*2;
+
+function _traceInstructions(pc, minclocks, maxclocks, subaddr, constraints) {
+ //console.log("trace", hex(pc), minclocks, maxclocks);
+ if (!minclocks) minclocks = 0;
+ if (!maxclocks) maxclocks = 0;
+ if (!constraints) constraints = {};
+ var modified = true;
+ var abort = false;
+ for (var i=0; i<1000 && modified && !abort; i++) {
+ modified = false;
+ var meta = getClockCountsAtPC(pc);
+ var lob = platform.readAddress(pc+1);
+ var hib = platform.readAddress(pc+2);
+ var addr = lob + (hib << 8);
+ var pc0 = pc;
+ if (!pc2minclocks[pc0] || minclocks < pc2minclocks[pc0]) {
+ pc2minclocks[pc0] = minclocks;
+ modified = true;
+ }
+ if (!pc2maxclocks[pc0] || maxclocks > pc2maxclocks[pc0]) {
+ pc2maxclocks[pc0] = maxclocks;
+ modified = true;
+ }
+ //console.log(hex(pc),minclocks,maxclocks,meta);
+ if (!meta.insnlength) {
+ console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta);
+ break;
+ }
+ pc += meta.insnlength;
+ var oldconstraints = constraints;
+ constraints = null;
+ // TODO: if jump to zero-page, maybe assume RTS?
+ switch (meta.opcode) {
+ /*
+ case 0xb9: // TODO: hack for zero page,y
+ if (addr < 0x100)
+ meta.maxCycles -= 1;
+ break;
+ */
+ case 0x85:
+ if (lob == 0x2) { // STA WSYNC
+ minclocks = maxclocks = 0;
+ meta.minCycles = meta.maxCycles = 0;
+ }
+ break;
+ case 0x20: // JSR
+ _traceInstructions(addr, minclocks, maxclocks, addr, constraints);
+ var result = jsrresult[addr];
+ if (result) {
+ minclocks = result.minclocks;
+ maxclocks = result.maxclocks;
+ } else {
+ console.log("No JSR result!", hex(pc), hex(addr));
+ return;
+ }
+ break;
+ case 0x4c: // JMP
+ pc = addr; // TODO: make sure in ROM space
+ break;
+ case 0x60: // RTS
+ if (subaddr) {
+ // TODO: combine with previous result
+ var result = jsrresult[subaddr];
+ if (!result) {
+ result = {minclocks:minclocks, maxclocks:maxclocks};
+ } else {
+ result = {
+ minclocks:Math.min(minclocks,result.minclocks),
+ maxclocks:Math.max(maxclocks,result.maxclocks)
+ }
+ }
+ jsrresult[subaddr] = result;
+ console.log("RTS", hex(pc), hex(subaddr), jsrresult[subaddr]);
+ }
+ return;
+ case 0x10: case 0x30: // branch
+ case 0x50: case 0x70:
+ case 0x90: case 0xB0:
+ case 0xD0: case 0xF0:
+ var newpc = pc + byte2signed(lob);
+ var crosspage = (pc>>8) != (newpc>>8);
+ if (!crosspage) meta.maxCycles--;
+ // TODO: other instructions might modify flags too
+ var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)];
+ var cons0 = constraintEquals(oldconstraints, cons[0]);
+ var cons1 = constraintEquals(oldconstraints, cons[1]);
+ if (cons0 !== false) {
+ _traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]);
+ }
+ if (cons1 === false) {
+ console.log("abort", hex(pc), oldconstraints, cons[1]);
+ abort = true;
+ }
+ constraints = cons[1]; // not taken
+ meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
+ break;
+ case 0x6c:
+ console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
+ return;
+ }
+ // TODO: wraparound?
+ minclocks = Math.min(MAX_CLOCKS, minclocks + meta.minCycles);
+ maxclocks = Math.min(MAX_CLOCKS, maxclocks + meta.maxCycles);
+ }
+}
+
+function showLoopTimingForPC(pc) {
+ pc2minclocks = {};
+ pc2maxclocks = {};
+ jsrresult = {};
+ // recurse through all traces
+ _traceInstructions(pc | platform.getOriginPC(), MAX_CLOCKS, MAX_CLOCKS);
+ // show the lines
+ for (var line in sourcefile.line2offset) {
+ var pc = sourcefile.line2offset[line];
+ var minclocks = pc2minclocks[pc];
+ var maxclocks = pc2maxclocks[pc];
+ if (minclocks>=0 && maxclocks>=0) {
+ var s;
+ if (maxclocks == minclocks)
+ s = minclocks + "";
+ else
+ s = minclocks + "-" + maxclocks;
+ if (maxclocks == MAX_CLOCKS)
+ s += "+";
+ var textel = document.createTextNode(s);
+ editor.setGutterMarker(line-1, "gutter-bytes", textel);
+ }
+ }
+}
+
+function traceTiming() {
+ trace_pending_at_pc = platform.getOriginPC();
+ setCode(editor.getValue());
+}
diff --git a/src/ui.js b/src/ui.js
index a30bc8ab..a352aaed 100644
--- a/src/ui.js
+++ b/src/ui.js
@@ -470,32 +470,6 @@ function setCurrentLine(line) {
var lastDebugInfo;
var lastDebugState;
-function highlightDifferences(s1, s2) {
- var split1 = s1.split(/(\S+\s+)/).filter(function(n) {return n});
- var split2 = s2.split(/(\S+\s+)/).filter(function(n) {return n});
- var i = 0;
- var j = 0;
- var result = "";
- while (i < split1.length && j < split2.length) {
- var w1 = split1[i];
- var w2 = split2[j];
- if (w2 && w2.indexOf("\n") >= 0) {
- while (i < s1.length && split1[i].indexOf("\n") < 0)
- i++;
- }
- if (w1 != w2) {
- w2 = '' + w2 + '';
- }
- result += w2;
- i++;
- j++;
- }
- while (j < split2.length) {
- result += split2[j++];
- }
- return result;
-}
-
function showMemory(state) {
var s = "";
if (state) {
@@ -611,11 +585,6 @@ function getClockCountsAtPC(pc) {
return meta; // minCycles, maxCycles
}
-var pc2minclocks = {};
-var pc2maxclocks = {};
-var jsrresult = {};
-var MAX_CLOCKS = 76*2;
-
function byte2signed(b) {
b &= 0xff;
return (b < 0x80) ? b : -(256-b);
@@ -647,141 +616,6 @@ function constraintEquals(a,b) {
return null;
}
-// TODO: move to file
-function _traceInstructions(pc, minclocks, maxclocks, subaddr, constraints) {
- //console.log("trace", hex(pc), minclocks, maxclocks);
- if (!minclocks) minclocks = 0;
- if (!maxclocks) maxclocks = 0;
- if (!constraints) constraints = {};
- var modified = true;
- var abort = false;
- for (var i=0; i<1000 && modified && !abort; i++) {
- modified = false;
- var meta = getClockCountsAtPC(pc);
- var lob = platform.readAddress(pc+1);
- var hib = platform.readAddress(pc+2);
- var addr = lob + (hib << 8);
- var pc0 = pc;
- if (!pc2minclocks[pc0] || minclocks < pc2minclocks[pc0]) {
- pc2minclocks[pc0] = minclocks;
- modified = true;
- }
- if (!pc2maxclocks[pc0] || maxclocks > pc2maxclocks[pc0]) {
- pc2maxclocks[pc0] = maxclocks;
- modified = true;
- }
- //console.log(hex(pc),minclocks,maxclocks,meta);
- if (!meta.insnlength) {
- console.log("Illegal instruction!", hex(pc), hex(meta.opcode), meta);
- break;
- }
- pc += meta.insnlength;
- var oldconstraints = constraints;
- constraints = null;
- // TODO: if jump to zero-page, maybe assume RTS?
- switch (meta.opcode) {
- /*
- case 0xb9: // TODO: hack for zero page,y
- if (addr < 0x100)
- meta.maxCycles -= 1;
- break;
- */
- case 0x85:
- if (lob == 0x2) { // STA WSYNC
- minclocks = maxclocks = 0;
- meta.minCycles = meta.maxCycles = 0;
- }
- break;
- case 0x20: // JSR
- _traceInstructions(addr, minclocks, maxclocks, addr, constraints);
- var result = jsrresult[addr];
- if (result) {
- minclocks = result.minclocks;
- maxclocks = result.maxclocks;
- } else {
- console.log("No JSR result!", hex(pc), hex(addr));
- return;
- }
- break;
- case 0x4c: // JMP
- pc = addr; // TODO: make sure in ROM space
- break;
- case 0x60: // RTS
- if (subaddr) {
- // TODO: combine with previous result
- var result = jsrresult[subaddr];
- if (!result) {
- result = {minclocks:minclocks, maxclocks:maxclocks};
- } else {
- result = {
- minclocks:Math.min(minclocks,result.minclocks),
- maxclocks:Math.max(maxclocks,result.maxclocks)
- }
- }
- jsrresult[subaddr] = result;
- console.log("RTS", hex(pc), hex(subaddr), jsrresult[subaddr]);
- }
- return;
- case 0x10: case 0x30: // branch
- case 0x50: case 0x70:
- case 0x90: case 0xB0:
- case 0xD0: case 0xF0:
- var newpc = pc + byte2signed(lob);
- var crosspage = (pc>>8) != (newpc>>8);
- if (!crosspage) meta.maxCycles--;
- // TODO: other instructions might modify flags too
- var cons = BRANCH_CONSTRAINTS[Math.floor((meta.opcode-0x10)/0x20)];
- var cons0 = constraintEquals(oldconstraints, cons[0]);
- var cons1 = constraintEquals(oldconstraints, cons[1]);
- if (cons0 !== false) {
- _traceInstructions(newpc, minclocks+meta.maxCycles, maxclocks+meta.maxCycles, subaddr, cons[0]);
- }
- if (cons1 === false) {
- console.log("abort", hex(pc), oldconstraints, cons[1]);
- abort = true;
- }
- constraints = cons[1]; // not taken
- meta.maxCycles = meta.minCycles; // branch not taken, no extra clock(s)
- break;
- case 0x6c:
- console.log("Instruction not supported!", hex(pc), hex(meta.opcode), meta); // TODO
- return;
- }
- // TODO: wraparound?
- minclocks = Math.min(MAX_CLOCKS, minclocks + meta.minCycles);
- maxclocks = Math.min(MAX_CLOCKS, maxclocks + meta.maxCycles);
- }
-}
-
-function showLoopTimingForPC(pc) {
- pc2minclocks = {};
- pc2maxclocks = {};
- jsrresult = {};
- // recurse through all traces
- _traceInstructions(pc | platform.getOriginPC(), MAX_CLOCKS, MAX_CLOCKS);
- // show the lines
- for (var line in sourcefile.line2offset) {
- var pc = sourcefile.line2offset[line];
- var minclocks = pc2minclocks[pc];
- var maxclocks = pc2maxclocks[pc];
- if (minclocks>=0 && maxclocks>=0) {
- var s;
- if (maxclocks == minclocks)
- s = minclocks + "";
- else
- s = minclocks + "-" + maxclocks;
- if (maxclocks == MAX_CLOCKS)
- s += "+";
- var textel = document.createTextNode(s);
- editor.setGutterMarker(line-1, "gutter-bytes", textel);
- }
- }
-}
-
-function traceTiming() {
- trace_pending_at_pc = platform.getOriginPC();
- setCode(editor.getValue());
-}
/*
function showLoopTimingForCurrentLine() {
diff --git a/src/util.js b/src/util.js
index e544b06f..74a903d6 100644
--- a/src/util.js
+++ b/src/util.js
@@ -11,6 +11,32 @@ function hex(v, nd) {
}
}
+function highlightDifferences(s1, s2) {
+ var split1 = s1.split(/(\S+\s+)/).filter(function(n) {return n});
+ var split2 = s2.split(/(\S+\s+)/).filter(function(n) {return n});
+ var i = 0;
+ var j = 0;
+ var result = "";
+ while (i < split1.length && j < split2.length) {
+ var w1 = split1[i];
+ var w2 = split2[j];
+ if (w2 && w2.indexOf("\n") >= 0) {
+ while (i < s1.length && split1[i].indexOf("\n") < 0)
+ i++;
+ }
+ if (w1 != w2) {
+ w2 = '' + w2 + '';
+ }
+ result += w2;
+ i++;
+ j++;
+ }
+ while (j < split2.length) {
+ result += split2[j++];
+ }
+ return result;
+}
+
function lzgmini() {
// Constants