diff options
author | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-03-13 17:01:22 +0000 |
---|---|---|
committer | bellard <bellard@c046a42c-6fe2-441c-8c8c-71466251a162> | 2005-03-13 17:01:22 +0000 |
commit | 4ecc31906d7535c4ad88fcc63968bef412dd67ba (patch) | |
tree | 9a38e0726804d67f9458293a32daabf9e7f1d894 /target-ppc/op_helper.c | |
parent | 4c2e770f377a2c38bb26c24e333b747511ddd040 (diff) |
fpu fixes (Jocelyn Mayer) - soft float support
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@1335 c046a42c-6fe2-441c-8c8c-71466251a162
Diffstat (limited to 'target-ppc/op_helper.c')
-rw-r--r-- | target-ppc/op_helper.c | 145 |
1 files changed, 97 insertions, 48 deletions
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c index 20aba8b6eb..102249d414 100644 --- a/target-ppc/op_helper.c +++ b/target-ppc/op_helper.c @@ -213,7 +213,7 @@ void do_store_fpscr (uint32_t mask) uint32_t u[2]; } s; } u; - int i; + int i, rnd_type; u.d = FT0; if (mask & 0x80) @@ -227,21 +227,23 @@ void do_store_fpscr (uint32_t mask) switch (env->fpscr[0] & 0x3) { case 0: /* Best approximation (round to nearest) */ - fesetround(FE_TONEAREST); + rnd_type = float_round_nearest_even; break; case 1: /* Smaller magnitude (round toward zero) */ - fesetround(FE_TOWARDZERO); + rnd_type = float_round_to_zero; break; case 2: /* Round toward +infinite */ - fesetround(FE_UPWARD); + rnd_type = float_round_up; break; + default: case 3: /* Round toward -infinite */ - fesetround(FE_DOWNWARD); + rnd_type = float_round_down; break; } + set_float_rounding_mode(rnd_type, &env->fp_status); } void do_fctiw (void) @@ -249,16 +251,14 @@ void do_fctiw (void) union { double d; uint64_t i; - } *p = (void *)&FT1; + } p; - if (FT0 > (double)0x7FFFFFFF) - p->i = 0x7FFFFFFFULL << 32; - else if (FT0 < -(double)0x80000000) - p->i = 0x80000000ULL << 32; - else - p->i = 0; - p->i |= (uint32_t)FT0; - FT0 = p->d; + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PPC 750 (aka G3) + */ + p.i = float64_to_int32(FT0, &env->fp_status); + p.i |= 0xFFF80000ULL << 32; + FT0 = p.d; } void do_fctiwz (void) @@ -266,39 +266,36 @@ void do_fctiwz (void) union { double d; uint64_t i; - } *p = (void *)&FT1; - int cround = fegetround(); - - fesetround(FE_TOWARDZERO); - if (FT0 > (double)0x7FFFFFFF) - p->i = 0x7FFFFFFFULL << 32; - else if (FT0 < -(double)0x80000000) - p->i = 0x80000000ULL << 32; - else - p->i = 0; - p->i |= (uint32_t)FT0; - FT0 = p->d; - fesetround(cround); + } p; + + /* XXX: higher bits are not supposed to be significant. + * to make tests easier, return the same as a real PPC 750 (aka G3) + */ + p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status); + p.i |= 0xFFF80000ULL << 32; + FT0 = p.d; } void do_fnmadd (void) { - FT0 = -((FT0 * FT1) + FT2); + FT0 = (FT0 * FT1) + FT2; + if (!isnan(FT0)) + FT0 = -FT0; } void do_fnmsub (void) { - FT0 = -((FT0 * FT1) - FT2); + FT0 = (FT0 * FT1) - FT2; + if (!isnan(FT0)) + FT0 = -FT0; } -void do_fnmadds (void) +void do_fdiv (void) { - FT0 = -((FTS0 * FTS1) + FTS2); -} - -void do_fnmsubs (void) -{ - FT0 = -((FTS0 * FTS1) - FTS2); + if (FT0 == -0.0 && FT1 == -0.0) + FT0 = 0.0 / 0.0; + else + FT0 /= FT1; } void do_fsqrt (void) @@ -306,27 +303,65 @@ void do_fsqrt (void) FT0 = sqrt(FT0); } -void do_fsqrts (void) -{ - FT0 = (float)sqrt((float)FT0); -} - void do_fres (void) { - FT0 = 1.0 / FT0; + union { + double d; + uint64_t i; + } p; + + if (isnormal(FT0)) { + FT0 = (float)(1.0 / FT0); + } else { + p.d = FT0; + if (p.i == 0x8000000000000000ULL) { + p.i = 0xFFF0000000000000ULL; + } else if (p.i == 0x0000000000000000ULL) { + p.i = 0x7FF0000000000000ULL; + } else if (isnan(FT0)) { + p.i = 0x7FF8000000000000ULL; + } else if (FT0 < 0.0) { + p.i = 0x8000000000000000ULL; + } else { + p.i = 0x0000000000000000ULL; + } + FT0 = p.d; + } } -void do_fsqrte (void) +void do_frsqrte (void) { - FT0 = 1.0 / sqrt(FT0); + union { + double d; + uint64_t i; + } p; + + if (isnormal(FT0) && FT0 > 0.0) { + FT0 = (float)(1.0 / sqrt(FT0)); + } else { + p.d = FT0; + if (p.i == 0x8000000000000000ULL) { + p.i = 0xFFF0000000000000ULL; + } else if (p.i == 0x0000000000000000ULL) { + p.i = 0x7FF0000000000000ULL; + } else if (isnan(FT0)) { + if (!(p.i & 0x0008000000000000ULL)) + p.i |= 0x000FFFFFFFFFFFFFULL; + } else if (FT0 < 0) { + p.i = 0x7FF8000000000000ULL; + } else { + p.i = 0x0000000000000000ULL; + } + FT0 = p.d; + } } void do_fsel (void) { if (FT0 >= 0) - FT0 = FT2; - else FT0 = FT1; + else + FT0 = FT2; } void do_fcmpu (void) @@ -371,12 +406,26 @@ void do_fcmpo (void) void do_fabs (void) { - FT0 = fabsl(FT0); + union { + double d; + uint64_t i; + } p; + + p.d = FT0; + p.i &= ~0x8000000000000000ULL; + FT0 = p.d; } void do_fnabs (void) { - FT0 = -fabsl(FT0); + union { + double d; + uint64_t i; + } p; + + p.d = FT0; + p.i |= 0x8000000000000000ULL; + FT0 = p.d; } /* Instruction cache invalidation helper */ |