From cff039f6a5c139aa436bda84f62d4219f7432ced Mon Sep 17 00:00:00 2001 From: Jeff Parsons Date: Fri, 17 Feb 2017 13:31:17 -0800 Subject: [PATCH] Added the reduce() function, to reduce 72-bit extended values to 36-bit values, magnitude permitting --- modules/shared/lib/int36.js | 138 +++++++++++++++++++++++++++++++++--- 1 file changed, 129 insertions(+), 9 deletions(-) diff --git a/modules/shared/lib/int36.js b/modules/shared/lib/int36.js index 286970646..7351c45f8 100644 --- a/modules/shared/lib/int36.js +++ b/modules/shared/lib/int36.js @@ -30,6 +30,93 @@ var DEBUG = true; +/* + 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. + */ + + /** * @class Int36 * @property {number} value @@ -37,17 +124,21 @@ var DEBUG = true; * @property {number|null} remainder * @property {number} error * - * 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 > MAXPOS. + * 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 > MAXPOS. * - * The 'extended' property stores an additional 36 bits of data from a multiplication; - * it must also be set prior to a division. Internally, it will be set to null whenever the - * current value is not extended. + * 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 + * value is not extended. * - * The 'remainder' property stores the remainder from the last division. You should assume it - * will be set to null by any other operation. + * The 'remainder' property stores the remainder from the last division. You should assume it will + * 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 { @@ -389,7 +480,7 @@ class Int36 { * mulExtended(value) * * To support 72-bit results, we perform the multiplication process as you would "by hand", - * treating the operands to be multiplied as two 2-digit numbers, where each digit is an 18-bit + * treating the operands to be multiplied as two 2-digit numbers, where each "digit" is an 18-bit * number (base 2^18). Each individual multiplication of these 18-bit "digits" will produce * a result within 2^36, well within JavaScript integer accuracy. * @@ -524,6 +615,8 @@ class Int36 { if (fNegQ) this.negate(); + this.reduce(); + if (fNegR && this.remainder) { this.remainder = Int36.BIT36 - this.remainder; } @@ -532,7 +625,7 @@ class Int36 { /** * extend() * - * Set the extended field to match the sign of the value (if not already set). + * Sets extended to match the sign of value (if not already set). */ extend() { @@ -541,6 +634,33 @@ class Int36 { } } + /** + * reduce() + * + * 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. + * + * is not entirely applicable to us. For one thing, when value is MINNEG 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, so be aware of these mismatches in both terminology and format when converting an Int36 to/from PDP-10 + * registers/memory. + */ + reduce() + { + if (this.extended == 0 && this.value <= Int36.MAXPOS || this.extended == Int36.MAXVAL && this.value > Int36.MAXPOS) { + this.extended = null; + } + } + /** * isNegative() *