Added "lsh" and "rot" commands to the Int36 command-line tool to mimic the PDP-10 opcodes and test their logic
This commit is contained in:
parent
ffb7fa1221
commit
234e8871ba
6 changed files with 308 additions and 45 deletions
|
|
@ -1402,7 +1402,7 @@ PDP10.opASH = function(op, acc)
|
|||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value (+/-255).
|
||||
*/
|
||||
var s = (this.regEA << 14) >> 24;
|
||||
var s = ((this.regEA << 14) >> 14) % 256;
|
||||
if (s) {
|
||||
var w = this.readWord(acc), bitsShifted;
|
||||
/*
|
||||
|
|
@ -1474,7 +1474,7 @@ PDP10.opROT = function(op, acc)
|
|||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value, modulo 36 (+/-35).
|
||||
*/
|
||||
var s = ((this.regEA << 14) >> 24) % 36;
|
||||
var s = ((this.regEA << 14) >> 14) % 36;
|
||||
if (s) {
|
||||
var w = this.readWord(acc);
|
||||
/*
|
||||
|
|
@ -1523,7 +1523,7 @@ PDP10.opLSH = function(op, acc)
|
|||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value (+/-255).
|
||||
*/
|
||||
var s = (this.regEA << 14) >> 24;
|
||||
var s = ((this.regEA << 14) >> 14) % 256;
|
||||
if (s) {
|
||||
var w = this.readWord(acc);
|
||||
if (s > 0) {
|
||||
|
|
@ -1598,7 +1598,7 @@ PDP10.opROTC = function(op, acc)
|
|||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value, modulo 72 (+/-71).
|
||||
*/
|
||||
var s = ((this.regEA << 14) >> 24) % 72;
|
||||
var s = ((this.regEA << 14) >> 14) % 72;
|
||||
if (s) {
|
||||
var wLeft = this.readWord(acc);
|
||||
var wRight = this.readWord((acc + 1) & 0o17);
|
||||
|
|
@ -1638,7 +1638,7 @@ PDP10.opLSHC = function(op, acc)
|
|||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value (+/-255).
|
||||
*/
|
||||
var s = (this.regEA << 14) >> 24;
|
||||
var s = ((this.regEA << 14) >> 14) % 256;
|
||||
if (s) {
|
||||
var wLeft = this.readWord(acc);
|
||||
var wRight = this.readWord((acc + 1) & 0o17);
|
||||
|
|
|
|||
|
|
@ -84,7 +84,7 @@ var PDP10 = {
|
|||
MODEL_KA10: 1001,
|
||||
|
||||
/*
|
||||
* This constant is used to mark points in the code where the physical address being returned
|
||||
* ADDR_INVALID is used to mark points in the code where the physical address being returned
|
||||
* is invalid and should not be used.
|
||||
*
|
||||
* In a 32-bit CPU, -1 (ie, 0xffffffff) could actually be a valid address, so consider changing
|
||||
|
|
|
|||
|
|
@ -71,10 +71,21 @@ function test(sCmd, fREPL)
|
|||
if (sNum2 != null) console.log(sOp + " " + dumpInt36(i36Op));
|
||||
|
||||
switch(sOp) {
|
||||
case "get":
|
||||
break;
|
||||
|
||||
case "set":
|
||||
i36Reg = new Int36(i36Op);
|
||||
break;
|
||||
|
||||
case "ext":
|
||||
i36Reg.extend(i36Op);
|
||||
break;
|
||||
|
||||
case "red":
|
||||
i36Reg.reduce(i36Op);
|
||||
break;
|
||||
|
||||
case "add":
|
||||
i36Reg.add(i36Op);
|
||||
break;
|
||||
|
|
@ -91,6 +102,18 @@ function test(sCmd, fREPL)
|
|||
i36Reg.div(i36Op);
|
||||
break;
|
||||
|
||||
case "ash":
|
||||
i36Reg.ash(i36Op);
|
||||
break;
|
||||
|
||||
case "lsh":
|
||||
i36Reg.lsh(i36Op);
|
||||
break;
|
||||
|
||||
case "rot":
|
||||
i36Reg.rot(i36Op);
|
||||
break;
|
||||
|
||||
default:
|
||||
console.log("unrecognized command: " + sCmd);
|
||||
return false;
|
||||
|
|
@ -151,6 +174,12 @@ test("set 0o577777777777 0o777777777777");
|
|||
test("set 0o677777777777 0o777777777777");
|
||||
test("set 0o777777777777 0o777777777777");
|
||||
|
||||
test("set 0o112233445566");
|
||||
test("lsh 3");
|
||||
test("lsh -3");
|
||||
test("rot 6");
|
||||
test("rot -6");
|
||||
|
||||
repl.start({
|
||||
prompt: "int36> ",
|
||||
input: process.stdin,
|
||||
|
|
|
|||
|
|
@ -500,6 +500,8 @@ class Int36 {
|
|||
var fNeg = false, extended;
|
||||
var n1 = this.value, n2 = value;
|
||||
|
||||
this.error = Int36.ERROR.NONE;
|
||||
|
||||
if (n1 > Int36.MAXPOS) {
|
||||
n1 = Int36.BIT36 - n1;
|
||||
fNeg = !fNeg;
|
||||
|
|
@ -573,6 +575,8 @@ class Int36 {
|
|||
*/
|
||||
divExtended(divisor)
|
||||
{
|
||||
this.error = Int36.ERROR.NONE;
|
||||
|
||||
if (!divisor) {
|
||||
this.error |= Int36.ERROR.DIVZERO;
|
||||
return;
|
||||
|
|
@ -649,6 +653,236 @@ class Int36 {
|
|||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* ash(i36)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {Int36} i36 (18-bit value representing the number of bits to arithmetically shift left (> 0) or right (< 0))
|
||||
*/
|
||||
ash(i36)
|
||||
{
|
||||
this.ashNum(i36.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* ashNum(num)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {number} num (18-bit value representing the number of bits to arithmetically shift left (> 0) or right (< 0))
|
||||
*/
|
||||
ashNum(num)
|
||||
{
|
||||
num = Int36.validate(num);
|
||||
this.error = Int36.ERROR.NONE;
|
||||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value (+/-255).
|
||||
*/
|
||||
var s = ((num << 14) >> 14) % 256;
|
||||
if (s) {
|
||||
if (this.extended == null) {
|
||||
/*
|
||||
* Simulate opASH()
|
||||
*/
|
||||
var w = this.value, bitsShifted;
|
||||
/*
|
||||
* Convert the unsigned word (w) to a signed value (i), for convenience.
|
||||
*/
|
||||
var i = w > Int36.MAXPOS? -(Int36.BIT36 - w) : w;
|
||||
if (s > 0) {
|
||||
if (s >= 35) {
|
||||
i = (i < 0? Int36.BIT35 : 0);
|
||||
bitsShifted = Int36.MAXPOS;
|
||||
} else {
|
||||
i = (i * Math.pow(2, s)) % Int36.BIT35;
|
||||
/*
|
||||
* bitsShifted must be set to the mask of all magnitude bits shifted out of
|
||||
* the original word. Using 8-bit signed words as an example, this table shows
|
||||
* the bitsShifted values that would correspond to shifting 1-7 bits left:
|
||||
*
|
||||
* shifts bitsShifted value calculation
|
||||
* ------ ----------- ------ -----------
|
||||
* 1 0b01000000 128-64 128-Math.pow(2, 7-1)
|
||||
* 2 0b01100000 128-32 128-Math.pow(2, 7-2)
|
||||
* 3 0b01110000 128-16 128-Math.pow(2, 7-3)
|
||||
* ...
|
||||
* 7 0b01111111 128-1 128-Math.pow(2, 7-7)
|
||||
*/
|
||||
bitsShifted = Int36.BIT35 - Math.pow(2, 35 - s);
|
||||
}
|
||||
if (w <= Int36.MAXPOS) {
|
||||
/*
|
||||
* 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.
|
||||
*/
|
||||
if (w + bitsShifted > Int36.MAXPOS) 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.
|
||||
*/
|
||||
if (w - bitsShifted <= Int36.MAXPOS) this.error |= Int36.ERROR.OVERFLOW;
|
||||
}
|
||||
} else {
|
||||
if (s <= -35) {
|
||||
i = (i < 0? -1 : 0);
|
||||
} else {
|
||||
i = Math.trunc(i / Math.pow(2, -s));
|
||||
}
|
||||
}
|
||||
w = (i < 0? i + Int36.BIT36 : i);
|
||||
this.value = w;
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Simulate opASHC()
|
||||
*/
|
||||
console.log("ASHC unimplemented");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* lsh(i36)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {Int36} i36 (18-bit value representing the number of bits to logically shift left (> 0) or right (< 0))
|
||||
*/
|
||||
lsh(i36)
|
||||
{
|
||||
this.lshNum(i36.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* lshNum(num)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {number} num (18-bit value representing the number of bits to logically shift left (> 0) or right (< 0))
|
||||
*/
|
||||
lshNum(num)
|
||||
{
|
||||
num = Int36.validate(num);
|
||||
this.error = Int36.ERROR.NONE;
|
||||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value (+/-255).
|
||||
*/
|
||||
var s = ((num << 14) >> 14) % 256;
|
||||
if (s) {
|
||||
if (this.extended == null) {
|
||||
var w = this.value;
|
||||
if (s > 0) {
|
||||
if (s >= 36) {
|
||||
w = 0;
|
||||
} else {
|
||||
w = (w * Math.pow(2, s)) % Int36.BIT36;
|
||||
}
|
||||
} else {
|
||||
if (s <= -36) {
|
||||
w = 0;
|
||||
} else {
|
||||
w = Math.trunc(w / Math.pow(2, -s));
|
||||
}
|
||||
}
|
||||
this.value = w;
|
||||
} else {
|
||||
var wRight = this.value;
|
||||
var wLeft = this.extended;
|
||||
if (s > 0) {
|
||||
if (s >= 36) {
|
||||
wRight = 0;
|
||||
if (s >= 72) {
|
||||
wLeft = 0;
|
||||
} else {
|
||||
wLeft = (wRight * Math.pow(2, s - 36)) % Int36.BIT36;
|
||||
}
|
||||
} else {
|
||||
wLeft = ((wLeft * Math.pow(2, s)) % Int36.BIT36) + Math.trunc(wRight / Math.pow(2, 36 - s));
|
||||
wRight = (wRight * Math.pow(2, s)) % Int36.BIT36;
|
||||
}
|
||||
} else {
|
||||
if (s <= -36) {
|
||||
wLeft = 0;
|
||||
if (s <= -72) {
|
||||
wRight = 0;
|
||||
} else {
|
||||
wRight = Math.trunc(wLeft / Math.pow(2, -s - 36));
|
||||
}
|
||||
} else {
|
||||
wRight = Math.trunc(wRight / Math.pow(2, -s)) + ((wLeft * Math.pow(2, 36 + s)) % Int36.BIT36);
|
||||
wLeft = Math.trunc(wLeft / Math.pow(2, -s));
|
||||
}
|
||||
}
|
||||
this.value = wRight;
|
||||
this.extended = wLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* rot(i36)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {Int36} i36 (18-bit value representing the number of bits to logically rotate left (> 0) or right (< 0))
|
||||
*/
|
||||
rot(i36)
|
||||
{
|
||||
this.rotNum(i36.value);
|
||||
}
|
||||
|
||||
/**
|
||||
* rotNum(num)
|
||||
*
|
||||
* @this {Int36}
|
||||
* @param {number} num (18-bit value representing the number of bits to logically rotate left (> 0) or right (< 0))
|
||||
*/
|
||||
rotNum(num)
|
||||
{
|
||||
var s;
|
||||
num = Int36.validate(num);
|
||||
this.error = Int36.ERROR.NONE;
|
||||
|
||||
if (this.extended == null) {
|
||||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value, modulo 36 (+/-35).
|
||||
*/
|
||||
s = ((num << 14) >> 14) % 36;
|
||||
if (s) {
|
||||
var w = this.value;
|
||||
/*
|
||||
* Note that a right rotation (s < 0) of s bits is equivalent to a left rotation (s > 0) of 36 + s bits.
|
||||
*/
|
||||
if (s < 0) s = 36 + s;
|
||||
w = ((w * Math.pow(2, s)) % Int36.BIT36) + Math.trunc(w / Math.pow(2, 36 - s));
|
||||
this.value = w;
|
||||
}
|
||||
}
|
||||
else {
|
||||
/*
|
||||
* Convert the unsigned 18-bit value in regEA to a signed 8-bit value, modulo 72 (+/-71).
|
||||
*/
|
||||
s = ((num << 14) >> 14) % 72;
|
||||
if (s) {
|
||||
var wRight = this.value;
|
||||
var wLeft = this.extended;
|
||||
var wLeftOrig = wLeft;
|
||||
/*
|
||||
* Note that a right rotation (s < 0) of s bits is equivalent to a left rotation (s > 0) of 72 + s bits.
|
||||
*/
|
||||
if (s < 0) s = 72 + s;
|
||||
if (s < 36) {
|
||||
wLeft = ((wLeft * Math.pow(2, s)) % Int36.BIT36) + Math.trunc(wRight / Math.pow(2, 36 - s));
|
||||
wRight = ((wRight * Math.pow(2, s)) % Int36.BIT36) + Math.trunc(wLeftOrig / Math.pow(2, 36 - s));
|
||||
} else {
|
||||
wLeft = ((wRight * Math.pow(2, s - 36)) % Int36.BIT36) + Math.trunc(wLeft / Math.pow(2, 72 - s));
|
||||
wRight = ((wLeftOrig * Math.pow(2, s - 36)) % Int36.BIT36) + Math.trunc(wRight / Math.pow(2, 72 - s));
|
||||
}
|
||||
this.value = wRight;
|
||||
this.extended = wLeft;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* extend()
|
||||
*
|
||||
|
|
@ -734,7 +968,6 @@ class Int36 {
|
|||
if (this.value) {
|
||||
this.value = Int36.BIT36 - this.value;
|
||||
}
|
||||
this.error = Int36.ERROR.NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -865,14 +1098,15 @@ class Int36 {
|
|||
}
|
||||
|
||||
/**
|
||||
* validate(num)
|
||||
* validate(num, bits)
|
||||
*
|
||||
* This ensures that any incoming (external) 36-bit values conform to our internal requirements.
|
||||
*
|
||||
* @param {number} num
|
||||
* @param {number} [bits] (an alternative number of bits to restrict the value to)
|
||||
* @return {number}
|
||||
*/
|
||||
static validate(num)
|
||||
static validate(num, bits = 36)
|
||||
{
|
||||
/*
|
||||
* Although it's expected that most callers will supply unsigned 36-bit values, we're nice about
|
||||
|
|
@ -882,7 +1116,7 @@ class Int36 {
|
|||
if (num < 0 && num >= Int36.MINNEG) {
|
||||
num += Int36.BIT36;
|
||||
}
|
||||
var value = Math.trunc(Math.abs(num)) % Int36.BIT36;
|
||||
var value = Math.trunc(Math.abs(num)) % Math.pow(2, bits);
|
||||
if (DEBUG && num !== value) {
|
||||
console.log("Int36.validate(" + num + " out of range, truncated to " + value + ")");
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in a new issue