Added the reduce() function, to reduce 72-bit extended values to 36-bit values, magnitude permitting
This commit is contained in:
parent
689adca2a7
commit
cff039f6a5
1 changed files with 129 additions and 9 deletions
|
|
@ -30,6 +30,93 @@
|
||||||
|
|
||||||
var DEBUG = true;
|
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
|
* @class Int36
|
||||||
* @property {number} value
|
* @property {number} value
|
||||||
|
|
@ -37,17 +124,21 @@ var DEBUG = true;
|
||||||
* @property {number|null} remainder
|
* @property {number|null} remainder
|
||||||
* @property {number} error
|
* @property {number} error
|
||||||
*
|
*
|
||||||
* The 'value' property stores the 36-bit value as an unsigned integer. When the value
|
* The 'value' property stores the 36-bit value as an unsigned integer. When the value should be
|
||||||
* should be interpreted as a signed quantity, subtract BIT36 whenever value > MAXPOS.
|
* interpreted as a signed quantity, subtract BIT36 whenever value > MAXPOS.
|
||||||
*
|
*
|
||||||
* The 'extended' property stores an additional 36 bits of data from a multiplication;
|
* The 'extended' property stores an additional 36 bits of data from a multiplication, and can also
|
||||||
* it must also be set prior to a division. Internally, it will be set to null whenever the
|
* provide an additional 36 bits of data to a division. Internally, it should be null whenever the
|
||||||
* current value is not extended.
|
* value is not extended.
|
||||||
*
|
*
|
||||||
* The 'remainder' property stores the remainder from the last division. You should assume it
|
* The 'remainder' property stores the remainder from the last division. You should assume it will
|
||||||
* will be set to null by any other operation.
|
* be set to null by any other operation.
|
||||||
*
|
*
|
||||||
* The 'error' property records any error(s) from the last 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 {
|
class Int36 {
|
||||||
|
|
@ -389,7 +480,7 @@ class Int36 {
|
||||||
* mulExtended(value)
|
* mulExtended(value)
|
||||||
*
|
*
|
||||||
* To support 72-bit results, we perform the multiplication process as you would "by hand",
|
* 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
|
* number (base 2^18). Each individual multiplication of these 18-bit "digits" will produce
|
||||||
* a result within 2^36, well within JavaScript integer accuracy.
|
* a result within 2^36, well within JavaScript integer accuracy.
|
||||||
*
|
*
|
||||||
|
|
@ -524,6 +615,8 @@ class Int36 {
|
||||||
|
|
||||||
if (fNegQ) this.negate();
|
if (fNegQ) this.negate();
|
||||||
|
|
||||||
|
this.reduce();
|
||||||
|
|
||||||
if (fNegR && this.remainder) {
|
if (fNegR && this.remainder) {
|
||||||
this.remainder = Int36.BIT36 - this.remainder;
|
this.remainder = Int36.BIT36 - this.remainder;
|
||||||
}
|
}
|
||||||
|
|
@ -532,7 +625,7 @@ class Int36 {
|
||||||
/**
|
/**
|
||||||
* extend()
|
* 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()
|
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()
|
* isNegative()
|
||||||
*
|
*
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue