summaryrefslogtreecommitdiff
path: root/target/m68k/fpu_helper.c
diff options
context:
space:
mode:
authorLaurent Vivier <laurent@vivier.eu>2017-06-20 22:51:20 +0200
committerLaurent Vivier <laurent@vivier.eu>2017-06-21 22:11:55 +0200
commitba62494483ab51ee31c70952b6ce5171a31860b1 (patch)
tree34879597689b9df1d8b8e9548051e1abdbb66e5f /target/m68k/fpu_helper.c
parent5a4526b26ab22b255126301227c5ceb2f0222bc5 (diff)
target-m68k: add FPCR and FPSR
Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Richard Henderson <rth@twiddle.net> Message-Id: <20170620205121.26515-6-laurent@vivier.eu>
Diffstat (limited to 'target/m68k/fpu_helper.c')
-rw-r--r--target/m68k/fpu_helper.c117
1 files changed, 101 insertions, 16 deletions
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index f4d38216d2..a9e17f5e98 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -58,9 +58,74 @@ void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val)
res->d = floatx80_round_to_int(val->d, &env->fp_status);
}
+static void m68k_restore_precision_mode(CPUM68KState *env)
+{
+ switch (env->fpcr & FPCR_PREC_MASK) {
+ case FPCR_PREC_X: /* extended */
+ set_floatx80_rounding_precision(80, &env->fp_status);
+ break;
+ case FPCR_PREC_S: /* single */
+ set_floatx80_rounding_precision(32, &env->fp_status);
+ break;
+ case FPCR_PREC_D: /* double */
+ set_floatx80_rounding_precision(64, &env->fp_status);
+ break;
+ case FPCR_PREC_U: /* undefined */
+ default:
+ break;
+ }
+}
+
+static void cf_restore_precision_mode(CPUM68KState *env)
+{
+ if (env->fpcr & FPCR_PREC_S) { /* single */
+ set_floatx80_rounding_precision(32, &env->fp_status);
+ } else { /* double */
+ set_floatx80_rounding_precision(64, &env->fp_status);
+ }
+}
+
+static void restore_rounding_mode(CPUM68KState *env)
+{
+ switch (env->fpcr & FPCR_RND_MASK) {
+ case FPCR_RND_N: /* round to nearest */
+ set_float_rounding_mode(float_round_nearest_even, &env->fp_status);
+ break;
+ case FPCR_RND_Z: /* round to zero */
+ set_float_rounding_mode(float_round_to_zero, &env->fp_status);
+ break;
+ case FPCR_RND_M: /* round toward minus infinity */
+ set_float_rounding_mode(float_round_down, &env->fp_status);
+ break;
+ case FPCR_RND_P: /* round toward positive infinity */
+ set_float_rounding_mode(float_round_up, &env->fp_status);
+ break;
+ }
+}
+
+void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val)
+{
+ env->fpcr = val & 0xffff;
+
+ if (m68k_feature(env, M68K_FEATURE_CF_FPU)) {
+ cf_restore_precision_mode(env);
+ } else {
+ m68k_restore_precision_mode(env);
+ }
+ restore_rounding_mode(env);
+}
+
void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val)
{
+ int rounding_mode = get_float_rounding_mode(&env->fp_status);
+ set_float_rounding_mode(float_round_to_zero, &env->fp_status);
res->d = floatx80_round_to_int(val->d, &env->fp_status);
+ set_float_rounding_mode(rounding_mode, &env->fp_status);
+}
+
+void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val)
+{
+ cpu_m68k_set_fpcr(env, val);
}
void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val)
@@ -98,24 +163,44 @@ void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
res->d = floatx80_div(val1->d, val0->d, &env->fp_status);
}
-void HELPER(fsub_cmp)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1)
-{
- /* ??? This may incorrectly raise exceptions. */
- /* ??? Should flush denormals to zero. */
- res->d = floatx80_sub(val0->d, val1->d, &env->fp_status);
- if (floatx80_is_quiet_nan(res->d, &env->fp_status)) {
- /* +/-inf compares equal against itself, but sub returns nan. */
- if (!floatx80_is_quiet_nan(val0->d, &env->fp_status)
- && !floatx80_is_quiet_nan(val1->d, &env->fp_status)) {
- res->d = floatx80_zero;
- if (floatx80_lt_quiet(val0->d, res->d, &env->fp_status)) {
- res->d = floatx80_chs(res->d);
- }
- }
+static int float_comp_to_cc(int float_compare)
+{
+ switch (float_compare) {
+ case float_relation_equal:
+ return FPSR_CC_Z;
+ case float_relation_less:
+ return FPSR_CC_N;
+ case float_relation_unordered:
+ return FPSR_CC_A;
+ case float_relation_greater:
+ return 0;
+ default:
+ g_assert_not_reached();
}
}
-uint32_t HELPER(fcompare)(CPUM68KState *env, FPReg *val)
+void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1)
{
- return floatx80_compare_quiet(val->d, floatx80_zero, &env->fp_status);
+ int float_compare;
+
+ float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status);
+ env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare);
+}
+
+void HELPER(ftst)(CPUM68KState *env, FPReg *val)
+{
+ uint32_t cc = 0;
+
+ if (floatx80_is_neg(val->d)) {
+ cc |= FPSR_CC_N;
+ }
+
+ if (floatx80_is_any_nan(val->d)) {
+ cc |= FPSR_CC_A;
+ } else if (floatx80_is_infinity(val->d)) {
+ cc |= FPSR_CC_I;
+ } else if (floatx80_is_zero(val->d)) {
+ cc |= FPSR_CC_Z;
+ }
+ env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | cc;
}