1637 lines
67 KiB
C
1637 lines
67 KiB
C
/* sel32_fltpt.c: SEL 32 floating point instructions processing.
|
|
|
|
Copyright (c) 2018-2021, James C. Bevier
|
|
Portions provided by Richard Cornwell, Geert Rolf and other SIMH contributers
|
|
|
|
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
|
|
JAMES C. BEVIER 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.
|
|
|
|
This set of subroutines simulate the excess 64 floating point instructions.
|
|
ADFW - add memory float to register
|
|
ADFD - add memory double to register pair
|
|
SUFW - subtract memory float from register
|
|
SUFD - subtract memory double from register pair
|
|
MPFW - multiply register by memory float
|
|
MPFD - multiply register pair by memory double
|
|
DVFW - divide register by memory float
|
|
DVFD - divide register pair by memory double
|
|
FIXW - convert float to integer (32 bit)
|
|
FIXD - convert double to long long (64 bit)
|
|
FLTW - convert integer (32 bit) to float
|
|
FLTD - convert long long (64 bit) to double
|
|
ADRFW - add regist float to register
|
|
SURFW - subtract register float from register
|
|
DVRFW - divide register float by register float
|
|
MPRFW - multiply register float by register float
|
|
ADRFD - add register pair double to register pair double
|
|
SURFD - subtract register pair double from register pair double
|
|
DVRFD - divide register pair double by register pair double
|
|
MPRFD - multiply register pair double by register pair double
|
|
|
|
Floating Point Formats
|
|
float
|
|
S - 1 sign bit
|
|
X - 7 bit exponent
|
|
M - 24 bit mantissa
|
|
S XXXXXXX MMMMMMMM MMMMMMMM MMMMMMMM
|
|
double
|
|
S - 1 sign bit
|
|
X - 7 bit exponent
|
|
M - 56 bit mantissa
|
|
S XXXXXXX MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM MMMMMMMM
|
|
|
|
*/
|
|
|
|
#include "sel32_defs.h"
|
|
#include <math.h>
|
|
|
|
uint32 s_fixw(uint32 val, uint32 *cc);
|
|
uint32 s_fltw(uint32 val, uint32 *cc);
|
|
|
|
t_uint64 s_fixd(t_uint64 val, uint32 *cc);
|
|
t_uint64 s_fltd(t_uint64 val, uint32 *cc);
|
|
|
|
uint32 s_nor(uint32 reg, uint32 *exp);
|
|
t_uint64 s_nord(t_uint64 reg, uint32 *exp);
|
|
|
|
uint32 s_mpfw(uint32 reg, uint32 mem, uint32 *cc);
|
|
uint32 s_dvfw(uint32 reg, uint32 mem, uint32 *cc);
|
|
|
|
uint32 s_adfw(uint32 reg, uint32 mem, uint32 *cc);
|
|
uint32 s_sufw(uint32 reg, uint32 mem, uint32 *cc);
|
|
|
|
t_uint64 s_adfd(t_uint64 reg, t_uint64 mem, uint32 *cc);
|
|
t_uint64 s_sufd(t_uint64 reg, t_uint64 mem, uint32 *cc);
|
|
|
|
t_uint64 s_mpfd(t_uint64 reg, t_uint64 mem, uint32 *cc);
|
|
t_uint64 s_dvfd(t_uint64 reg, t_uint64 mem, uint32 *cc);
|
|
|
|
uint32 s_normfw(uint32 num, uint32 *cc);
|
|
t_uint64 s_normfd(t_uint64 num, uint32 *cc);
|
|
|
|
#define NORMASK 0xf8000000 /* normalize 5 bit mask */
|
|
#define DNORMASK 0xf800000000000000ll /* double normalize 5 bit mask */
|
|
#define EXMASK 0x7f000000 /* exponent mask */
|
|
#define FRMASK 0x80ffffff /* fraction mask */
|
|
#define DEXMASK 0x7f00000000000000ll /* exponent mask */
|
|
#define DFSVAL 0xff00000000000000ll /* minus full scale value */
|
|
#define DFRMASK 0x80ffffffffffffffll /* fraction mask */
|
|
#define NEGATE32(val) ((~val) + 1) /* negate a value 16/32/64 bits */
|
|
|
|
/**************************************************************
|
|
* Common routine for finishing the various F.P. instruction *
|
|
* *
|
|
* Floating point operations not terminating with an arith- *
|
|
* metic exception produce the following condition codes: *
|
|
* *
|
|
* CC1 CC2 CC3 CC4 Definition *
|
|
* ------------------------------------------------------- *
|
|
* 0 1 0 0 no exception, fraction positive *
|
|
* 0 0 1 0 no exception, fraction negative *
|
|
* 0 0 0 1 no exception, fraction = zero *
|
|
* *
|
|
* *
|
|
* an arithmetic exception produces the follwing condition *
|
|
* code settings: *
|
|
* *
|
|
* CC1 CC2 CC3 CC4 Definition *
|
|
* -------------------------------------------------------- *
|
|
* 1 0 1 0 exp underflow, fraction negative *
|
|
* 1 0 1 1 exp overflow, fraction negative *
|
|
* 1 1 0 0 exp underflow, fraction positive *
|
|
* 1 1 0 1 exp overflow, fraction positive *
|
|
* *
|
|
**************************************************************/
|
|
|
|
/* normalize floating point fraction */
|
|
uint32 s_nor(uint32 reg, uint32 *exp) {
|
|
uint32 texp = 0; /* no exponent yet */
|
|
|
|
if (reg != 0) { /* do nothing if reg is already zero */
|
|
uint32 mv = reg & NORMASK; /* mask off bits 0-4 */
|
|
while ((mv == 0) || (mv == NORMASK)) {
|
|
/* not normalized yet, so shift 4 bits left */
|
|
reg <<= 4; /* move over 4 bits */
|
|
texp++; /* bump shift count */
|
|
mv = reg & NORMASK; /* just look at bits 0-4 */
|
|
}
|
|
/* bits 0-4 of reg is neither 0 nor all ones */
|
|
/* show that reg is normalized */
|
|
texp = (uint32)(0x40-(int32)texp); /* subtract shift count from 0x40 */
|
|
}
|
|
*exp = texp; /* return exponent */
|
|
return (reg); /* return normalized register */
|
|
}
|
|
|
|
/* normalize double floating point number */
|
|
t_uint64 s_nord(t_uint64 reg, uint32 *exp) {
|
|
uint32 texp = 0; /* no exponent yet */
|
|
|
|
if (reg != 0) { /* do nothing if reg is already zero */
|
|
t_uint64 mv = reg & DNORMASK; /* mask off bits 0-4 */
|
|
while ((mv == 0) || (mv == DNORMASK)) {
|
|
/* not normalized yet, so shift 4 bits left */
|
|
reg <<= 4; /* move over 4 bits */
|
|
texp++; /* bump shift count */
|
|
mv = reg & DNORMASK; /* just look at bits 0-4 */
|
|
}
|
|
/* bits 0-4 of reg is neither 0 nor all ones */
|
|
/* show that reg is normalized */
|
|
texp = (uint32)(0x40-(int32)texp); /* subtract shift count from 0x40 */
|
|
}
|
|
*exp = texp; /* return exponent */
|
|
return (reg); /* return normalized double register */
|
|
}
|
|
|
|
/* normalize the memory value when adding number to zero */
|
|
uint32 s_normfw(uint32 num, uint32 *cc) {
|
|
uint32 ret;
|
|
int32 val; /* temp word */
|
|
int32 exp; /* exponent */
|
|
int32 CCs; /* condition codes */
|
|
uint8 sign; /* original sign */
|
|
|
|
if (num == 0) { /* make sure we have a number */
|
|
*cc = CC4BIT; /* set the cc's */
|
|
return 0; /* return zero */
|
|
}
|
|
sign = 0;
|
|
|
|
/* special case 0x80000000 (-0) to set CCs to 1011 * value to 0x80000001 */
|
|
if (num == 0x80000000) {
|
|
CCs = CC1BIT|CC3BIT|CC4BIT; /* we have AE, exp overflow, neg frac */
|
|
ret = 0x80000001; /* return max neg value */
|
|
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return result */
|
|
}
|
|
|
|
/* special case pos exponent & zero mantissa to be 0 */
|
|
if (((num & 0x80000000) == 0) && ((num & 0xff000000) > 0) && ((num & 0x00ffffff) == 0)) {
|
|
ret = 0; /* 0 to any power is still 0 */
|
|
CCs = CC4BIT; /* set zero CC */
|
|
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return result */
|
|
}
|
|
|
|
/* if we have 1xxx xxxx 0000 0000 0000 0000 0000 0000 */
|
|
/* we need to convert to 1yyy yyyy 1111 0000 0000 0000 0000 0000 */
|
|
/* where y = x - 1 */
|
|
if ((num & 0x80ffffff) == 0x80000000) {
|
|
int nexp = (0x7f000000 & num) - 0x01000000;
|
|
num = 0x80000000 | (nexp & 0x7f000000) | 0x00f00000;
|
|
}
|
|
|
|
exp = (num & 0x7f000000) >> 24; /* get exponent */
|
|
if (num & 0x80000000) { /* test for neg */
|
|
sign = 1; /* we are neq */
|
|
num = NEGATE32(num); /* two's complement */
|
|
exp ^= 0x7f; /* complement exponent */
|
|
}
|
|
val = num & 0x00ffffff; /* get mantissa */
|
|
|
|
/* now make sure number is normalized */
|
|
while ((val != 0) && ((val & 0x00f00000) == 0)) {
|
|
val <<= 4; /* move up a nibble */
|
|
exp--; /* and decrease exponent */
|
|
}
|
|
|
|
if (exp < 0) { /* check for underflow */
|
|
CCs = CC1BIT; /* we have underflow */
|
|
if (sign & 1) /* we are neq */
|
|
CCs |= CC3BIT; /* set neg CC */
|
|
else
|
|
CCs |= CC2BIT; /* set pos CC */
|
|
ret = 0; /* number too small, make 0 */
|
|
exp = 0; /* exponent too */
|
|
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return result */
|
|
}
|
|
|
|
/* rebuild normalized number */
|
|
val = ((val & 0x00ffffff) | ((exp & 0x7f) << 24));
|
|
if (sign & 1) /* we are neq */
|
|
val = NEGATE32(val); /* two's complement */
|
|
if (val == 0)
|
|
CCs = CC4BIT; /* show zero */
|
|
else if (val & 0x80000000) /* neqative? */
|
|
CCs = CC3BIT; /* show negative */
|
|
else
|
|
CCs = CC2BIT; /* show positive */
|
|
ret = val; /* return normalized number */
|
|
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return result */
|
|
}
|
|
|
|
#ifdef FOR_DEBUG
|
|
/* sfpval - determine floating point data value */
|
|
float sfpval(uint32 val)
|
|
{
|
|
uint32 wd32;
|
|
float num;
|
|
int32 exp;
|
|
|
|
exp = ((val >> 24) & 0x7f) - 0x40; /* get exponent and remove excess 0x40 */
|
|
wd32 = val & 0x00ffffff; /* get mantissa */
|
|
num = (float)wd32; /* make it a float */
|
|
num *= exp2f((4*exp) - 24); /* raise to power of exponent */
|
|
if (val & 0x80000000)
|
|
num *= -1.0; /* if negative, return negative num */
|
|
return num; /* return value */
|
|
}
|
|
|
|
/* dfpval - determine double floating point data value */
|
|
double dfpval(t_uint64 wd64)
|
|
{
|
|
double dbl;
|
|
int32 exp;
|
|
t_uint64 sav = wd64;
|
|
|
|
if (wd64 & 0x8000000000000000ll)
|
|
wd64 = NEGATE32(wd64);
|
|
exp = ((wd64 >> 56) & 0x7f) - 0x40; /* get exponent and remove excess 0x40 */
|
|
wd64 &= 0x00ffffffffffffffll; /* get 56 bit mantissa */
|
|
dbl = (double)wd64; /* make it a double float */
|
|
dbl *= exp2((4*exp) - 56); /* raise to power of exponent */
|
|
if (sav & 0x8000000000000000ll)
|
|
dbl *= -1.0; /* if negative, return negative num */
|
|
return dbl; /* return value */
|
|
}
|
|
#endif /* FOR_DEBUG */
|
|
|
|
/* normalize the memory value when adding number to zero */
|
|
t_uint64 s_normfd(t_uint64 num, uint32 *cc) {
|
|
t_uint64 ret;
|
|
t_uint64 val; /* temp word */
|
|
int32 exp; /* exponent */
|
|
int32 CCs; /* condition codes */
|
|
uint8 sign; /* original sign */
|
|
|
|
if (num == 0) { /* make sure we have a number */
|
|
*cc = CC4BIT; /* set the cc's */
|
|
return 0; /* return zero */
|
|
}
|
|
sign = 0;
|
|
|
|
/* special case 0x8000000000000000 (-0) to set CCs to 1011 */
|
|
/* and value to 0x8000000000000001 */
|
|
if (num == 0x8000000000000000LL) {
|
|
CCs = CC1BIT|CC3BIT|CC4BIT; /* we have AE, exp overflow, neg frac */
|
|
ret = 0x8000000000000001LL; /* return max neg value */
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return normalized result */
|
|
}
|
|
|
|
/* special case pos exponent & zero mantissa to be 0 */
|
|
if (((num & 0x8000000000000000LL) == 0) && ((num & 0xff00000000000000LL) > 0) &&
|
|
(num & 0x00ffffffffffffffLL) == 0) {
|
|
ret = 0; /* 0 to any power is still 0 */
|
|
CCs = CC4BIT; /* set zero CC */
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return normalized result */
|
|
}
|
|
|
|
/* if we have 1xxx xxxx 0000 0000 0000 0000 0000 0000 */
|
|
/* we need to convert to 1yyy yyyy 1111 0000 0000 0000 0000 0000 */
|
|
/* where y = x - 1 */
|
|
if ((num & 0x80ffffffffffffffLL) == 0x8000000000000000LL) {
|
|
t_uint64 nexp = (0x7f00000000000000LL & num) - 0x0100000000000000LL;
|
|
num = 0x8000000000000000LL | (nexp & 0x7f00000000000000LL) | 0x00f0000000000000LL;
|
|
}
|
|
|
|
exp = (num & 0x7f00000000000000LL) >> 56; /* get exponent */
|
|
if (num & 0x8000000000000000LL) { /* test for neg */
|
|
sign = 1; /* we are neq */
|
|
num = NEGATE32(num); /* two's complement */
|
|
exp ^= 0x7f; /* complement exponent */
|
|
}
|
|
val = num & 0x00ffffffffffffffLL; /* get mantissa */
|
|
|
|
/* now make sure number is normalized */
|
|
while ((val != 0) && ((val & 0x00f0000000000000LL) == 0)) {
|
|
val <<= 4; /* move up a nibble */
|
|
exp--; /* and decrease exponent */
|
|
}
|
|
|
|
if (exp < 0) {
|
|
CCs = CC1BIT; /* we have underflow */
|
|
if (sign & 1) /* we are neg */
|
|
CCs |= CC3BIT; /* set neg CC */
|
|
else
|
|
CCs |= CC2BIT; /* set pos CC */
|
|
ret = 0; /* number too small, make 0 */
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return normalized result */
|
|
}
|
|
|
|
/* rebuild normalized number */
|
|
ret = ((val & 0x00ffffffffffffffll) | (((t_uint64)exp & 0x7f) << 56));
|
|
if (sign & 1) /* we were neg */
|
|
ret = NEGATE32(ret); /* two's complement */
|
|
if (ret == 0)
|
|
CCs = CC4BIT; /* show zero */
|
|
else if (ret & 0x8000000000000000LL) /* neqative? */
|
|
CCs = CC3BIT; /* show negative */
|
|
else
|
|
CCs = CC2BIT; /* show positive */
|
|
|
|
/* return normalized number */
|
|
*cc = CCs; /* set the cc's */
|
|
return ret; /* return normalized result */
|
|
}
|
|
|
|
/* convert from 32 bit float to 32 bit integer */
|
|
/* set CC1 if overflow/underflow exception */
|
|
uint32 s_fixw(uint32 fltv, uint32 *cc) {
|
|
uint32 CC = 0, temp, temp2, sc;
|
|
uint32 neg = 0; /* clear neg flag */
|
|
|
|
if (fltv & MSIGN) { /* check for negative */
|
|
fltv = NEGATE32(fltv); /* make src positive */
|
|
neg = 1; /* set neg val flag */
|
|
} else {
|
|
if (fltv == 0) { /* value zero? */
|
|
temp = 0; /* return zero */
|
|
goto setcc; /* go set CC's */
|
|
}
|
|
/* gt 0, fall through */
|
|
}
|
|
temp2 = (uint32)(fltv >> 24); /* get exponent */
|
|
fltv <<= 8; /* move src to upper 3 bytes */
|
|
temp2 -= 64; /* take off excess notation */
|
|
temp = temp2; /* save val */
|
|
if ((int32)temp2 == 0) /* exp of zero means zero */
|
|
goto setcc; /* go set CC's */
|
|
if (temp2 & MSIGN) {
|
|
/* set CC1 for underflow */
|
|
if (neg) {
|
|
temp = 0x7fffffff; /* too big, set to max value */
|
|
goto OVFLO; /* go set CC's */
|
|
} else {
|
|
temp = 0; /* assume zero for small values */
|
|
goto UNFLO; /* go set CC's */
|
|
}
|
|
}
|
|
|
|
temp2 -= 8; /* see if in range */
|
|
temp = fltv; /* save val */
|
|
if ((temp2 == 0) && (fltv == 0x80000000) && (neg == 1))
|
|
goto setcc; /* go set CC's */
|
|
if ((int32)temp2 > 0) {
|
|
/* set CC1 for overflow */
|
|
temp = 0; /* assume zero for small values */
|
|
goto OVFLO; /* go set CC's */
|
|
}
|
|
sc = (NEGATE32(temp2) * 4); /* pos shift cnt * 4 */
|
|
fltv >>= sc; /* do 4 bit shifts */
|
|
|
|
/* see if overflow to neg */
|
|
/* set CC1 for overflow */
|
|
if (fltv & MSIGN) {
|
|
/* set CC1 for overflow */
|
|
temp = 0; /* assume zero for small values */
|
|
goto OVFLO; /* go set CC's */
|
|
}
|
|
|
|
/* see if original value was negative */
|
|
if (neg)
|
|
fltv = NEGATE32(fltv); /* put back to negative */
|
|
temp = fltv; /* return integer value */
|
|
/* come here to set cc's and return */
|
|
/* temp has return value */
|
|
setcc:
|
|
if (temp & MSIGN)
|
|
CC |= CC3BIT; /* CC3 for neg */
|
|
else if (temp == 0)
|
|
CC |= CC4BIT; /* CC4 for zero */
|
|
else
|
|
CC |= CC2BIT; /* CC2 for greater than zero */
|
|
/* return temp for destination reg */
|
|
*cc = CC; /* return CC's */
|
|
return temp; /* return result */
|
|
|
|
/* handle underflow/overflow */
|
|
OVFLO:
|
|
CC |= CC4BIT; /* set CC4 for exponent overflow */
|
|
UNFLO:
|
|
CC |= CC1BIT; /* set CC1 for arithmetic exception */
|
|
if (neg) /* test for negative */
|
|
CC |= CC3BIT; /* set neg fraction bit CC3 */
|
|
else
|
|
CC |= CC2BIT; /* set pos fraction bit CC2 */
|
|
*cc = CC; /* return CC's */
|
|
return temp; /* return result */
|
|
}
|
|
|
|
/* convert from 32 bit integer to 32 bit float */
|
|
/* No overflow (CC1) can be generated */
|
|
uint32 s_fltw(uint32 intv, uint32 *cc) {
|
|
uint32 CC = 0;
|
|
uint32 ret;
|
|
uint32 neg = 0; /* zero sign flag */
|
|
uint32 exp = 0; /* exponent */
|
|
uint32 val = (int32)intv; /* integer value */
|
|
|
|
if (intv & 0x80000000) {
|
|
val = NEGATE32(intv);
|
|
neg = 1;
|
|
}
|
|
while ((val != 0) && ((val & 0xf0000000) == 0)) {
|
|
val <<= 4; /* no round up */
|
|
exp--;
|
|
}
|
|
if (val != 0)
|
|
exp += 0x48; /* set default exponent */
|
|
/* shift value rt 8 bits and round */
|
|
if (val & 0x80) {
|
|
if (neg) {
|
|
if (val & 0x7f)
|
|
val = (val >> 8) + 1; /* round up */
|
|
else
|
|
val = (val >> 8); /* no round up */
|
|
} else {
|
|
val = (val >> 8) + 1; /* round up */
|
|
}
|
|
} else
|
|
val = (val >> 8); /* no round up */
|
|
if (val & 0x01000000) {
|
|
val = (val >> 4); /* move 1 nibble */
|
|
exp++;
|
|
}
|
|
ret = (exp << 24) | (val & 0x00ffffff); /* merge value */
|
|
if (neg)
|
|
ret = NEGATE32(ret);
|
|
if (ret & MSIGN)
|
|
CC |= CC3BIT; /* CC3 for neg */
|
|
else if (ret == 0)
|
|
CC |= CC4BIT; /* CC4 for zero */
|
|
else
|
|
CC |= CC2BIT; /* CC2 for greater than zero */
|
|
/* return temp for destination reg */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
|
|
/* convert from 64 bit double to 64 bit integer */
|
|
/* set CC1 if overflow/underflow exception */
|
|
t_uint64 s_fixd(t_uint64 dblv, uint32 *cc) {
|
|
uint32 temp2, CC = 0, neg = 0, sc = 0;
|
|
t_uint64 dest;
|
|
|
|
/* neg and CC flags already set to zero */
|
|
if (dblv & DMSIGN) {
|
|
dblv = NEGATE32(dblv); /* make src positive */
|
|
neg = 1; /* set neg val flag */
|
|
} else {
|
|
if (dblv == 0) {
|
|
dest = 0; /* return zero */
|
|
goto dodblcc; /* go set CC's */
|
|
}
|
|
/* gt 0, fall through */
|
|
}
|
|
|
|
temp2 = (uint32)(dblv >> 56); /* get exponent */
|
|
dblv <<= 8; /* move fraction to upper 7 bytes */
|
|
temp2 -= 64; /* take off excess notation */
|
|
dest = temp2; /* save val */
|
|
if ((int32)temp2 == 0) /* zero exp means zero */
|
|
goto dodblcc; /* go set CC's */
|
|
if (temp2 & MSIGN) {
|
|
/* set CC1 for underflow */
|
|
if (neg) {
|
|
dest = 0x7fffffffffffffff; /* too big, set to max value */
|
|
goto DOVFLO; /* go set CC's */
|
|
} else {
|
|
dest = 0; /* assume zero for small values */
|
|
goto DUNFLO; /* go set CC's */
|
|
}
|
|
}
|
|
|
|
temp2 -= 16; /* see if in range */
|
|
dest = dblv; /* save val */
|
|
if ((temp2 == 0) && (dblv == DMSIGN) && (neg == 1))
|
|
goto dodblcc; /* go set CC's */
|
|
if ((int32)temp2 > 0) {
|
|
/* set CC1 for overflow */
|
|
dest = 0; /* assume zero for small values */
|
|
goto DOVFLO; /* go set CC's */
|
|
}
|
|
sc = (NEGATE32(temp2) * 4); /* pos shift cnt * 4 */
|
|
dblv >>= sc; /* do 4 bit shifts */
|
|
|
|
/* see if overflow to neg */
|
|
if (dblv & DMSIGN) {
|
|
/* set CC1 for overflow */
|
|
dest = 0; /* assume zero for small values */
|
|
goto DOVFLO; /* go set CC's */
|
|
}
|
|
/* see if original values was negative */
|
|
if (neg)
|
|
dblv = NEGATE32(dblv); /* put back to negative */
|
|
dest = dblv; /* return integer value */
|
|
|
|
dodblcc:
|
|
/* dest has return value */
|
|
if (dest & DMSIGN)
|
|
CC |= CC3BIT; /* CC3 for neg */
|
|
else if (dest == 0)
|
|
CC |= CC4BIT; /* CC4 for zero */
|
|
else
|
|
CC |= CC2BIT; /* CC2 for greater than zero */
|
|
*cc = CC; /* return CC's */
|
|
return dest; /* return result */
|
|
|
|
/* handle underflow/overflow */
|
|
DOVFLO:
|
|
CC |= CC4BIT; /* set CC4 for exponent overflow */
|
|
DUNFLO:
|
|
CC |= CC1BIT; /* set CC1 for arithmetic exception */
|
|
if (neg) /* test for negative */
|
|
CC |= CC3BIT; /* set neg fraction bit CC3 */
|
|
else
|
|
CC |= CC2BIT; /* set pos fraction bit CC2 */
|
|
*cc = CC; /* return CC's */
|
|
return dest; /* return result */
|
|
}
|
|
|
|
/* convert from 64 bit integer to 64 bit double */
|
|
/* No overflow (CC1) can be generated */
|
|
t_uint64 s_fltd(t_uint64 intv, uint32 *cc) {
|
|
t_uint64 ret = 0; /* zero return val */
|
|
uint32 neg = 0; /* zero sign flag */
|
|
uint32 CC = 0; /* n0 CC's yet */
|
|
uint32 exp = 0; /* exponent */
|
|
t_uint64 val = intv; /* integer value */
|
|
|
|
if (intv & DMSIGN) {
|
|
val = NEGATE32(intv); /* make src positive */
|
|
neg = 1; /* set neg flag */
|
|
} else {
|
|
if (intv == 0) { /* see if zero */
|
|
ret = 0; /* return zero */
|
|
CC = CC4BIT; /* CC4 for zero */
|
|
/* return 0 for destination regs */
|
|
*cc = CC; /* return CC's */
|
|
return ret; /* return result */
|
|
}
|
|
/* gt 0, fall through */
|
|
}
|
|
|
|
/* see if normalized */
|
|
while ((val) && ((val & 0xf000000000000000ll) == 0)) {
|
|
val <<= 4; /* zero, shift in next nibble */
|
|
exp--; /* decr exp value */
|
|
}
|
|
if (val != 0)
|
|
exp += 0x50; /* default exponent */
|
|
|
|
/* shift value rt 8 bits and round */
|
|
if (val & 0x91ll) {
|
|
if (neg) {
|
|
if (val & 0x7fl)
|
|
val = (val >> 8) + 1; /* round up */
|
|
else
|
|
val = (val >> 8); /* no round up */
|
|
} else {
|
|
if (val & 0x7fl)
|
|
val = (val >> 8); /* no round up */
|
|
else
|
|
val = (val >> 8) + 1; /* round up */
|
|
}
|
|
} else
|
|
val = (val >> 8); /* no round up */
|
|
|
|
if (val & 0x0100000000000000ll) {
|
|
val = (val >> 4); /* no round up */
|
|
exp++;
|
|
}
|
|
ret = (((t_uint64)exp) << 56) | (val & 0x00ffffffffffffffll); /* merge value */
|
|
|
|
if (neg)
|
|
ret = NEGATE32(ret);
|
|
if (ret & DMSIGN)
|
|
CC |= CC3BIT; /* CC3 for neg */
|
|
else if (ret == 0)
|
|
CC |= CC4BIT; /* CC4 for zero */
|
|
else
|
|
CC |= CC2BIT; /* CC2 for greater than zero */
|
|
|
|
/* return temp for destination regs */
|
|
*cc = CC; /* return CC's */
|
|
return ret; /* return result */
|
|
}
|
|
|
|
#define CMASK 0x10000000 /* carry mask */
|
|
#define EMASK 0x7f000000 /* single exponent mask */
|
|
#define UMASK 0x0ffffff0 /* single fp mask */
|
|
#define XMASK 0x0fffffff /* single fp mask */
|
|
#define MMASK 0x00ffffff /* single mantissa mask */
|
|
#define NMASK 0x0f000000 /* single nibble mask */
|
|
#define ZMASK 0x00f00000 /* single nibble mask */
|
|
|
|
/* this new version is perfect against the diags, so good */
|
|
/* do new SEL floating add derived from IBM370 code */
|
|
/* Add/Sub single floating point */
|
|
uint32 s_adfw(uint32 reg, uint32 mem, uint32 *cc)
|
|
{
|
|
uint32 res, ret;
|
|
char sign = 0;
|
|
int er, em, temp;
|
|
uint32 CC;
|
|
|
|
/* first we want to make sure the numbers are normalized */
|
|
ret = s_normfw(reg, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
reg = ret; /* use normalized value */
|
|
if (mem == 0) { /* test for add of zero */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return normalized results */
|
|
}
|
|
|
|
ret = s_normfw(mem, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
mem = ret; /* use normalized value */
|
|
if (reg == 0) { /* test for add to zero */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results results */
|
|
}
|
|
|
|
/* extract reg exponent and mantissa */
|
|
if (reg & MSIGN) { /* reg negative */
|
|
sign |= 2; /* set neg flag */
|
|
reg = NEGATE32(reg); /* make negative positive */
|
|
}
|
|
er = (reg & EMASK) >> 24; /* extract reg exponent */
|
|
reg &= MMASK; /* extract reg mantissa */
|
|
|
|
/* extract mem exponent and mantissa */
|
|
if (mem & MSIGN) { /* mem negative */
|
|
sign |= 1; /* set neg flag */
|
|
mem = NEGATE32(mem); /* make negative positive */
|
|
}
|
|
em = (mem & EMASK) >> 24; /* extract mem exponent */
|
|
mem &= MMASK; /* extract mem mantissa */
|
|
|
|
temp = er - em; /* get signed exp difference */
|
|
mem = mem << 4; /* align mem for guard digit */
|
|
reg = reg << 4; /* align reg for guard digit */
|
|
|
|
if (temp > 0) { /* reg exp > mem exp */
|
|
if (temp > 8) {
|
|
mem = 0; /* if too much difference, make zero */
|
|
} else {
|
|
/* Shift mem right if reg has larger exponent */
|
|
while (temp-- != 0) {
|
|
mem >>= 4; /* adjust for exponent difference */
|
|
em++; /* bump exponent */
|
|
}
|
|
}
|
|
} else
|
|
if (temp < 0) { /* reg < mem exp */
|
|
if (temp < -8) {
|
|
reg = 0; /* if too much difference, make zero */
|
|
er = em; /* make exponents the same using mem exp */
|
|
} else {
|
|
/* Shift reg right if mem has larger exponent */
|
|
while (temp++ != 0) {
|
|
reg >>= 4; /* adjust for exponent difference */
|
|
er++; /* bump exponent */
|
|
}
|
|
}
|
|
}
|
|
|
|
/* exponents should be equal now */
|
|
/* add results */
|
|
if (sign == 2 || sign == 1) {
|
|
/* different signs so do subtract */
|
|
mem ^= XMASK; /* complement the value */
|
|
mem++; /* increment the value */
|
|
res = reg + mem; /* add the values */
|
|
if (res & CMASK) { /* see if carry */
|
|
res &= XMASK; /* clear off the carry bit */
|
|
} else {
|
|
sign ^= 2; /* flip the sign */
|
|
res ^= XMASK; /* and negate the value by comp */
|
|
res++; /* incr */
|
|
}
|
|
} else {
|
|
res = reg + mem; /* same sign, just add */
|
|
if (sign == 3)
|
|
res += 7; /* round number */
|
|
}
|
|
|
|
/* If overflow, shift right 4 bits */
|
|
if (res & CMASK) { /* see if overflow carry */
|
|
res >>= 4; /* move mantissa down 4 bits */
|
|
er++; /* and adjust exponent */
|
|
if (er >= 128) { /* if exponent too large, overflow */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic overflow */
|
|
/* OVERFLOW */
|
|
/* set CC2 & CC3 on exit */
|
|
CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
if (CC & CC3BIT) /* NEG overflow? */
|
|
res = 0x80000001; /* yes */
|
|
else
|
|
res = 0x7FFFFFFF; /* no, pos */
|
|
/* Store result */
|
|
*cc = CC; /* save CC's */
|
|
return res;
|
|
}
|
|
}
|
|
|
|
CC = 0; /* no CC's yet */
|
|
/* Set condition codes */
|
|
if (res != 0) /* see if non zero */
|
|
CC |= (sign & 2) ? 1 : 2;
|
|
else {
|
|
er = sign = 0; /* we have zero CC4 */
|
|
}
|
|
|
|
/* normalize the fraction */
|
|
if (CC != 0) { /* check for zero value */
|
|
while ((res != 0) && ((res & NMASK) == 0)) {
|
|
res <<= 4; /* adjust mantisa by a nibble */
|
|
er--; /* and adjust exponent smaller by 1 */
|
|
}
|
|
/* Check if underflow */
|
|
if (er < 0) {
|
|
/* UNDERFLOW */
|
|
CC |= CC1BIT; /* set arithmetic exception */
|
|
CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
res = 0; /* make all zero */
|
|
/* Store result */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return value */
|
|
}
|
|
} else {
|
|
/* result is zero */
|
|
sign = er = 0; /* make abs zero */
|
|
}
|
|
|
|
res >>= 4; /* remove the guard nibble */
|
|
|
|
/* create result */
|
|
res |= (er << 24) & EXMASK; /* combine exponent & mantissa */
|
|
|
|
/* set the CC's */
|
|
if (CC == 0) {
|
|
CC = CC4BIT; /* zero value */
|
|
} else {
|
|
if (sign & 2)
|
|
res = NEGATE32(res); /* make negative */
|
|
CC = (CC & 3) << 28; /* neg is CC3, pos is CC2 */
|
|
}
|
|
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return result */
|
|
}
|
|
|
|
/* subtract memory floating point number from register floating point number */
|
|
uint32 s_sufw(uint32 reg, uint32 mem, uint32 *cc) {
|
|
return s_adfw(reg, NEGATE32(mem), cc);
|
|
}
|
|
|
|
/* multiply register floating point number by memory floating point number */
|
|
/* set CC1 if overflow/underflow */
|
|
/* use revised normalization code */
|
|
uint32 s_mpfw(uint32 reg, uint32 mem, uint32 *cc) {
|
|
uint32 res, ret;
|
|
int sign = 0;
|
|
int lsb = 0;
|
|
int er, em, temp;
|
|
uint32 CC;
|
|
|
|
/* first we want to make sure the numbers are normalized */
|
|
ret = s_normfw(reg, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
reg = ret; /* use normalized value */
|
|
|
|
ret = s_normfw(mem, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
mem = ret; /* use normalized value */
|
|
|
|
/* see if multiply by zero */
|
|
if ((reg == 0) || (mem == 0)) { /* test for mult by zero */
|
|
*cc = CC4BIT; /* set CC 4 for 0 */
|
|
return 0; /* return results */
|
|
}
|
|
|
|
/* extract reg exponent and mantissa */
|
|
if (reg & MSIGN) { /* reg negative */
|
|
sign ^= 1; /* set neg flag */
|
|
reg = NEGATE32(reg); /* make negative positive */
|
|
}
|
|
if (reg & 0x1) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
er = (reg & EXMASK) >> 24; /* extract reg exponent */
|
|
reg &= MMASK; /* extract reg mantissa */
|
|
|
|
/* extract mem exponent and mantissa */
|
|
if (mem & MSIGN) { /* mem negative */
|
|
sign ^= 1; /* set neg flag */
|
|
mem = NEGATE32(mem); /* make negative positive */
|
|
}
|
|
if (mem & 0x1) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
em = (mem & EXMASK) >> 24; /* extract mem exponent */
|
|
mem &= MMASK; /* extract mem mantissa */
|
|
|
|
er = er + em - 0x40; /* get the exp value */
|
|
reg = reg << 4; /* create guard digit */
|
|
mem = mem << 4; /* create guard digit */
|
|
|
|
res = 0; /* zero result for multiply */
|
|
/* Do multiply with guard bit */
|
|
for (temp = 0; temp < 28; temp++) {
|
|
/* Add if we need too */
|
|
if (reg & 1)
|
|
res += mem;
|
|
/* Shift right by one */
|
|
reg >>= 1;
|
|
res >>= 1;
|
|
}
|
|
|
|
/* fix up some boundry rounding */
|
|
if ((res >= 0x01000000) && (sign == 0)) {
|
|
res += 0x8;
|
|
}
|
|
if ((res == 0x00FFFFFF) && (sign == 1) && (er != 1)) {
|
|
if (lsb == 1) {
|
|
if ((er != 0x41) && (er != 0x81)) {
|
|
res += 0x1;
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If overflow, shift right 4 bits */
|
|
if (res & 0x70000000) { /* see if overflow carry */
|
|
res >>= 4; /* move mantissa down 4 bits */
|
|
er++; /* and adjust exponent */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT; /* set arithmetic exception */
|
|
CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
if (CC & CC3BIT) /* NEG overflow? */
|
|
res = 0x80000001; /* double yes */
|
|
else
|
|
res = 0x7FFFFFFF; /* no, pos */
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
}
|
|
|
|
/* Align the results & normalize */
|
|
if (res != 0) {
|
|
while ((res != 0) && (res & NMASK) == 0) {
|
|
res <<= 4;
|
|
er--;
|
|
}
|
|
/* Check if overflow */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic exception */
|
|
if (sign & 1) {
|
|
CC |= CC3BIT;
|
|
res = 0x80000001; /* neg overflow 1011 */
|
|
} else {
|
|
CC |= CC2BIT;
|
|
res = 0x7FFFFFFF; /* pos overflow 1101 */
|
|
}
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
/* Check if underflow */
|
|
if (er < 0) {
|
|
/* UNDERFLOW */
|
|
res = 0; /* make return value zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
CC |= CC1BIT; /* set arithmetic exception */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
res >>= 4; /* remove guard nibble */
|
|
} else
|
|
er = sign = 0;
|
|
|
|
res &= MMASK; /* clear exponent */
|
|
|
|
res |= ((((uint32)er) << 24) & EXMASK); /* merge exp and mantissa */
|
|
|
|
if (sign == 1) /* is result to be negative */
|
|
res = NEGATE32(res); /* make value negative */
|
|
|
|
CC = 0;
|
|
if (res != 0) /* see if non zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
else
|
|
CC = CC4BIT; /* set zero cc */
|
|
|
|
/* return results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
|
|
/* divide register float by memory float */
|
|
uint32 s_dvfw(uint32 reg, uint32 mem, uint32 *cc) {
|
|
uint32 CC = 0, temp, temp2, sign;
|
|
uint32 expm, expr;
|
|
t_uint64 dtemp;
|
|
|
|
/* process operator */
|
|
sign = mem & MSIGN; /* save original value for sign */
|
|
if (mem == 0) /* check for divide by zero */
|
|
goto DOVFLO; /* go process divide overflow */
|
|
|
|
if (mem & MSIGN) /* check for negative */
|
|
mem = NEGATE32(mem); /* make mem positive */
|
|
|
|
expm = (mem >> 24); /* get operand exponent */
|
|
mem <<= 8; /* move fraction to upper 3 bytes */
|
|
mem >>= 1; /* adjust fraction for divide */
|
|
|
|
/* process operand */
|
|
if (reg == 0) {
|
|
temp = 0; /* return zero */
|
|
goto setcc; /* go set CC's */
|
|
}
|
|
if (reg & MSIGN) { /* check for negative */
|
|
reg = NEGATE32(reg); /* make reg positive */
|
|
sign ^= MSIGN; /* complement sign */
|
|
}
|
|
expr = (reg >> 24); /* get operator exponent */
|
|
reg <<= 8; /* move fraction to upper 3 bytes */
|
|
reg >>= 6; /* adjust fraction for divide */
|
|
|
|
temp = expr - expm; /* subtract exponents */
|
|
dtemp = ((t_uint64)reg) << 32; /* put reg fraction in upper 32 bits */
|
|
temp2 = (uint32)(dtemp / mem); /* divide reg fraction by mem fraction */
|
|
temp2 >>= 3; /* shift out excess bits */
|
|
temp2 <<= 3; /* replace with zero bits */
|
|
|
|
if (sign & MSIGN)
|
|
temp2 = NEGATE32(temp2); /* if negative, negate fraction */
|
|
/* normalize the result in temp and put exponent into expr */
|
|
temp2 = s_nor(temp2, &expr); /* normalize fraction */
|
|
temp += 1; /* adjust exponent */
|
|
|
|
//RROUND:
|
|
if (temp2 == MSIGN) { /* check for minus zero */
|
|
temp2 = 0xF8000000; /* yes, fixup value */
|
|
expr++; /* bump exponent */
|
|
}
|
|
|
|
if ((int32)temp2 >= 0x7fffffc0) /* check for special rounding */
|
|
goto RRND2; /* no special handling */
|
|
|
|
if (expr != 0x40) { /* result normalized? */
|
|
goto RRND2; /* if not, don't round */
|
|
}
|
|
/* result normalized */
|
|
if ((sign & MSIGN) == 0)
|
|
goto RRND1; /* if sign set, don't round yet */
|
|
expr += temp; /* add exponent */
|
|
|
|
if (expr & MSIGN) /* test for underflow */
|
|
goto DUNFLO; /* go process underflow */
|
|
|
|
if ((int32)expr > 0x7f) /* test for overflow */
|
|
goto DOVFLO; /* go process overflow */
|
|
|
|
expr ^= FMASK; /* complement exponent */
|
|
temp2 += 0x40; /* round at bit 25 */
|
|
goto RRND3; /* go merge code */
|
|
|
|
RRND1:
|
|
temp2 += 0x40; /* round at bit 25 */
|
|
RRND2:
|
|
expr += temp; /* add exponent */
|
|
|
|
if (expr & MSIGN) /* test for underflow */
|
|
goto DUNFLO; /* go process underflow */
|
|
|
|
if ((int32)expr > 0x7f) /* test for overflow */
|
|
goto DOVFLO; /* go process overflow */
|
|
|
|
if (sign & MSIGN) /* test for negative */
|
|
expr ^= FMASK; /* yes, complement exponent */
|
|
RRND3:
|
|
temp2 <<= 1; /* adjust fraction */
|
|
temp = (expr << 24) | (temp2 >> 8); /* merge exp & fraction */
|
|
goto setcc; /* go set CC's */
|
|
|
|
DOVFLO:
|
|
CC |= CC4BIT; /* set CC4 for exponent overflow */
|
|
DUNFLO:
|
|
CC |= CC1BIT; /* set CC1 for arithmetic exception */
|
|
if (sign & MSIGN) /* test for negative */
|
|
CC |= CC3BIT; /* set neg fraction bit CC3 */
|
|
else
|
|
CC |= CC2BIT; /* set pos fraction bit CC2 */
|
|
*cc = CC; /* return CC's */
|
|
/* return value is not valid, but return fixup value anyway */
|
|
switch ((CC >> 27) & 3) { /* rt justify CC3 & CC4 */
|
|
case 0:
|
|
return 0; /* pos underflow */
|
|
break;
|
|
case 1:
|
|
return 0x7fffffff; /* positive overflow */
|
|
break;
|
|
case 2:
|
|
return 0; /* neg underflow */
|
|
break;
|
|
case 3:
|
|
return 0x80000001; /* negative overflow */
|
|
break;
|
|
}
|
|
setcc:
|
|
/* come here to set cc's and return */
|
|
/* temp has return value */
|
|
if (temp & MSIGN)
|
|
CC |= CC3BIT; /* CC3 for neg */
|
|
else
|
|
if (temp == 0)
|
|
CC |= CC4BIT; /* CC4 for zero */
|
|
else
|
|
CC |= CC2BIT; /* CC2 for greater than zero */
|
|
/* return temp to destination reg */
|
|
*cc = CC; /* return CC's */
|
|
return temp; /* return result */
|
|
}
|
|
|
|
#define DMMASK 0x00ffffffffffffffLL /* double mantissa mask */
|
|
#define DCMASK 0x1000000000000000LL /* double carry mask */
|
|
#define DIBMASK 0x0fffffffffffffffLL /* double fp nibble mask */
|
|
#define DUMASK 0x0ffffffffffffff0LL /* double fp mask */
|
|
#define DNMASK 0x0f00000000000000LL /* double nibble mask */
|
|
#define DZMASK 0x00f0000000000000LL /* shifted nibble mask */
|
|
|
|
/* add memory floating point number to register floating point number */
|
|
/* this code creates an extra guard digit, so it is more accurate than SEL */
|
|
/* The code was modified to have the same results as SEL, so we will use this one */
|
|
/* set CC1 if overflow/underflow */
|
|
t_uint64 s_adfd(t_uint64 reg, t_uint64 mem, uint32 *cc)
|
|
{
|
|
t_uint64 res, ret;
|
|
uint8 sign = 0;
|
|
int er, em, temp;
|
|
uint32 CC;
|
|
|
|
/* first we want to make sure the numbers are normalized */
|
|
ret = s_normfd(reg, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
reg = ret; /* use normalized value */
|
|
if (mem == 0) { /* test for add of zero */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return normalized results */
|
|
}
|
|
|
|
ret = s_normfd(mem, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
mem = ret; /* use normalized value */
|
|
if (reg == 0) { /* test for add to zero */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results results */
|
|
}
|
|
|
|
/* process the memory operand value */
|
|
/* extract exponent and mantissa */
|
|
if (reg & DMSIGN) { /* reg negative */
|
|
sign |= 2; /* set neg flag */
|
|
reg = NEGATE32(reg); /* make negative positive */
|
|
}
|
|
er = (reg & DEXMASK) >> 56; /* extract reg exponent */
|
|
reg &= DMMASK; /* extract reg mantissa */
|
|
|
|
/* extract mem exponent and mantissa */
|
|
if (mem & DMSIGN) { /* mem negative */
|
|
sign |= 1; /* set neg flag */
|
|
mem = NEGATE32(mem); /* make negative positive */
|
|
}
|
|
em = (mem & DEXMASK) >> 56; /* extract mem exponent */
|
|
mem &= DMMASK; /* extract mem mantissa */
|
|
|
|
mem = mem << 4; /* align mem for normalization */
|
|
reg = reg << 4; /* align reg for normalization */
|
|
temp = er - em; /* get signed exp difference */
|
|
|
|
if (temp > 0) { /* reg exp > mem exp */
|
|
if (temp > 15) {
|
|
mem = 0; /* if too much difference, make zero */
|
|
} else {
|
|
/* Shift mem right if reg has larger exponent */
|
|
mem >>= (4 * temp); /* adjust for exponent difference */
|
|
}
|
|
} else
|
|
if (temp < 0) { /* reg < mem exp */
|
|
if (temp < -15) {
|
|
reg = 0; /* if too much difference, make zero */
|
|
} else
|
|
/* Shift reg right if mem larger */
|
|
reg >>= (4 * (-temp)); /* adjust for exponent difference */
|
|
er = em; /* make exponents the same */
|
|
}
|
|
|
|
/* er now has equal exponent for both values */
|
|
/* add results */
|
|
if (sign == 2 || sign == 1) {
|
|
/* different signs so do subtract */
|
|
mem ^= DIBMASK; /* complement the value and inc */
|
|
mem++; /* negate all but upper nibble */
|
|
res = reg + mem; /* add the values */
|
|
if (res & DCMASK) { /* see if carry */
|
|
res &= DIBMASK; /* clear off the carry bit */
|
|
} else {
|
|
sign ^= 2; /* flip the sign */
|
|
res ^= DIBMASK; /* and negate the value */
|
|
res++; /* negate all but the upper nibble */
|
|
}
|
|
} else {
|
|
res = reg + mem; /* same sign, just add */
|
|
if ((res & 0x0fffffffffffff80ll) != 0x0fffffffffffff80ll) {
|
|
if (sign == 3)
|
|
res += 7; /* round number */
|
|
if ((sign == 3) && (er == 0x7e))
|
|
res |= 0x0f00ll; /* round number more */
|
|
if (sign == 0)
|
|
res += 0xf; /* round number */
|
|
}
|
|
}
|
|
/* following statement effectively removes guard nibble to be like SEL */
|
|
res &= 0xfffffffffffffff0ll; /* remove extra bits */
|
|
|
|
/* If overflow, shift right 4 bits */
|
|
if (res & DCMASK) { /* see if overflow carry */
|
|
res >>= 4; /* move mantissa down 4 bits */
|
|
er++; /* and adjust exponent */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic overflow */
|
|
/* set CC2 & CC3 on exit */
|
|
CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
if (CC & CC3BIT) /* NEG overflow? */
|
|
res = 0x8000000000000001; /* double yes */
|
|
else
|
|
res = 0x7FFFFFFFFFFFFFFF; /* no, pos */
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
}
|
|
|
|
CC = 0;
|
|
/* Set condition codes */
|
|
if (res != 0) /* see if non zero */
|
|
CC |= (sign & 2) ? 1 : 2;
|
|
else {
|
|
er = sign = 0; /* we have zero CC4 */
|
|
}
|
|
|
|
/* normalize the fraction */
|
|
if (res != 0) { /* see if non zero */
|
|
while ((res != 0) && (res & DNMASK) == 0) {
|
|
res <<= 4; /* adjust mantisa by a nibble */
|
|
er--; /* and adjust exponent smaller by 1 */
|
|
}
|
|
/* Check if exponent underflow */
|
|
if (er < 0) {
|
|
/* UNDERFLOW */
|
|
CC |= CC1BIT; /* set arithmetic exception */
|
|
CC |= (sign & 2)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
res = 0; /* make all zero */
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
} else {
|
|
/* result is zero */
|
|
sign = er = 0; /* make abs zero */
|
|
}
|
|
|
|
res >>= 4; /* remove the carryout nibble */
|
|
res &= DMMASK; /* clear exponent */
|
|
|
|
res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */
|
|
|
|
/* Set condition codes */
|
|
if (CC == 0) {
|
|
CC = CC4BIT; /* set zero cc */
|
|
} else {
|
|
if (sign & 2) /* see if negative */
|
|
res = NEGATE32(res); /* make negative */
|
|
CC = (CC & 3) << 28; /* neg is CC3, pos is CC2 */
|
|
}
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
|
|
/* subtract memory floating point number from register floating point number */
|
|
t_uint64 s_sufd(t_uint64 reg, t_uint64 mem, uint32 *cc) {
|
|
return s_adfd(reg, NEGATE32(mem), cc);
|
|
}
|
|
|
|
/* multiply register floating point number by memory floating point number */
|
|
/* set CC1 if overflow/underflow */
|
|
/* use revised normalization code */
|
|
t_uint64 s_mpfd(t_uint64 reg, t_uint64 mem, uint32 *cc) {
|
|
t_uint64 res, ret;
|
|
int sign = 0;
|
|
int lsb = 0;
|
|
int er, em, temp;
|
|
uint32 CC;
|
|
|
|
/* first we want to make sure the numbers are normalized */
|
|
ret = s_normfd(reg, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
reg = ret; /* use normalized value */
|
|
|
|
ret = s_normfd(mem, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
mem = ret; /* use normalized value */
|
|
|
|
/* see if multiply by zero */
|
|
if ((reg == 0ll) || (mem == 0ll)) { /* test for mult by zero */
|
|
*cc = CC4BIT; /* set CC 4 for 0 */
|
|
return 0ll; /* return results results */
|
|
}
|
|
|
|
/* extract reg exponent and mantissa */
|
|
if (reg & DMSIGN) { /* reg negative */
|
|
sign ^= 1; /* set neg flag */
|
|
reg = NEGATE32(reg); /* make negative positive */
|
|
}
|
|
if (reg & 0x1ll) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
er = (reg & DEXMASK) >> 56; /* extract reg exponent */
|
|
reg &= DMMASK; /* extract reg mantissa */
|
|
|
|
/* extract mem exponent and mantissa */
|
|
if (mem & DMSIGN) { /* mem negative */
|
|
sign ^= 1; /* set neg flag */
|
|
mem = NEGATE32(mem); /* make negative positive */
|
|
}
|
|
if (mem & 0x1ll) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
em = (mem & DEXMASK) >> 56; /* extract mem exponent */
|
|
mem &= DMMASK; /* extract mem mantissa */
|
|
|
|
er = er + em - 0x40; /* get the exp value */
|
|
|
|
res = 0; /* zero result for multiply */
|
|
/* multiply by doing shifts and adds */
|
|
for (temp = 0; temp < 56; temp++) {
|
|
/* Add if we need too */
|
|
if (reg & 1)
|
|
res += mem;
|
|
/* Shift right by one */
|
|
reg >>= 1;
|
|
res >>= 1;
|
|
}
|
|
er++; /* adjust exp for extra nible shift */
|
|
|
|
/* fix up some boundry conditions */
|
|
if ((res >= 0x0010000000000000ll) && (sign == 1)) {
|
|
res += 0x1;
|
|
}
|
|
else
|
|
if ((res == 0x000FFFFFFFFFFFFFll) && (sign == 1) && (er != 1)) {
|
|
if (lsb == 0) {
|
|
if ((er == 0x41) || (er == 0x81)) {
|
|
er++;
|
|
}
|
|
} else {
|
|
res += 0x1ll;
|
|
}
|
|
}
|
|
|
|
/* If overflow, shift right 4 bits */
|
|
if (res & DEXMASK) { /* see if overflow carry */
|
|
res >>= 4; /* move mantissa down 4 bits */
|
|
er++; /* and adjust exponent */
|
|
if (er >= 0x80) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT; /* set arithmetic exception */
|
|
CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
if (CC & CC3BIT) /* NEG overflow? */
|
|
res = 0x8000000000000001ll; /* double yes */
|
|
else
|
|
res = 0x7FFFFFFFFFFFFFFFll; /* no, pos */
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
}
|
|
|
|
/* Align the results */
|
|
if (res != 0) {
|
|
while ((res != 0) && (res & DNMASK) == 0) {
|
|
res <<= 4; /* move over mantessa */
|
|
er--; /* reduce exponent cocunt by 1 */
|
|
if ((res == 0x00FFFFFFFFFFFFF0ll) && (sign == 1)) {
|
|
if (lsb == 0) {
|
|
er--;
|
|
}
|
|
else {
|
|
res += 0x10ll;
|
|
}
|
|
}
|
|
}
|
|
/* Check if overflow */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic exception */
|
|
if (sign & 1) {
|
|
CC |= CC3BIT; /* neg CC */
|
|
res = 0x8000000000000001ll; /* neg overflow 1011 */
|
|
} else {
|
|
CC |= CC2BIT; /* pos CC */
|
|
res = 0x7FFFFFFFFFFFFFFFll; /* pos overflow 1101 */
|
|
}
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
/* Check if underflow */
|
|
if (er < 0) {
|
|
/* UNDERFLOW */
|
|
res = 0; /* make return value zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
CC |= CC1BIT; /* set arithmetic exception */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
res >>= 4; /* remove guard nibble */
|
|
} else {
|
|
er = sign = 0; /* have real zero */
|
|
}
|
|
|
|
res &= DMMASK; /* clear exponent */
|
|
|
|
res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */
|
|
if (sign == 1) /* is result to be negative */
|
|
res = NEGATE32(res); /* make value negative */
|
|
|
|
/* determine CC's for result */
|
|
CC = 0;
|
|
if (res != 0) /* see if non zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
else
|
|
CC = CC4BIT; /* set zero cc */
|
|
|
|
/* return results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
|
|
/* divide register floating point number by memory floating point number */
|
|
/* set CC1 if overflow/underflow */
|
|
/* use revised normalization code */
|
|
t_uint64 s_dvfd(t_uint64 reg, t_uint64 mem, uint32 *cc) {
|
|
t_uint64 res, ret;
|
|
int sign = 0;
|
|
int sign2 = 0;
|
|
int lsb = 0;
|
|
int er, em, temp;
|
|
uint32 CC;
|
|
|
|
/* first we want to make sure the numbers are normalized */
|
|
ret = s_normfd(reg, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
reg = ret; /* use normalized value */
|
|
|
|
ret = s_normfd(mem, &CC); /* get the reg value */
|
|
if (CC & CC1BIT) { /* see if we have AE */
|
|
*cc = CC; /* save CC's */
|
|
return ret; /* return results */
|
|
}
|
|
mem = ret; /* use normalized value */
|
|
|
|
/* see if divide by or into zero */
|
|
if ((reg == 0ll) || (mem == 0ll)) { /* test for divide by zero */
|
|
*cc = CC4BIT; /* set CC 4 for 0 */
|
|
return 0ll; /* return results */
|
|
}
|
|
|
|
/* extract reg exponent and mantissa */
|
|
if (reg & DMSIGN) { /* reg negative */
|
|
sign ^= 1; /* set neg flag */
|
|
reg = NEGATE32(reg); /* make negative positive */
|
|
sign2 |= 2; /* set neg flag */
|
|
}
|
|
if (reg & 0x1ll) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
er = (reg & DEXMASK) >> 56; /* extract reg exponent */
|
|
reg &= DMMASK; /* extract reg mantissa */
|
|
|
|
/* extract mem exponent and mantissa */
|
|
if (mem & DMSIGN) { /* mem negative */
|
|
sign ^= 1; /* set neg flag */
|
|
mem = NEGATE32(mem); /* make negative positive */
|
|
sign2 |= 1; /* set neg flag */
|
|
}
|
|
if (mem & 0x1ll) /* test lsb */
|
|
lsb = 1; /* reg is odd */
|
|
em = (mem & DEXMASK) >> 56; /* extract mem exponent */
|
|
mem &= DMMASK; /* extract mem mantissa */
|
|
|
|
er = er - em + 0x40; /* get the exp value */
|
|
|
|
/* move left 1 nibble for divide */
|
|
/* Shift numbers up 4 bits so as not to lose precision below */
|
|
reg <<= 4;
|
|
mem <<= 4;
|
|
|
|
/* see if we need to adjust divisor if larger that dividend */
|
|
if (reg > mem) {
|
|
reg >>= 4;
|
|
er++;
|
|
}
|
|
|
|
mem ^= DIBMASK; /* change sign of mem val to do add */
|
|
mem++; /* comp & incr */
|
|
|
|
res = 0; /* zero result for multiply */
|
|
/* do divide by using shift & add (subt) */
|
|
for (temp = 56; temp > 0; temp--) {
|
|
t_uint64 tmp;
|
|
|
|
/* Add if we need too */
|
|
/* Shift left by one */
|
|
reg <<= 1;
|
|
/* Subtract remainder to dividend */
|
|
tmp = reg + mem;
|
|
|
|
/* Shift quotent left one bit */
|
|
res <<= 1;
|
|
|
|
/* If remainder larger then divisor replace */
|
|
if ((tmp & DCMASK) != 0) {
|
|
reg = tmp;
|
|
res |= 1;
|
|
}
|
|
}
|
|
|
|
/* Compute one final set to see if rounding needed */
|
|
/* Shift left by one */
|
|
reg <<= 1;
|
|
/* Subtract remainder to dividend */
|
|
reg += mem;
|
|
|
|
/* If .5 off, round, but do not cause carry overflow */
|
|
if (((reg & DMSIGN) != 0) && (res != 0x00FFFFFFFFFFFFFFll)){
|
|
res++;
|
|
}
|
|
|
|
/* fix up some boundry condtions to make diags happy */
|
|
if (res == 0x00FFFFFFFFFFFFF1ll) {
|
|
res += 0x0fll; /* round up by nibble */
|
|
}
|
|
else
|
|
if (res == 0x00FFFFFFFFFFFFF8ll) {
|
|
res &= 0x0FFFFFFFFFFFFFC0ll; /* remove some extra bits */
|
|
}
|
|
else
|
|
if (res == 0x00FFFFFFFFFFFFFFll) {
|
|
if (lsb == 0) {
|
|
res += 0x1ll; /* round up by bit to force carry */
|
|
} else {
|
|
if (sign) {
|
|
if (sign2 == 1) {
|
|
res &= 0x00FFFFFFFFFFFFF0ll; /* clear last nibble */
|
|
} else {
|
|
res += 0x1ll; /* round up by bit to for carry */
|
|
}
|
|
} else {
|
|
if (sign2 == 3) {
|
|
res += 0x1ll; /* round up by bit to for carry */
|
|
} else {
|
|
res &= 0x00FFFFFFFFFFFFF0ll; /* clear last nibble */
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* diags should be happy now */
|
|
|
|
/* If overflow, shift right 4 bits */
|
|
if (res & DEXMASK) { /* see if overflow carry */
|
|
res >>= 4; /* move mantissa down 4 bits */
|
|
er++; /* and adjust exponent */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic exception */
|
|
CC |= (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
if (CC & CC3BIT) /* NEG overflow? */
|
|
res = 0x8000000000000001ll; /* double yes */
|
|
else
|
|
res = 0x7FFFFFFFFFFFFFFFll; /* no, pos */
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
}
|
|
|
|
/* Align the results */
|
|
if ((res) != 0) {
|
|
while ((res != 0) && (res & DZMASK) == 0) {
|
|
res <<= 4;
|
|
er--;
|
|
}
|
|
/* Check if overflow */
|
|
if (er >= 128) { /* if exponent is too large, overflow */
|
|
/* OVERFLOW */
|
|
CC = CC1BIT|CC4BIT; /* set arithmetic exception */
|
|
if (sign & 1) {
|
|
CC |= CC3BIT;
|
|
res = 0x8000000000000001ll; /* neg overflow 1011 */
|
|
} else {
|
|
CC |= CC2BIT;
|
|
res = 0x7FFFFFFFFFFFFFFFll; /* pos overflow 1101 */
|
|
}
|
|
/* store results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
/* Check if underflow */
|
|
if (er < 0) {
|
|
/* UNDERFLOW */
|
|
res = 0; /* make return value zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
CC |= CC1BIT; /* set arithmetic exception */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
} else {
|
|
er = sign = 0;
|
|
}
|
|
|
|
res &= DMMASK; /* clear exponent space */
|
|
|
|
res |= ((((t_uint64)er) << 56) & DEXMASK); /* merge exp and mantissa */
|
|
if (sign & 1) /* is result to be negative */
|
|
res = NEGATE32(res); /* make value negative */
|
|
|
|
/* determine CC's for result */
|
|
CC = 0;
|
|
if (res != 0) /* see if non zero */
|
|
CC = (sign & 1)?CC3BIT:CC2BIT; /* neg is CC3, pos is CC2 */
|
|
else
|
|
CC = CC4BIT; /* set zero cc */
|
|
|
|
/* return results */
|
|
*cc = CC; /* save CC's */
|
|
return res; /* return results */
|
|
}
|
|
|