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

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,15 +656,28 @@ 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) {
fError = true;
break;
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) {
@ -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)
};
/*