Cleaned up the expression parser, fixed some PDP-10 evaluation errors, and added support unary minus

This commit is contained in:
Jeff 2017-03-16 10:30:56 -07:00 • committed by Jeff Parsons
commit 6e0e489bee
8 changed files with 536 additions and 447 deletions

File diff suppressed because one or more lines are too long

View file

@ -18,7 +18,7 @@ The module is currently comprised of the following components, as listed in [pac
* [bus.js](/modules/pdp10/lib/bus.js)
* [computer.js](/modules/pdp10/lib/computer.js)
* [cpu.js](/modules/pdp10/lib/cpu.js)
* [cpuop.js](/modules/pdp10/lib/cpuop.js)
* [cpuops.js](/modules/pdp10/lib/cpuops.js)
* [cpustate.js](/modules/pdp10/lib/cpustate.js)
* [debugger.js](/modules/pdp10/lib/debugger.js)
* [defines.js](/modules/pdp10/lib/defines.js)

View file

@ -6378,8 +6378,11 @@ PDP10.split72 = function(res, ext)
PDP10.setAddFlags = function(dst, src, res)
{
/*
* Isolate the top two bits of dst, src, and res by "shifting" them into bits 0 and 1 of the following variables.
* Isolate the top two bits of dst, src, and res by "shifting" them into bits 0 and 1 of the
* following variables. Note that shifting with division only works when the values are unsigned
* (which they MUST be).
*/
Component.assert(dst >= 0 && src >= 0 && res >= 0);
var dst01 = Math.trunc(dst / PDP10.TWO_POW34);
var src01 = Math.trunc(src / PDP10.TWO_POW34);
var res01 = Math.trunc(res / PDP10.TWO_POW34);
@ -6440,7 +6443,11 @@ PDP10.AND = function(dst, src)
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*
* Finally, all 36-bit data within a PDP-10 machine should ALWAYS be unsigned, which we now assert,
* because the divisions below would not yield correct results with negative inputs.
*/
Component.assert(dst >= 0 && src >= 0);
return ((((dst / PDP10.TWO_POW32)|0) & ((src / PDP10.TWO_POW32)|0)) * PDP10.TWO_POW32) + ((dst & src) >>> 0);
};
@ -6491,7 +6498,11 @@ PDP10.EQV = function(dst, src)
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*
* Finally, all 36-bit data within a PDP-10 machine should ALWAYS be unsigned, which we now assert,
* because the divisions below would not yield correct results with negative inputs.
*/
Component.assert(dst >= 0 && src >= 0);
return ((~(((dst / PDP10.TWO_POW32)|0) ^ ((src / PDP10.TWO_POW32)|0)) & 0o17) * PDP10.TWO_POW32) + (~(dst ^ src) >>> 0);
};
@ -6514,7 +6525,11 @@ PDP10.IOR = function(dst, src)
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*
* Finally, all 36-bit data within a PDP-10 machine should ALWAYS be unsigned, which we now assert,
* because the divisions below would not yield correct results with negative inputs.
*/
Component.assert(dst >= 0 && src >= 0);
return ((((dst / PDP10.TWO_POW32)|0) | ((src / PDP10.TWO_POW32)|0)) * PDP10.TWO_POW32) + ((dst | src) >>> 0);
};
@ -6536,7 +6551,11 @@ PDP10.NOT = function(src)
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*
* Finally, all 36-bit data within a PDP-10 machine should ALWAYS be unsigned, which we now assert,
* because the divisions below would not yield correct results with negative inputs.
*/
Component.assert(src >= 0);
return ((~((src / PDP10.TWO_POW32)|0) & 0o17) * PDP10.TWO_POW32) + (~src >>> 0);
};
@ -6572,7 +6591,11 @@ PDP10.XOR = function(dst, src)
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*
* Finally, all 36-bit data within a PDP-10 machine should ALWAYS be unsigned, which we now assert,
* because the divisions below would not yield correct results with negative inputs.
*/
Component.assert(dst >= 0 && src >= 0);
return ((((dst / PDP10.TWO_POW32)|0) ^ ((src / PDP10.TWO_POW32)|0)) * PDP10.TWO_POW32) + ((dst ^ src) >>> 0);
};

View file

@ -350,6 +350,11 @@ class Debugger extends Component
if (this.nBits <= 32) {
return dst & src;
}
/*
* Negative values don't yield correct results when dividing, so pass them through an unsigned truncate().
*/
dst = this.truncate(dst, 0, true);
src = this.truncate(src, 0, true);
return ((((dst / Debugger.TWO_POW32)|0) & ((src / Debugger.TWO_POW32)|0)) * Debugger.TWO_POW32) + ((dst & src) >>> 0);
}
@ -379,9 +384,48 @@ class Debugger extends Component
if (this.nBits <= 32) {
return dst | src;
}
/*
* Negative values don't yield correct results when dividing, so pass them through an unsigned truncate().
*/
dst = this.truncate(dst, 0, true);
src = this.truncate(src, 0, true);
return ((((dst / Debugger.TWO_POW32)|0) | ((src / Debugger.TWO_POW32)|0)) * Debugger.TWO_POW32) + ((dst | src) >>> 0);
}
/**
* evalXOR(dst, src)
*
* Adapted from /modules/pdp10/lib/cpuops.js:PDP10.XOR().
*
* Performs the logical "exclusive-or" (XOR) of two operands > 32 bits.
*
* @this {Debugger}
* @param {number} dst
* @param {number} src
* @return {number} (dst ^ src)
*/
evalXOR(dst, src)
{
/*
* We XOR the low 32 bits separately from the higher bits, and then combine them with addition.
* Since all bits above 32 will be zero, and since 0 XOR 0 is 0, no special masking for the higher
* bits is required.
*
* WARNING: When using JavaScript's 32-bit operators with values that could set bit 31 and produce a
* negative value, it's critical to perform a final right-shift of 0, ensuring that the final result is
* positive.
*/
if (this.nBits <= 32) {
return dst | src;
}
/*
* Negative values don't yield correct results when dividing, so pass them through an unsigned truncate().
*/
dst = this.truncate(dst, 0, true);
src = this.truncate(src, 0, true);
return ((((dst / Debugger.TWO_POW32)|0) ^ ((src / Debugger.TWO_POW32)|0)) * Debugger.TWO_POW32) + ((dst ^ src) >>> 0);
}
/**
* truncate(v, nBits, fUnsigned)
*
@ -415,9 +459,16 @@ class Debugger extends Component
if (nBits <= 32) {
vNew = v | 0;
} else {
/*
* For negative values, we require them to fit within nBits - 1, reserving the left-most bit
* for the sign bit, but for positive values, we can't really be sure if the caller is treating
* the left-most bit as a sign bit or not, so the upper range is based on nBits.
*/
limit = Math.pow(2, nBits - 1);
if (v < -limit || v >= limit) {
if (v < -limit) {
vNew = v % limit;
} else if (v >= limit * 2) {
vNew = v % (limit * 2);
}
}
}
@ -469,7 +520,7 @@ class Debugger extends Component
break;
case '/':
if (!val2) return false;
valNew = val1 / val2;
valNew = Math.trunc(val1 / val2);
break;
case '%':
if (!val2) return false;
@ -479,6 +530,7 @@ class Debugger extends Component
valNew = val1 + val2;
break;
case '-':
case '--':
valNew = val1 - val2;
break;
case '<<':
@ -516,7 +568,7 @@ class Debugger extends Component
valNew = this.evalIOR(val1, val2);
break;
case '^^': // since MACRO-10 uses '^' for base overrides, you must now use '^^' for bitwise exclusive-or (XOR)
valNew = val1 ^ val2;
valNew = this.evalXOR(val1, val2);
break;
case '&&':
valNew = (val1 && val2? 1 : 0);
@ -527,7 +579,7 @@ class Debugger extends Component
default:
return false;
}
aVals.push(this.truncate(valNew, this.nBits));
aVals.push(this.truncate(valNew));
}
return true;
}
@ -583,6 +635,7 @@ class Debugger extends Component
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
@ -603,16 +656,29 @@ class Debugger extends Component
*
* WARNING: Whenever you make changes to this RegExp, make sure you update aBinOpPrecedence as needed, too.
*/
var regExp = /(\|\||&&|\||^^|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
var regExp = /(\|\||&&|\||\^\^|&|!=|!|==|>=|>>>|>>|>|<=|<<|<|-|\+|%|\/|\*)/;
var asValues = sExp.split(regExp);
while (i < asValues.length) {
var sValue = asValues[i++];
var cchValue = sValue.length;
var sOp = null, cchOp = 0;
if (i < asValues.length) {
sOp = asValues[i++]; cchOp = sOp.length;
}
sValue = Str.trim(sValue);
if (!sValue) {
if (sOp != '-') {
fError = true;
break;
}
/*
* We detect a unary minus by the presence of a blank value, and replace the unary minus
* with a "double minus", which does NOT mean decrement, but rather transforms the unary
* operator into a high-priority binary operator (subtraction from zero).
*/
sOp = '--'; sValue = '0';
}
var v = this.parseValue(sValue, null, fQuiet);
if (v === undefined) {
if (this.sUndefined == null && fQuiet) {
@ -624,9 +690,8 @@ class Debugger extends Component
break;
}
}
aVals.push(this.truncate(v, this.nBits));
if (i == asValues.length) break;
var sOp = asValues[i++], cchOp = sOp.length;
aVals.push(this.truncate(v));
if (!sOp) break;
this.assert(Debugger.aBinOpPrecedence[sOp] != null);
if (aOps.length && Debugger.aBinOpPrecedence[sOp] < Debugger.aBinOpPrecedence[aOps[aOps.length-1]]) {
this.evalOps(aVals, aOps, 1);
@ -913,7 +978,8 @@ if (DEBUGGER) {
'+': 8, // addition
'%': 9, // remainder
'/': 9, // division
'*': 9 // multiplication
'*': 9, // multiplication
'--': 10, // subtract from zero (conversion of a unary minus)
};
/*

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

File diff suppressed because one or more lines are too long