Merge branch 'master' into gh-pages

This commit is contained in:
Jeff Parsons 2017-03-09 15:39:37 -08:00
commit 1f1ab6db85
21 changed files with 2136 additions and 1044 deletions

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 it is too large Load diff

View file

@ -179,7 +179,12 @@ class CPUStatePDP10 extends CPUPDP10 {
this.regXC = -1; // if >= 0 this supersedes regPC (refers to an opcode from XCT)
this.regBP = -1; // active byte pointer (-1 if none)
this.regPS = 0; // assorted processor flags (see PSFLAG bit definitions)
this.regExt = 0; // internal "extension" register used for 72-bit calculations (eg, MUL)
this.regExt = 0; // internal "extension" register used for 72-bit MUL and DIV calculations
this.regRes = [0, 0]; // four internal "double-length" registers used for 72-bit DIV calculations
this.regPow = [0, 0];
this.regDiv = [0, 0];
this.regRem = [0, 0];
/*
* This is queried and displayed by the Panel when it's not displaying its own ADDRESS register
@ -481,11 +486,10 @@ class CPUStatePDP10 extends CPUPDP10 {
}
/**
* setPC()
* setPC(addr)
*
* NOTE: Unlike other PCjs emulators, such as PCx86, where all PC updates MUST go through the setPC()
* function, this function is nothing more than a convenience, because in the PDP-11, the PC can be loaded
* like any other general register. We fully expect this function to be inlined at runtime.
* Updates the PC register with the new address after masking it with ADDR_LIMIT (in case the
* new address was the result of an unchecked calculation).
*
* @this {CPUStatePDP10}
* @param {number} addr

View file

@ -90,6 +90,7 @@ class DebuggerPDP10 extends Debugger {
*/
this.fInit = false;
this.fParens = true;
this.nBusWidth = 18; // default value, updated by initBus()
/*
* Most commands that require an address call parseAddr(), and if a dbgAddr parameter is supplied
@ -315,6 +316,7 @@ class DebuggerPDP10 extends Debugger {
this.cmp = cmp;
this.cpu = cpu;
this.panel = cmp.panel;
this.nBusWidth = bus.getWidth();
/*
* Re-initialize Debugger message support if necessary
@ -527,6 +529,7 @@ class DebuggerPDP10 extends Debugger {
addr = this.parseExpression(sAddr, fPrint);
}
if (addr != null) {
addr = this.validateWord(addr, this.nBusWidth);
this.setAddr(dbgAddr, addr, fPhysical, nBase);
}
return dbgAddr;
@ -549,6 +552,31 @@ class DebuggerPDP10 extends Debugger {
}
}
/**
* validateWord(w, bits)
*
* @this {DebuggerPDP10}
* @param {number} w
* @param {number} [bits]
* @return {number}
*/
validateWord(w, bits = 36)
{
/*
* Although it's expected that most callers will supply unsigned 36-bit values, we're nice about
* converting any signed values to their unsigned (two's complement) counterpart, provided they are
* within the acceptable range. Any values outside that range will be dealt with afterward.
*/
if (w < 0 && w >= -PDP10.INT_LIMIT) {
w += PDP10.WORD_LIMIT;
}
var value = Math.trunc(Math.abs(w)) % Math.pow(2, bits);
if (DEBUG && w !== value) {
this.println("validateWord(" + Str.toOct(w) + "): out of range, truncated to " + Str.toOct(value));
}
return value;
}
/**
* incAddr(dbgAddr, inc)
*
@ -2780,14 +2808,12 @@ class DebuggerPDP10 extends Debugger {
var dbgAddr = this.parseAddr(sAddr, this.dbgAddrData);
if (!dbgAddr) return;
for (var i = 2; i < asArgs.length; i++) {
var vNew = this.parseExpression(asArgs[i]);
if (vNew === undefined) {
this.println("unknown value: " + asArgs[i]);
break;
}
this.println("changing " + this.toStrAddr(dbgAddr) + " from " + this.toStrWord(fnGet.call(this, dbgAddr)) + " to " + this.toStrWord(vNew));
var w = this.parseExpression(asArgs[i]);
if (w === undefined) break;
w = this.validateWord(w);
this.println("changing " + this.toStrAddr(dbgAddr) + " from " + this.toStrWord(fnGet.call(this, dbgAddr)) + " to " + this.toStrWord(w));
//noinspection JSUnresolvedFunction
fnSet.call(this, dbgAddr, vNew, 1);
fnSet.call(this, dbgAddr, w, 1);
}
}
@ -3679,7 +3705,7 @@ class DebuggerPDP10 extends Debugger {
}
}
} catch(e) {
this.println("debugger error: " + (e.stack || e.message));
this.println("Debugger " + (e.stack || e.message));
result = false;
}
return result;
@ -3843,7 +3869,7 @@ if (DEBUGGER) {
ASHC: 57, FSC: 58, FADR: 59, FSBR: 60,
FMPR: 61, FDVR: 62, DFN: 63, UFA: 64,
FAD: 65, FSB: 66, FMP: 67, FDV: 68,
AOBJP: 69, AOBJN: 70, CAI: 71, CA: 72,
AOBJP: 69, AOBJN: 70, CAI: 71, CAM: 72,
JUMP: 73, SKIP: 74, AOJ: 75, AOS: 76,
SOJ: 77, SOS: 78, TR: 79, TL: 80,
TD: 81, TS: 82, XCT: 83, JFFO: 84,
@ -3877,7 +3903,7 @@ if (DEBUGGER) {
"ASHC", "FSC", "FADR", "FSBR",
"FMPR", "FDVR", "DFN", "UFA",
"FAD", "FSB", "FMP", "FDV",
"AOBJP", "AOBJN", "CAI", "CA",
"AOBJP", "AOBJN", "CAI", "CAM",
"JUMP", "SKIP", "AOJ", "AOS",
"SOJ", "SOS", "TR", "TL",
"TD", "TS", "XCT", "JFFO",
@ -4000,7 +4026,7 @@ if (DEBUGGER) {
},
[PDP10.OPCODE.OPCOMP]: { // 0o77000
0o30000: DebuggerPDP10.OPS.CAI,
0o31000: DebuggerPDP10.OPS.CA,
0o31000: DebuggerPDP10.OPS.CAM,
0o32000: DebuggerPDP10.OPS.JUMP,
0o33000: DebuggerPDP10.OPS.SKIP,
0o34000: DebuggerPDP10.OPS.AOJ,

View file

@ -109,6 +109,7 @@ var PDP10 = {
* values as unsigned quantities, these are the unsigned equivalents.
*/
WORD_INVALID: -1,
HINT_MASK: Math.pow(2, 17) - 1, // 131,071 (377777) signed half-word (half-int) mask
HALF_MASK: Math.pow(2, 18) - 1, // 262,143 (000000 777777): unsigned half-word mask
HALF_SHIFT: Math.pow(2, 18), // 262,144 (000001 000000): unsigned half-word shift
INT_MASK: Math.pow(2, 35) - 1, // 34,359,738,367 (377777 777777): signed word (magnitude) mask
@ -116,14 +117,9 @@ var PDP10 = {
WORD_MASK: Math.pow(2, 36) - 1, // 68,719,476,735 (777777 777777): unsigned word mask
WORD_LIMIT: Math.pow(2, 36), // 68,719,476,736 (1 000000 000000): unsigned word limit
MAX_POS18: Math.pow(2, 17) - 1, // 131,071 (377777)
MIN_NEG18: Math.pow(2, 17), // -131,072 (400000)
MAX_POS36: Math.pow(2, 35) - 1, // 34,359,738,367 (377777 777777)
MIN_NEG36: Math.pow(2, 35), // -34,359,738,368 (400000 000000)
TWO_POW36: Math.pow(2, 36), // the two's complement of a 36-bit value is (value? TWO_POW36 - value : 0)
TWO_POW34: Math.pow(2, 34),
TWO_POW32: Math.pow(2, 32),
TWO_POW34: Math.pow(2, 34),
TWO_POW36: Math.pow(2, 36), // the two's complement of a 36-bit value is (value? TWO_POW36 - value : 0)
/*
* PDP-10 opcodes are 36-bit values, most of which use the following layout:

View file

@ -217,7 +217,7 @@ class MemoryPDP10 {
* converting any signed values to their unsigned (two's complement) counterpart, provided they are
* within the acceptable range. Any values outside that range will be dealt with afterward.
*/
if (pattern < 0 && pattern >= -PDP10.MIN_NEG36) {
if (pattern < 0 && pattern >= -PDP10.INT_LIMIT) {
pattern += PDP10.WORD_LIMIT;
}
pattern = Math.trunc(Math.abs(pattern)) % PDP10.WORD_LIMIT;

View file

@ -123,7 +123,7 @@
* @unrestricted
*
* The 'value' property stores the 36-bit value as an unsigned integer. When the value should be
* interpreted as a signed quantity, subtract BIT36 whenever value > MAX_POS36.
* interpreted as a signed quantity, subtract BIT36 whenever value > INT_MASK.
*
* The 'extended' property stores an additional 36 bits of data from a multiplication, and can also
* provide an additional 36 bits of data to a division. Internally, it should be null whenever the
@ -153,7 +153,7 @@ class Int36 {
* 0 <= i <= Math.pow(2, 36) - 1
*
* The lower bound is ZERO and the upper bound is Int36.WORD_MASK. Whenever an Int36 value should be
* interpreted as a signed value, values above Int36.MAX_POS36 (ie, values with bit 35 set) should be
* interpreted as a signed value, values above Int36.INT_MASK (ie, values with bit 35 set) should be
* converted to their signed counterpart by subtracting the value from Int36.WORD_LIMIT (aka WORD_MASK + 1);
* the easiest way to do that is call isNeg() to check the sign bit and then call negate() as
* appropriate.
@ -330,12 +330,12 @@ class Int36 {
/**
* truncate(result, operand, fSub)
*
* The range of valid results (0 - WORD_MASK) is divided into two equal sub-ranges: 0 to MAX_POS36,
* where the sign bit is zero (the bottom range), and MAX_POS36+1 to WORD_MASK (the top range), where
* The range of valid results (0 - WORD_MASK) is divided into two equal sub-ranges: 0 to INT_MASK,
* where the sign bit is zero (the bottom range), and INT_MASK+1 to WORD_MASK (the top range), where
* the sign bit is one. During a single arithmetic operation, the result can "wrap around" from
* the bottom range to the top, or from the top range to the bottom, but it's an overflow/underflow
* error ONLY if the result "wraps across" the midpoint between the two ranges, producing an
* unnaturally small delta (<= MAX_POS36).
* unnaturally small delta (<= INT_MASK).
*
* This can be confirmed independently by examining the sign bits (BIT35) of the original value
* (V), the operand (O), the result (R), as well as two intermediate calculations, VR = (V ^ R)
@ -413,9 +413,9 @@ class Int36 {
var v = this.value / Int36.INT_LIMIT, r = result / Int36.INT_LIMIT, o = operand / Int36.INT_LIMIT;
e = ((v ^ r) & (o ^ (fSub? v : r))) & 1;
}
if ((result > Int36.MAX_POS36) != (this.value > Int36.MAX_POS36)) {
if ((result > Int36.INT_MASK) != (this.value > Int36.INT_MASK)) {
var delta = result - this.value;
if (Math.abs(delta) <= Int36.MAX_POS36) {
if (Math.abs(delta) <= Int36.INT_MASK) {
this.error |= (delta > 0? Int36.ERROR.OVERFLOW : Int36.ERROR.UNDERFLOW);
if (DEBUG && (delta > 0) != !(e & v)) e = 0;
}
@ -514,12 +514,12 @@ class Int36 {
this.error = Int36.ERROR.NONE;
if (n1 > Int36.MAX_POS36) {
if (n1 > Int36.INT_MASK) {
n1 = Int36.WORD_LIMIT - n1;
fNeg = !fNeg;
}
if (n2 > Int36.MAX_POS36) {
if (n2 > Int36.INT_MASK) {
n2 = Int36.WORD_LIMIT - n2;
fNeg = !fNeg;
}
@ -599,7 +599,7 @@ class Int36 {
var fNegQ = false, fNegR = false;
if (divisor > Int36.MAX_POS36) {
if (divisor > Int36.INT_MASK) {
divisor = Int36.WORD_LIMIT - divisor;
fNegQ = !fNegQ;
}
@ -704,11 +704,11 @@ class Int36 {
/*
* Convert the unsigned word (w) to a signed value (i), for convenience.
*/
var i = w > Int36.MAX_POS36? -(Int36.WORD_LIMIT - w) : w;
var i = w > Int36.INT_MASK? -(Int36.WORD_LIMIT - w) : w;
if (s > 0) {
if (s >= 35) {
i = (i < 0? Int36.INT_LIMIT : 0);
bitsShifted = Int36.MAX_POS36;
bitsShifted = Int36.INT_MASK;
} else {
i = (i * Math.pow(2, s)) % Int36.INT_LIMIT;
/*
@ -726,20 +726,20 @@ class Int36 {
*/
bitsShifted = Int36.INT_LIMIT - Math.pow(2, 35 - s);
}
if (w <= Int36.MAX_POS36) {
if (w <= Int36.INT_MASK) {
/*
* Since w was positive, overflow occurs ONLY if any of the bits we shifted out were 1s.
* If all those bits in the original value (w) were 0s, then adding bitsShifted to it could NOT
* produce a value > MAX_POS36.
* produce a value > INT_MASK.
*/
if (w + bitsShifted > Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (w + bitsShifted > Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
} else {
/*
* Since w was negative, overflow occurs ONLY if any of the bits we shifted out were 0s.
* If all those bits in the original value (w) were 1s, subtracting bitsShifted from it could NOT
* produce a value <= MAX_POS36.
* produce a value <= INT_MASK.
*/
if (w - bitsShifted <= Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (w - bitsShifted <= Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
}
} else {
if (s <= -35) {
@ -793,20 +793,20 @@ class Int36 {
*/
wLeft = (wRight * Math.pow(2, s - 35)) % Int36.INT_LIMIT;
bits = Int36.INT_LIMIT - Math.pow(2, 70 - s);
if (wLeftOrig <= Int36.MAX_POS36) {
if (wLeftOrig <= Int36.INT_MASK) {
/*
* Since wLeft was positive, overflow occurs ONLY if any of the bits we shifted out were 1s.
* If all those bits in the original value were 0s, then adding bits to it could NOT produce
* a value > MAX_POS36.
* a value > INT_MASK.
*/
if (wRight + bits > Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (wRight + bits > Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
} else {
/*
* Since wLeft was negative, overflow occurs ONLY if any of the bits we shifted out were 0s.
* If all those bits in the original value were 1s, subtracting bits from it could NOT produce
* a value <= MAX_POS36.
* a value <= INT_MASK.
*/
if (wRight - bits <= Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (wRight - bits <= Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
}
}
wRight = 0;
@ -824,20 +824,20 @@ class Int36 {
* Determine overflow and update the sign bits.
*/
bits = Int36.INT_LIMIT - Math.pow(2, 35 - s);
if (wLeftOrig <= Int36.MAX_POS36) {
if (wLeftOrig <= Int36.INT_MASK) {
/*
* Since wLeft was positive, overflow occurs ONLY if any of the bits we shifted out were 1s.
* If all those bits in the original value were 0s, then adding bits to it could NOT produce
* a value > MAX_POS36.
* a value > INT_MASK.
*/
if (wLeftOrig + bits > Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (wLeftOrig + bits > Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
} else {
/*
* Since wLeft was negative, overflow occurs ONLY if any of the bits we shifted out were 0s.
* If all those bits in the original value were 1s, subtracting bits from it could NOT produce
* a value <= MAX_POS36.
* a value <= INT_MASK.
*/
if (wLeftOrig - bits <= Int36.MAX_POS36) this.error |= Int36.ERROR.OVERFLOW;
if (wLeftOrig - bits <= Int36.INT_MASK) this.error |= Int36.ERROR.OVERFLOW;
/*
* Last but not least, update the sign bits of wLeft and wRight to indicate negative values.
*/
@ -1058,7 +1058,7 @@ class Int36 {
extend()
{
if (this.extended == null) {
this.extended = (this.value > Int36.MAX_POS36? Int36.WORD_MASK : 0);
this.extended = (this.value > Int36.INT_MASK? Int36.WORD_MASK : 0);
}
}
@ -1071,7 +1071,7 @@ class Int36 {
*/
reduce()
{
if (this.extended == 0 && this.value <= Int36.MAX_POS36 || this.extended == Int36.WORD_MASK && this.value > Int36.MAX_POS36) {
if (this.extended == 0 && this.value <= Int36.INT_MASK || this.extended == Int36.WORD_MASK && this.value > Int36.INT_MASK) {
this.extended = null;
this.magnitude = 35;
}
@ -1085,18 +1085,18 @@ class Int36 {
*/
isNeg()
{
return (this.extended > Int36.MAX_POS36 || this.extended == null && this.value > Int36.MAX_POS36);
return (this.extended > Int36.INT_MASK || this.extended == null && this.value > Int36.INT_MASK);
}
/**
* negate()
*
* negate() MUST automatically extend the value, because the two's complement of the most negative
* number (MIN_NEG36) still has its sign bit set, so we must rely on the sign of the extended value to
* number (INT_LIMIT) still has its sign bit set, so we must rely on the sign of the extended value to
* compensate.
*
* This is handled below by setting extended first, based on the opposite of the value's current sign;
* we could negate extended AFTER negating value, but then we'd need a special test for the MIN_NEG36 value.
* we could negate extended AFTER negating value, but then we'd need a special test for the INT_LIMIT value.
*
* A side-effect of this call is that the magnitude is forced to 71, on the presumption that we're about
* to perform an arithmetic operation that is more easily performed on a unified 71-bit value than two
@ -1111,7 +1111,7 @@ class Int36 {
* Set extended to the OPPOSITE of the current value.
*/
this.assert(this.magnitude == 35);
this.extended = (this.value > Int36.MAX_POS36? 0 : Int36.WORD_MASK);
this.extended = (this.value > Int36.INT_MASK? 0 : Int36.WORD_MASK);
this.magnitude = 71;
}
else {
@ -1352,12 +1352,12 @@ class Int36 {
* converting any signed values to their unsigned (two's complement) counterpart, provided they are
* within the acceptable range. Any values outside that range will be dealt with afterward.
*/
if (num < 0 && num >= -Int36.MIN_NEG36) {
if (num < 0 && num >= -Int36.INT_LIMIT) {
num += Int36.WORD_LIMIT;
}
var value = Math.trunc(Math.abs(num)) % Math.pow(2, bits);
if (DEBUG && num !== value) {
console.log("Int36.validate(" + Int36.octal(num) + " out of range, truncated to " + Int36.octal(value) + ")");
console.log("Int36.validate(" + Int36.octal(num) + "): out of range, truncated to " + Int36.octal(value));
}
return value;
}
@ -1370,16 +1370,13 @@ Int36.ERROR = {
DIVZERO: 0x4
};
Int36.HINT_MASK = Math.pow(2, 17) - 1; // 131,071 (377777): signed half-word (half-int) mask
Int36.HINT_LIMIT = Math.pow(2, 17); // 131,072 (400000): signed half-word (half-int) limit
Int36.HALF_MASK = Math.pow(2, 18) - 1; // 262,143 (000000 777777): unsigned half-word mask
Int36.HALF_SHIFT = Math.pow(2, 18); // 262,144 (000001 000000): unsigned half-word shift
Int36.INT_MASK = Math.pow(2, 35) - 1; // 34,359,738,367 (377777 777777): signed word (magnitude) mask
Int36.INT_LIMIT = Math.pow(2, 35); // 34,359,738,368 (400000 000000): signed word (magnitude) limit
Int36.WORD_MASK = Math.pow(2, 36) - 1; // 68,719,476,735
Int36.WORD_MASK = Math.pow(2, 36) - 1; // 68,719,476,735 (777777 777777): unsigned word mask
Int36.WORD_LIMIT = Math.pow(2, 36); // 68,719,476,736 (1 000000 000000): unsigned word limit
Int36.MAX_POS18 = Math.pow(2, 17) - 1; // 131,071 (377777)
Int36.MIN_NEG18 = Math.pow(2, 17); // -131,072 (400000)
Int36.MAX_POS36 = Math.pow(2, 35) - 1; // 34,359,738,367 (377777 777777)
Int36.MIN_NEG36 = Math.pow(2, 35); // -34,359,738,368 (400000 000000)
if (NODE) module.exports = Int36;

View file

@ -44,10 +44,10 @@ class Str {
*/
static isValidInt(s, base)
{
if (!base || base == 10) return s.match(/^[0-9]+$/) !== null;
if (base == 16) return s.match(/^[0-9a-f]+$/i) !== null;
if (base == 8) return s.match(/^[0-7]+$/) !== null;
if (base == 2) return s.match(/^[01]+$/) !== null;
if (!base || base == 10) return s.match(/^-?[0-9]+$/) !== null;
if (base == 16) return s.match(/^-?[0-9a-f]+$/i) !== null;
if (base == 8) return s.match(/^-?[0-7]+$/) !== null;
if (base == 2) return s.match(/^-?[01]+$/) !== null;
return false;
}
@ -165,9 +165,9 @@ class Str {
* values displayed differently.
*/
var s = "";
if (n == null || isNaN(n)) {
while (cch-- > 0) s = '?' + s;
} else {
if (isNaN(n)) {
n = null;
} else if (n != null) {
/*
* Callers that produced an input by dividing by a power of two rather than shifting (in order
* to access more than 32 bits) may produce a fractional result, which ordinarily we would simply
@ -175,6 +175,20 @@ class Str {
* this value as a sign-extension.
*/
if (n < 0 && n > -1) n = -1;
/*
* Negative values should be two's complemented according to the number of digits; for example,
* 12 octal digits implies an upper limit 8^12.
*/
if (n < 0) {
n += Math.pow(radix, cch);
}
if (n < 0 || n >= Math.pow(radix, cch)) {
n = null;
}
}
if (n == null) {
while (cch-- > 0) s = '?' + s;
} else {
while (cch-- > 0) {
var d = n % radix;
d += (d >= 0 && d <= 9? 0x30 : 0x41 - 10);
@ -266,7 +280,7 @@ class Str {
if (cch) {
if (cch > 12) cch = 12;
} else {
cch = (n & ~0xffffff)? 11 : ((n & ~0xffff)? 8 : 6);
cch = (n & ~0xffffff)? 12 : ((n & ~0xffff)? 8 : 6);
}
return Str.toBase(n, 8, cch, fPrefix? "0o" : "");
}

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

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

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long