diff --git a/apps/pdp10/tests/optest-v01.json b/apps/pdp10/tests/optest-v01.json index a485b0ab5..c4f307219 100644 --- a/apps/pdp10/tests/optest-v01.json +++ b/apps/pdp10/tests/optest-v01.json @@ -47,5 +47,7 @@ 0o265040000123, // 000122: JSP 1,.+1 0o270000000000, // 000123: ADD 0,0 0o265040000125, // 000124: JSP 1,.+1 + 0o255740000126, // 000125: JFCL 17,126 + 0o224100000002, // 000126: MUL 2,2 ] } diff --git a/apps/pdp10/tests/optest-v01.simh b/apps/pdp10/tests/optest-v01.simh index a5700bc71..f6adb463b 100644 --- a/apps/pdp10/tests/optest-v01.simh +++ b/apps/pdp10/tests/optest-v01.simh @@ -1,24 +1,26 @@ -dep 100 505040111111 -dep 101 541040444444 -dep 102 544100000001 -dep 103 504100000001 -dep 104 570140000001 -dep 105 530200000002 -dep 106 544240000003 -dep 107 504300000004 -dep 110 573000000005 -dep 111 533000000006 -dep 112 541000000010 -dep 113 251000000017 -dep 114 577040000002 -dep 115 261240000011 -dep 116 262240000005 -dep 117 515040170600 -dep 120 135100000001 -dep 121 270000000000 -dep 122 265040000123 -dep 123 270000000000 -dep 124 265040000125 +dep 100 HRLI 1,111111 +dep 101 HRRI 1,444444 +dep 102 HLR 2,1 +dep 103 HRL 2,1 +dep 104 HRRE 3,1 +dep 105 HLLE 4,2 +dep 106 HLR 5,3 +dep 107 HRL 6,4 +dep 110 HRRES 0,5 +dep 111 HLLES 0,6 +dep 112 HRRI 0,10 +dep 113 BLT 0,17 +dep 114 HLRES 1,2 +dep 115 PUSH 5,11 +dep 116 POP 5,5 +dep 117 HRLZI 1,170600 +dep 120 LDB 2,1 +dep 121 ADD 0,0 +dep 122 JSP 1,123 +dep 123 ADD 0,0 +dep 124 JSP 1,125 +dep 125 JFCL 17,126 +dep 126 MUL 2,2 ex -m 100-116 dep pc 100 step 11 diff --git a/modules/pdp10/lib/cpuops.js b/modules/pdp10/lib/cpuops.js index a20646350..1499599c4 100644 --- a/modules/pdp10/lib/cpuops.js +++ b/modules/pdp10/lib/cpuops.js @@ -33,6 +33,92 @@ if (NODE) { var PDP10 = require("./defines"); } +/* + From the "PDP-10 System Reference Manual", May 1968, p. 1-4: + + 1.1 NUMBER SYSTEM + + The program can interpret a data word as a 36-digit, unsigned binary number, or the left and right + halves of a word can be taken as separate 18-bit numbers. The PDP-10 repertoire includes instructions + that effectively add or subtract one from both halves of a word, so the right half can be used for + address modification when the word is addressed as an index register, while the left half is used to + keep a control count. + + The standard arithmetic instructions in the PDP-10 use twos complement, fixed point conventions to do + binary arithmetic. In a word used as a number, bit 0 (the leftmost bit) represents the sign, 0 for positive, + 1 for negative. In a positive number the remaining 35 bits are the magnitude in ordinary binary notation. + The negative of a number is obtained by taking its twos complement. If x is an n-digit binary number, its + twos complement is 2^n - x, and its ones complement is (2^n - 1) - x, or equivalently (2^n - x) - 1. + + Subtracting a number from 2^n - 1 (ie, from all 1s) is equivalent to performing the logical complement, + ie changing all 0s to 1s and all 1s to 0s. Therefore, to form the twos complement one takes the logical + complement (usually referred to merely as the complement) of the entire word including the sign, and adds + 1 to the result. In a negative number the sign bit is 1, and the remaining bits are the twos complement + of the magnitude. + + Zero is represented by a word containing all 0s. Complementing this number produces all 1s, and adding + 1 to that produces all 0s again. Hence there is only one zero representation and its sign is positive. + Since the numbers are symmetrical in magnitude about a single zero representation, all even numbers both + positive and negative end in 0, all odd numbers in 1 (a number all 1s represents -1). But since there are + the same number of positive and negative numbers and zero is positive, there is one more negative number + than there are nonzero positive numbers. This is the most negative number and it cannot be produced by + negating any positive number (its octal representation is 400000 000000 and its magnitude is one greater + than the largest positive number). + + If ones complements were used for negatives one could read a negative number by attaching significance + to the as instead of the 1s. In twos complement notation each negative number is one greater than the + complement of the positive number of the same magnitude, so one can read a negative number by attaching + significance to the rightmost 1 and attaching significance to the 0s at the left of it (the negative number + of largest magnitude has a 1 in only the sign position). In a negative integer, 1s may be discarded at the + left, just as leading 0s may be dropped in a positive integer. In a negative fraction, 0s may be discarded + at the right. So long as only 0s are discarded, the number remains in twos complement form because it + still has a 1 that possesses significance; but if a portion including the rightmost 1 is discarded, the + remaining part of the fraction is now a ones complement. + + The computer does not keep track of a binary point - the programmer must adopt a point convention and shift + the magnitude of the result to conform to the convention used. Two common conventions are to regard a number + as an integer (binary point at the right) or as a proper fraction (binary point at the left); in these two + cases the range of numbers represented by a single word is -2^35 to 2^35 - 1, or -1 to 1 - 2^35. Since + multiplication and division make use of double length numbers, there are special instructions for performing + these operations with integral operands. + + SIDEBAR: Multiplication produces a double length product, and the programmer must remember that discarding + the low order part of a double length negative leaves the high order part in correct twos complement form + only if the low order part is null. + + ... + + 2.5 FIXED POINT ARITHMETIC + + For fixed point arithmetic the PDP-10 has instructions for arithmetic shifting (which is essentially + multiplication by a power of 2) as well as for performing addition, subtraction, multiplication and division + of numbers in fixed point format [§ 1.1]. In such numbers the position of the binary point is arbitrary + (the programmer may adopt any point convention). The add and subtract instructions involve only single length + numbers, whereas multiply supplies a double length product, and divide uses a double length dividend. The high + and low order words respectively of a double length fixed point number are in accumulators A and A+1 (mod 20), + where the magnitude is the 70-bit string in bits 1-35 of the two words and the signs of the two are identical. + There are also integer multiply and divide instructions that involve only single length numbers and are + especially suited for handling smaller integers, particularly those of eighteen bits or less such as addresses + (of course they can be used for small fractions as well provided the programmer keeps track of the binary point). + For convenience in the following, all operands are assumed to be integers (binary point at the right). + + The processor has four flags, Overflow, Carry 0, Carry 1 and No Divide, that indicate when the magnitude of a + number is or would be larger than can be accommodated. Carry 0 and Carry 1 actually detect carries out of bits + 0 and 1 in certain instructions that employ fixed point arithmetic operations: the add and subtract instructions + treated here, the move instructions that produce the negative or magnitude of the word moved [§ 2.2], and the + arithmetic test instructions that increment or decrement the test word [§ 2.7]. In these instructions an + incorrect result is indicated - and the Overflow flag set - if the carries are different, ie if there is a carry + into the sign but not out of it, or vice versa. The Overflow flag is also set by No Divide being set, which + means the processor has failed to perform a division because the magnitude of the dividend is greater than or + equal to that of the divisor, or in integer divide, simply that the divisor is zero. In other overflow cases + only Overflow itself is set: these include too large a product in multiplication, and loss of significant bits + in left arithmetic shifting. + + SIDEBAR: Overflow is determined directly from the carries, not from the carry flags, as their states may reflect + events in previous instructions. + + */ + /** * opKA10(op) * @@ -5315,6 +5401,7 @@ PDP10.doADD = function(dst, src) * only possible out-of-bounds value is a result >= WORD_LIMIT, which the mod cures. */ var res = (dst + src) % PDP10.WORD_LIMIT; + //noinspection JSUnresolvedFunction PDP10.setAddFlags.call(this, dst, src, res); return res; }; @@ -5423,7 +5510,7 @@ PDP10.doIOR = function(dst, src) * @this {CPUStatePDP10} * @param {number} dst (36-bit value) * @param {number} src (36-bit value) - * @return {number} (dst * src) (the low 36 bits of the result; the high 36 bits are stored in regExt) + * @return {number} (dst * src) (the high 36 bits of the result; the low 36 bits are stored in regExt) */ PDP10.doMUL = function(dst, src) { @@ -5491,8 +5578,8 @@ PDP10.doMUL = function(dst, src) this.regPS |= PDP10.PSFLAG.OVFL; } - this.regExt = ext; - return res; + this.regExt = res; + return ext; }; /** @@ -5545,6 +5632,7 @@ PDP10.doSUB = function(dst, src) * We can leverage setAddFlags() by treating the subtraction as addition; * since res = dst - src, it is also true that dst = res + src. */ + //noinspection JSUnresolvedFunction PDP10.setAddFlags.call(this, res, src, dst); return res; }; diff --git a/modules/shared/lib/int36.js b/modules/shared/lib/int36.js index ace715e25..d3d625f76 100644 --- a/modules/shared/lib/int36.js +++ b/modules/shared/lib/int36.js @@ -60,7 +60,6 @@ negating any positive number (its octal representation is 400000 000000 and its magnitude is one greater than the largest positive number). - If ones complements were used for negatives one could read a negative number by attaching significance to the as instead of the 1s. In twos complement notation each negative number is one greater than the complement of the positive number of the same magnitude, so one can read a negative number by attaching @@ -138,10 +137,6 @@ * be set to null by any other operation. * * The 'error' property records any error(s) from the last operation. - * - * NOTE: What we call extended Int36 values DEC refers to as "double length numbers", and they refer - * to the 'extended' portion as the "low order part" and the 'value' portion as the "high order part", - * presumably because they number the left-most significant bit 0. */ class Int36 { /** @@ -1072,21 +1067,6 @@ class Int36 { * * Unsets extended if it's superfluous; opposite of extend(). * - * It's worth noting DEC's SIDEBAR comment (from above): - * - * Multiplication produces a double length product, and the programmer must remember that discarding - * the low order part ['extended'] of a double length negative leaves the high order part ['value'] in - * correct twos complement form only if the low order part ['extended'] is null [zero]. - * - * is not applicable when we're using 71-bit magnitude values; for example, when value is MIN_NEG36 and - * extended is ZERO, we interpret that extended value as 34,359,738,368; we cannot simply eliminate the - * extended portion, otherwise value would be interpreted as -34,359,738,368. - * - * DEC can say that because each of the words in a PDP-10 double-length product contains its own sign bit, - * resulting in only 70 bits of magnitude. However, we don't store our extended (double-length) results that - * way, unless setMagnitude(70) has been called, so be aware of these mismatches in both terminology and - * format when converting an Int36 to/from PDP-10 registers/memory. - * * @this {Int36} */ reduce() diff --git a/versions/pdpjs/1.34.2/pdp10-dbg.js b/versions/pdpjs/1.34.2/pdp10-dbg.js index 5a8e79319..1561c91ef 100644 --- a/versions/pdpjs/1.34.2/pdp10-dbg.js +++ b/versions/pdpjs/1.34.2/pdp10-dbg.js @@ -104,7 +104,7 @@ function de(a,b){var c=this.g(this.f),d=this.g(b);this.i(b,ee(a,d,c)+(c-(c&A)))} function je(a,b){var c=this.f*E,d=this.g(b);this.i(b,ee(a,d,c)+c)}function ke(a,b){b=(this.g(b)&A)*E;var c=this.g(this.f);this.i(this.f,ee(a,c,b)+b)}function le(a,b){var c=this.g(this.f),d=(c&A)*E,c=ee(a,c,d)+d;this.i(this.f,c);b&&this.i(b,c)}function me(a,b){var c=this.g(this.f)&A,d=this.g(b);this.i(b,ne(a,d,c)+c)}function oe(a,b){var c=this.g(b);this.i(b,ne(a,c,this.f)+this.f)}function pe(a,b){b=this.g(b)&A;var c=this.g(this.f);this.i(this.f,ne(a,c,b)+b)} function qe(a,b){var c=this.g(this.f),d=c;if(a&=384)switch(d&=A,a){case 256:d+=A*E;break;case 384:d+=c>Vb?A*E:0}c=d;this.i(this.f,c);b&&this.i(b,c)}function re(a,b){var c=this.g(this.f)/E|0,d=this.g(b);this.i(b,ne(a,d,c)+c)}function se(a,b){var c=this.g(b);this.i(b,ne(a,c,0))}function te(a,b){b=this.g(b)/E|0;var c=this.g(this.f);this.i(this.f,ne(a,c,b)+b)}function ue(a,b){var c=this.g(this.f),d=c/E|0,c=ne(a,c,d)+d;this.i(this.f,c);b&&this.i(b,c)}function S(a){ve[a&7].call(this,a,a>>3&127)} function we(){}function Q(a){this.j("undefined opcode: "+wa(a));wd(this,-1);this.V()}function xe(a){a>I&&(a!=Wb?a=Xb-a:this.u|=163840);return a}function ye(a,b){var c=(a+b)%H;ze.call(this,a,b,c);return c}function U(a,b){return((a/J|0)&(b/J|0))*J+((a&b)>>>0)}function Ae(a,b){return((a/J|0)^(b/J|0))*J+((a^b)>>>0)}function Be(a,b){return(~((a/J|0)^(b/J|0))&15)*J+(~(a^b)>>>0)}function V(a,b){return(a/J|0|b/J|0)*J+((a|b)>>>0)} -function Ce(a,b){var c=b;b=!1;var d;a>I&&(a=H-a,b=!b);c>I&&(c=H-c,b=!b);if(aI&&(a=H-a,b=!b);c>I&&(c=H-c,b=!b);if(ac&&(c+=H);ze.call(this,c,b,a);return c}function ze(a,b,c){a=Math.trunc(a/Yb);b=Math.trunc(b/Yb);c=Math.trunc(c/Yb);var d=a^(a^b)&(b^c);this.u=this.u|(d&2?65536:0)|(d&1?32768:0)|((a^c)&(b^c)&2?131072:0)}function ne(a,b,c){switch(a&384){case 0:b-=b&A;break;case 128:b=0;break;case 256:b=A*E;break;case 384:b=c>Vb?A*E:0}return b}function ee(a,b,c){switch(a&384){case 0:b&=A;break;case 128:b=0;break;case 256:b=A;break;case 384:b=c>I?A:0}return b} var zd=[R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,R,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,Q,function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},Ad,function(a,b){0>this.F&&Ad.call(this);Bd.call(this,0,b)},Bd,function(a,b){0>this.F&&Ad.call(this);Xd.call(this,0,b)},Xd,function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)}, function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)},function(a){this.b(a)}, diff --git a/versions/pdpjs/1.34.2/pdp10.js b/versions/pdpjs/1.34.2/pdp10.js index 9c501b284..c590fd0bb 100644 --- a/versions/pdpjs/1.34.2/pdp10.js +++ b/versions/pdpjs/1.34.2/pdp10.js @@ -100,7 +100,7 @@ function uc(a,b){var c=this.c(this.b),d=c;if(a&=384)switch(d-=d&I,a){case 256:d+ function zc(a,b){var c=this.c(this.b)&I,d=this.c(b);this.f(b,Ac(a,d,c)+c)}function Bc(a,b){var c=this.c(b);this.f(b,Ac(a,c,this.b)+this.b)}function Cc(a,b){b=this.c(b)&I;var c=this.c(this.b);this.f(this.b,Ac(a,c,b)+b)}function Dc(a,b){var c=this.c(this.b),d=c;if(a&=384)switch(d&=I,a){case 256:d+=I*J;break;case 384:d+=c>Za?I*J:0}c=d;this.f(this.b,c);b&&this.f(b,c)}function Ec(a,b){var c=this.c(this.b)/J|0,d=this.c(b);this.f(b,Ac(a,d,c)+c)}function Fc(a,b){var c=this.c(b);this.f(b,Ac(a,c,0))} function Gc(a,b){b=this.c(b)/J|0;var c=this.c(this.b);this.f(this.b,Ac(a,c,b)+b)}function Hc(a,b){var c=this.c(this.b),d=c/J|0,c=Ac(a,c,d)+d;this.f(this.b,c);b&&this.f(b,c)}function X(a){Ic[a&7].call(this,a,a>>3&127)}function Jc(){}function U(a){this.M("undefined opcode: "+la(a));gc(this);R(this)}function Kc(a){a>O&&(a!=$a?a=ab-a:this.g|=163840);return a}function Lc(a,b){var c=(a+b)%N;Mc.call(this,a,b,c);return c}function Y(a,b){return((a/P|0)&(b/P|0))*P+((a&b)>>>0)} function Nc(a,b){return((a/P|0)^(b/P|0))*P+((a^b)>>>0)}function Oc(a,b){return(~((a/P|0)^(b/P|0))&15)*P+(~(a^b)>>>0)}function Z(a,b){return(a/P|0|b/P|0)*P+((a|b)>>>0)} -function Pc(a,b){var c=b;b=!1;var d;a>O&&(a=N-a,b=!b);c>O&&(c=N-c,b=!b);if(aO&&(a=N-a,b=!b);c>O&&(c=N-c,b=!b);if(ac&&(c+=N);Mc.call(this,c,b,a);return c}function Mc(a,b,c){a=Math.trunc(a/bb);b=Math.trunc(b/bb);c=Math.trunc(c/bb);var d=a^(a^b)&(b^c);this.g=this.g|(d&2?65536:0)|(d&1?32768:0)|((a^c)&(b^c)&2?131072:0)}function Ac(a,b,c){switch(a&384){case 0:b-=b&I;break;case 128:b=0;break;case 256:b=I*J;break;case 384:b=c>Za?I*J:0}return b}function W(a,b,c){switch(a&384){case 0:b&=I;break;case 128:b=0;break;case 256:b=I;break;case 384:b=c>O?I:0}return b} var hc=[V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,V,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,U,function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},ic,function(a,b){0>this.o&&ic.call(this);jc.call(this,0,b)},jc,function(a,b){0>this.o&&ic.call(this);kc.call(this,0,b)},kc,function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)}, function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},function(a){this.a(a)},