#include <sys/param.h>
#include <sys/proc.h>
#include <machine/fpu.h>
#include <machine/frame.h>
#include <machine/ieeefp.h>
#include <lib/libkern/softfloat.h>
#include <m88k/m88k/fpu.h>
#define CC_UN 0x00000001
#define CC_LEG 0x00000002
#define CC_EQ 0x00000004
#define CC_NE 0x00000008
#define CC_GT 0x00000010
#define CC_LE 0x00000020
#define CC_LT 0x00000040
#define CC_GE 0x00000080
#define CC_OU 0x00000100
#define CC_IB 0x00000200
#define CC_IN 0x00000400
#define CC_OB 0x00000800
#define CC_UE 0x00001000
#define CC_LG 0x00002000
#define CC_UG 0x00004000
#define CC_ULE 0x00008000
#define CC_UL 0x00010000
#define CC_UGE 0x00020000
#define float32_is_nan(a) \
(0xff000000 < (a << 1))
#define float32_is_signaling_nan(a) \
((((a >> 22) & 0x1ff) == 0x1fe) && (a & 0x003fffff))
void
fpu_store(struct trapframe *frame, u_int regno, u_int orig_width, u_int width,
fparg *src)
{
u_int32_t tmp;
u_int rd;
switch (width) {
case FTYPE_INT:
rd = float_get_round(frame->tf_fpcr);
switch (orig_width) {
case FTYPE_SNG:
if (rd == FP_RZ)
tmp = float32_to_int32_round_to_zero(src->sng);
else
tmp = float32_to_int32(src->sng);
break;
case FTYPE_DBL:
if (rd == FP_RZ)
tmp = float64_to_int32_round_to_zero(src->dbl);
else
tmp = float64_to_int32(src->dbl);
break;
}
if (regno != 0)
frame->tf_r[regno] = tmp;
break;
case FTYPE_SNG:
switch (orig_width) {
case FTYPE_SNG:
tmp = src->sng;
break;
case FTYPE_DBL:
tmp = float64_to_float32(src->dbl);
break;
}
if (regno != 0)
frame->tf_r[regno] = tmp;
break;
case FTYPE_DBL:
switch (orig_width) {
case FTYPE_DBL:
tmp = (u_int32_t)(src->dbl >> 32);
if (regno != 0)
frame->tf_r[regno] = tmp;
tmp = (u_int32_t)src->dbl;
if (regno != 31)
frame->tf_r[regno + 1] = tmp;
break;
}
break;
}
}
u_int
fpu_precision(u_int ts1, u_int ts2, u_int td)
{
return max(td, max(ts1, ts2));
}
void
fpu_compare(struct trapframe *frame, fparg *s1, fparg *s2, u_int width,
u_int rd, u_int fcmpu)
{
u_int32_t cc;
int zero, s1positive, s2positive;
switch (width) {
case FTYPE_SNG:
if (float32_is_nan(s1->sng)) {
if (!fcmpu || float32_is_signaling_nan(s1->sng))
float_set_invalid();
cc = CC_UN;
goto done;
}
if (float32_is_nan(s2->sng)) {
if (!fcmpu || float32_is_signaling_nan(s2->sng))
float_set_invalid();
cc = CC_UN;
goto done;
}
break;
case FTYPE_DBL:
if (float64_is_nan(s1->dbl)) {
if (!fcmpu || float64_is_signaling_nan(s1->dbl))
float_set_invalid();
cc = CC_UN;
goto done;
}
if (float64_is_nan(s2->dbl)) {
if (!fcmpu || float64_is_signaling_nan(s2->dbl))
float_set_invalid();
cc = CC_UN;
goto done;
}
break;
}
switch (width) {
case FTYPE_SNG:
if (float32_eq(s1->sng, s2->sng))
cc = CC_EQ;
else if (float32_lt(s1->sng, s2->sng))
cc = CC_LT | CC_NE;
else
cc = CC_GT | CC_NE;
break;
case FTYPE_DBL:
if (float64_eq(s1->dbl, s2->dbl))
cc = CC_EQ;
else if (float64_lt(s1->dbl, s2->dbl))
cc = CC_LT | CC_NE;
else
cc = CC_GT | CC_NE;
break;
}
done:
if (cc & CC_UN)
cc |= CC_NE | CC_UE | CC_UG | CC_ULE | CC_UL | CC_UGE;
if (cc & CC_EQ)
cc |= CC_LE | CC_GE | CC_UE;
if (cc & CC_GT)
cc |= CC_GE;
if (cc & CC_LT)
cc |= CC_LE;
if (cc & (CC_LT | CC_GT))
cc |= CC_LG;
if (cc & (CC_LT | CC_GT | CC_EQ))
cc |= CC_LEG;
if (cc & CC_GT)
cc |= CC_UG;
if (cc & CC_LE)
cc |= CC_ULE;
if (cc & CC_LT)
cc |= CC_UL;
if (cc & CC_GE)
cc |= CC_UGE;
#ifdef M88100
if (CPU_IS88100) {
cc &= ~(CC_UE | CC_LG | CC_UG | CC_ULE | CC_UL | CC_UGE);
}
#endif
if (!(cc & CC_UN)) {
switch (width) {
case FTYPE_SNG:
s2positive = s2->sng >> 31 == 0;
break;
case FTYPE_DBL:
s2positive = s2->dbl >> 63 == 0;
break;
}
if (!s2positive)
goto completed;
if (cc & CC_EQ) {
cc |= CC_IB | CC_OB;
goto completed;
}
switch (width) {
case FTYPE_SNG:
zero = float32_eq(s1->sng, 0);
break;
case FTYPE_DBL:
zero = float64_eq(s1->dbl, 0LL);
break;
}
if (zero) {
cc |= CC_IB | CC_OB;
goto completed;
}
if (cc & CC_GT) {
cc |= CC_OU | CC_OB;
} else {
switch (width) {
case FTYPE_SNG:
s1positive = s1->sng >> 31 == 0;
break;
case FTYPE_DBL:
s1positive = s1->dbl >> 63 == 0;
break;
}
if (s1positive) {
cc |= CC_IB | CC_IN;
} else {
cc |= CC_OU | CC_OB;
}
}
}
completed:
if (rd != 0)
frame->tf_r[rd] = cc;
}