#include "sim-main.h"
#define CPU cpu
#define SD CPU_STATE(cpu)
#define FP_PS_upper(v) (((v) >> 32) & (unsigned)0xFFFFFFFF)
#define FP_PS_lower(v) ((v) & (unsigned)0xFFFFFFFF)
#define FP_PS_cat(u,l) (((unsigned64)((u) & (unsigned)0xFFFFFFFF) << 32) \
| (unsigned64)((l) & 0xFFFFFFFF))
#define FPQNaN_SINGLE (0x7FBFFFFF)
#define FPQNaN_WORD (0x7FFFFFFF)
#define FPQNaN_DOUBLE (UNSIGNED64 (0x7FF7FFFFFFFFFFFF))
#define FPQNaN_LONG (UNSIGNED64 (0x7FFFFFFFFFFFFFFF))
#define FPQNaN_PS (FP_PS_cat (FPQNaN_SINGLE, FPQNaN_SINGLE))
static const char *fpu_format_name (FP_formats fmt);
#ifdef DEBUG
static const char *fpu_rounding_mode_name (int rm);
#endif
uword64
value_fpr (sim_cpu *cpu,
address_word cia,
int fpr,
FP_formats fmt)
{
uword64 value = 0;
int err = 0;
if ((fmt == fmt_uninterpreted) || (fmt == fmt_unknown))
{
#if 1
fmt = FPR_STATE[fpr];
#else
fmt = fmt_long;
#endif
}
if (FPR_STATE[fpr] == fmt_uninterpreted)
{
FPR_STATE[fpr] = fmt;
#ifdef DEBUG
printf ("DBG: Register %d was fmt_uninterpreted. Now %s\n", fpr,
fpu_format_name (fmt));
#endif
}
if (fmt != FPR_STATE[fpr])
{
sim_io_eprintf (SD, "FPR %d (format %s) being accessed with format %s - setting to unknown (PC = 0x%s)\n",
fpr, fpu_format_name (FPR_STATE[fpr]),
fpu_format_name (fmt), pr_addr (cia));
FPR_STATE[fpr] = fmt_unknown;
}
if (FPR_STATE[fpr] == fmt_unknown)
{
switch (fmt)
{
case fmt_single: value = FPQNaN_SINGLE; break;
case fmt_double: value = FPQNaN_DOUBLE; break;
case fmt_word: value = FPQNaN_WORD; break;
case fmt_long: value = FPQNaN_LONG; break;
case fmt_ps: value = FPQNaN_PS; break;
default: err = -1; break;
}
}
else if (SizeFGR () == 64)
{
switch (fmt)
{
case fmt_single:
case fmt_word:
value = (FGR[fpr] & 0xFFFFFFFF);
break;
case fmt_uninterpreted:
case fmt_double:
case fmt_long:
case fmt_ps:
value = FGR[fpr];
break;
default:
err = -1;
break;
}
}
else
{
switch (fmt)
{
case fmt_single:
case fmt_word:
value = (FGR[fpr] & 0xFFFFFFFF);
break;
case fmt_uninterpreted:
case fmt_double:
case fmt_long:
if ((fpr & 1) == 0)
{
#ifdef DEBUG
printf ("DBG: ValueFPR: FGR[%d] = %s, FGR[%d] = %s\n",
fpr + 1, pr_uword64 ((uword64) FGR[fpr+1]),
fpr, pr_uword64 ((uword64) FGR[fpr]));
#endif
value = ((((uword64) FGR[fpr+1]) << 32)
| (FGR[fpr] & 0xFFFFFFFF));
}
else
{
SignalException (ReservedInstruction, 0);
}
break;
case fmt_ps:
SignalException (ReservedInstruction, 0);
break;
default:
err = -1;
break;
}
}
if (err)
SignalExceptionSimulatorFault ("Unrecognised FP format in ValueFPR ()");
#ifdef DEBUG
printf ("DBG: ValueFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d\n",
fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
SizeFGR ());
#endif
return (value);
}
void
store_fpr (sim_cpu *cpu,
address_word cia,
int fpr,
FP_formats fmt,
uword64 value)
{
int err = 0;
#ifdef DEBUG
printf ("DBG: StoreFPR: fpr = %d, fmt = %s, value = 0x%s : PC = 0x%s : SizeFGR () = %d, \n",
fpr, fpu_format_name (fmt), pr_uword64 (value), pr_addr (cia),
SizeFGR ());
#endif
if (SizeFGR () == 64)
{
switch (fmt)
{
case fmt_uninterpreted_32:
fmt = fmt_uninterpreted;
case fmt_single:
case fmt_word:
if (STATE_VERBOSE_P (SD))
sim_io_eprintf (SD,
"Warning: PC 0x%s: interp.c store_fpr DEADCODE\n",
pr_addr (cia));
FGR[fpr] = (((uword64) 0xDEADC0DE << 32) | (value & 0xFFFFFFFF));
FPR_STATE[fpr] = fmt;
break;
case fmt_uninterpreted_64:
fmt = fmt_uninterpreted;
case fmt_uninterpreted:
case fmt_double:
case fmt_long:
case fmt_ps:
FGR[fpr] = value;
FPR_STATE[fpr] = fmt;
break;
default:
FPR_STATE[fpr] = fmt_unknown;
err = -1;
break;
}
}
else
{
switch (fmt)
{
case fmt_uninterpreted_32:
fmt = fmt_uninterpreted;
case fmt_single:
case fmt_word:
FGR[fpr] = (value & 0xFFFFFFFF);
FPR_STATE[fpr] = fmt;
break;
case fmt_uninterpreted_64:
fmt = fmt_uninterpreted;
case fmt_uninterpreted:
case fmt_double:
case fmt_long:
if ((fpr & 1) == 0)
{
FGR[fpr+1] = (value >> 32);
FGR[fpr] = (value & 0xFFFFFFFF);
FPR_STATE[fpr + 1] = fmt;
FPR_STATE[fpr] = fmt;
}
else
{
FPR_STATE[fpr] = fmt_unknown;
FPR_STATE[fpr + 1] = fmt_unknown;
SignalException (ReservedInstruction, 0);
}
break;
case fmt_ps:
FPR_STATE[fpr] = fmt_unknown;
SignalException (ReservedInstruction, 0);
break;
default:
FPR_STATE[fpr] = fmt_unknown;
err = -1;
break;
}
}
if (err)
SignalExceptionSimulatorFault ("Unrecognised FP format in StoreFPR ()");
#ifdef DEBUG
printf ("DBG: StoreFPR: fpr[%d] = 0x%s (format %s)\n",
fpr, pr_uword64 (FGR[fpr]), fpu_format_name (fmt));
#endif
return;
}
void
test_fcsr (sim_cpu *cpu,
address_word cia)
{
unsigned int cause;
cause = (FCSR & fcsr_CAUSE_mask) >> fcsr_CAUSE_shift;
if ((cause & ((FCSR & fcsr_ENABLES_mask) >> fcsr_ENABLES_shift)) != 0
|| (cause & (1 << UO)))
{
SignalExceptionFPE();
}
}
unsigned_word
value_fcr(sim_cpu *cpu,
address_word cia,
int fcr)
{
unsigned32 value = 0;
switch (fcr)
{
case 0:
value = FCR0;
break;
case 25:
value = (FCR31 & fcsr_FCC_mask) >> fcsr_FCC_shift;
value = (value & 0x1) | (value >> 1);
break;
case 26:
value = FCR31 & (fcsr_CAUSE_mask | fcsr_FLAGS_mask);
break;
case 28:
value = FCR31 & (fcsr_ENABLES_mask | fcsr_RM_mask);
if ((FCR31 & fcsr_FS) != 0)
value |= fenr_FS;
break;
case 31:
value = FCR31 & ~fcsr_ZERO_mask;
break;
}
return (EXTEND32 (value));
}
void
store_fcr(sim_cpu *cpu,
address_word cia,
int fcr,
unsigned_word value)
{
unsigned32 v;
v = VL4_8(value);
switch (fcr)
{
case 25:
v = (v << 1) | (v & 0x1);
FCR31 &= ~fcsr_FCC_mask;
FCR31 |= ((v << fcsr_FCC_shift) & fcsr_FCC_mask);
break;
case 26:
FCR31 &= ~(fcsr_CAUSE_mask | fcsr_FLAGS_mask);
FCR31 |= (v & (fcsr_CAUSE_mask | fcsr_FLAGS_mask));
test_fcsr(cpu, cia);
break;
case 28:
if ((v & fenr_FS) != 0)
v |= fcsr_FS;
else
v &= ~fcsr_FS;
FCR31 &= (fcsr_FCC_mask | fcsr_CAUSE_mask | fcsr_FLAGS_mask);
FCR31 |= (v & (fcsr_FS | fcsr_ENABLES_mask | fcsr_RM_mask));
test_fcsr(cpu, cia);
break;
case 31:
FCR31 = v & ~fcsr_ZERO_mask;
test_fcsr(cpu, cia);
break;
}
}
void
update_fcsr (sim_cpu *cpu,
address_word cia,
sim_fpu_status status)
{
FCSR &= ~fcsr_CAUSE_mask;
if (status != 0)
{
unsigned int cause = 0;
if (status & (sim_fpu_status_invalid_snan
| sim_fpu_status_invalid_isi
| sim_fpu_status_invalid_idi
| sim_fpu_status_invalid_zdz
| sim_fpu_status_invalid_imz
| sim_fpu_status_invalid_cmp
| sim_fpu_status_invalid_sqrt
| sim_fpu_status_invalid_cvi))
cause |= (1 << IO);
if (status & sim_fpu_status_invalid_div0)
cause |= (1 << DZ);
if (status & sim_fpu_status_overflow)
cause |= (1 << OF);
if (status & sim_fpu_status_underflow)
cause |= (1 << UF);
if (status & sim_fpu_status_inexact)
cause |= (1 << IR);
#if 0
if (status & sim_fpu_status_unimplemented)
cause |= (1 << UO);
#endif
FCSR |= (cause << fcsr_CAUSE_shift);
test_fcsr (cpu, cia);
FCSR |= ((cause & ~(1 << UO)) << fcsr_FLAGS_shift);
}
return;
}
static sim_fpu_round
rounding_mode(int rm)
{
sim_fpu_round round;
switch (rm)
{
case FP_RM_NEAREST:
round = sim_fpu_round_near;
break;
case FP_RM_TOZERO:
round = sim_fpu_round_zero;
break;
case FP_RM_TOPINF:
round = sim_fpu_round_up;
break;
case FP_RM_TOMINF:
round = sim_fpu_round_down;
break;
default:
round = 0;
fprintf (stderr, "Bad switch\n");
abort ();
}
return round;
}
static sim_fpu_denorm
denorm_mode(sim_cpu *cpu)
{
sim_fpu_denorm denorm;
if (GETFS())
denorm = sim_fpu_denorm_zero;
else
denorm = 0;
return denorm;
}
static sim_fpu_status
fp_test(unsigned64 op1,
unsigned64 op2,
FP_formats fmt,
int abs,
int cond,
int *condition)
{
sim_fpu wop1;
sim_fpu wop2;
sim_fpu_status status = 0;
int less, equal, unordered;
switch (fmt)
{
case fmt_single:
{
sim_fpu_32to (&wop1, op1);
sim_fpu_32to (&wop2, op2);
break;
}
case fmt_double:
{
sim_fpu_64to (&wop1, op1);
sim_fpu_64to (&wop2, op2);
break;
}
default:
fprintf (stderr, "Bad switch\n");
abort ();
}
if (sim_fpu_is_nan (&wop1) || sim_fpu_is_nan (&wop2))
{
if ((cond & (1 << 3)) ||
sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2))
status = sim_fpu_status_invalid_snan;
less = 0;
equal = 0;
unordered = 1;
}
else
{
if (abs)
{
status |= sim_fpu_abs (&wop1, &wop1);
status |= sim_fpu_abs (&wop2, &wop2);
}
equal = sim_fpu_is_eq (&wop1, &wop2);
less = !equal && sim_fpu_is_lt (&wop1, &wop2);
unordered = 0;
}
*condition = (((cond & (1 << 2)) && less)
|| ((cond & (1 << 1)) && equal)
|| ((cond & (1 << 0)) && unordered));
return status;
}
void
fp_cmp(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt,
int abs,
int cond,
int cc)
{
sim_fpu_status status = 0;
switch (fmt)
{
case fmt_single:
case fmt_double:
{
int result;
status = fp_test(op1, op2, fmt, abs, cond, &result);
update_fcsr (cpu, cia, status);
SETFCC (cc, result);
break;
}
case fmt_ps:
{
int result0, result1;
status = fp_test(FP_PS_lower (op1), FP_PS_lower (op2), fmt_single,
abs, cond, &result0);
status |= fp_test(FP_PS_upper (op1), FP_PS_upper (op2), fmt_single,
abs, cond, &result1);
update_fcsr (cpu, cia, status);
SETFCC (cc, result0);
SETFCC (cc+1, result1);
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
}
static unsigned64
fp_unary(sim_cpu *cpu,
address_word cia,
int (*sim_fpu_op)(sim_fpu *, const sim_fpu *),
unsigned64 op,
FP_formats fmt)
{
sim_fpu wop;
sim_fpu ans;
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_denorm denorm = denorm_mode (cpu);
sim_fpu_status status = 0;
unsigned64 result = 0;
switch (fmt)
{
case fmt_single:
{
unsigned32 res;
sim_fpu_32to (&wop, op);
status |= (*sim_fpu_op) (&ans, &wop);
status |= sim_fpu_round_32 (&ans, round, denorm);
sim_fpu_to32 (&res, &ans);
result = res;
break;
}
case fmt_double:
{
unsigned64 res;
sim_fpu_64to (&wop, op);
status |= (*sim_fpu_op) (&ans, &wop);
status |= sim_fpu_round_64 (&ans, round, denorm);
sim_fpu_to64 (&res, &ans);
result = res;
break;
}
case fmt_ps:
{
int status_u = 0, status_l = 0;
unsigned32 res_u, res_l;
sim_fpu_32to (&wop, FP_PS_upper(op));
status_u |= (*sim_fpu_op) (&ans, &wop);
sim_fpu_to32 (&res_u, &ans);
sim_fpu_32to (&wop, FP_PS_lower(op));
status_l |= (*sim_fpu_op) (&ans, &wop);
sim_fpu_to32 (&res_l, &ans);
result = FP_PS_cat(res_u, res_l);
status = status_u | status_l;
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status);
return result;
}
static unsigned64
fp_binary(sim_cpu *cpu,
address_word cia,
int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
sim_fpu wop1;
sim_fpu wop2;
sim_fpu ans;
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_denorm denorm = denorm_mode (cpu);
sim_fpu_status status = 0;
unsigned64 result = 0;
switch (fmt)
{
case fmt_single:
{
unsigned32 res;
sim_fpu_32to (&wop1, op1);
sim_fpu_32to (&wop2, op2);
status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
status |= sim_fpu_round_32 (&ans, round, denorm);
sim_fpu_to32 (&res, &ans);
result = res;
break;
}
case fmt_double:
{
unsigned64 res;
sim_fpu_64to (&wop1, op1);
sim_fpu_64to (&wop2, op2);
status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
status |= sim_fpu_round_64 (&ans, round, denorm);
sim_fpu_to64 (&res, &ans);
result = res;
break;
}
case fmt_ps:
{
int status_u = 0, status_l = 0;
unsigned32 res_u, res_l;
sim_fpu_32to (&wop1, FP_PS_upper(op1));
sim_fpu_32to (&wop2, FP_PS_upper(op2));
status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
sim_fpu_to32 (&res_u, &ans);
sim_fpu_32to (&wop1, FP_PS_lower(op1));
sim_fpu_32to (&wop2, FP_PS_lower(op2));
status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
sim_fpu_to32 (&res_l, &ans);
result = FP_PS_cat(res_u, res_l);
status = status_u | status_l;
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status);
return result;
}
static sim_fpu_status
inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
int scale,
int negate,
FP_formats fmt,
sim_fpu_round round,
sim_fpu_denorm denorm,
unsigned64 *result)
{
sim_fpu wop1;
sim_fpu wop2;
sim_fpu ans;
sim_fpu_status status = 0;
sim_fpu_status op_status;
unsigned64 temp = 0;
switch (fmt)
{
case fmt_single:
{
unsigned32 res;
sim_fpu_32to (&wop1, op1);
sim_fpu_32to (&wop2, op2);
status |= sim_fpu_mul (&ans, &wop1, &wop2);
if (scale != 0 && sim_fpu_is_number (&ans))
ans.normal_exp += scale;
status |= sim_fpu_round_32 (&ans, round, denorm);
wop1 = ans;
op_status = 0;
sim_fpu_32to (&wop2, op3);
op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
op_status |= sim_fpu_round_32 (&ans, round, denorm);
status |= op_status;
if (negate)
{
wop1 = ans;
op_status = sim_fpu_neg (&ans, &wop1);
op_status |= sim_fpu_round_32 (&ans, round, denorm);
status |= op_status;
}
sim_fpu_to32 (&res, &ans);
temp = res;
break;
}
case fmt_double:
{
unsigned64 res;
sim_fpu_64to (&wop1, op1);
sim_fpu_64to (&wop2, op2);
status |= sim_fpu_mul (&ans, &wop1, &wop2);
if (scale != 0 && sim_fpu_is_number (&ans))
ans.normal_exp += scale;
status |= sim_fpu_round_64 (&ans, round, denorm);
wop1 = ans;
op_status = 0;
sim_fpu_64to (&wop2, op3);
op_status |= (*sim_fpu_op) (&ans, &wop1, &wop2);
op_status |= sim_fpu_round_64 (&ans, round, denorm);
status |= op_status;
if (negate)
{
wop1 = ans;
op_status = sim_fpu_neg (&ans, &wop1);
op_status |= sim_fpu_round_64 (&ans, round, denorm);
status |= op_status;
}
sim_fpu_to64 (&res, &ans);
temp = res;
break;
}
default:
fprintf (stderr, "Bad switch\n");
abort ();
}
*result = temp;
return status;
}
static unsigned64
fp_mac(sim_cpu *cpu,
address_word cia,
int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
int scale,
int negate,
FP_formats fmt)
{
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_denorm denorm = denorm_mode (cpu);
sim_fpu_status status = 0;
unsigned64 result = 0;
switch (fmt)
{
case fmt_single:
case fmt_double:
status = inner_mac(sim_fpu_op, op1, op2, op3, scale,
negate, fmt, round, denorm, &result);
break;
case fmt_ps:
{
int status_u, status_l;
unsigned64 result_u, result_l;
status_u = inner_mac(sim_fpu_op, FP_PS_upper(op1), FP_PS_upper(op2),
FP_PS_upper(op3), scale, negate, fmt_single,
round, denorm, &result_u);
status_l = inner_mac(sim_fpu_op, FP_PS_lower(op1), FP_PS_lower(op2),
FP_PS_lower(op3), scale, negate, fmt_single,
round, denorm, &result_l);
result = FP_PS_cat(result_u, result_l);
status = status_u | status_l;
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status);
return result;
}
static sim_fpu_status
inner_rsqrt(unsigned64 op1,
FP_formats fmt,
sim_fpu_round round,
sim_fpu_denorm denorm,
unsigned64 *result)
{
sim_fpu wop1;
sim_fpu ans;
sim_fpu_status status = 0;
sim_fpu_status op_status;
unsigned64 temp = 0;
switch (fmt)
{
case fmt_single:
{
unsigned32 res;
sim_fpu_32to (&wop1, op1);
status |= sim_fpu_sqrt (&ans, &wop1);
status |= sim_fpu_round_32 (&ans, status, round);
wop1 = ans;
op_status = sim_fpu_inv (&ans, &wop1);
op_status |= sim_fpu_round_32 (&ans, round, denorm);
sim_fpu_to32 (&res, &ans);
temp = res;
status |= op_status;
break;
}
case fmt_double:
{
unsigned64 res;
sim_fpu_64to (&wop1, op1);
status |= sim_fpu_sqrt (&ans, &wop1);
status |= sim_fpu_round_64 (&ans, round, denorm);
wop1 = ans;
op_status = sim_fpu_inv (&ans, &wop1);
op_status |= sim_fpu_round_64 (&ans, round, denorm);
sim_fpu_to64 (&res, &ans);
temp = res;
status |= op_status;
break;
}
default:
fprintf (stderr, "Bad switch\n");
abort ();
}
*result = temp;
return status;
}
static unsigned64
fp_inv_sqrt(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
FP_formats fmt)
{
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_round denorm = denorm_mode (cpu);
sim_fpu_status status = 0;
unsigned64 result = 0;
switch (fmt)
{
case fmt_single:
case fmt_double:
status = inner_rsqrt (op1, fmt, round, denorm, &result);
break;
case fmt_ps:
{
int status_u, status_l;
unsigned64 result_u, result_l;
status_u = inner_rsqrt (FP_PS_upper(op1), fmt_single, round, denorm,
&result_u);
status_l = inner_rsqrt (FP_PS_lower(op1), fmt_single, round, denorm,
&result_l);
result = FP_PS_cat(result_u, result_l);
status = status_u | status_l;
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status);
return result;
}
unsigned64
fp_abs(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
return fp_unary(cpu, cia, &sim_fpu_abs, op, fmt);
}
unsigned64
fp_neg(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
return fp_unary(cpu, cia, &sim_fpu_neg, op, fmt);
}
unsigned64
fp_add(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary(cpu, cia, &sim_fpu_add, op1, op2, fmt);
}
unsigned64
fp_sub(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary(cpu, cia, &sim_fpu_sub, op1, op2, fmt);
}
unsigned64
fp_mul(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary(cpu, cia, &sim_fpu_mul, op1, op2, fmt);
}
unsigned64
fp_div(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary(cpu, cia, &sim_fpu_div, op1, op2, fmt);
}
unsigned64
fp_recip(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
return fp_unary(cpu, cia, &sim_fpu_inv, op, fmt);
}
unsigned64
fp_sqrt(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
return fp_unary(cpu, cia, &sim_fpu_sqrt, op, fmt);
}
unsigned64
fp_rsqrt(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
return fp_inv_sqrt(cpu, cia, op, fmt);
}
unsigned64
fp_madd(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
FP_formats fmt)
{
return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 0, fmt);
}
unsigned64
fp_msub(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
FP_formats fmt)
{
return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 0, fmt);
}
unsigned64
fp_nmadd(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
FP_formats fmt)
{
return fp_mac(cpu, cia, &sim_fpu_add, op1, op2, op3, 0, 1, fmt);
}
unsigned64
fp_nmsub(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
unsigned64 op3,
FP_formats fmt)
{
return fp_mac(cpu, cia, &sim_fpu_sub, op1, op2, op3, 0, 1, fmt);
}
static unsigned64
fp_binary_r(sim_cpu *cpu,
address_word cia,
int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
unsigned64 op1,
unsigned64 op2)
{
sim_fpu wop1;
sim_fpu wop2;
sim_fpu ans;
sim_fpu_round round = rounding_mode (GETRM ());
sim_fpu_denorm denorm = denorm_mode (cpu);
sim_fpu_status status_u, status_l;
unsigned64 result;
unsigned32 res_u, res_l;
status_u = 0;
sim_fpu_32to (&wop1, FP_PS_upper (op1));
sim_fpu_32to (&wop2, FP_PS_lower (op1));
status_u |= (*sim_fpu_op) (&ans, &wop1, &wop2);
status_u |= sim_fpu_round_32 (&ans, round, denorm);
sim_fpu_to32 (&res_u, &ans);
status_l = 0;
sim_fpu_32to (&wop1, FP_PS_upper (op2));
sim_fpu_32to (&wop2, FP_PS_lower (op2));
status_l |= (*sim_fpu_op) (&ans, &wop1, &wop2);
status_l |= sim_fpu_round_32 (&ans, round, denorm);
sim_fpu_to32 (&res_l, &ans);
result = FP_PS_cat (res_u, res_l);
update_fcsr (cpu, cia, status_u | status_l);
return result;
}
unsigned64
fp_add_r(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary_r (cpu, cia, &sim_fpu_add, op1, op2);
}
unsigned64
fp_mul_r(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
return fp_binary_r (cpu, cia, &sim_fpu_mul, op1, op2);
}
#define NR_FRAC_GUARD (60)
#define IMPLICIT_1 LSBIT64 (NR_FRAC_GUARD)
static int
fpu_inv1(sim_fpu *f, const sim_fpu *l)
{
static const sim_fpu sim_fpu_one = {
sim_fpu_class_number, 0, IMPLICIT_1, 0
};
int status = 0;
sim_fpu t;
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_maxfp;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
if (sim_fpu_is_infinity (l))
{
*f = sim_fpu_zero;
f->sign = l->sign;
return status;
}
status |= sim_fpu_div (f, &sim_fpu_one, l);
return status;
}
static int
fpu_inv1_32(sim_fpu *f, const sim_fpu *l)
{
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_max32;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
return fpu_inv1 (f, l);
}
static int
fpu_inv1_64(sim_fpu *f, const sim_fpu *l)
{
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_max64;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
return fpu_inv1 (f, l);
}
unsigned64
fp_recip1(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
switch (fmt)
{
case fmt_single:
case fmt_ps:
return fp_unary (cpu, cia, &fpu_inv1_32, op, fmt);
case fmt_double:
return fp_unary (cpu, cia, &fpu_inv1_64, op, fmt);
}
return 0;
}
unsigned64
fp_recip2(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
static const unsigned64 one_single = UNSIGNED64 (0x3F800000);
static const unsigned64 one_double = UNSIGNED64 (0x3FF0000000000000);
static const unsigned64 one_ps = (UNSIGNED64 (0x3F800000) << 32 | UNSIGNED64 (0x3F800000));
unsigned64 one;
switch (fmt)
{
case fmt_single: one = one_single; break;
case fmt_double: one = one_double; break;
case fmt_ps: one = one_ps; break;
default: one = 0; abort ();
}
return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, one, 0, 1, fmt);
}
static int
fpu_inv_sqrt1(sim_fpu *f, const sim_fpu *l)
{
static const sim_fpu sim_fpu_one = {
sim_fpu_class_number, 0, IMPLICIT_1, 0
};
int status = 0;
sim_fpu t;
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_maxfp;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
if (sim_fpu_is_infinity (l))
{
if (!l->sign)
{
f->class = sim_fpu_class_zero;
f->sign = 0;
}
else
{
*f = sim_fpu_qnan;
status = sim_fpu_status_invalid_sqrt;
}
return status;
}
status |= sim_fpu_sqrt (&t, l);
status |= sim_fpu_div (f, &sim_fpu_one, &t);
return status;
}
static int
fpu_inv_sqrt1_32(sim_fpu *f, const sim_fpu *l)
{
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_max32;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
return fpu_inv_sqrt1 (f, l);
}
static int
fpu_inv_sqrt1_64(sim_fpu *f, const sim_fpu *l)
{
if (sim_fpu_is_zero (l))
{
*f = sim_fpu_max64;
f->sign = l->sign;
return sim_fpu_status_invalid_div0;
}
return fpu_inv_sqrt1 (f, l);
}
unsigned64
fp_rsqrt1(sim_cpu *cpu,
address_word cia,
unsigned64 op,
FP_formats fmt)
{
switch (fmt)
{
case fmt_single:
case fmt_ps:
return fp_unary (cpu, cia, &fpu_inv_sqrt1_32, op, fmt);
case fmt_double:
return fp_unary (cpu, cia, &fpu_inv_sqrt1_64, op, fmt);
}
return 0;
}
unsigned64
fp_rsqrt2(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
static const unsigned64 half_single = UNSIGNED64 (0x3F000000);
static const unsigned64 half_double = UNSIGNED64 (0x3FE0000000000000);
static const unsigned64 half_ps = (UNSIGNED64 (0x3F000000) << 32 | UNSIGNED64 (0x3F000000));
unsigned64 half;
switch (fmt)
{
case fmt_single: half = half_single; break;
case fmt_double: half = half_double; break;
case fmt_ps: half = half_ps; break;
default: half = 0; abort ();
}
return fp_mac (cpu, cia, &sim_fpu_sub, op1, op2, half, -1, 1, fmt);
}
uword64
convert (sim_cpu *cpu,
address_word cia,
int rm,
uword64 op,
FP_formats from,
FP_formats to)
{
sim_fpu wop;
sim_fpu_round round = rounding_mode (rm);
sim_fpu_denorm denorm = denorm_mode (cpu);
unsigned32 result32;
unsigned64 result64;
sim_fpu_status status = 0;
switch (from)
{
case fmt_double:
sim_fpu_64to (&wop, op);
break;
case fmt_single:
sim_fpu_32to (&wop, op);
break;
case fmt_word:
status = sim_fpu_i32to (&wop, op, round);
break;
case fmt_long:
status = sim_fpu_i64to (&wop, op, round);
break;
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
switch (to)
{
case fmt_single:
status |= sim_fpu_round_32 (&wop, round, denorm);
if (sim_fpu_is_qnan (&wop))
wop = sim_fpu_qnan;
sim_fpu_to32 (&result32, &wop);
result64 = result32;
break;
case fmt_double:
status |= sim_fpu_round_64 (&wop, round, denorm);
if (sim_fpu_is_qnan (&wop))
wop = sim_fpu_qnan;
sim_fpu_to64 (&result64, &wop);
break;
case fmt_word:
status |= sim_fpu_to32i (&result32, &wop, round);
result64 = result32;
break;
case fmt_long:
status |= sim_fpu_to64i (&result64, &wop, round);
break;
default:
result64 = 0;
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status);
return result64;
}
unsigned64
ps_lower(sim_cpu *cpu,
address_word cia,
unsigned64 op)
{
return FP_PS_lower (op);
}
unsigned64
ps_upper(sim_cpu *cpu,
address_word cia,
unsigned64 op)
{
return FP_PS_upper(op);
}
unsigned64
pack_ps(sim_cpu *cpu,
address_word cia,
unsigned64 op1,
unsigned64 op2,
FP_formats fmt)
{
unsigned64 result = 0;
switch (fmt)
{
case fmt_single:
{
sim_fpu wop;
unsigned32 res_u, res_l;
sim_fpu_32to (&wop, op1);
sim_fpu_to32 (&res_u, &wop);
sim_fpu_32to (&wop, op2);
sim_fpu_to32 (&res_l, &wop);
result = FP_PS_cat(res_u, res_l);
break;
}
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
return result;
}
unsigned64
convert_ps (sim_cpu *cpu,
address_word cia,
int rm,
unsigned64 op,
FP_formats from,
FP_formats to)
{
sim_fpu wop_u, wop_l;
sim_fpu_round round = rounding_mode (rm);
sim_fpu_denorm denorm = denorm_mode (cpu);
unsigned32 res_u, res_l;
unsigned64 result;
sim_fpu_status status_u = 0, status_l = 0;
switch (from)
{
case fmt_word:
sim_fpu_i32to (&wop_u, (op >> 32) & (unsigned)0xFFFFFFFF, round);
sim_fpu_i32to (&wop_l, op & (unsigned)0xFFFFFFFF, round);
break;
case fmt_ps:
sim_fpu_32to (&wop_u, FP_PS_upper(op));
sim_fpu_32to (&wop_l, FP_PS_lower(op));
break;
default:
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
switch (to)
{
case fmt_word:
status_u |= sim_fpu_to32i (&res_u, &wop_u, round);
status_l |= sim_fpu_to32i (&res_l, &wop_l, round);
result = (((unsigned64)res_u) << 32) | (unsigned64)res_l;
break;
case fmt_ps:
status_u |= sim_fpu_round_32 (&wop_u, 0, round);
status_l |= sim_fpu_round_32 (&wop_l, 0, round);
sim_fpu_to32 (&res_u, &wop_u);
sim_fpu_to32 (&res_l, &wop_l);
result = FP_PS_cat(res_u, res_l);
break;
default:
result = 0;
sim_io_eprintf (SD, "Bad switch\n");
abort ();
}
update_fcsr (cpu, cia, status_u | status_l);
return result;
}
static const char *
fpu_format_name (FP_formats fmt)
{
switch (fmt)
{
case fmt_single:
return "single";
case fmt_double:
return "double";
case fmt_word:
return "word";
case fmt_long:
return "long";
case fmt_ps:
return "ps";
case fmt_unknown:
return "<unknown>";
case fmt_uninterpreted:
return "<uninterpreted>";
case fmt_uninterpreted_32:
return "<uninterpreted_32>";
case fmt_uninterpreted_64:
return "<uninterpreted_64>";
default:
return "<format error>";
}
}
#ifdef DEBUG
static const char *
fpu_rounding_mode_name (int rm)
{
switch (rm)
{
case FP_RM_NEAREST:
return "Round";
case FP_RM_TOZERO:
return "Trunc";
case FP_RM_TOPINF:
return "Ceil";
case FP_RM_TOMINF:
return "Floor";
default:
return "<rounding mode error>";
}
}
#endif