First stab at factoring some common Debugger functionality into a shared component
This commit is contained in:
parent
5969514c7e
commit
a8bb0c7841
42 changed files with 3721 additions and 4457 deletions
File diff suppressed because one or more lines are too long
|
|
@ -68,7 +68,7 @@ if (NODE) {
|
|||
* @extends Component
|
||||
* @param {Object} parmsBus
|
||||
* @param {CPUState} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
function Bus(parmsBus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -152,7 +152,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
Component.error("Unable to find CPU component");
|
||||
return;
|
||||
}
|
||||
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
|
||||
this.dbg = /** @type {Debugger6502} */ (Component.getComponentByType("Debugger", this.id));
|
||||
|
||||
/*
|
||||
* Enumerate all Video components for future updateVideo() calls.
|
||||
|
|
|
|||
|
|
@ -160,7 +160,7 @@ CPU.BUTTONS = ["power", "reset"];
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -34,7 +34,12 @@
|
|||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var APPCLASS = "pc8080"; // this @define is the default application class (formerly APPCLASS) to use
|
||||
var APPCLASS = "pc6502"; // this @define is the default application class (eg, "pcx86", "c1pjs")
|
||||
|
||||
/**
|
||||
* @define {string}
|
||||
*/
|
||||
var APPNAME = "PC6502"; // this @define is the default application name (eg, "PCx86", "C1Pjs")
|
||||
|
||||
/**
|
||||
* @define {boolean}
|
||||
|
|
@ -69,6 +74,26 @@ var BYTEARRAYS = false;
|
|||
*/
|
||||
var TYPEDARRAYS = (typeof ArrayBuffer !== 'undefined');
|
||||
|
||||
/*
|
||||
* Combine all the shared globals and machine-specific globals into one machine-specific global object,
|
||||
* which all machine components should start using; eg: "if (PCX86.DEBUG) ..." instead of "if (DEBUG) ...".
|
||||
*/
|
||||
var PC6502 = {
|
||||
APPCLASS: APPCLASS,
|
||||
APPNAME: APPNAME,
|
||||
APPVERSION: APPVERSION, // shared
|
||||
BYTEARRAYS: BYTEARRAYS,
|
||||
COMPILED: COMPILED, // shared
|
||||
CSSCLASS: CSSCLASS, // shared
|
||||
DEBUG: DEBUG, // shared
|
||||
DEBUGGER: DEBUGGER,
|
||||
MAXDEBUG: MAXDEBUG, // shared
|
||||
PRIVATE: PRIVATE, // shared
|
||||
TYPEDARRAYS: TYPEDARRAYS,
|
||||
SITEHOST: SITEHOST, // shared
|
||||
XMLVERSION: XMLVERSION // shared
|
||||
};
|
||||
|
||||
if (NODE) {
|
||||
global.APPCLASS = APPCLASS;
|
||||
global.DEBUGGER = DEBUGGER;
|
||||
|
|
|
|||
|
|
@ -237,7 +237,7 @@ Memory.prototype = {
|
|||
* @this {Memory}
|
||||
* @param {Memory} mem
|
||||
* @param {number} [type]
|
||||
* @param {Debugger} [dbg]
|
||||
* @param {Debugger6502} [dbg]
|
||||
*/
|
||||
clone: function(mem, type, dbg) {
|
||||
/*
|
||||
|
|
@ -485,7 +485,7 @@ Memory.prototype = {
|
|||
* copyBreakpoints(dbg, mem)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {Debugger} [dbg]
|
||||
* @param {Debugger6502} [dbg]
|
||||
* @param {Memory} [mem] (outgoing Memory block to copy breakpoints from, if any)
|
||||
*/
|
||||
copyBreakpoints: function(dbg, mem) {
|
||||
|
|
|
|||
|
|
@ -87,7 +87,7 @@ Panel.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPUState} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -74,7 +74,7 @@ Component.subclass(RAM);
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPUState} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -133,7 +133,7 @@ Component.subclass(ROM);
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPUState} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
ROM.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -223,7 +223,7 @@ Video.COLORS = {
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPUState} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {Debugger6502} dbg
|
||||
*/
|
||||
Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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 State = require("../../shared/lib/state");
|
||||
var PC8080 = require("./defines");
|
||||
var CPUDef8080 = require("./cpudef");
|
||||
|
|
@ -68,7 +69,7 @@ var DbgAddr8080;
|
|||
* Debugger8080(parmsDbg)
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @extends Debugger
|
||||
* @param {Object} parmsDbg
|
||||
*
|
||||
* The Debugger8080 component supports the following optional (parmsDbg) properties:
|
||||
|
|
@ -89,17 +90,6 @@ function Debugger8080(parmsDbg)
|
|||
|
||||
this.style = Debugger8080.STYLE_8080;
|
||||
|
||||
/*
|
||||
* These keep track of instruction activity, but only when tracing or when Debugger8080 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,
|
||||
|
|
@ -111,13 +101,6 @@ function Debugger8080(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.
|
||||
*/
|
||||
|
|
@ -138,21 +121,6 @@ function Debugger8080(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 +182,7 @@ function Debugger8080(parmsDbg)
|
|||
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.subclass(Debugger8080);
|
||||
Component.subclass(Debugger8080, Debugger);
|
||||
|
||||
/*
|
||||
* NOTE: Every Debugger8080 property from here to the first prototype function definition (initBus()) is a
|
||||
|
|
@ -2622,379 +2590,6 @@ if (DEBUGGER) {
|
|||
return s;
|
||||
};
|
||||
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @param {Array.<number>} aVals
|
||||
* @param {Array.<string>} aOps
|
||||
* @param {number} [cOps] (default is all)
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @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
|
||||
*/
|
||||
Debugger8080.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(Debugger8080.aBinOpPrecedence[sOp] != null);
|
||||
if (aOps.length && Debugger8080.aBinOpPrecedence[sOp] < Debugger8080.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 {Debugger8080}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @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
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @param {string|null} sVar
|
||||
* @param {number|undefined} value
|
||||
* @return {boolean} true if value defined, false if not
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @param {string} [sVar]
|
||||
* @return {boolean} true if all value(s) defined, false if not
|
||||
*/
|
||||
Debugger8080.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 {Debugger8080}
|
||||
* @param {string} sVar
|
||||
*/
|
||||
Debugger8080.prototype.delVariable = function(sVar)
|
||||
{
|
||||
delete this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* getVariable(sVar)
|
||||
*
|
||||
* @this {Debugger8080}
|
||||
* @param {string} sVar
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
Debugger8080.prototype.getVariable = function(sVar)
|
||||
{
|
||||
return this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* setVariable(sVar, value)
|
||||
*
|
||||
* @this {Debugger8080}
|
||||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
*/
|
||||
Debugger8080.prototype.setVariable = function(sVar, value)
|
||||
{
|
||||
this.aVariables[sVar] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* comparePairs(p1, p2)
|
||||
*
|
||||
|
|
|
|||
|
|
@ -76,7 +76,8 @@ try {
|
|||
* 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.
|
||||
* actually want to create, not the "cpu" superclass. See aSubClasses for the complete
|
||||
* list of subclassed components we manage like that.
|
||||
*
|
||||
* TODO: Update the list of ignored (ie, ignorable) components.
|
||||
*/
|
||||
|
|
@ -84,6 +85,10 @@ var Component;
|
|||
var dbg;
|
||||
var aComponents = [];
|
||||
var asComponentsIgnore = ["embed","save"];
|
||||
var aSubClasses = {
|
||||
"pcx86/lib/x86cpu": "pcx86/lib/cpu",
|
||||
"pcx86/lib/debugger": "shared/lib/debugger"
|
||||
};
|
||||
|
||||
/**
|
||||
* loadComponents(asFiles)
|
||||
|
|
@ -106,9 +111,17 @@ function loadComponents(asFiles)
|
|||
* we'll want to access later (eg, "component" in getComponentByType()).
|
||||
*/
|
||||
var fn = require(lib + "../../../" + sFile);
|
||||
if (sName == "x86cpu") {
|
||||
var sSuperClass = null;
|
||||
for (var s in aSubClasses) {
|
||||
if (sFile.indexOf(s) >= 0) {
|
||||
sSuperClass = aSubClasses[s];
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (sSuperClass) {
|
||||
for (var j = 0; j < aComponents.length; j++) {
|
||||
if (aComponents[j].name == "cpu") {
|
||||
if (aComponents[j].path.indexOf(sSuperClass) >= 0) {
|
||||
if (fDebug) console.log("updating superclass " + aComponents[j].path + " with subclass " + sFile);
|
||||
aComponents[j].Create = fn;
|
||||
sName = null;
|
||||
break;
|
||||
|
|
@ -121,7 +134,7 @@ function loadComponents(asFiles)
|
|||
}; // jshint ignore:line
|
||||
}
|
||||
if (sName) {
|
||||
aComponents.push({name: sName, Create: fn, objects: []});
|
||||
aComponents.push({name: sName, path: sFile, Create: fn, objects: []});
|
||||
}
|
||||
if (sName == "defines") {
|
||||
/*
|
||||
|
|
|
|||
|
|
@ -68,7 +68,7 @@ if (NODE) {
|
|||
* @extends Component
|
||||
* @param {Object} parmsBus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
function Bus(parmsBus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -1294,7 +1294,7 @@ ChipSet.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
ChipSet.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ function Computer(parmsComputer, parmsMachine, fSuspended) {
|
|||
Component.error("Unable to find CPU component");
|
||||
return;
|
||||
}
|
||||
this.dbg = /** @type {Debugger} */ (Component.getComponentByType("Debugger", this.id));
|
||||
this.dbg = /** @type {DebuggerX86} */ (Component.getComponentByType("Debugger", this.id));
|
||||
|
||||
/*
|
||||
* Enumerate all Video components for future updateVideo() calls.
|
||||
|
|
|
|||
|
|
@ -159,7 +159,7 @@ CPU.BUTTONS = ["power", "reset"];
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
CPU.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -811,7 +811,7 @@ var SectorInfo;
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
Disk.prototype.initBus = function(cmp, bus, cpu, dbg) {
|
||||
this.dbg = dbg;
|
||||
|
|
|
|||
|
|
@ -623,7 +623,7 @@ FDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
FDC.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -594,7 +594,7 @@ HDC.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
HDC.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -31,22 +31,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Components that previously used Debugger interrupt definitions by including:
|
||||
*
|
||||
* var Debugger = require("./debugger");
|
||||
*
|
||||
* and using:
|
||||
*
|
||||
* Debugger.INT.FOO
|
||||
*
|
||||
* must now instead include:
|
||||
*
|
||||
* var Interrupts = require("./interrupts");
|
||||
*
|
||||
* and then replace all occurrences of "Debugger.INT.FOO" with "Interrupts.FOO".
|
||||
*/
|
||||
|
||||
var Interrupts = {
|
||||
VIDEO: 0x10,
|
||||
DISK: 0x13,
|
||||
|
|
@ -184,7 +168,7 @@ if (DEBUGGER) {
|
|||
0x4A8: ["SAVE_PTR",4] // POINTER TO EGA PARAMETER CONTROL BLOCK
|
||||
},
|
||||
/*
|
||||
* See Debugger.prototype.replaceRegs() for the rules governing how register contents are replaced in the strings below.
|
||||
* See DebuggerX86.prototype.replaceRegs() for the rules governing how register contents are replaced in the strings below.
|
||||
*
|
||||
* Replacements occur in the following order:
|
||||
*
|
||||
|
|
@ -1546,7 +1530,7 @@ if (DEBUGGER) {
|
|||
* INT 2E Execute command (*) (2.0+)
|
||||
* Entry: DS:SI->command string-1 followed by a carriage return
|
||||
* Exit: All registers except CS and IP are destroyed
|
||||
* Note: This function is not re-enterant and should not be issued from a
|
||||
* Note: This function is not re-entrant and should not be issued from a
|
||||
* batch file.
|
||||
*
|
||||
* INT 2F Multiplex (3.0+)
|
||||
|
|
|
|||
|
|
@ -1324,7 +1324,7 @@ Keyboard.prototype.findBinding = function(simCode, sType, fDown)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
Keyboard.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -288,7 +288,7 @@ Memory.prototype = {
|
|||
* @this {Memory}
|
||||
* @param {Memory} mem
|
||||
* @param {number} [type]
|
||||
* @param {Debugger} [dbg]
|
||||
* @param {DebuggerX86} [dbg]
|
||||
*/
|
||||
clone: function(mem, type, dbg) {
|
||||
/*
|
||||
|
|
@ -620,7 +620,7 @@ Memory.prototype = {
|
|||
* copyBreakpoints(dbg, mem)
|
||||
*
|
||||
* @this {Memory}
|
||||
* @param {Debugger} [dbg]
|
||||
* @param {DebuggerX86} [dbg]
|
||||
* @param {Memory} [mem] (outgoing Memory block to copy breakpoints from, if any)
|
||||
*/
|
||||
copyBreakpoints: function(dbg, mem) {
|
||||
|
|
|
|||
|
|
@ -31,22 +31,6 @@
|
|||
|
||||
"use strict";
|
||||
|
||||
/*
|
||||
* Components that previously used Debugger messages definitions by including:
|
||||
*
|
||||
* var Debugger = require("./debugger");
|
||||
*
|
||||
* and using:
|
||||
*
|
||||
* Debugger.MESSAGE.FOO
|
||||
*
|
||||
* must now instead include:
|
||||
*
|
||||
* var Messages = require("./messages");
|
||||
*
|
||||
* and then replace all occurrences of "Debugger.MESSAGE.FOO" with "Messages.FOO".
|
||||
*/
|
||||
|
||||
var Messages = {
|
||||
CPU: 0x00000001,
|
||||
SEG: 0x00000002,
|
||||
|
|
|
|||
|
|
@ -181,7 +181,7 @@ Mouse.BUTTON = {
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
Mouse.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -341,7 +341,7 @@ Panel.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
Panel.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -230,7 +230,7 @@ ParallelPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValu
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
ParallelPort.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -77,7 +77,7 @@ Component.subclass(RAM);
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
RAM.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -162,7 +162,7 @@ ROM.BIOS.RESET_FLAG_WARMBOOT = 0x1234; // value stored at ROM.BIOS.RESET_FLAG t
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
ROM.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -456,7 +456,7 @@ SerialPort.prototype.setBinding = function(sHTMLType, sBinding, control, sValue)
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
SerialPort.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -2899,7 +2899,7 @@ Video.TOUCH = {
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
Video.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -163,7 +163,7 @@ Component.subclass(X86FPU);
|
|||
* @param {Computer} cmp
|
||||
* @param {Bus} bus
|
||||
* @param {X86CPU} cpu
|
||||
* @param {Debugger} dbg
|
||||
* @param {DebuggerX86} dbg
|
||||
*/
|
||||
X86FPU.prototype.initBus = function(cmp, bus, cpu, dbg)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -39,6 +39,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 State = require("../../shared/lib/state");
|
||||
var PDP11 = require("./defines");
|
||||
var CPUPDP11 = require("./cpu");
|
||||
|
|
@ -69,7 +70,7 @@ var DbgAddrPDP11;
|
|||
* DebuggerPDP11(parmsDbg)
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @extends Debugger
|
||||
* @param {Object} parmsDbg
|
||||
*
|
||||
* The DebuggerPDP11 component supports the following optional (parmsDbg) properties:
|
||||
|
|
@ -86,18 +87,7 @@ function DebuggerPDP11(parmsDbg)
|
|||
{
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.call(this, "Debugger", parmsDbg, DebuggerPDP11);
|
||||
|
||||
/*
|
||||
* These keep track of instruction activity, but only when tracing or when DebuggerPDP11 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;
|
||||
Debugger.call(this, parmsDbg);
|
||||
|
||||
/*
|
||||
* Most commands that require an address call parseAddr(), which defaults to dbgAddrNextCode
|
||||
|
|
@ -110,13 +100,6 @@ function DebuggerPDP11(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.
|
||||
*/
|
||||
|
|
@ -137,21 +120,6 @@ function DebuggerPDP11(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,
|
||||
|
|
@ -213,7 +181,7 @@ function DebuggerPDP11(parmsDbg)
|
|||
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.subclass(DebuggerPDP11);
|
||||
Component.subclass(DebuggerPDP11, Debugger);
|
||||
|
||||
/*
|
||||
* NOTE: Every DebuggerPDP11 property from here to the first prototype function definition (initBus()) is a
|
||||
|
|
@ -2138,379 +2106,6 @@ if (DEBUGGER) {
|
|||
return "no regs";
|
||||
};
|
||||
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {Array.<number>} aVals
|
||||
* @param {Array.<string>} aOps
|
||||
* @param {number} [cOps] (default is all)
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @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
|
||||
*/
|
||||
DebuggerPDP11.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(DebuggerPDP11.aBinOpPrecedence[sOp] != null);
|
||||
if (aOps.length && DebuggerPDP11.aBinOpPrecedence[sOp] < DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @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
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {string|null} sVar
|
||||
* @param {number|undefined} value
|
||||
* @return {boolean} true if value defined, false if not
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {string} [sVar]
|
||||
* @return {boolean} true if all value(s) defined, false if not
|
||||
*/
|
||||
DebuggerPDP11.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 {DebuggerPDP11}
|
||||
* @param {string} sVar
|
||||
*/
|
||||
DebuggerPDP11.prototype.delVariable = function(sVar)
|
||||
{
|
||||
delete this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* getVariable(sVar)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {string} sVar
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
DebuggerPDP11.prototype.getVariable = function(sVar)
|
||||
{
|
||||
return this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* setVariable(sVar, value)
|
||||
*
|
||||
* @this {DebuggerPDP11}
|
||||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
*/
|
||||
DebuggerPDP11.prototype.setVariable = function(sVar, value)
|
||||
{
|
||||
this.aVariables[sVar] = value;
|
||||
};
|
||||
|
||||
/**
|
||||
* comparePairs(p1, p2)
|
||||
*
|
||||
|
|
|
|||
473
modules/shared/lib/debugger.js
Normal file
473
modules/shared/lib/debugger.js
Normal file
|
|
@ -0,0 +1,473 @@
|
|||
/**
|
||||
* @fileoverview Common PCjs Debugger support.
|
||||
* @author <a href="mailto:Jeff@pcjs.org">Jeff Parsons</a>
|
||||
* @version 1.0
|
||||
* Created 2012-Jun-21
|
||||
*
|
||||
* Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>
|
||||
*
|
||||
* This file is part of PCjs, a computer emulation software project at <http://pcjs.org/>.
|
||||
*
|
||||
* PCjs is free software: you can redistribute it and/or modify it under the terms of the
|
||||
* GNU General Public License as published by the Free Software Foundation, either version 3
|
||||
* of the License, or (at your option) any later version.
|
||||
*
|
||||
* PCjs is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without
|
||||
* even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License along with PCjs. If not,
|
||||
* see <http://www.gnu.org/licenses/gpl.html>.
|
||||
*
|
||||
* You are required to include the above copyright notice in every source code file of every
|
||||
* copy or modified version of this work, and to display that copyright notice on every screen
|
||||
* that loads or runs any version of this software (see COPYRIGHT in /modules/shared/lib/defines.js).
|
||||
*
|
||||
* Some PCjs files also attempt to load external resource files, such as character-image files,
|
||||
* ROM files, and disk image files. Those external resource files are not considered part of PCjs
|
||||
* for purposes of the GNU General Public License, and the author does not claim any copyright
|
||||
* as to their contents.
|
||||
*/
|
||||
|
||||
"use strict";
|
||||
|
||||
if (DEBUGGER) {
|
||||
if (NODE) {
|
||||
var str = require("../../shared/lib/strlib");
|
||||
var Component = require("../../shared/lib/component");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Debugger(parmsDbg)
|
||||
*
|
||||
* @constructor
|
||||
* @extends Component
|
||||
* @param {Object} parmsDbg
|
||||
*
|
||||
* The Debugger component is a shared component containing a subset of functionality used by
|
||||
* the other CPU-specific Debuggers (eg, DebuggerX86). Over time, the goal is to factor out as
|
||||
* much common debugging support from those components into this one.
|
||||
*/
|
||||
function Debugger(parmsDbg)
|
||||
{
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.call(this, "Debugger", parmsDbg, Debugger);
|
||||
|
||||
/*
|
||||
* 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;
|
||||
|
||||
/*
|
||||
* 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 = [];
|
||||
|
||||
/*
|
||||
* 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 = {};
|
||||
|
||||
} // endif DEBUGGER
|
||||
}
|
||||
|
||||
if (DEBUGGER) {
|
||||
|
||||
Component.subclass(Debugger);
|
||||
|
||||
Debugger.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 {Debugger}
|
||||
* @param {Array.<number>} aVals
|
||||
* @param {Array.<string>} aOps
|
||||
* @param {number} [cOps] (default is all)
|
||||
* @return {boolean} true if successful, false if error
|
||||
*/
|
||||
Debugger.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 {Debugger}
|
||||
* @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
|
||||
*/
|
||||
Debugger.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(Debugger.aBinOpPrecedence[sOp] != null);
|
||||
if (aOps.length && Debugger.aBinOpPrecedence[sOp] < Debugger.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 {Debugger}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.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), dbgAddr.fData32? 8 : 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 {Debugger}
|
||||
* @param {string} s
|
||||
* @return {string}
|
||||
*/
|
||||
Debugger.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 {Debugger}
|
||||
* @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
|
||||
*/
|
||||
Debugger.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 {Debugger}
|
||||
* @param {string|null} sVar
|
||||
* @param {number|undefined} value
|
||||
* @return {boolean} true if value defined, false if not
|
||||
*/
|
||||
Debugger.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 {Debugger}
|
||||
* @param {string} [sVar]
|
||||
* @return {boolean} true if all value(s) defined, false if not
|
||||
*/
|
||||
Debugger.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 {Debugger}
|
||||
* @param {string} sVar
|
||||
*/
|
||||
Debugger.prototype.delVariable = function(sVar)
|
||||
{
|
||||
delete this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* getVariable(sVar)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sVar
|
||||
* @return {number|undefined}
|
||||
*/
|
||||
Debugger.prototype.getVariable = function(sVar)
|
||||
{
|
||||
return this.aVariables[sVar];
|
||||
};
|
||||
|
||||
/**
|
||||
* setVariable(sVar, value)
|
||||
*
|
||||
* @this {Debugger}
|
||||
* @param {string} sVar
|
||||
* @param {number} value
|
||||
*/
|
||||
Debugger.prototype.setVariable = function(sVar, value)
|
||||
{
|
||||
this.aVariables[sVar] = value;
|
||||
};
|
||||
|
||||
} // endif DEBUGGER
|
||||
|
||||
if (NODE) module.exports = Debugger;
|
||||
|
|
@ -35,13 +35,9 @@
|
|||
* @define {string}
|
||||
*/
|
||||
var APPVERSION = "1.x.x"; // this @define is overridden by the Closure Compiler with the version in package.json
|
||||
|
||||
var XMLVERSION = null; // this is set in non-COMPILED builds by embedMachine() if a version number was found in the machine XML
|
||||
|
||||
var COPYRIGHT = "Copyright © 2012-2016 Jeff Parsons <Jeff@pcjs.org>";
|
||||
|
||||
var LICENSE = "License: GPL version 3 or later <http://gnu.org/licenses/gpl.html>";
|
||||
|
||||
var CSSCLASS = "pcjs";
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -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 (only 2, 10 and 16 are supported)
|
||||
* @param {number} [base] is the radix of the number represented above; 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)
|
||||
|
|
|
|||
|
|
@ -135,6 +135,7 @@
|
|||
"./modules/pcx86/lib/disk.js",
|
||||
"./modules/pcx86/lib/fdc.js",
|
||||
"./modules/pcx86/lib/hdc.js",
|
||||
"./modules/shared/lib/debugger.js",
|
||||
"./modules/pcx86/lib/debugger.js",
|
||||
"./modules/pcx86/lib/computer.js",
|
||||
"./modules/shared/lib/state.js",
|
||||
|
|
@ -165,6 +166,7 @@
|
|||
"./modules/pc8080/lib/keyboard.js",
|
||||
"./modules/pc8080/lib/video.js",
|
||||
"./modules/pc8080/lib/serialport.js",
|
||||
"./modules/shared/lib/debugger.js",
|
||||
"./modules/pc8080/lib/debugger.js",
|
||||
"./modules/pc8080/lib/computer.js",
|
||||
"./modules/shared/lib/state.js",
|
||||
|
|
@ -190,6 +192,7 @@
|
|||
"./modules/pdp11/lib/rom.js",
|
||||
"./modules/pdp11/lib/ram.js",
|
||||
"./modules/pdp11/lib/keyboard.js",
|
||||
"./modules/shared/lib/debugger.js",
|
||||
"./modules/pdp11/lib/debugger.js",
|
||||
"./modules/pdp11/lib/computer.js",
|
||||
"./modules/shared/lib/state.js",
|
||||
|
|
|
|||
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
Loading…
Reference in a new issue