Some more Debugger factoring
This commit is contained in:
parent
522563a72b
commit
e5c5a8e6be
17 changed files with 1124 additions and 1539 deletions
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
(function(){var l,aa,ba={163840:[40,1,8],184320:[40,1,9],327680:[40,2,8],368640:[40,2,9],737280:[80,2,9],1228800:[80,2,15],1474560:[80,2,18],2949120:[80,2,36],21368320:[615,4,17]};
|
||||
function ca(a,b){var c;if(a){b||(b=16);if("$"==a.charAt(0))b=16,a=a.substr(1);else if("0x"==a.substr(0,2))b=16,a=a.substr(2);else{var d=a.charAt(a.length-1).toLowerCase();"h"==d?(b=16,d=null):"."==d&&(b=10,d=null);null==d&&(a=a.substr(0,a.length-1))}var e,d=a,f=b;(f&&10!=f?16==f?null!==d.match(/^[0-9a-f]+$/i):2==f&&null!==d.match(/^[01]+$/i):null!==d.match(/^[0-9]+$/))&&!isNaN(e=parseInt(a,b))&&(c=e|0)}return c}
|
||||
function ca(a,b){var c;if(a){b||(b=10);if("$"==a.charAt(0))b=16,a=a.substr(1);else if("0x"==a.substr(0,2))b=16,a=a.substr(2);else{var d=a.charAt(a.length-1).toLowerCase();"h"==d?(b=16,d=null):"."==d&&(b=10,d=null);null==d&&(a=a.substr(0,a.length-1))}var e,d=a,f=b;(f&&10!=f?16==f?null!==d.match(/^[0-9a-f]+$/i):2==f&&null!==d.match(/^[01]+$/i):null!==d.match(/^[0-9]+$/))&&!isNaN(e=parseInt(a,b))&&(c=e|0)}return c}
|
||||
function ea(a,b){var c="";void 0===b?b=8:8<b&&(b=8);if(null==a||isNaN(a))for(;0<b--;)c="?"+c;else for(;0<b--;){var d=a&15,d=d+(0<=d&&9>=d?48:55),c=String.fromCharCode(d)+c;a>>=4}return c}function fa(a,b){var c=a,d=a.lastIndexOf("/");0<=d&&(c=a.substr(d+1));d=c.indexOf("&");0<d&&(c=c.substr(0,d));b&&(d=c.lastIndexOf("."),0<d&&(c=c.substring(0,d)));return c}function ga(a){var b="",c=a.lastIndexOf(".");0<=c&&(b=a.substr(c+1).toLowerCase());return b}
|
||||
function ha(a,b){return-1!==a.indexOf(b,a.length-b.length)}var ia={"&":"&","<":"<",">":">",'"':""","'":"'"};function ja(a){return a.replace(/[&<>"']/g,function(a){return ia[a]})}function ka(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}
|
||||
var la={0:"NUL",1:"SOH",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",7:"BEL",8:"BS",9:"TAB",11:"VT",12:"FF",13:"CR",14:"SO",15:"SI",16:"DLE",17:"XON",18:"DC2",19:"XOFF",20:"DC4",21:"NAK",22:"SYN",23:"ETB",24:"CAN",25:"EM",26:"SUB",27:"ESC",28:"FS",29:"GS",30:"RS",31:"US"},ma=Date.now||function(){return+new Date};
|
||||
|
|
|
|||
|
|
@ -37,6 +37,7 @@ if (DEBUGGER) {
|
|||
var usr = require("../../shared/lib/usrlib");
|
||||
var web = require("../../shared/lib/weblib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
var Debugger = require("../../shared/lib/debugger");
|
||||
var PC6502 = require("./defines");
|
||||
var Messages = require("./messages");
|
||||
var Memory = require("./memory");
|
||||
|
|
@ -68,7 +69,7 @@ var DbgAddr6502;
|
|||
* Debugger6502(parmsDbg)
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @extends Debugger
|
||||
* @param {Object} parmsDbg
|
||||
*
|
||||
* The Debugger6502 component supports the following optional (parmsDbg) properties:
|
||||
|
|
@ -89,17 +90,6 @@ function Debugger6502(parmsDbg)
|
|||
|
||||
this.style = Debugger6502.STYLE_8080;
|
||||
|
||||
/*
|
||||
* These keep track of instruction activity, but only when tracing or when Debugger checks
|
||||
* have been enabled (eg, one or more breakpoints have been set).
|
||||
*
|
||||
* They are zeroed by the reset() notification handler. cInstructions is advanced by
|
||||
* stepCPU() and checkInstruction() calls. nCycles is updated by every stepCPU() or stop()
|
||||
* call and simply represents the number of cycles performed by the last run of instructions.
|
||||
*/
|
||||
this.nCycles = 0;
|
||||
this.cOpcodes = this.cOpcodesStart = 0;
|
||||
|
||||
/*
|
||||
* Most commands that require an address call parseAddr(), which defaults to dbgAddrNextCode
|
||||
* or dbgAddrNextData when no address has been given. doDump() and doUnassemble(), in turn,
|
||||
|
|
@ -110,18 +100,6 @@ function Debugger6502(parmsDbg)
|
|||
*/
|
||||
this.dbgAddrNextCode = this.newAddr();
|
||||
this.dbgAddrNextData = this.newAddr();
|
||||
|
||||
/*
|
||||
* This maintains command history. New commands are inserted at index 0 of the array.
|
||||
* When Enter is pressed on an empty input buffer, we default to the command at aPrevCmds[0].
|
||||
*/
|
||||
this.iPrevCmd = -1;
|
||||
this.aPrevCmds = [];
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
this.dbgAddrAssemble = this.newAddr();
|
||||
|
||||
/*
|
||||
|
|
@ -138,21 +116,6 @@ function Debugger6502(parmsDbg)
|
|||
*/
|
||||
this.aSymbolTable = [];
|
||||
|
||||
/*
|
||||
* aVariables is an object with properties that grows as setVariable() assigns more variables;
|
||||
* each property corresponds to one variable, where the property name is the variable name (ie,
|
||||
* a string beginning with a letter or underscore, followed by zero or more additional letters,
|
||||
* digits, or underscores) and the property value is the variable's numeric value. See doVar()
|
||||
* and setVariable() for details.
|
||||
*
|
||||
* Note that parseValue(), through its reliance on str.parseInt(), assumes a default base of 16
|
||||
* if no base is explicitly indicated (eg, a trailing decimal period), and if you define variable
|
||||
* names containing exclusively hex alpha characters (a-f), those variables will take precedence
|
||||
* over the corresponding hex values. In other words, if you define variables "a" and "b", you
|
||||
* will no longer be able to simply type "a" or "b" to specify the decimal values 10 or 11.
|
||||
*/
|
||||
this.aVariables = {};
|
||||
|
||||
/*
|
||||
* clearBreakpoints() initializes the breakpoints lists: aBreakExec is a list of addresses
|
||||
* to halt on whenever attempting to execute an instruction at the corresponding address,
|
||||
|
|
@ -214,7 +177,7 @@ function Debugger6502(parmsDbg)
|
|||
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.subclass(Debugger6502);
|
||||
Component.subclass(Debugger6502, Debugger);
|
||||
|
||||
/*
|
||||
* NOTE: Every Debugger property from here to the first prototype function definition (initBus()) is a
|
||||
|
|
@ -2652,379 +2615,6 @@ if (DEBUGGER) {
|
|||
return s;
|
||||
};
|
||||
|
||||
Debugger6502.aBinOpPrecedence = {
|
||||
'||': 0, // logical OR
|
||||
'&&': 1, // logical AND
|
||||
'|': 2, // bitwise OR
|
||||
'^': 3, // bitwise XOR
|
||||
'&': 4, // bitwise AND
|
||||
'!=': 5, // inequality
|
||||
'==': 5, // equality
|
||||
'>=': 6, // greater than or equal to
|
||||
'>': 6, // greater than
|
||||
'<=': 6, // less than or equal to
|
||||
'<': 6, // less than
|
||||
'>>>': 7, // unsigned bitwise right shift
|
||||
'>>': 7, // bitwise right shift
|
||||
'<<': 7, // bitwise left shift
|
||||
'-': 8, // subtraction
|
||||
'+': 8, // addition
|
||||
'%': 9, // remainder
|
||||
'/': 9, // division
|
||||
'*': 9 // multiplication
|
||||
};
|
||||
|
||||
/**
|
||||
* evalExpression(aVals, aOps, cOps)
|
||||
*
|
||||
* In Node, if you set a variable to 0x80000001; ie:
|
||||
*
|
||||
* foo=0x80000001|0
|
||||
*
|
||||
* and then calculate foo*foo using "(foo*foo).toString(2)", the result is:
|
||||
*
|
||||
* '11111111111111111111111111111100000000000000000000000000000000'
|
||||
*
|
||||
* which is slightly incorrect because it has overflowed JavaScript's floating-point precision.
|
||||
*
|
||||
* 0x80000001 in decimal is -2147483647, so the product is 4611686014132420609, which is 0x3FFFFFFF00000001.
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {Array.<number>} aVals
|
||||
* @param {Array.<string>} aOps
|
||||
* @param {number} [cOps] (default is all)
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
Debugger6502.prototype.evalExpression = function(aVals, aOps, cOps)
|
||||
{
|
||||
cOps = cOps || -1;
|
||||
while (cOps-- && aOps.length) {
|
||||
var chOp = aOps.pop();
|
||||
if (aVals.length < 2) return false;
|
||||
var valNew;
|
||||
var val2 = aVals.pop();
|
||||
var val1 = aVals.pop();
|
||||
switch(chOp) {
|
||||
case '*':
|
||||
valNew = val1 * val2;
|
||||
break;
|
||||
case '/':
|
||||
if (!val2) return false;
|
||||
valNew = val1 / val2;
|
||||
break;
|
||||
case '%':
|
||||
if (!val2) return false;
|
||||
valNew = val1 % val2;
|
||||
break;
|
||||
case '+':
|
||||
valNew = val1 + val2;
|
||||
break;
|
||||
case '-':
|
||||
valNew = val1 - val2;
|
||||
break;
|
||||
case '<<':
|
||||
valNew = val1 << val2;
|
||||
break;
|
||||
case '>>':
|
||||
valNew = val1 >> val2;
|
||||
break;
|
||||
case '>>>':
|
||||
valNew = val1 >>> val2;
|
||||
break;
|
||||
case '<':
|
||||
valNew = (val1 < val2? 1 : 0);
|
||||
break;
|
||||
case '<=':
|
||||
valNew = (val1 <= val2? 1 : 0);
|
||||
break;
|
||||
case '>':
|
||||
valNew = (val1 > val2? 1 : 0);
|
||||
break;
|
||||
case '>=':
|
||||
valNew = (val1 >= val2? 1 : 0);
|
||||
break;
|
||||
case '==':
|
||||
valNew = (val1 == val2? 1 : 0);
|
||||
break;
|
||||
case '!=':
|
||||
valNew = (val1 != val2? 1 : 0);
|
||||
break;
|
||||
case '&':
|
||||
valNew = val1 & val2;
|
||||
break;
|
||||
case '^':
|
||||
valNew = val1 ^ val2;
|
||||
break;
|
||||
case '|':
|
||||
valNew = val1 | val2;
|
||||
break;
|
||||
case '&&':
|
||||
valNew = (val1 && val2? 1 : 0);
|
||||
break;
|
||||
case '||':
|
||||
valNew = (val1 || val2? 1 : 0);
|
||||
break;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
aVals.push(valNew|0);
|
||||
}
|
||||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseExpression(sExp, fPrint)
|
||||
*
|
||||
* A quick-and-dirty expression parser. It takes an expression like:
|
||||
*
|
||||
* EDX+EDX*4+12345678
|
||||
*
|
||||
* and builds a value stack in aVals and a "binop" (binary operator) stack in aOps:
|
||||
*
|
||||
* aVals aOps
|
||||
* ----- ----
|
||||
* EDX +
|
||||
* EDX *
|
||||
* 4 +
|
||||
* ...
|
||||
*
|
||||
* We pop 1 "binop" from aOps and 2 values from aVals whenever a "binop" of lower priority than its
|
||||
* predecessor is encountered, evaluate, and push the result back onto aVals.
|
||||
*
|
||||
* Unary operators like '~' and ternary operators like '?:' are not supported; neither are parentheses.
|
||||
*
|
||||
* However, parseReference() now makes it possible to write parenthetical-style sub-expressions by using
|
||||
* {...} (braces), as well as address references by using [...] (brackets).
|
||||
*
|
||||
* Why am I using braces instead of parentheses for sub-expressions? Because parseReference() serves
|
||||
* multiple purposes, the other being reference replacement in message strings passing through replaceRegs(),
|
||||
* and I didn't want parentheses taking on a new meaning in message strings.
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string|undefined} sExp
|
||||
* @param {boolean} [fPrint] is true to print all resolved values, false for quiet parsing
|
||||
* @return {number|undefined} numeric value, or undefined if sExp contains any undefined or invalid values
|
||||
*/
|
||||
Debugger6502.prototype.parseExpression = function(sExp, fPrint)
|
||||
{
|
||||
var value;
|
||||
|
||||
if (sExp) {
|
||||
/*
|
||||
* First process (and eliminate) any references, aka sub-expressions.
|
||||
*/
|
||||
sExp = this.parseReference(sExp);
|
||||
|
||||
var i = 0;
|
||||
var fError = false;
|
||||
var sExpOrig = sExp;
|
||||
var aVals = [], aOps = [];
|
||||
/*
|
||||
* All browsers (including, I believe, IE9 and up) support the following idiosyncrasy of a regexp split():
|
||||
* when the regexp uses a capturing pattern, the resulting array will include entries for all the pattern
|
||||
* matches along with the non-matches. This effectively means that, in the set of expressions that we
|
||||
* support, all even entries in asValues will contain "values" and all odd entries will contain "operators".
|
||||
*
|
||||
* And although I tried to list the supported operators in "precedential" order, bitwise operators must
|
||||
* be out-of-order so that we don't mistakenly match either '>' or '<' when they're part of '>>' or '<<'.
|
||||
*/
|
||||
var regExp = /(\|\||&&|\||^|&|!=|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
|
||||
var asValues = sExp.split(regExp);
|
||||
while (i < asValues.length) {
|
||||
var sValue = asValues[i++];
|
||||
var cchValue = sValue.length;
|
||||
var s = str.trim(sValue);
|
||||
if (!s) {
|
||||
fError = true;
|
||||
break;
|
||||
}
|
||||
var v = this.parseValue(s, null, fPrint === false);
|
||||
if (v === undefined) {
|
||||
fError = true;
|
||||
fPrint = false;
|
||||
break;
|
||||
}
|
||||
aVals.push(v);
|
||||
if (i == asValues.length) break;
|
||||
var sOp = asValues[i++], cchOp = sOp.length;
|
||||
this.assert(Debugger6502.aBinOpPrecedence[sOp] != null);
|
||||
if (aOps.length && Debugger6502.aBinOpPrecedence[sOp] < Debugger6502.aBinOpPrecedence[aOps[aOps.length-1]]) {
|
||||
this.evalExpression(aVals, aOps, 1);
|
||||
}
|
||||
aOps.push(sOp);
|
||||
sExp = sExp.substr(cchValue + cchOp);
|
||||
}
|
||||
if (!this.evalExpression(aVals, aOps) || aVals.length != 1) {
|
||||
fError = true;
|
||||
}
|
||||
if (!fError) {
|
||||
value = aVals.pop();
|
||||
if (fPrint) this.printValue(null, value);
|
||||
} else {
|
||||
if (fPrint) this.println("error parsing '" + sExpOrig + "' at character " + (sExpOrig.length - sExp.length));
|
||||
}
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseReference(s)
|
||||
*
|
||||
* Returns the given string with any "{expression}" sequences replaced with the value of the expression,
|
||||
* and any "[address]" references replaced with the contents of the address. Expressions are parsed BEFORE
|
||||
* addresses. Owing to this function's simplistic parsing, nested braces/brackets are not supported
|
||||
* (define intermediate variables if needed).
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger6502.prototype.parseReference = function(s)
|
||||
{
|
||||
var a;
|
||||
while (a = s.match(/\{(.*?)}/)) {
|
||||
if (a[1].indexOf('{') >= 0) break; // unsupported nested brace(s)
|
||||
var value = this.parseExpression(a[1]);
|
||||
s = s.replace('{' + a[1] + '}', value != null? str.toHex(value) : "undefined");
|
||||
}
|
||||
while (a = s.match(/\[(.*?)]/)) {
|
||||
if (a[1].indexOf('[') >= 0) break; // unsupported nested bracket(s)
|
||||
var dbgAddr = this.parseAddr(a[1]);
|
||||
s = s.replace('[' + a[1] + ']', dbgAddr? str.toHex(this.getWord(dbgAddr), 4) : "undefined");
|
||||
}
|
||||
return this.parseSysVars(s);
|
||||
};
|
||||
|
||||
/**
|
||||
* parseSysVars(s)
|
||||
*
|
||||
* Returns the given string with any recognized "$var" replaced with its value; eg:
|
||||
*
|
||||
* $ops: the number of opcodes executed since the last time it was displayed (or reset)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger6502.prototype.parseSysVars = function(s)
|
||||
{
|
||||
var a;
|
||||
while (a = s.match(/\$([a-z]+)/i)) {
|
||||
var v = null;
|
||||
switch(a[1].toLowerCase()) {
|
||||
case "ops":
|
||||
v = this.cOpcodes - this.cOpcodesStart;
|
||||
break;
|
||||
}
|
||||
if (v == null) break;
|
||||
s = s.replace(a[0], v.toString());
|
||||
}
|
||||
return s;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseValue(sValue, sName, fQuiet)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string|undefined} sValue
|
||||
* @param {string|null} [sName] is the name of the value, if any
|
||||
* @param {boolean} [fQuiet]
|
||||
* @return {number|undefined} numeric value, or undefined if sValue is either undefined or invalid
|
||||
*/
|
||||
Debugger6502.prototype.parseValue = function(sValue, sName, fQuiet)
|
||||
{
|
||||
var value;
|
||||
if (sValue !== undefined) {
|
||||
var iReg = this.getRegIndex(sValue);
|
||||
if (iReg >= 0) {
|
||||
value = this.getRegValue(iReg);
|
||||
} else {
|
||||
value = this.getVariable(sValue);
|
||||
if (value === undefined) value = str.parseInt(sValue);
|
||||
}
|
||||
if (value === undefined && !fQuiet) this.println("invalid " + (sName? sName : "value") + ": " + sValue);
|
||||
} else {
|
||||
if (!fQuiet) this.println("missing " + (sName || "value"));
|
||||
}
|
||||
return value;
|
||||
};
|
||||
|
||||
/**
|
||||
* printValue(sVar, value)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string|null} sVar
|
||||
* @param {number|undefined} value
|
||||
* @return {boolean} true if value defined, false if not
|
||||
*/
|
||||
Debugger6502.prototype.printValue = function(sVar, value)
|
||||
{
|
||||
var sValue;
|
||||
var fDefined = false;
|
||||
if (value !== undefined) {
|
||||
fDefined = true;
|
||||
sValue = str.toHexLong(value) + " " + value + ". (" + str.toBinBytes(value) + ")";
|
||||
}
|
||||
sVar = (sVar != null? (sVar + ": ") : "");
|
||||
this.println(sVar + sValue);
|
||||
return fDefined;
|
||||
};
|
||||
|
||||
/**
|
||||
* printVariable(sVar)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} [sVar]
|
||||
* @return {boolean} true if all value(s) defined, false if not
|
||||
*/
|
||||
Debugger6502.prototype.printVariable = function(sVar)
|
||||
{
|
||||
if (sVar) {
|
||||
return this.printValue(sVar, this.aVariables[sVar]);
|
||||
}
|
||||
var cVariables = 0;
|
||||
for (sVar in this.aVariables) {
|
||||
this.printValue(sVar, this.aVariables[sVar]);
|
||||
cVariables++;
|
||||
}
|
||||
return cVariables > 0;
|
||||
};
|
||||
|
||||
/**
|
||||
* delVariable(sVar)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} sVar
|
||||
*/
|
||||
Debugger6502.prototype.delVariable = function(sVar)
|
||||
{
|
||||
delete this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* getVariable(sVar)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} sVar
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
Debugger6502.prototype.getVariable = function(sVar)
|
||||
{
|
||||
return this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* setVariable(sVar, value)
|
||||
*
|
||||
* @this {Debugger6502}
|
||||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
*/
|
||||
Debugger6502.prototype.setVariable = function(sVar, value)
|
||||
{
|
||||
this.aVariables[sVar] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* comparePairs(p1, p2)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -100,11 +100,6 @@ function Debugger8080(parmsDbg)
|
|||
*/
|
||||
this.dbgAddrNextCode = this.newAddr();
|
||||
this.dbgAddrNextData = this.newAddr();
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
this.dbgAddrAssemble = this.newAddr();
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -73,11 +73,7 @@ try {
|
|||
* ]
|
||||
*
|
||||
* Every component name comes from the component filename, minus the ".js" extension;
|
||||
* Create is the constructor returned by require(). The only bit of fudging we do is
|
||||
* overriding the constructor for component "cpu" with the constructor for "x86cpu",
|
||||
* because when a "cpu" definition is encountered, it's the "x86cpu" subclass that we
|
||||
* actually want to create, not the "cpu" superclass. See aSubClasses for the complete
|
||||
* list of subclassed components we manage like that.
|
||||
* Create is the constructor returned by require().
|
||||
*
|
||||
* TODO: Update the list of ignored (ie, ignorable) components.
|
||||
*/
|
||||
|
|
@ -85,6 +81,13 @@ var Component;
|
|||
var dbg;
|
||||
var aComponents = [];
|
||||
var asComponentsIgnore = ["embed", "save"];
|
||||
|
||||
/*
|
||||
* A few of the components are subclasses of other classes (eg, "x86cpu" is a subclass
|
||||
* of "cpu"). In those situations, we "hoist" the subclass constructor into the
|
||||
* corresponding superclass, because it is the name of the superclass that we rely on during
|
||||
* machine initialization.
|
||||
*/
|
||||
var aSubClasses = {
|
||||
"pcx86/lib/x86cpu": "pcx86/lib/cpu",
|
||||
"pcx86/lib/debugger": "shared/lib/debugger"
|
||||
|
|
|
|||
|
|
@ -164,11 +164,6 @@ function DebuggerX86(parmsDbg)
|
|||
*/
|
||||
this.dbgAddrNextCode = this.newAddr();
|
||||
this.dbgAddrNextData = this.newAddr();
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
this.dbgAddrAssemble = this.newAddr();
|
||||
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -2631,7 +2631,7 @@ Card.prototype.dumpVideoCard = function()
|
|||
* TODO: Make these options more general-purpose (it currently assumes a conventional VGA planar layout).
|
||||
*
|
||||
* @this {Card}
|
||||
* @param {Array.<string>} asArgs
|
||||
* @param {Array.<string>} asArgs (all numeric arguments default to base 16 unless otherwise specified)
|
||||
*/
|
||||
Card.prototype.dumpVideoBuffer = function(asArgs)
|
||||
{
|
||||
|
|
@ -2648,12 +2648,12 @@ Card.prototype.dumpVideoBuffer = function(asArgs)
|
|||
|
||||
var s = asArgs[i];
|
||||
if (!i) {
|
||||
idw = str.parseInt(s);
|
||||
idw = str.parseInt(s, 16);
|
||||
continue;
|
||||
}
|
||||
|
||||
var ch = s.charAt(0);
|
||||
j = str.parseInt(s.substr(1));
|
||||
j = str.parseInt(s.substr(1), 16);
|
||||
|
||||
switch(ch) {
|
||||
case 'l':
|
||||
|
|
|
|||
|
|
@ -99,11 +99,6 @@ function DebuggerPDP11(parmsDbg)
|
|||
*/
|
||||
this.dbgAddrNextCode = this.newAddr();
|
||||
this.dbgAddrNextData = this.newAddr();
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
this.dbgAddrAssemble = this.newAddr();
|
||||
|
||||
/*
|
||||
|
|
@ -3434,83 +3429,6 @@ if (DEBUGGER) {
|
|||
}
|
||||
};
|
||||
|
||||
/**
|
||||
* parseCommand(sCmd, fSave, chSep)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {string|undefined} sCmd
|
||||
* @param {boolean} [fSave] is true to save the command, false if not
|
||||
* @param {string} [chSep] is the command separator character (default is ';')
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
DebuggerPDP11.prototype.parseCommand = function(sCmd, fSave, chSep)
|
||||
{
|
||||
if (fSave) {
|
||||
if (!sCmd) {
|
||||
if (this.fAssemble) {
|
||||
sCmd = "end";
|
||||
} else {
|
||||
sCmd = this.aPrevCmds[this.iPrevCmd+1];
|
||||
}
|
||||
} else {
|
||||
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
|
||||
this.aPrevCmds.splice(0, 0, sCmd);
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
this.iPrevCmd--;
|
||||
}
|
||||
}
|
||||
var a = [];
|
||||
if (sCmd) {
|
||||
/*
|
||||
* With the introduction of breakpoint commands (ie, quoted command sequences
|
||||
* associated with a breakpoint), we can no longer perform simplistic splitting.
|
||||
*
|
||||
* a = sCmd.split(chSep || ';');
|
||||
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||
*
|
||||
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
|
||||
*
|
||||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.toLowerCase().replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
chSep = chSep || ';';
|
||||
/*
|
||||
* NOTE: Processing charAt() up to and INCLUDING length is not a typo; we're taking
|
||||
* advantage of the fact that charAt() with an invalid index returns an empty string,
|
||||
* allowing us to use the same substring() call to capture the final portion of sCmd.
|
||||
*
|
||||
* In a sense, it allows us to pretend that the string ends with a zero terminator.
|
||||
*/
|
||||
for (var i = 0; i <= sCmd.length; i++) {
|
||||
var ch = sCmd.charAt(i);
|
||||
if (ch == '"' || ch == "'") {
|
||||
if (!chQuote) {
|
||||
chQuote = ch;
|
||||
} else if (ch == chQuote) {
|
||||
chQuote = null;
|
||||
}
|
||||
}
|
||||
else if (ch == chSep && !chQuote || !ch) {
|
||||
/*
|
||||
* Recall that substring() accepts starting (inclusive) and ending (exclusive)
|
||||
* indexes, whereas substr() accepts a starting index and a length. We need the former.
|
||||
*/
|
||||
a.push(str.trim(sCmd.substring(iPrev, i)));
|
||||
iPrev = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* shiftArgs(asArgs)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -66,6 +66,11 @@ function Debugger(parmsDbg)
|
|||
this.nCycles = 0;
|
||||
this.cOpcodes = this.cOpcodesStart = 0;
|
||||
|
||||
/*
|
||||
* fAssemble is true when "assemble mode" is active, false when not.
|
||||
*/
|
||||
this.fAssemble = false;
|
||||
|
||||
/*
|
||||
* This maintains command history. New commands are inserted at index 0 of the array.
|
||||
* When Enter is pressed on an empty input buffer, we default to the command at aPrevCmds[0].
|
||||
|
|
@ -215,6 +220,83 @@ if (DEBUGGER) {
|
|||
return true;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseCommand(sCmd, fSave, chSep)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string|undefined} sCmd
|
||||
* @param {boolean} [fSave] is true to save the command, false if not
|
||||
* @param {string} [chSep] is the command separator character (default is ';')
|
||||
* @return {Array.<string>}
|
||||
*/
|
||||
Debugger.prototype.parseCommand = function(sCmd, fSave, chSep)
|
||||
{
|
||||
if (fSave) {
|
||||
if (!sCmd) {
|
||||
if (this.fAssemble) {
|
||||
sCmd = "end";
|
||||
} else {
|
||||
sCmd = this.aPrevCmds[this.iPrevCmd+1];
|
||||
}
|
||||
} else {
|
||||
if (this.iPrevCmd < 0 && this.aPrevCmds.length) {
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
if (this.iPrevCmd < 0 || sCmd != this.aPrevCmds[this.iPrevCmd]) {
|
||||
this.aPrevCmds.splice(0, 0, sCmd);
|
||||
this.iPrevCmd = 0;
|
||||
}
|
||||
this.iPrevCmd--;
|
||||
}
|
||||
}
|
||||
var a = [];
|
||||
if (sCmd) {
|
||||
/*
|
||||
* With the introduction of breakpoint commands (ie, quoted command sequences
|
||||
* associated with a breakpoint), we can no longer perform simplistic splitting.
|
||||
*
|
||||
* a = sCmd.split(chSep || ';');
|
||||
* for (var i = 0; i < a.length; i++) a[i] = str.trim(a[i]);
|
||||
*
|
||||
* We may now split on semi-colons ONLY if they are outside a quoted sequence.
|
||||
*
|
||||
* Also, to allow quoted strings *inside* breakpoint commands, we first replace all
|
||||
* DOUBLE double-quotes with single quotes.
|
||||
*/
|
||||
sCmd = sCmd.toLowerCase().replace(/""/g, "'");
|
||||
|
||||
var iPrev = 0;
|
||||
var chQuote = null;
|
||||
chSep = chSep || ';';
|
||||
/*
|
||||
* NOTE: Processing charAt() up to and INCLUDING length is not a typo; we're taking
|
||||
* advantage of the fact that charAt() with an invalid index returns an empty string,
|
||||
* allowing us to use the same substring() call to capture the final portion of sCmd.
|
||||
*
|
||||
* In a sense, it allows us to pretend that the string ends with a zero terminator.
|
||||
*/
|
||||
for (var i = 0; i <= sCmd.length; i++) {
|
||||
var ch = sCmd.charAt(i);
|
||||
if (ch == '"' || ch == "'") {
|
||||
if (!chQuote) {
|
||||
chQuote = ch;
|
||||
} else if (ch == chQuote) {
|
||||
chQuote = null;
|
||||
}
|
||||
}
|
||||
else if (ch == chSep && !chQuote || !ch) {
|
||||
/*
|
||||
* Recall that substring() accepts starting (inclusive) and ending (exclusive)
|
||||
* indexes, whereas substr() accepts a starting index and a length. We need the former.
|
||||
*/
|
||||
a.push(str.trim(sCmd.substring(iPrev, i)));
|
||||
iPrev = i + 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return a;
|
||||
};
|
||||
|
||||
/**
|
||||
* parseExpression(sExp, fPrint)
|
||||
*
|
||||
|
|
@ -383,7 +465,7 @@ if (DEBUGGER) {
|
|||
value = this.getRegValue(iReg);
|
||||
} else {
|
||||
value = this.getVariable(sValue);
|
||||
if (value === undefined) value = str.parseInt(sValue);
|
||||
if (value === undefined) value = str.parseInt(sValue, 16);
|
||||
}
|
||||
if (value === undefined && !fQuiet) this.println("invalid " + (sName? sName : "value") + ": " + sValue);
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -41,7 +41,7 @@ var str = {};
|
|||
* So use this function to validate the entire string.
|
||||
*
|
||||
* @param {string} s is the string representation of some number
|
||||
* @param {number} [base] is the radix of the number represented above; 10 is the default (only 2, 8, 10 and 16 are supported)
|
||||
* @param {number} [base] is the radix of the above number; 10 is the default (only 2, 8, 10 and 16 are supported)
|
||||
* @return {boolean} true if valid, false if invalid (or the specified base isn't supported)
|
||||
*/
|
||||
str.isValidInt = function(s, base)
|
||||
|
|
@ -63,14 +63,14 @@ str.isValidInt = function(s, base)
|
|||
* for binary, because 1) it's not commonly used, and 2) it conflicts with valid hex sequences.
|
||||
*
|
||||
* @param {string} s is the string representation of some number
|
||||
* @param {number} [base] is the default radix to use (default is 16); can be overridden by prefixes/suffixes
|
||||
* @param {number} [base] is the default radix to use (default is 10); can be overridden by prefixes/suffixes
|
||||
* @return {number|undefined} corresponding value, or undefined if invalid
|
||||
*/
|
||||
str.parseInt = function(s, base)
|
||||
{
|
||||
var value;
|
||||
if (s) {
|
||||
if (!base) base = 16;
|
||||
if (!base) base = 10;
|
||||
if (s.charAt(0) == '$') {
|
||||
base = 16;
|
||||
s = s.substr(1);
|
||||
|
|
|
|||
|
|
@ -232,7 +232,7 @@ TextOut.prototype.massageLines = function()
|
|||
var cBytes = 0, c;
|
||||
var sBytes = match[1], sASCIILine = "";
|
||||
for (var i = 0; i < sBytes.length; i += 2) {
|
||||
c = str.parseInt(sBytes.substr(i, 2));
|
||||
c = str.parseInt(sBytes.substr(i, 2), 16);
|
||||
if (c != 0x0D && c != 0x0A && (c < 0x20 || c >= 0x7F)) {
|
||||
c = 0x2E;
|
||||
fASCII = false;
|
||||
|
|
@ -357,7 +357,7 @@ TextOut.prototype.labelTargets = function()
|
|||
} else {
|
||||
sTarget = sTarget.substr(sShort.length);
|
||||
}
|
||||
target = str.parseInt(sTarget);
|
||||
target = str.parseInt(sTarget, 16);
|
||||
if (target == undefined) continue;
|
||||
if (aTargets.indexOf(target) < 0) {
|
||||
aTargets.push(target);
|
||||
|
|
@ -376,7 +376,7 @@ TextOut.prototype.labelTargets = function()
|
|||
for (i = 0; i < this.asLines.length; i++) {
|
||||
as = this.getLineParts(i);
|
||||
if (!as) continue;
|
||||
addr = str.parseInt(as[4]);
|
||||
addr = str.parseInt(as[4], 16);
|
||||
if (addr == undefined) continue;
|
||||
j = aTargets.indexOf(addr);
|
||||
if (j >= 0) {
|
||||
|
|
@ -398,7 +398,7 @@ TextOut.prototype.labelTargets = function()
|
|||
as = this.getLineParts(i);
|
||||
if (!as) continue;
|
||||
if (as[3].charAt(0) == chPrefix) {
|
||||
addr = str.parseInt(as[3].substr(1));
|
||||
addr = str.parseInt(as[3].substr(1), 16);
|
||||
if (aTargets.indexOf(addr) >= 0) {
|
||||
/*
|
||||
* Instead of putting back the original target address, let's just convert the line to a "db"
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
|
@ -1,5 +1,5 @@
|
|||
(function(){var l,aa,ba={163840:[40,1,8],184320:[40,1,9],327680:[40,2,8],368640:[40,2,9],737280:[80,2,9],1228800:[80,2,15],1474560:[80,2,18],2949120:[80,2,36],21368320:[615,4,17]};
|
||||
function ca(a,b){var c;if(a){b||(b=16);if("$"==a.charAt(0))b=16,a=a.substr(1);else if("0x"==a.substr(0,2))b=16,a=a.substr(2);else{var d=a.charAt(a.length-1).toLowerCase();"h"==d?(b=16,d=null):"."==d&&(b=10,d=null);null==d&&(a=a.substr(0,a.length-1))}var e,d=a,f=b;(f&&10!=f?16==f?null!==d.match(/^[0-9a-f]+$/i):2==f&&null!==d.match(/^[01]+$/i):null!==d.match(/^[0-9]+$/))&&!isNaN(e=parseInt(a,b))&&(c=e|0)}return c}
|
||||
function ca(a,b){var c;if(a){b||(b=10);if("$"==a.charAt(0))b=16,a=a.substr(1);else if("0x"==a.substr(0,2))b=16,a=a.substr(2);else{var d=a.charAt(a.length-1).toLowerCase();"h"==d?(b=16,d=null):"."==d&&(b=10,d=null);null==d&&(a=a.substr(0,a.length-1))}var e,d=a,f=b;(f&&10!=f?16==f?null!==d.match(/^[0-9a-f]+$/i):2==f&&null!==d.match(/^[01]+$/i):null!==d.match(/^[0-9]+$/))&&!isNaN(e=parseInt(a,b))&&(c=e|0)}return c}
|
||||
function ea(a,b){var c="";void 0===b?b=8:8<b&&(b=8);if(null==a||isNaN(a))for(;0<b--;)c="?"+c;else for(;0<b--;){var d=a&15,d=d+(0<=d&&9>=d?48:55),c=String.fromCharCode(d)+c;a>>=4}return c}function fa(a,b){var c=a,d=a.lastIndexOf("/");0<=d&&(c=a.substr(d+1));d=c.indexOf("&");0<d&&(c=c.substr(0,d));b&&(d=c.lastIndexOf("."),0<d&&(c=c.substring(0,d)));return c}function ga(a){var b="",c=a.lastIndexOf(".");0<=c&&(b=a.substr(c+1).toLowerCase());return b}
|
||||
function ha(a,b){return-1!==a.indexOf(b,a.length-b.length)}var ia={"&":"&","<":"<",">":">",'"':""","'":"'"};function ja(a){return a.replace(/[&<>"']/g,function(a){return ia[a]})}function ka(a){return String.prototype.trim?a.trim():a.replace(/^\s+|\s+$/g,"")}
|
||||
var la={0:"NUL",1:"SOH",2:"STX",3:"ETX",4:"EOT",5:"ENQ",6:"ACK",7:"BEL",8:"BS",9:"TAB",11:"VT",12:"FF",13:"CR",14:"SO",15:"SI",16:"DLE",17:"XON",18:"DC2",19:"XOFF",20:"DC4",21:"NAK",22:"SYN",23:"ETB",24:"CAN",25:"EM",26:"SUB",27:"ESC",28:"FS",29:"GS",30:"RS",31:"US"},ma=Date.now||function(){return+new Date};
|
||||
|
|
|
|||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
Loading…
Reference in a new issue