421 lines
19 KiB
C
421 lines
19 KiB
C
/* sigma_fp.c: XDS Sigma floating point simulator
|
|
|
|
Copyright (c) 2007-2008, Robert M Supnik
|
|
|
|
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
|
|
ROBERT M SUPNIK 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 Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
*/
|
|
|
|
#include "sigma_defs.h"
|
|
|
|
#define UFP_V_GUARD 4
|
|
#define UFP_NORM (FP_NORM << UFP_V_GUARD)
|
|
#define UFP_CARRY (UFP_NORM << 4)
|
|
#define UFP_FRHI (UFP_CARRY|UFP_NORM|FP_M_FRHI)
|
|
#define UFP_FRLO 0xFFFFFFFF
|
|
|
|
/* Double precision fraction add/subtract/compare */
|
|
/* Note: UFP_ADD (s, r, r) will not work!!! */
|
|
|
|
#define UFP_ADD(s1,s2,d) do { \
|
|
d.l = (s1.l + s2.l) & UFP_FRLO; \
|
|
d.h = (s1.h + s2.h + (d.l < s2.l)) & UFP_FRHI; \
|
|
} while (0)
|
|
|
|
#define UFP_SUB(s1,s2,d) do { \
|
|
d.h = (s1.h - s2.h - (s1.l < s2.l)) & UFP_FRHI; \
|
|
d.l = (s1.l - s2.l) & UFP_FRLO; \
|
|
} while (0)
|
|
|
|
#define UFP_GE(s1,s2) ((s1.h > s2.h) || \
|
|
((s1.h == s2.h) && (s1.l >= s2.l)))
|
|
|
|
/* Variable and constant shifts; for constants, 0 < k < 32 */
|
|
|
|
#define UFP_RSH_V(v,s) do { \
|
|
if ((s) < 32) { \
|
|
v.l = ((v.l >> (s)) | \
|
|
( v.h << (32 - (s)))) & UFP_FRLO; \
|
|
v.h = v.h >> (s); \
|
|
} \
|
|
else if ((s) < 64) { \
|
|
v.l = v.h >> ((s) - 32); \
|
|
v.h = 0; \
|
|
} \
|
|
else v.l = v.h = 0; \
|
|
} while (0)
|
|
|
|
#define UFP_RSH_K(v,s) do { \
|
|
v.l = ((v.l >> (s)) | \
|
|
(v.h << (32 - (s)))) & UFP_FRLO; \
|
|
v.h = v.h >> (s); \
|
|
} while (0)
|
|
|
|
#define UFP_LSH_K(v,s) do { \
|
|
v.h = ((v.h << (s)) | \
|
|
(v.l >> (32 - (s)))) & UFP_FRHI; \
|
|
v.l = (v.l << (s)) & UFP_FRLO; \
|
|
} while (0)
|
|
|
|
#define UFP_RSH_KP(v,s) do { \
|
|
v->l = ((v->l >> (s)) | \
|
|
(v->h << (32 - (s)))) & UFP_FRLO; \
|
|
v->h = v->h >> (s); \
|
|
} while (0)
|
|
|
|
#define UFP_LSH_KP(v,s) do { \
|
|
v->h = ((v->h << (s)) | \
|
|
(v->l >> (32 - (s)))) & UFP_FRHI; \
|
|
v->l = (v->l << (s)) & UFP_FRLO; \
|
|
} while (0)
|
|
|
|
typedef struct {
|
|
uint32 sign;
|
|
int32 exp;
|
|
uint32 h;
|
|
uint32 l;
|
|
} ufp_t;
|
|
|
|
extern uint32 *R;
|
|
extern uint32 PSW1;
|
|
extern uint32 CC;
|
|
|
|
void fp_unpack (uint32 hi, uint32 lo, ufp_t *dst);
|
|
t_bool fp_clnzro (ufp_t *src, t_bool abnorm);
|
|
uint32 fp_pack (ufp_t *src, uint32 rn, t_bool dbl, t_bool rndtrap);
|
|
uint32 fp_norm (ufp_t *src);
|
|
|
|
uint32 fp (uint32 op, uint32 rn, uint32 bva)
|
|
{
|
|
uint32 rh, rl, mh, ml, i, ediff, nsh;
|
|
t_bool s1nz, s2nz;
|
|
t_bool dbl = ((op & 0x20) == 0);
|
|
ufp_t fop1, fop2, t;
|
|
ufp_t res = { 0, 0, 0, 0 };
|
|
uint32 tr;
|
|
|
|
if (dbl) { /* double prec? */
|
|
rh = R[rn]; /* get reg operands */
|
|
rl = R[rn|1];
|
|
if ((tr = ReadD (bva, &mh, &ml, VR)) != 0) /* get mem word */
|
|
return tr;
|
|
}
|
|
else { /* single precision */
|
|
rh = R[rn]; /* pad to double */
|
|
rl = 0;
|
|
if ((tr = ReadW (bva, &mh, VR)) != 0)
|
|
return tr;
|
|
ml = 0;
|
|
}
|
|
fp_unpack (rh, rl, &fop1); /* unpack, test */
|
|
fp_unpack (mh, ml, &fop2);
|
|
CC = 0;
|
|
|
|
switch (op) { /* case on opcode */
|
|
|
|
case OP_FSS: /* subtract */
|
|
case OP_FSL:
|
|
fop2.sign = fop2.sign ^ 1; /* invert mem sign */
|
|
/* fall through */
|
|
case OP_FAS: /* add */
|
|
case OP_FAL:
|
|
s1nz = fp_clnzro (&fop1, TRUE); /* test, clean op1 */
|
|
s2nz = fp_clnzro (&fop2, TRUE);
|
|
if (!s1nz) /* op1 = 0? res = op2 */
|
|
res = fop2;
|
|
else if (!s2nz) /* op2 = 0? res = op1 */
|
|
res = fop1;
|
|
else { /* both non-zero */
|
|
if (fop1.exp < fop2.exp) { /* exp1 < exp2? */
|
|
t = fop2; /* swap operands */
|
|
fop2 = fop1;
|
|
fop1 = t;
|
|
}
|
|
ediff = fop1.exp - fop2.exp; /* exp difference */
|
|
res.sign = fop1.sign; /* result sign, exp */
|
|
res.exp = fop1.exp;
|
|
if (ediff) { /* any difference? */
|
|
UFP_RSH_V (fop2, ediff * 4); /* shift frac */
|
|
if (dbl) { /* double? */
|
|
if ((PSW1 & PSW1_FR) == 0) /* rounding off? */
|
|
fop2.l &= ~0xF; /* no guard */
|
|
}
|
|
else fop2.l = 0; /* single? clr lo */
|
|
}
|
|
if (fop1.sign ^ fop2.sign) { /* eff subtract */
|
|
if (UFP_GE (fop1, fop2)) { /* fop1 >= fop2? */
|
|
UFP_SUB (fop1, fop2, res); /* sub fractions */
|
|
}
|
|
else { /* fop2 > fop1 */
|
|
UFP_SUB (fop2, fop1, res); /* rev subtract */
|
|
res.sign = fop2.sign; /* change signs */
|
|
}
|
|
} /* end subtract */
|
|
else { /* eff add */
|
|
UFP_ADD (fop1, fop2, res); /* add fractions */
|
|
if (res.h & UFP_CARRY) { /* carry out? */
|
|
UFP_RSH_K (res, 4); /* renormalize */
|
|
res.exp = res.exp + 1; /* incr exp */
|
|
}
|
|
} /* end add */
|
|
} /* end nz operands */
|
|
if (!dbl) /* single? clr lo */
|
|
res.l = 0;
|
|
if ((PSW1 & PSW1_FN) == 0) { /* postnormalize? */
|
|
if ((res.h | res.l) == 0) { /* result zero? */
|
|
CC = CC1; /* set signif flag */
|
|
if (PSW1 & PSW1_FS) /* trap enabled? */
|
|
return TR_FLT;
|
|
return fp_pack (&res, rn, dbl, FALSE); /* pack up */
|
|
}
|
|
nsh = fp_norm (&res); /* normalize */
|
|
if ((res.exp < 0) && /* underflow? */
|
|
!(PSW1 & PSW1_FZ) && /* !FN */
|
|
(PSW1 & PSW1_FS) && /* FS */
|
|
(nsh > 2)) { /* shifts > 2? */
|
|
CC = CC1 | (res.sign? CC4: CC3); /* signif CC's */
|
|
return TR_FLT; /* trap */
|
|
} /* end if underflow */
|
|
else if (nsh > 2) { /* shifts > 2? */
|
|
CC |= CC1 | (res.sign? CC4: CC3); /* set CC1 */
|
|
if (PSW1 & PSW1_FS) /* trap enabled? */
|
|
return TR_FLT;
|
|
}
|
|
} /* end if postnorm */
|
|
return fp_pack (&res, rn, dbl, TRUE); /* pack result */
|
|
|
|
case OP_FMS:
|
|
case OP_FML: /* floating multiply */
|
|
s1nz = fp_clnzro (&fop1, FALSE); /* test, clean op1 */
|
|
s2nz = fp_clnzro (&fop2, FALSE);
|
|
if (s1nz && s2nz) { /* both non-zero? */
|
|
fp_norm (&fop1); /* prenormalize */
|
|
fp_norm (&fop2);
|
|
UFP_RSH_K (fop2, 4); /* undo guard */
|
|
res.sign = fop1.sign ^ fop2.sign; /* result sign */
|
|
res.exp = fop1.exp + fop2.exp - FP_BIAS; /* result exp */
|
|
if (!dbl) { /* 24b x 24b? */
|
|
for (i = 0; i < 24; i++) { /* 24 iterations */
|
|
if (fop2.h & 1)
|
|
res.h = res.h + fop1.h; /* add hi only */
|
|
UFP_RSH_K (res, 1); /* shift dp res */
|
|
fop2.h = fop2.h >> 1;
|
|
}
|
|
res.l = 0; /* single prec */
|
|
}
|
|
else { /* some low 0's */
|
|
for (i = 0; i < 56; i++) { /* 56 iterations */
|
|
if (fop2.l & 1) {
|
|
UFP_ADD (res, fop1, res);
|
|
}
|
|
UFP_RSH_K (res, 1);
|
|
UFP_RSH_K (fop2, 1);
|
|
}
|
|
}
|
|
fp_norm (&res); /* normalize result */
|
|
}
|
|
return fp_pack (&res, rn, dbl, TRUE); /* pack result */
|
|
|
|
case OP_FDS:
|
|
case OP_FDL: /* floating divide */
|
|
s1nz = fp_clnzro (&fop1, FALSE); /* test, clean op1 */
|
|
s2nz = fp_clnzro (&fop2, FALSE);
|
|
if (!s2nz) { /* divide by zero? */
|
|
CC = CC2; /* set CC2 */
|
|
return TR_FLT; /* trap */
|
|
}
|
|
if (s1nz) { /* divd non-zero? */
|
|
fp_norm (&fop1); /* prenormalize */
|
|
fp_norm (&fop2);
|
|
res.sign = fop1.sign ^ fop2.sign; /* result sign */
|
|
res.exp = fop1.exp - fop2.exp + FP_BIAS; /* result exp */
|
|
if (!UFP_GE (fop1, fop2)) {
|
|
UFP_LSH_K (fop1, 4); /* ensure success */
|
|
}
|
|
else res.exp = res.exp + 1; /* incr exponent */
|
|
for (i = 0; i < (uint32)(dbl? 15: 7); i++) {/* 7/15 hex digits */
|
|
UFP_LSH_K (res, 4); /* shift quotient */
|
|
while (UFP_GE (fop1, fop2)) { /* while sub works */
|
|
UFP_SUB (fop1, fop2, fop1); /* decrement */
|
|
res.l = res.l + 1; /* add quo bit */
|
|
}
|
|
UFP_LSH_K (fop1, 4); /* shift divd */
|
|
} /* end hex loop */
|
|
if (!dbl) { /* single? */
|
|
res.h = res.l; /* move quotient */
|
|
res.l = 0;
|
|
}
|
|
fp_norm (&res); /* normalize result */
|
|
}
|
|
return fp_pack (&res, rn, dbl, TRUE); /* pack result */
|
|
} /* end case */
|
|
|
|
return SCPE_IERR;
|
|
}
|
|
|
|
void ShiftF (uint32 rn, uint32 stype, uint32 sc)
|
|
{
|
|
uint32 opnd, opnd1;
|
|
ufp_t src;
|
|
|
|
opnd = R[rn]; /* get operands */
|
|
opnd1 = stype? R[rn|1]: 0; /* zextend single */
|
|
fp_unpack (opnd, opnd1, &src); /* unpack */
|
|
|
|
CC = 0;
|
|
if (sc & SCSIGN) { /* right? */
|
|
sc = SHF_M_SC + 1 - sc;
|
|
while (sc > 0) { /* while count */
|
|
UFP_RSH_K (src, 4); /* shift right hex */
|
|
if (stype) /* zero guard */
|
|
src.l &= ~0xF;
|
|
else src.h &= ~0xF;
|
|
src.exp++; /* incr exp */
|
|
sc--;
|
|
if (src.exp > FP_M_EXP) { /* overflow? */
|
|
CC |= CC2; /* set CC2, stop */
|
|
break;
|
|
}
|
|
} /* end while */
|
|
if ((src.h | src.l) == 0) { /* result 0? */
|
|
if (stype) /* result is true 0 */
|
|
R[rn|1] = 0;
|
|
R[rn] = 0;
|
|
CC = 0;
|
|
return;
|
|
}
|
|
}
|
|
else { /* left */
|
|
if ((src.h | src.l) == 0) { /* fraction 0? */
|
|
if (stype) /* result is true 0 */
|
|
R[rn|1] = 0;
|
|
R[rn] = 0;
|
|
CC = CC1;
|
|
return;
|
|
}
|
|
while ((sc > 0) && ((src.h & UFP_NORM) == 0)) { /* while count & !norm */
|
|
UFP_LSH_K (src, 4); /* hex shift left */
|
|
src.exp--; /* decr exp */
|
|
sc--;
|
|
if (src.exp < 0) { /* underflow? */
|
|
CC |= CC2; /* set CC2, stop */
|
|
break;
|
|
}
|
|
} /* end while */
|
|
if (src.h & UFP_NORM) /* normalized? */
|
|
CC |= CC1; /* set CC1 */
|
|
}
|
|
fp_pack (&src, rn, stype, FALSE); /* pack result */
|
|
return;
|
|
}
|
|
|
|
void fp_unpack (uint32 hi, uint32 lo, ufp_t *dst)
|
|
{
|
|
dst->sign = FP_GETSIGN (hi); /* get sign */
|
|
if (dst->sign) /* negative? */
|
|
NEG_D (hi, lo); /* 2's compl */
|
|
dst->h = FP_GETFRHI (hi); /* get fraction */
|
|
dst->l = FP_GETFRLO (lo);
|
|
dst->exp = FP_GETEXP (hi); /* get exp */
|
|
UFP_LSH_KP (dst, 4); /* guard result */
|
|
return;
|
|
}
|
|
|
|
/* Test for and clean a floating point zero
|
|
abnorm defines whether to allow "abnormal" zeros */
|
|
|
|
t_bool fp_clnzro (ufp_t *src, t_bool abnorm)
|
|
{
|
|
if (((src->h | src->l) == 0) && /* frac zero and */
|
|
(!abnorm || (src->exp == 0))) { /* exp zero or !ab */
|
|
src->sign = 0; /* true zero */
|
|
src->exp = 0;
|
|
return FALSE;
|
|
}
|
|
return TRUE; /* non-zero */
|
|
}
|
|
|
|
uint32 fp_pack (ufp_t *src, uint32 rn, t_bool dbl, t_bool rndtrap)
|
|
{
|
|
static ufp_t fp_zero = { 0, 0, 0, 0};
|
|
uint32 opnd, opnd1;
|
|
|
|
if (src->h || (dbl && src->l)) { /* result != 0? */
|
|
CC |= (src->sign? CC4: CC3); /* set CC's */
|
|
if (rndtrap) { /* round, trap? */
|
|
if (PSW1 & PSW1_FR) { /* round? */
|
|
if (dbl) { /* double prec? */
|
|
src->l = (src->l + 0x8) & UFP_FRLO;
|
|
src->h = src->h + (src->l < 0x8);
|
|
}
|
|
else src->h = src->h + 0x8; /* no, single */
|
|
if (src->h & UFP_CARRY) { /* carry out? */
|
|
UFP_RSH_KP (src, 4); /* renormalize */
|
|
src->exp = src->exp + 1;
|
|
}
|
|
} /* end if round */
|
|
if (src->exp > FP_M_EXP) { /* overflow? */
|
|
CC |= CC2; /* flag */
|
|
return TR_FLT;
|
|
}
|
|
else if (src->exp < 0) { /* underflow? */
|
|
if (PSW1 & PSW1_FZ) { /* trap enabled? */
|
|
CC |= CC1 | CC2; /* flag */
|
|
return TR_FLT;
|
|
}
|
|
*src = fp_zero; /* result 0 */
|
|
CC = CC1|CC2; /* special CC's */
|
|
}
|
|
} /* end rnd trap */
|
|
UFP_RSH_KP (src, 4); /* remove guard */
|
|
if (!dbl) /* single? lose lower */
|
|
src->l = 0;
|
|
if ((src->h | src->l) == 0) /* result now 0? */
|
|
src->exp = src->sign = 0;
|
|
}
|
|
else *src = fp_zero;
|
|
opnd = ((src->exp & FP_M_EXP) << FP_V_EXP) | /* repack */
|
|
((src->h & FP_M_FRHI) << FP_V_FRHI);
|
|
opnd1 = src->l & FP_M_FRLO;
|
|
if (src->sign) /* negative? */
|
|
NEG_D (opnd, opnd1);
|
|
R[rn] = opnd; /* store result */
|
|
if (dbl && ((rn & 1) == 0))
|
|
R[rn|1] = opnd1;
|
|
return 0;
|
|
}
|
|
|
|
uint32 fp_norm (ufp_t *src)
|
|
{
|
|
uint32 nsh;
|
|
|
|
nsh = 0;
|
|
src->h &= UFP_FRHI;
|
|
if (src->h || src->l) { /* if non-zero */
|
|
while ((src->h & UFP_NORM) == 0) { /* until normalized */
|
|
UFP_LSH_KP (src, 4); /* hex shift left */
|
|
src->exp--; /* decr exponent */
|
|
nsh++; /* count shifts */
|
|
}
|
|
}
|
|
return nsh;
|
|
}
|
|
|