/* hp3000_cpu_fp.c: HP 3000 floating-point arithmetic simulator | |
Copyright (c) 2016, J. David Bryan | |
Permission is hereby granted, free of charge, to any person obtaining a copy | |
of this software and associated documentation files (the "Software"), to deal | |
in the Software without restriction, including without limitation the rights | |
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
copies of the Software, and to permit persons to whom the Software is | |
furnished to do so, subject to the following conditions: | |
The above copyright notice and this permission notice shall be included in | |
all copies or substantial portions of the Software. | |
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE | |
AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN | |
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION | |
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
Except as contained in this notice, the name of the author shall not be used | |
in advertising or otherwise to promote the sale, use or other dealings in | |
this Software without prior written authorization from the author. | |
03-Feb-16 JDB First release version | |
25-Aug-15 JDB Fixed FSUB zero subtrahend bug (from Norwin Malmberg) | |
01-Apr-15 JDB Passes the floating point tests in the CPU diagnostic (D420A1) | |
29-Mar-15 JDB Created | |
References: | |
- HP 3000 Series II System Microprogram Listing | |
(30000-90023, August 1976) | |
- HP 3000 Series II/III System Reference Manual | |
(30000-90020, July 1978) | |
- Machine Instruction Set Reference Manual | |
(30000-90022, June 1984) | |
This module implements multiple-precision floating-point operations to | |
support the HP 3000 CPU instruction set. It employs 64-bit integer | |
arithmetic for speed and simplicity of implementation. The host compiler | |
must support 64-bit integers. | |
HP 3000 computers use a proprietary floating-point format. All 3000s | |
support two-word "single-precision" floating-point arithmetic as standard | |
equipment. The original HP 3000 CX and Series I CPUs support three-word | |
"extended-precision" floating-point arithmetic when the optional HP 30011A | |
Extended Instruction Set microcode was installed. The Series II and later | |
machines replace the three-word instructions with four-word "double- | |
precision" floating-point arithmetic and include the EIS as part of the | |
standard equipment. | |
Floating-point numbers have this format: | |
0 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| S | exponent biased by +256 | positive mantissa | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| positive mantissa | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| positive mantissa | (extended) | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| positive mantissa | (double) | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
That is, the three- and four-word formats merely extended the mantissa with | |
no change to the exponent range. | |
The mantissa is represented in sign-magnitude format. The mantissa is always | |
positive, with an assumed "1" to the left of the MSB, and the sign bit is set | |
for negative values. The exponent is in "excess-256" format, i.e., | |
represented as an unsigned value biased by +256, giving an unbiased range of | |
-256 to +255. The binary point is assumed to be between the leading "1" and | |
the MSB, so a zero value must be handled as a special case of all bits equal | |
to zero, which otherwise would represent the value +1.0 * 2 ** -256. | |
Normalization shifts the mantissa left and decrements the exponent until a | |
"1" bit appears in bit 9. | |
The use of sign-magnitude format means that floating-point negation merely | |
complements the sign bit, and floating-point comparison simply checks the | |
signs and, if they are the same, then applies an integer comparison to the | |
packed values. However, it also implies the existence of a "negative zero" | |
value, represented by all zeros except for the sign bit. This value is | |
undefined; if a negative zero is supplied as an operand to one of the | |
arithmetic routines, it is treated as positive zero. Negative zero is never | |
returned even if, e.g., it is supplied as the dividend or multiplier. | |
This implementation provides add, subtract, multiply, divide, float, and fix | |
operations on two-, three-, and four-word floating point operands. The | |
routines are called via a common floating-point executor ("fp_exec") by | |
supplying the operation to be performed and the operand(s) on which to act. | |
An operand contains the packed (i.e., in-memory) representation and the | |
precision of the value. The returned value includes the packed | |
representation and the precision, along with a value that indicates whether | |
or not the operation resulted in an arithmetic trap. It is the | |
responsibility of the caller to take the trap if it is indicated. | |
*/ | |
#include "hp3000_defs.h" | |
#include "hp3000_cpu.h" | |
#include "hp3000_cpu_fp.h" | |
/* Program constants */ | |
#define EXPONENT_BIAS 256 /* the exponent is biased by +256 */ | |
#define MIN_EXPONENT -256 /* the smallest representable exponent */ | |
#define MAX_EXPONENT +255 /* the largest representable exponent */ | |
#define EXPONENT_MASK 0077700 /* the mask to isolate the exponent in the first word */ | |
#define MANTISSA_MASK 0000077 /* the mask to isolate the mantissa in the first word */ | |
#define EXPONENT_SHIFT 6 /* the exponent alignment shift */ | |
#define MANTISSA_SHIFT 0 /* the mantissa alignment shift */ | |
#define UNPACKED_BITS 54 /* the number of significant bits in the unpacked mantissa */ | |
#define IMPLIED_BIT ((t_uint64) 1 << UNPACKED_BITS) /* the implied MSB in the mantissa */ | |
#define CARRY_BIT ((t_uint64) 1 << UNPACKED_BITS + 1) /* the carry from the MSB in the mantissa */ | |
#define DELTA_ALIGNMENT (D64_WIDTH - UNPACKED_BITS) /* net shift to align the binary point */ | |
/* Floating-point accessors */ | |
#define MANTISSA(w) ((t_uint64) (((w) & MANTISSA_MASK) >> MANTISSA_SHIFT)) | |
#define EXPONENT(w) ((int32) (((w) & EXPONENT_MASK) >> EXPONENT_SHIFT)) | |
#define TO_EXPONENT(w) ((w) + EXPONENT_BIAS << EXPONENT_SHIFT & EXPONENT_MASK) | |
#define DENORMALIZED(m) (((m) & IMPLIED_BIT) == 0) | |
/* Floating-point unpacked representation */ | |
typedef struct { | |
t_uint64 mantissa; /* the unsigned mantissa */ | |
int32 exponent; /* the unbiased exponent */ | |
t_bool negative; /* TRUE if the mantissa is negative */ | |
FP_OPSIZE precision; /* the precision currently expressed by the value */ | |
} FPU; | |
static const FPU zero = { 0, 0, FALSE, fp_f }; /* an unpacked zero value */ | |
/* Floating-point descriptors */ | |
static const int32 mantissa_bits [] = { /* the number of mantissa bits, indexed by FP_OPSIZE */ | |
16 - 1, /* in_s bits available - sign bit */ | |
32 - 1, /* in_d bits available - sign bit */ | |
22 + 1, /* fp_f bits explicit + bits implied */ | |
38 + 1, /* fp_x bits explicit + bits implied */ | |
54 + 1 /* fp_e bits explicit + bits implied */ | |
}; | |
static const t_uint64 mantissa_mask [] = { /* the mask to the mantissa bits, indexed by FP_OPSIZE */ | |
((t_uint64) 1 << 16) - 1 << 0, /* in_s 16-bit mantissa */ | |
((t_uint64) 1 << 32) - 1 << 0, /* in_d 32-bit mantissa */ | |
((t_uint64) 1 << 22) - 1 << 32, /* fp_f 22-bit mantissa */ | |
((t_uint64) 1 << 38) - 1 << 16, /* fp_x 38-bit mantissa */ | |
((t_uint64) 1 << 54) - 1 << 0 /* fp_e 54-bit mantissa */ | |
}; | |
static const t_uint64 half_lsb [] = { /* half of the LSB for rounding, indexed by FP_OPSIZE */ | |
0, /* in_s not used */ | |
0, /* in_d not used */ | |
(t_uint64) 1 << 31, /* fp_f word 2 LSB */ | |
(t_uint64) 1 << 15, /* fp_x word 3 LSB */ | |
(t_uint64) 1 << 0 /* fp_e word 4 LSB */ | |
}; | |
/* Floating-point local utility routine declarations */ | |
static FPU unpack (FP_OPND packed); | |
static FP_OPND norm_round_pack (FPU unpacked); | |
static TRAP_CLASS add (FPU *sum, FPU augend, FPU addend); | |
static TRAP_CLASS subtract (FPU *difference, FPU minuend, FPU subtrahend); | |
static TRAP_CLASS multiply (FPU *product, FPU multiplicand, FPU multiplier); | |
static TRAP_CLASS divide (FPU *quotient, FPU dividend, FPU divisor); | |
static TRAP_CLASS ffloat (FPU *real, FPU integer); | |
static TRAP_CLASS fix (FPU *integer, FPU real, t_bool round); | |
/* Floating-point global routines */ | |
/* Execute a floating-point operation. | |
The operator specified by the "operation" parameter is applied to the | |
"left_op" and to the "right_op" (if applicable), and the result is returned. | |
The "precision" fields of the operands must be set to the representations | |
stored within before calling this routine. | |
On entry, the left and right (if needed) operands are unpacked, and the | |
executor for the specified operation is called. The result is normalized, | |
rounded, and packed. Any trap condition detected by the operator routine is | |
set into the packed operand, unless the normalize/round/pack routine detected | |
its own trap condition. Finally, the packed result is returned. | |
*/ | |
FP_OPND fp_exec (FP_OPR operator, FP_OPND left_op, FP_OPND right_op) | |
{ | |
FPU left, right, result; | |
FP_OPND result_op; | |
TRAP_CLASS trap; | |
left = unpack (left_op); /* unpack the left-hand operand */ | |
if (operator <= fp_div) /* if the operator requires two operands */ | |
right = unpack (right_op); /* then unpack the right-hand operation */ | |
switch (operator) { /* dispatch the floating-point operation */ | |
case fp_add: | |
trap = add (&result, left, right); /* add the two operands */ | |
break; | |
case fp_sub: | |
trap = subtract (&result, left, right); /* subtract the two operands */ | |
break; | |
case fp_mpy: | |
trap = multiply (&result, left, right); /* multiply the two operands */ | |
break; | |
case fp_div: | |
trap = divide (&result, left, right); /* divide the two operands */ | |
break; | |
case fp_flt: | |
trap = ffloat (&result, left); /* convert the integer operand to a floating-point number */ | |
break; | |
case fp_fixr: | |
trap = fix (&result, left, TRUE); /* round the floating-point operand to an integer */ | |
break; | |
case fp_fixt: | |
trap = fix (&result, left, FALSE); /* truncate the floating-point operand to an integer */ | |
break; | |
default: | |
result = zero; /* if an unimplemented operation is requested */ | |
result.precision = left.precision; /* then return a zero of the appropriate precision */ | |
trap = trap_Unimplemented; /* and trap for an Unimplemented Instruction */ | |
break; | |
} /* all cases are handled */ | |
result_op = norm_round_pack (result); /* normalize, round, and pack the result */ | |
if (result_op.trap == trap_None) /* if the pack operation succeeded */ | |
result_op.trap = trap; /* then set any arithmetic trap returned by the operation */ | |
return result_op; /* return the result */ | |
} | |
/* Floating-point local utility routine declarations */ | |
/* Unpack a packed operand. | |
A packed integer or floating-point value is split into separate mantissa and | |
exponent variables. The multiple words of the mantissa are concatenated into | |
a single 64-bit unsigned value, and the exponent is shifted with recovery of | |
the sign. | |
The absolute values of single and double integers are unpacked into the | |
mantissas and preshifted by 32 or 16 bits, respectively, to reduce the | |
shifting needed for normalization. The resulting value is unnormalized, but | |
the exponent is set correctly to reflect the preshift. The precisions for | |
unpacked integers are set to single-precision but are valid for extended- and | |
double-precision, as the unpacked representations are identical. | |
The packed floating-point representation contains an implied "1" bit | |
preceding the binary point in the mantissa, except if the floating-point | |
value is zero. The unpacked mantissa includes the implied bit. The bias is | |
removed from the exponent, producing a signed value, and the sign of the | |
mantissa is set from the sign of the packed value. | |
A packed zero value is represented by all words set to zero. In the unpacked | |
representation, the mantissa is zero, the exponent is the minimum value | |
(-256), and the sign is always positive (as "negative zero" is undefined). | |
Implementation notes: | |
1. Integers could have been copied directly to the mantissa with the | |
exponents set to the appropriate values (54 in this case). However, the | |
current implementation unpacks integers only in preparation for repacking | |
as floating-point numbers i.e., to implement the "float" operator. This | |
would require a larger number of shifts to normalize the values -- as | |
many as 54 to normalize the value 1. Preshifting reduces the number of | |
normalizing shifts needed to between 6 and 22. | |
*/ | |
static FPU unpack (FP_OPND packed) | |
{ | |
FPU unpacked; | |
uint32 word; | |
switch (packed.precision) { /* dispatch based on the operand precision */ | |
case in_s: /* unpack a single integer */ | |
word = packed.words [0]; /* from the first word */ | |
if (word & D16_SIGN) { /* if the value is negative */ | |
word = NEG16 (word); /* then make it positive */ | |
unpacked.negative = TRUE; /* and set the mantissa sign flag */ | |
} | |
else /* otherwise the value is positive */ | |
unpacked.negative = FALSE; /* so clear the sign flag */ | |
unpacked.mantissa = (t_uint64) word << 32; /* store the preshifted value as the mantissa */ | |
unpacked.exponent = UNPACKED_BITS - 32; /* and set the exponent to account for the shift */ | |
unpacked.precision = fp_f; /* set the precision */ | |
break; | |
case in_d: /* unpack a double integer */ | |
word = TO_DWORD (packed.words [0], packed.words [1]); /* from the first two words */ | |
if (word & D32_SIGN) { /* if the value is negative */ | |
word = NEG32 (word); /* then make it positive */ | |
unpacked.negative = TRUE; /* and set the mantissa sign flag */ | |
} | |
else /* otherwise the value is positive */ | |
unpacked.negative = FALSE; /* so clear the sign flag */ | |
unpacked.mantissa = (t_uint64) word << 16; /* store the preshifted value as the mantissa */ | |
unpacked.exponent = UNPACKED_BITS - 16; /* and set the exponent to account for the shift */ | |
unpacked.precision = fp_f; /* set the precision */ | |
break; | |
case fp_f: /* unpack a single-precision */ | |
case fp_x: /* extended-precision */ | |
case fp_e: /* or double-precision floating-point number */ | |
unpacked.mantissa = MANTISSA (packed.words [0]); /* starting with the first word */ | |
for (word = 1; word <= 3; word++) { /* unpack from one to three more words */ | |
unpacked.mantissa <<= 16; /* shift the accumulated value */ | |
if (word < TO_COUNT (packed.precision)) /* if all words are not included yet */ | |
unpacked.mantissa |= packed.words [word]; /* then merge the next word into value */ | |
} | |
unpacked.exponent = /* store the exponent */ | |
EXPONENT (packed.words [0]) - EXPONENT_BIAS; /* after removing the bias */ | |
if (unpacked.exponent == MIN_EXPONENT /* if the biased exponent and mantissa are zero */ | |
&& unpacked.mantissa == 0) /* then the mantissa is positive */ | |
unpacked.negative = FALSE; /* regardless of the packed sign */ | |
else { /* otherwise the value is non-zero */ | |
unpacked.mantissa |= IMPLIED_BIT; /* so add back the implied "1" bit */ | |
unpacked.negative = ((packed.words [0] & D16_SIGN) != 0); /* and set the sign as directed */ | |
} | |
unpacked.precision = packed.precision; /* set the precision */ | |
break; | |
} /* all cases are handled */ | |
return unpacked; /* return the unpacked value */ | |
} | |
/* Normalize, round, and pack an unpacked value. | |
An unpacked value is normalized, rounded, and packed into the representation | |
indicated by the operand precision. If the supplied value cannot be | |
represented, the appropriate trap indication is returned. | |
A single- or double-integer is packed into the first word or two words of the | |
result as a twos-complement value. If the value is too large for the result | |
precision, an Integer Overflow trap is indicated, and a zero value is | |
returned. | |
For a real of any precision, the mantissa is first normalized by shifting | |
right if the carry bit is set, or by shifting left until the implied bit is | |
set. The exponent is adjusted for any shifts performed. The value is then | |
rounded by adding one-half of the least-significant bit; if that causes a | |
carry, the exponent is adjusted again. Finally, the mantissa is masked to | |
the number of bits corresponding to the desired precision and packed into the | |
in-memory representation. The exponent is checked, and it exceeds the | |
permitted range, the appropriate trap indication is returned. | |
Implementation notes: | |
1. If a carry occurs due to rounding, the mantissa is not shifted because | |
the carry bit will be masked off during packing. Incrementing the | |
exponent in this case is sufficient. | |
2. Masking the mantissa is required to remove the carry and implied bits | |
before packing. Masking the value bits in excess of the specified | |
precision is not required but is desirable to avoid implying more | |
precision than actually is present. | |
3. The result value +/-1 x 2 ** -256 is considered an underflow, as the | |
packed representation is identical to the zero representation, i.e., an | |
all-zeros value. | |
*/ | |
static FP_OPND norm_round_pack (FPU unpacked) | |
{ | |
FP_OPND packed; | |
int32 integer; | |
packed.precision = unpacked.precision; /* set the precision */ | |
if (unpacked.mantissa == 0) { /* if the mantissa is zero */ | |
packed.words [0] = 0; /* then set */ | |
packed.words [1] = 0; /* the packed */ | |
packed.words [2] = 0; /* representation to */ | |
packed.words [3] = 0; /* all zeros */ | |
packed.trap = trap_None; /* report that packing succeeded */ | |
} | |
else if (unpacked.precision <= in_d) /* if packing a single or double integer */ | |
if (unpacked.exponent >= mantissa_bits [unpacked.precision]) { /* then if the value is too large to fit */ | |
packed.words [0] = 0; /* then return */ | |
packed.words [1] = 0; /* a zero value */ | |
packed.trap = trap_Integer_Overflow; /* and an overflow trap */ | |
} | |
else { /* otherwise */ | |
integer = (int32) /* convert the value to an integer */ | |
(unpacked.mantissa >> UNPACKED_BITS - unpacked.exponent /* by shifting right to align */ | |
& mantissa_mask [unpacked.precision]); /* and masking to the desired precision */ | |
if (unpacked.negative) /* if the value is negative */ | |
integer = - integer; /* then negate the result */ | |
packed.words [0] = UPPER_WORD (integer); /* split the result */ | |
packed.words [1] = LOWER_WORD (integer); /* into the first two words */ | |
packed.trap = trap_None; /* report that packing succeeded */ | |
} | |
else { /* otherwise a real number is to be packed */ | |
if (unpacked.mantissa & CARRY_BIT) { /* if a carry out of the MSB has occurred */ | |
unpacked.mantissa >>= 1; /* then shift the mantissa to normalize */ | |
unpacked.exponent += 1; /* and increment the exponent to compensate */ | |
} | |
else /* otherwise */ | |
while (DENORMALIZED (unpacked.mantissa)) { /* while the mantissa is not in normal form */ | |
unpacked.mantissa <<= 1; /* shift the mantissa toward the implied-bit position */ | |
unpacked.exponent -= 1; /* and decrement the exponent to compensate */ | |
} | |
unpacked.mantissa += half_lsb [unpacked.precision]; /* round the mantissa by adding one-half of the LSB */ | |
if (unpacked.mantissa & CARRY_BIT) /* if rounding caused a carry out of the MSB */ | |
unpacked.exponent = unpacked.exponent + 1; /* then increment the exponent to compensate */ | |
unpacked.mantissa &= mantissa_mask [unpacked.precision]; /* mask the mantissa to the specified precision */ | |
packed.words [0] = (HP_WORD) (unpacked.mantissa >> 48) & DV_MASK /* pack the first word of the mantissa */ | |
| TO_EXPONENT (unpacked.exponent) /* with the exponent */ | |
| (unpacked.negative ? D16_SIGN : 0); /* and the sign bit */ | |
packed.words [1] = (HP_WORD) (unpacked.mantissa >> 32) & DV_MASK; /* pack the second */ | |
packed.words [2] = (HP_WORD) (unpacked.mantissa >> 16) & DV_MASK; /* and third */ | |
packed.words [3] = (HP_WORD) (unpacked.mantissa >> 0) & DV_MASK; /* and fourth words */ | |
if (unpacked.exponent < MIN_EXPONENT /* if the exponent is too small */ | |
|| unpacked.exponent == MIN_EXPONENT && unpacked.mantissa == 0) /* or the result would be all zeros */ | |
packed.trap = trap_Float_Underflow; /* then report an underflow trap */ | |
else if (unpacked.exponent > MAX_EXPONENT) /* otherwise if the exponent is too large */ | |
packed.trap = trap_Float_Overflow; /* then report an overflow trap */ | |
else /* otherwise */ | |
packed.trap = trap_None; /* report that packing succeeded */ | |
} | |
return packed; /* return the packed value */ | |
} | |
/* Add two unpacked numbers. | |
The sum of the two operands is returned. If one operand is zero and the | |
other is not, the non-zero operand is returned. If both operand are zero, a | |
"defined zero" is returned in case one or both operands are "negative zeros." | |
Otherwise, the difference between the operand exponents is determined. If | |
the magnitude of the difference between the exponents is greater than the | |
number of significant bits, then the smaller number has been scaled to zero | |
(swamped), and so the sum is simply the larger operand. However, if the sum | |
will be significant, the smaller mantissa is shifted to align with the larger | |
mantissa, and the larger exponent is used (as, after the scaling shift, the | |
smaller value has the same exponent). Finally, if the operand signs are the | |
same, the result is the sum of the mantissas. If the signs are different, | |
then the sum is the smaller value subtracted from the larger value, and the | |
result adopts the sign of the larger value. | |
Implementation notes: | |
1. If the addend is zero, the microcode converts the undefined value | |
"negative zero" to the defined positive zero if it is passed as the | |
augend. This also applies to the subtraction operator, which passes a | |
negative zero if the subtrahend is zero. | |
*/ | |
static TRAP_CLASS add (FPU *sum, FPU augend, FPU addend) | |
{ | |
int32 magnitude; | |
if (addend.mantissa == 0) /* if the addend is zero */ | |
if (augend.mantissa == 0) { /* then if the augend is also zero */ | |
*sum = zero; /* then the sum is (positive) zero */ | |
sum->precision = augend.precision; /* with the appropriate precision */ | |
} | |
else /* otherwise the augend is non-zero */ | |
*sum = augend; /* so the sum is just the augend */ | |
else if (augend.mantissa == 0) /* otherwise if the augend is zero */ | |
*sum = addend; /* then the sum is just the addend */ | |
else { /* otherwise both operands are non-zero */ | |
magnitude = augend.exponent - addend.exponent; /* so determine the magnitude of the difference */ | |
if (abs (magnitude) > mantissa_bits [augend.precision]) /* if one of the operands is swamped */ | |
if (magnitude > 0) /* then if the addend is smaller */ | |
*sum = augend; /* then return the augend */ | |
else /* otherwise */ | |
*sum = addend; /* return the addend */ | |
else { /* otherwise the addition is required */ | |
sum->precision = addend.precision; /* so set the precision to that of the operands */ | |
if (magnitude > 0) { /* if the addend is smaller */ | |
addend.mantissa >>= magnitude; /* then shift right to align the addend */ | |
sum->exponent = augend.exponent; /* and use the augend's exponent */ | |
} | |
else { /* otherwise the augend is smaller or the same */ | |
augend.mantissa >>= - magnitude; /* shift right to align the augend */ | |
sum->exponent = addend.exponent; /* and use the addend's exponent */ | |
} | |
if (addend.negative == augend.negative) { /* if the mantissa signs are the same */ | |
sum->mantissa = addend.mantissa + augend.mantissa; /* then add the mantissas */ | |
sum->negative = addend.negative; /* and use the addend sign for the sum */ | |
} | |
else if (addend.mantissa > augend.mantissa) { /* otherwise if the addend is larger */ | |
sum->mantissa = addend.mantissa - augend.mantissa; /* then subtract the augend */ | |
sum->negative = addend.negative; /* and use the addend sign */ | |
} | |
else { /* otherwise the augend is larger */ | |
sum->mantissa = augend.mantissa - addend.mantissa; /* so subtract the addend */ | |
sum->negative = augend.negative; /* and use the augend sign */ | |
} | |
} | |
} | |
return trap_None; /* report that the addition succeeded */ | |
} | |
/* Subtract two unpacked numbers. | |
The difference of the two operands is returned. Subtraction is implemented | |
by negating the subtrahend and then adding the minuend. | |
Implementation notes: | |
1. If the subtrahend is zero, negating produces the undefined "negative | |
zero." However, the "add" routine handles this as positive zero, so we | |
do not need to worry about this condition. | |
*/ | |
static TRAP_CLASS subtract (FPU *difference, FPU minuend, FPU subtrahend) | |
{ | |
subtrahend.negative = ! subtrahend.negative; /* invert the sign of the subtrahend */ | |
add (difference, minuend, subtrahend); /* add to obtain the difference */ | |
return trap_None; /* report that the subtraction succeeded */ | |
} | |
/* Multiply two unpacked numbers. | |
The product of the two operands is returned. Conceptually, the | |
implementation requires a 64 x 64 = 128-bit multiply, rounded to the upper 64 | |
bits. Instead of implementing the FMPY or EMPY firmware algorithm directly, | |
which uses 16 x 16 = 32-bit partial-product multiplies, it is more efficient | |
under simulation to use 32 x 32 = 64-bit multiplications by splitting the | |
operand mantissas ("a" and "b") into 32-bit high and low ("h" and "l") parts | |
and forming the cross-products: | |
64-bit operands | |
ah al | |
+-------+-------+ | |
bh bl | |
+-------+-------+ | |
_________________ | |
al * bl [ll] | |
+-------+-------+ | |
ah * bl [hl] | |
+-------+-------+ | |
al * bh [lh] | |
+-------+-------+ | |
ah * bh [hh] | |
+-------+-------+ | |
_________________________________ | |
64-bit product | |
+-------+-------+ | |
If either operand is zero, a "defined zero" is returned in case one or both | |
operands are "negative zeros." Otherwise, the product exponent is set to the | |
sum of the operand exponents, and the four 64-bit cross-products are formed. | |
The lower 64 bits of the products are summed to form the carry into the upper | |
64 bits, which are summed to produce the product. The product mantissa is | |
aligned, and the product sign is set negative if the operand signs differ. | |
Mantissas are represented internally as fixed-point numbers with 54 bits to | |
the right of the binary point. That is, the real number represented is the | |
integer mantissa value * (2 ** -54), where the right-hand term represents the | |
delta for a change of one bit. Multiplication is therefore: | |
(product * delta) = (multiplicand * delta) * (multiplier * delta) | |
The product is: | |
product = (multiplicand * multiplier) * (delta * delta) / delta | |
...which reduces to: | |
product = multiplicand * multiplier * delta | |
Multiplying the product by (2 ** -54) is equivalent to right-shifting by 54. | |
However, using only the top 64 bits of the 128-bit product is equivalent to | |
right-shifting by 64, so the net correction is a left-shift by 10. | |
Implementation notes: | |
1. 32 x 32 = 64-bit multiplies use intrinsic instructions on the IA-32 | |
processor family. | |
*/ | |
static TRAP_CLASS multiply (FPU *product, FPU multiplicand, FPU multiplier) | |
{ | |
uint32 ah, al, bh, bl; | |
t_uint64 hh, hl, lh, ll, carry; | |
if (multiplicand.mantissa == 0 || multiplier.mantissa == 0) { /* if either operand is zero */ | |
*product = zero; /* then the product is (positive) zero */ | |
product->precision = multiplicand.precision; /* with the appropriate precision */ | |
} | |
else { /* otherwise both operands are non-zero */ | |
product->precision = multiplicand.precision; /* so set the precision to that of the operands */ | |
product->exponent = multiplicand.exponent /* the product exponent */ | |
+ multiplier.exponent; /* is the sum of the operand exponents */ | |
ah = (uint32) (multiplicand.mantissa >> D32_WIDTH); /* split the multiplicand */ | |
al = (uint32) (multiplicand.mantissa & D32_MASK); /* into high and low double-words */ | |
bh = (uint32) (multiplier.mantissa >> D32_WIDTH); /* split the multiplier */ | |
bl = (uint32) (multiplier.mantissa & D32_MASK); /* into high and low double-words */ | |
hh = ((t_uint64) ah * bh); /* form the */ | |
hl = ((t_uint64) ah * bl); /* four cross products */ | |
lh = ((t_uint64) al * bh); /* using 32 x 32 = 64-bit multiplies */ | |
ll = ((t_uint64) al * bl); /* for efficiency */ | |
carry = ((ll >> D32_WIDTH) + (hl & D32_MASK) /* add the upper half of "ll" to the lower halves of "hl" */ | |
+ (lh & D32_MASK)) >> D32_WIDTH; /* and "lh" and shift to leave just the carry bit */ | |
product->mantissa = hh + (hl >> D32_WIDTH) /* add "hh" to the upper halves of "hl" and "lh" */ | |
+ (lh >> D32_WIDTH) + carry; /* and the carry bit */ | |
product->mantissa <<= DELTA_ALIGNMENT; /* align the result */ | |
product->negative = /* set the product sign negative */ | |
(multiplicand.negative != multiplier.negative); /* if the operand signs differ */ | |
} | |
return trap_None; /* report that the multiplication succeeded */ | |
} | |
/* Divide two unpacked numbers. | |
The quotient of the two operands is returned, and the remainder is discarded. | |
Conceptually, the implementation requires a 128 / 64 = 64-bit division, with | |
64 bits of zeros appended to the dividend to get the required precision. | |
However, instead of implementing the FDIV or EDIV firmware algorithm | |
directly, which uses 32 / 16 = 16-bit trial divisions, it is more efficient | |
under simulation to use 64 / 32 = 64-bit divisions with the classic | |
divide-and-correct method. | |
This method considers the 64-bit dividend and divisor each to consist of two | |
32-bit "digits." The 64-bit dividend "ah,al" is divided by the first 32-bit | |
digit "bh" of the 64-bit divisor "bh,bl", yielding a 64-bit trial quotient | |
and a 64-bit remainder. A correction is developed by subtracting the product | |
of the second 32-bit digit "bl" of the divisor and the trial quotient from | |
the remainder. If the remainder is negative, the trial quotient is too | |
large, so it is decremented, and the (full 64-bit) divisor is added to the | |
correction. This is repeated until the correction is non-negative, | |
indicating that the first quotient digit is correct. The process is then | |
repeated using the corrected remainder as the dividend to develop the second | |
64-bit trial quotient and second quotient digit. The first quotient digit is | |
positioned, and the two quotient digits are then added to produce the final | |
64-bit quotient. The quotient mantissa is aligned, and the quotient sign is | |
set negative if the operand signs differ. | |
Mantissas are represented internally as fixed-point numbers with 54 bits to | |
the right of the binary point. That is, the real number represented is the | |
integer mantissa value * (2 ** -54), where the right-hand term represents the | |
delta for a change of one bit. Division is therefore: | |
(quotient * delta) = (dividend * delta) / (divisor * delta) | |
The quotient is: | |
quotient = (dividend / divisor) * (delta / (delta * delta)) | |
...which reduces to: | |
quotient = (dividend / divisor) / delta | |
Dividing the quotient by (2 ** -54) is equivalent to left-shifting by 54. | |
However, using only the top 64 bits of the 128-bit product is equivalent to | |
right-shifting by 64, so the net correction is a right-shift by 10. | |
See "Divide-and-Correct Methods for Multiple Precision Division" by Marvin L. | |
Stein, Communications of the ACM, August 1964 for background. | |
Implementation notes: | |
1. IA-32 processors do not have a 64 / 32 = 64-bit divide instruction (they | |
have a 64 / 32 = 32 instruction instead). Therefore, a run-time library | |
routine for 64 / 64 = 64 is generated. Consequently, "bh" and "bl" are | |
declared as 64-bit variables, as this produces simpler code than if they | |
were declared as 32-bit variables. | |
2. "bh" is guaranteed to be non-zero because the divisor mantissa is | |
normalized on entry. Therefore, no division-by-zero check is needed. | |
3. The quotient alignment shift logically expresses ((q1 << 32) + q2) >> 10, | |
but it must be implemented as (q1 << 22) + (q2 >> 10) as otherwise the | |
left-shift would lose significant bits. | |
*/ | |
static TRAP_CLASS divide (FPU *quotient, FPU dividend, FPU divisor) | |
{ | |
t_uint64 bh, bl, q1, q2, r1, r2; | |
t_int64 c1, c2; | |
if (divisor.mantissa == 0) { /* if the divisor is zero */ | |
*quotient = dividend; /* then return the dividend */ | |
return trap_Float_Zero_Divide; /* and report the error */ | |
} | |
else if (dividend.mantissa == 0) { /* otherwise if the dividend is zero */ | |
*quotient = zero; /* then the quotient is (positive) zero */ | |
quotient->precision = dividend.precision; /* with the appropriate precision */ | |
} | |
else { /* otherwise both operands are non-zero */ | |
quotient->precision = dividend.precision; /* so set the precision to that of the operands */ | |
quotient->exponent = dividend.exponent /* the quotient exponent */ | |
- divisor.exponent; /* is the difference of the operand exponents */ | |
bh = divisor.mantissa >> D32_WIDTH; /* split the divisor */ | |
bl = divisor.mantissa & D32_MASK; /* into high and low halves */ | |
q1 = dividend.mantissa / bh; /* form the first trial quotient */ | |
r1 = dividend.mantissa % bh; /* and remainder */ | |
c1 = r1 - bl * q1; /* form the first corrected remainder */ | |
while (c1 < 0) { /* while a correction is required */ | |
q1 = q1 - 1; /* the first trial quotient is too large */ | |
c1 = c1 + divisor.mantissa; /* so reduce it and increase the remainder */ | |
} | |
q2 = c1 / bh; /* form the second trial quotient */ | |
r2 = c1 % bh; /* and remainder */ | |
c2 = r2 - bl * q2; /* form the second corrected remainder */ | |
while (c2 < 0) { /* while a correction is required */ | |
q2 = q2 - 1; /* the second trial quotient is too large */ | |
c2 = c2 + divisor.mantissa; /* so reduce it and increase the remainder */ | |
} | |
quotient->mantissa = (q1 << D32_WIDTH - DELTA_ALIGNMENT) /* sum the quotient digits */ | |
+ (q2 >> DELTA_ALIGNMENT); /* and align the result */ | |
quotient->negative = /* set the quotient sign negative */ | |
(dividend.negative != divisor.negative); /* if the operand signs differ */ | |
} | |
return trap_None; /* report that the division succeeded */ | |
} | |
/* Float an integer to a floating-point value. | |
The integer operand is converted to a floating-point value and returned. The | |
desired precision of the result must be set before calling. | |
Conversion is simply a matter of copying the integer value. When the | |
unpacked value is normalized, it will be converted to floating-point format. | |
Implementation notes: | |
1. The incoming integer has already been unpacked into fp_f format, so we do | |
not need to set the precision here. | |
*/ | |
static TRAP_CLASS ffloat (FPU *real, FPU integer) | |
{ | |
*real = integer; /* copy the unpacked value */ | |
return trap_None; /* report that the float succeeded */ | |
} | |
/* Fix an unpacked floating-point value to an integer. | |
A floating-point value is converted to a double-word integer. If the "round" | |
parameter is true, the value is rounded before conversion; otherwise, it is | |
truncated. | |
If the real value is less than 0.5, then the integer value is zero. | |
Otherwise, if rounding is requested, add 0.5 (created by shifting a "1" into | |
the position immediately to the right of the least significant bit of the | |
integer result) to the value. Finally, the result precision is set. The | |
remaining conversion occurs when the result is packed. | |
Implementation notes: | |
1. The FIXR/FIXT microcode gives an Integer Overflow for exponent > 30, even | |
though -2 ** 31 (143700 000000) does fit in the result. | |
*/ | |
static TRAP_CLASS fix (FPU *integer, FPU real, t_bool round) | |
{ | |
if (real.exponent < -1) /* if the real value is < 0.5 */ | |
integer->mantissa = 0; /* then the integer value is 0 */ | |
else { /* otherwise the value is convertible */ | |
integer->mantissa = real.mantissa; /* so set the mantissa */ | |
if (round && real.exponent < UNPACKED_BITS) /* if rounding is requested and the value won't overflow */ | |
integer->mantissa += /* then add one-half of the LSB to the value */ | |
(t_uint64) 1 << (UNPACKED_BITS - real.exponent - 1); | |
} | |
integer->exponent = real.exponent; /* copy the exponent */ | |
integer->negative = real.negative; /* and sign */ | |
integer->precision = in_d; /* and set to pack to a double integer */ | |
return trap_None; /* report that the fix succeeded */ | |
} |