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;
|
||||
|
||||
/*
|
||||
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()
|
||||
*
|
||||
|
|
|
|||
Loading…
Reference in a new issue