aboutsummaryrefslogtreecommitdiff
path: root/sim/mips/cp1.c
diff options
context:
space:
mode:
Diffstat (limited to 'sim/mips/cp1.c')
-rw-r--r--sim/mips/cp1.c432
1 files changed, 402 insertions, 30 deletions
diff --git a/sim/mips/cp1.c b/sim/mips/cp1.c
index a6d1b56..196173c 100644
--- a/sim/mips/cp1.c
+++ b/sim/mips/cp1.c
@@ -100,6 +100,8 @@ along with this program. If not, see <http://www.gnu.org/licenses/>. */
#define FPQNaN_LONG (UNSIGNED64 (0x7FFFFFFFFFFFFFFF))
#define FPQNaN_PS (FP_PS_cat (FPQNaN_SINGLE, FPQNaN_SINGLE))
+static void update_fcsr (sim_cpu *, address_word, sim_fpu_status);
+
static const char *fpu_format_name (FP_formats fmt);
#ifdef DEBUG
static const char *fpu_rounding_mode_name (int rm);
@@ -127,7 +129,7 @@ value_fpr (sim_cpu *cpu,
}
/* For values not yet accessed, set to the desired format. */
- if (fmt < fmt_uninterpreted)
+ if (fmt < fmt_uninterpreted && fmt != fmt_dc32)
{
if (FPR_STATE[fpr] == fmt_uninterpreted)
{
@@ -137,7 +139,10 @@ value_fpr (sim_cpu *cpu,
fpu_format_name (fmt));
#endif /* DEBUG */
}
- else if (fmt != FPR_STATE[fpr])
+ else if (fmt != FPR_STATE[fpr]
+ && !(fmt == fmt_single
+ && FPR_STATE[fpr] == fmt_double
+ && (FGR[fpr] == 0 || FGR[fpr] == 0xFFFFFFFF)))
{
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]),
@@ -166,6 +171,7 @@ value_fpr (sim_cpu *cpu,
case fmt_uninterpreted_32:
case fmt_single:
case fmt_word:
+ case fmt_dc32:
value = (FGR[fpr] & 0xFFFFFFFF);
break;
@@ -557,8 +563,8 @@ fp_test(uint64_t op1,
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))
+ if ((cond & (1 << 3))
+ || sim_fpu_is_snan (&wop1) || sim_fpu_is_snan (&wop2))
status = sim_fpu_status_invalid_snan;
less = 0;
equal = 0;
@@ -581,6 +587,109 @@ fp_test(uint64_t op1,
return status;
}
+static const int sim_fpu_class_mips_mapping[] = {
+ FP_R6CLASS_SNAN, /* SIM_FPU_IS_SNAN = 1, Noisy not-a-number */
+ FP_R6CLASS_QNAN, /* SIM_FPU_IS_QNAN = 2, Quiet not-a-number */
+ FP_R6CLASS_NEGINF, /* SIM_FPU_IS_NINF = 3, -infinity */
+ FP_R6CLASS_POSINF, /* SIM_FPU_IS_PINF = 4, +infinity */
+ FP_R6CLASS_NEGNORM, /* SIM_FPU_IS_NNUMBER = 5, -num - [-MAX .. -MIN] */
+ FP_R6CLASS_POSNORM, /* SIM_FPU_IS_PNUMBER = 6, +num - [+MIN .. +MAX] */
+ FP_R6CLASS_NEGSUB, /* SIM_FPU_IS_NDENORM = 7, -denorm - (MIN .. 0) */
+ FP_R6CLASS_POSSUB, /* SIM_FPU_IS_PDENORM = 8, +denorm - (0 .. MIN) */
+ FP_R6CLASS_NEGZERO, /* SIM_FPU_IS_NZERO = 9, -0 */
+ FP_R6CLASS_POSZERO /* SIM_FPU_IS_PZERO = 10, +0 */
+};
+
+uint64_t
+fp_classify (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op,
+ FP_formats fmt)
+{
+ sim_fpu wop;
+
+ switch (fmt)
+ {
+ case fmt_single:
+ sim_fpu_32to (&wop, op);
+ break;
+ case fmt_double:
+ sim_fpu_64to (&wop, op);
+ break;
+ default:
+ sim_io_error (SD, "Bad switch\n");
+ }
+ return sim_fpu_class_mips_mapping[sim_fpu_classify (&wop) - 1];
+}
+
+int
+fp_rint (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op,
+ uint64_t *ans,
+ FP_formats fmt)
+{
+ sim_fpu wop = {0}, wtemp = {0}, wmagic = {0}, wans = {0};
+ int64_t intermediate;
+ int status = 0;
+ sim_fpu_round round = rounding_mode (GETRM());
+
+ switch (fmt)
+ {
+ case fmt_single:
+ sim_fpu_32to (&wop, op);
+ sim_fpu_32to (&wmagic, 0x4b000000);
+ break;
+ case fmt_double:
+ sim_fpu_64to (&wop, op);
+ sim_fpu_64to (&wmagic, 0x4330000000000000);
+ break;
+ default:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ if (sim_fpu_is_nan (&wop) || sim_fpu_is_infinity (&wop))
+ {
+ status = sim_fpu_status_invalid_cvi;
+ update_fcsr (cpu, cia, status);
+ return status;
+ }
+
+ switch (fmt)
+ {
+ case fmt_single:
+ if (sim_fpu_is_ge (&wop, &wmagic))
+ wans = wop;
+ else
+ {
+ sim_fpu_add (&wtemp, &wop, &wmagic);
+ sim_fpu_round_32 (&wtemp, round, sim_fpu_denorm_default);
+ sim_fpu_sub (&wans, &wtemp, &wmagic);
+ }
+ sim_fpu_to32 ((uint32_t *) ans, &wans);
+ break;
+ case fmt_double:
+ if (sim_fpu_is_ge (&wop, &wmagic))
+ wans = wop;
+ else
+ {
+ sim_fpu_add (&wtemp, &wop, &wmagic);
+ sim_fpu_round_64 (&wtemp, round, sim_fpu_denorm_default);
+ sim_fpu_sub (&wans, &wtemp, &wmagic);
+ }
+ sim_fpu_to64 (ans, &wans);
+ break;
+ default:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ if (*ans != op && status == 0)
+ status = sim_fpu_status_inexact;
+
+ update_fcsr (cpu, cia, status);
+ return status;
+}
+
void
fp_cmp(sim_cpu *cpu,
address_word cia,
@@ -620,11 +729,91 @@ fp_cmp(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
}
+uint64_t
+fp_r6_cmp (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ FP_formats fmt,
+ int cond)
+{
+ sim_fpu wop1, wop2;
+ int result = 0;
+ int signalling = cond & 0x8;
+
+ 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:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ switch (cond)
+ {
+ case FP_R6CMP_AF:
+ result = 0;
+ break;
+ case FP_R6CMP_UN:
+ result = sim_fpu_is_un (&wop1, &wop2);
+ break;
+ case FP_R6CMP_OR:
+ result = sim_fpu_is_or (&wop1, &wop2);
+ break;
+ case FP_R6CMP_EQ:
+ result = sim_fpu_is_eq (&wop1, &wop2);
+ break;
+ case FP_R6CMP_NE:
+ result = sim_fpu_is_ne (&wop1, &wop2);
+ break;
+ case FP_R6CMP_LT:
+ result = sim_fpu_is_lt (&wop1, &wop2);
+ break;
+ case FP_R6CMP_LE:
+ result = sim_fpu_is_le (&wop1, &wop2);
+ break;
+ case FP_R6CMP_UEQ:
+ result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_eq (&wop1, &wop2);
+ break;
+ case FP_R6CMP_UNE:
+ result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_ne (&wop1, &wop2);
+ break;
+ case FP_R6CMP_ULT:
+ result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_lt (&wop1, &wop2);
+ break;
+ case FP_R6CMP_ULE:
+ result = sim_fpu_is_un (&wop1, &wop2) || sim_fpu_is_le (&wop1, &wop2);
+ break;
+ default:
+ update_fcsr (cpu, cia, sim_fpu_status_invalid_cmp);
+ break;
+ }
+
+ if (result)
+ {
+ switch (fmt)
+ {
+ case fmt_single:
+ return 0xFFFFFFFF;
+ case fmt_double:
+ return 0xFFFFFFFFFFFFFFFF;
+ default:
+ sim_io_error (SD, "Bad switch\n");
+ }
+ }
+ else
+ return 0;
+}
/* Basic arithmetic operations. */
@@ -635,7 +824,7 @@ fp_unary(sim_cpu *cpu,
uint64_t op,
FP_formats fmt)
{
- sim_fpu wop;
+ sim_fpu wop = {0};
sim_fpu ans;
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_denorm denorm = denorm_mode (cpu);
@@ -680,8 +869,7 @@ fp_unary(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status);
@@ -696,9 +884,9 @@ fp_binary(sim_cpu *cpu,
uint64_t op2,
FP_formats fmt)
{
- sim_fpu wop1;
- sim_fpu wop2;
- sim_fpu ans;
+ sim_fpu wop1 = {0};
+ sim_fpu wop2 = {0};
+ sim_fpu ans = {0};
sim_fpu_round round = rounding_mode (GETRM());
sim_fpu_denorm denorm = denorm_mode (cpu);
sim_fpu_status status = 0;
@@ -746,8 +934,7 @@ fp_binary(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status);
@@ -786,7 +973,7 @@ inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
ans.normal_exp += scale;
status |= sim_fpu_round_32 (&ans, round, denorm);
wop1 = ans;
- op_status = 0;
+ 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);
@@ -812,7 +999,7 @@ inner_mac(int (*sim_fpu_op)(sim_fpu *, const sim_fpu *, const sim_fpu *),
ans.normal_exp += scale;
status |= sim_fpu_round_64 (&ans, round, denorm);
wop1 = ans;
- op_status = 0;
+ 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);
@@ -881,8 +1068,89 @@ fp_mac(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ update_fcsr (cpu, cia, status);
+ return result;
+}
+
+/* Common FMAC code for .s, .d. Defers setting FCSR to caller. */
+static sim_fpu_status
+inner_fmac (sim_cpu *cpu,
+ int (*sim_fpu_op) (sim_fpu *, const sim_fpu *, const sim_fpu *),
+ uint64_t op1,
+ uint64_t op2,
+ uint64_t op3,
+ sim_fpu_round round,
+ sim_fpu_denorm denorm,
+ FP_formats fmt,
+ uint64_t *result)
+{
+ sim_fpu wop1, wop2, ans;
+ sim_fpu_status status = 0;
+ sim_fpu_status op_status;
+ uint32_t t32 = 0;
+ uint64_t t64 = 0;
+
+ switch (fmt)
+ {
+ case fmt_single:
+ sim_fpu_32to (&wop1, op1);
+ sim_fpu_32to (&wop2, op2);
+ status |= sim_fpu_mul (&ans, &wop1, &wop2);
+ wop1 = ans;
+ op_status = 0;
+ sim_fpu_32to (&wop2, op3);
+ op_status |= (*sim_fpu_op) (&ans, &wop2, &wop1);
+ op_status |= sim_fpu_round_32 (&ans, round, denorm);
+ status |= op_status;
+ sim_fpu_to32 (&t32, &ans);
+ t64 = t32;
+ break;
+ case fmt_double:
+ sim_fpu_64to (&wop1, op1);
+ sim_fpu_64to (&wop2, op2);
+ status |= sim_fpu_mul (&ans, &wop1, &wop2);
+ wop1 = ans;
+ op_status = 0;
+ sim_fpu_64to (&wop2, op3);
+ op_status |= (*sim_fpu_op) (&ans, &wop2, &wop1);
+ op_status |= sim_fpu_round_64 (&ans, round, denorm);
+ status |= op_status;
+ sim_fpu_to64 (&t64, &ans);
+ break;
+ default:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ *result = t64;
+ return status;
+}
+
+static uint64_t
+fp_fmac (sim_cpu *cpu,
+ address_word cia,
+ int (*sim_fpu_op) (sim_fpu *, const sim_fpu *, const sim_fpu *),
+ uint64_t op1,
+ uint64_t op2,
+ uint64_t op3,
+ FP_formats fmt)
+{
+ sim_fpu_round round = rounding_mode (GETRM());
+ sim_fpu_denorm denorm = denorm_mode (cpu);
+ sim_fpu_status status = 0;
+ uint64_t result = 0;
+
+ switch (fmt)
+ {
+ case fmt_single:
+ case fmt_double:
+ status = inner_fmac (cpu, sim_fpu_op, op1, op2, op3,
+ round, denorm, fmt, &result);
+ break;
+ default:
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status);
@@ -972,8 +1240,7 @@ fp_inv_sqrt(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status);
@@ -1040,6 +1307,94 @@ fp_div(sim_cpu *cpu,
}
uint64_t
+fp_min (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ FP_formats fmt)
+{
+ return fp_binary (cpu, cia, &sim_fpu_min, op1, op2, fmt);
+}
+
+uint64_t
+fp_max (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ FP_formats fmt)
+{
+ return fp_binary (cpu, cia, &sim_fpu_max, op1, op2, fmt);
+}
+
+uint64_t
+fp_mina (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ FP_formats fmt)
+{
+ uint64_t ret;
+ sim_fpu wop1 = {0}, wop2 = {0}, waop1, waop2, wans;
+ sim_fpu_status status = 0;
+
+ 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:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ status |= sim_fpu_abs (&waop1, &wop1);
+ status |= sim_fpu_abs (&waop2, &wop2);
+ status |= sim_fpu_min (&wans, &waop1, &waop2);
+ ret = (sim_fpu_is_eq (&wans, &waop1)) ? op1 : op2;
+
+ update_fcsr (cpu, cia, status);
+ return ret;
+}
+
+uint64_t
+fp_maxa (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ FP_formats fmt)
+{
+ uint64_t ret;
+ sim_fpu wop1 = {0}, wop2 = {0}, waop1, waop2, wans;
+ sim_fpu_status status = 0;
+
+ 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:
+ sim_io_error (SD, "Bad switch\n");
+ }
+
+ status |= sim_fpu_abs (&waop1, &wop1);
+ status |= sim_fpu_abs (&waop2, &wop2);
+ status |= sim_fpu_max (&wans, &waop1, &waop2);
+ ret = (sim_fpu_is_eq (&wans, &waop1)) ? op1 : op2;
+
+ update_fcsr (cpu, cia, status);
+ return ret;
+}
+
+uint64_t
fp_recip(sim_cpu *cpu,
address_word cia,
uint64_t op,
@@ -1089,6 +1444,28 @@ fp_msub(sim_cpu *cpu,
}
uint64_t
+fp_fmadd (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ uint64_t op3,
+ FP_formats fmt)
+{
+ return fp_fmac (cpu, cia, &sim_fpu_add, op1, op2, op3, fmt);
+}
+
+uint64_t
+fp_fmsub (sim_cpu *cpu,
+ address_word cia,
+ uint64_t op1,
+ uint64_t op2,
+ uint64_t op3,
+ FP_formats fmt)
+{
+ return fp_fmac (cpu, cia, &sim_fpu_sub, op1, op2, op3, fmt);
+}
+
+uint64_t
fp_nmadd(sim_cpu *cpu,
address_word cia,
uint64_t op1,
@@ -1394,8 +1771,7 @@ convert (sim_cpu *cpu,
status = sim_fpu_i64to (&wop, op, round);
break;
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
/* Convert sim_fpu format into the output */
@@ -1430,8 +1806,7 @@ convert (sim_cpu *cpu,
break;
default:
result64 = 0;
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status);
@@ -1481,8 +1856,7 @@ pack_ps(sim_cpu *cpu,
break;
}
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
return result;
@@ -1517,8 +1891,7 @@ convert_ps (sim_cpu *cpu,
sim_fpu_32to (&wop_l, FP_PS_lower(op));
break;
default:
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
/* Convert sim_fpu format into the output */
@@ -1538,8 +1911,7 @@ convert_ps (sim_cpu *cpu,
break;
default:
result = 0;
- sim_io_eprintf (SD, "Bad switch\n");
- abort ();
+ sim_io_error (SD, "Bad switch\n");
}
update_fcsr (cpu, cia, status_u | status_l);