Fix PowerPC FPSCR update and floating-point exception generation
authorj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 27 Oct 2007 17:54:30 +0000 (17:54 +0000)
committerj_mayer <j_mayer@c046a42c-6fe2-441c-8c8c-71466251a162>
Sat, 27 Oct 2007 17:54:30 +0000 (17:54 +0000)
 in most useful cases.

git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@3458 c046a42c-6fe2-441c-8c8c-71466251a162

darwin-user/main.c
linux-user/main.c
target-ppc/cpu.h
target-ppc/helper.c
target-ppc/op.c
target-ppc/op_helper.c
target-ppc/op_helper.h
target-ppc/op_template.h
target-ppc/translate.c

index d0de491e0b80c4ee1ddd74175482e5dbf0c0d98b..70328ec8eeed368ec1f8e423e2517f89e22cbb29 100644 (file)
@@ -224,11 +224,6 @@ void cpu_loop(CPUPPCState *env)
             case POWERPC_EXCP_FP:
                 EXCP_DUMP(env, "Floating point program exception\n");
                 /* Set FX */
-                env->fpscr[7] |= 0x8;
-                /* Finally, update FEX */
-                if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
-                    ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
-                    env->fpscr[7] |= 0x4;
                 info.si_signo = SIGFPE;
                 info.si_errno = 0;
                 switch (env->error_code & 0xF) {
@@ -248,7 +243,7 @@ void cpu_loop(CPUPPCState *env)
                 case POWERPC_EXCP_FP_VXSOFT:
                     info.si_code = FPE_FLTINV;
                     break;
-                case POWERPC_EXCP_FP_VXNAN:
+                case POWERPC_EXCP_FP_VXSNAN:
                 case POWERPC_EXCP_FP_VXISI:
                 case POWERPC_EXCP_FP_VXIDI:
                 case POWERPC_EXCP_FP_VXIMZ:
index cac9561ba1ed7f419649fe7c149e7b3cd3d285ed..88a2e4886346b83e17950a9abf899cf48550c480 100644 (file)
@@ -829,12 +829,6 @@ void cpu_loop(CPUPPCState *env)
             switch (env->error_code & ~0xF) {
             case POWERPC_EXCP_FP:
                 EXCP_DUMP(env, "Floating point program exception\n");
-                /* Set FX */
-                env->fpscr[7] |= 0x8;
-                /* Finally, update FEX */
-                if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
-                    ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
-                    env->fpscr[7] |= 0x4;
                 info.si_signo = TARGET_SIGFPE;
                 info.si_errno = 0;
                 switch (env->error_code & 0xF) {
@@ -854,7 +848,7 @@ void cpu_loop(CPUPPCState *env)
                 case POWERPC_EXCP_FP_VXSOFT:
                     info.si_code = TARGET_FPE_FLTINV;
                     break;
-                case POWERPC_EXCP_FP_VXNAN:
+                case POWERPC_EXCP_FP_VXSNAN:
                 case POWERPC_EXCP_FP_VXISI:
                 case POWERPC_EXCP_FP_VXIDI:
                 case POWERPC_EXCP_FP_VXIMZ:
index fc1d9bbca3af8e9a0d36fc1d2d071435439559bd..196f98cae5a2f72adce0b3a87b67e850a790b69e 100644 (file)
@@ -239,7 +239,7 @@ enum {
     POWERPC_EXCP_FP_UX         = 0x02,  /* FP underflow                      */
     POWERPC_EXCP_FP_ZX         = 0x03,  /* FP divide by zero                 */
     POWERPC_EXCP_FP_XX         = 0x04,  /* FP inexact                        */
-    POWERPC_EXCP_FP_VXNAN      = 0x05,  /* FP invalid SNaN op                */
+    POWERPC_EXCP_FP_VXSNAN     = 0x05,  /* FP invalid SNaN op                */
     POWERPC_EXCP_FP_VXISI      = 0x06,  /* FP invalid infinite subtraction   */
     POWERPC_EXCP_FP_VXIDI      = 0x07,  /* FP invalid infinite divide        */
     POWERPC_EXCP_FP_VXZDZ      = 0x08,  /* FP invalid zero divide            */
@@ -433,14 +433,84 @@ enum {
     POWERPC_FLAG_PMM  = 0x00000400,
 };
 
+/*****************************************************************************/
+/* Floating point status and control register                                */
+#define FPSCR_FX     31 /* Floating-point exception summary                  */
+#define FPSCR_FEX    30 /* Floating-point enabled exception summary          */
+#define FPSCR_VX     29 /* Floating-point invalid operation exception summ.  */
+#define FPSCR_OX     28 /* Floating-point overflow exception                 */
+#define FPSCR_UX     27 /* Floating-point underflow exception                */
+#define FPSCR_ZX     26 /* Floating-point zero divide exception              */
+#define FPSCR_XX     25 /* Floating-point inexact exception                  */
+#define FPSCR_VXSNAN 24 /* Floating-point invalid operation exception (sNan) */
+#define FPSCR_VXISI  23 /* Floating-point invalid operation exception (inf)  */
+#define FPSCR_VXIDI  22 /* Floating-point invalid operation exception (inf)  */
+#define FPSCR_VXZDZ  21 /* Floating-point invalid operation exception (zero) */
+#define FPSCR_VXIMZ  20 /* Floating-point invalid operation exception (inf)  */
+#define FPSCR_VXVC   19 /* Floating-point invalid operation exception (comp) */
+#define FPSCR_FR     18 /* Floating-point fraction rounded                   */
+#define FPSCR_FI     17 /* Floating-point fraction inexact                   */
+#define FPSCR_C      16 /* Floating-point result class descriptor            */
+#define FPSCR_FL     15 /* Floating-point less than or negative              */
+#define FPSCR_FG     14 /* Floating-point greater than or negative           */
+#define FPSCR_FE     13 /* Floating-point equal or zero                      */
+#define FPSCR_FU     12 /* Floating-point unordered or NaN                   */
+#define FPSCR_FPCC   12 /* Floating-point condition code                     */
+#define FPSCR_FPRF   12 /* Floating-point result flags                       */
+#define FPSCR_VXSOFT 10 /* Floating-point invalid operation exception (soft) */
+#define FPSCR_VXSQRT 9  /* Floating-point invalid operation exception (sqrt) */
+#define FPSCR_VXCVI  8  /* Floating-point invalid operation exception (int)  */
+#define FPSCR_VE     7  /* Floating-point invalid operation exception enable */
+#define FPSCR_OE     6  /* Floating-point overflow exception enable          */
+#define FPSCR_UE     5  /* Floating-point undeflow exception enable          */
+#define FPSCR_ZE     4  /* Floating-point zero divide exception enable       */
+#define FPSCR_XE     3  /* Floating-point inexact exception enable           */
+#define FPSCR_NI     2  /* Floating-point non-IEEE mode                      */
+#define FPSCR_RN1    1
+#define FPSCR_RN     0  /* Floating-point rounding control                   */
+#define fpscr_fex    (((env->fpscr) >> FPSCR_FEX)    & 0x1)
+#define fpscr_vx     (((env->fpscr) >> FPSCR_VX)     & 0x1)
+#define fpscr_ox     (((env->fpscr) >> FPSCR_OX)     & 0x1)
+#define fpscr_ux     (((env->fpscr) >> FPSCR_UX)     & 0x1)
+#define fpscr_zx     (((env->fpscr) >> FPSCR_ZX)     & 0x1)
+#define fpscr_xx     (((env->fpscr) >> FPSCR_XX)     & 0x1)
+#define fpscr_vxsnan (((env->fpscr) >> FPSCR_VXSNAN) & 0x1)
+#define fpscr_vxisi  (((env->fpscr) >> FPSCR_VXISI)  & 0x1)
+#define fpscr_vxidi  (((env->fpscr) >> FPSCR_VXIDI)  & 0x1)
+#define fpscr_vxzdz  (((env->fpscr) >> FPSCR_VXZDZ)  & 0x1)
+#define fpscr_vximz  (((env->fpscr) >> FPSCR_VXIMZ)  & 0x1)
+#define fpscr_vxvc   (((env->fpscr) >> FPSCR_VXVC)   & 0x1)
+#define fpscr_fpcc   (((env->fpscr) >> FPSCR_FPCC)   & 0xF)
+#define fpscr_vxsoft (((env->fpscr) >> FPSCR_VXSOFT) & 0x1)
+#define fpscr_vxsqrt (((env->fpscr) >> FPSCR_VXSQRT) & 0x1)
+#define fpscr_vxcvi  (((env->fpscr) >> FPSCR_VXCVI)  & 0x1)
+#define fpscr_ve     (((env->fpscr) >> FPSCR_VE)     & 0x1)
+#define fpscr_oe     (((env->fpscr) >> FPSCR_OE)     & 0x1)
+#define fpscr_ue     (((env->fpscr) >> FPSCR_UE)     & 0x1)
+#define fpscr_ze     (((env->fpscr) >> FPSCR_ZE)     & 0x1)
+#define fpscr_xe     (((env->fpscr) >> FPSCR_XE)     & 0x1)
+#define fpscr_ni     (((env->fpscr) >> FPSCR_NI)     & 0x1)
+#define fpscr_rn     (((env->fpscr) >> FPSCR_RN)     & 0x3)
+/* Invalid operation exception summary */
+#define fpscr_ix ((env->fpscr) & ((1 << FPSCR_VXSNAN) | (1 << FPSCR_VXISI)  | \
+                                  (1 << FPSCR_VXIDI)  | (1 << FPSCR_VXZDZ)  | \
+                                  (1 << FPSCR_VXIMZ)  | (1 << FPSCR_VXVC)   | \
+                                  (1 << FPSCR_VXSOFT) | (1 << FPSCR_VXSQRT) | \
+                                  (1 << FPSCR_VXCVI)))
+/* exception summary */
+#define fpscr_ex  (((env->fpscr) >> FPSCR_XX) & 0x1F)
+/* enabled exception summary */
+#define fpscr_eex (((env->fpscr) >> FPSCR_XX) & ((env->fpscr) >> FPSCR_XE) &  \
+                   0x1F)
+
+/*****************************************************************************/
+/* The whole PowerPC CPU context */
 #if defined(TARGET_PPC64H)
 #define NB_MMU_MODES 3
 #else
 #define NB_MMU_MODES 2
 #endif
 
-/*****************************************************************************/
-/* The whole PowerPC CPU context */
 struct CPUPPCState {
     /* First are the most commonly used resources
      * during translated code execution
@@ -482,7 +552,7 @@ struct CPUPPCState {
     /* floating point registers */
     float64 fpr[32];
     /* floating point status and control register */
-    uint8_t fpscr[8];
+    uint32_t fpscr;
 
     CPU_COMMON
 
index 9bdd8835e990b698f5f2038a47e6e5e5abb43004..869d5b7677e527f0ff6c73c11b77c87f38767354 100644 (file)
@@ -2130,6 +2130,8 @@ static always_inline void powerpc_excp (CPUState *env,
                     fprintf(logfile, "Ignore floating point exception\n");
                 }
 #endif
+                env->exception_index = POWERPC_EXCP_NONE;
+                env->error_code = 0;
                 return;
             }
             new_msr &= ~((target_ulong)1 << MSR_RI);
@@ -2138,12 +2140,6 @@ static always_inline void powerpc_excp (CPUState *env,
                 new_msr |= (target_ulong)1 << MSR_HV;
 #endif
             msr |= 0x00100000;
-            /* Set FX */
-            env->fpscr[7] |= 0x8;
-            /* Finally, update FEX */
-            if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
-                ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
-                env->fpscr[7] |= 0x4;
             if (msr_fe0 != msr_fe1) {
                 msr |= 0x00010000;
                 goto store_current;
@@ -2199,8 +2195,11 @@ static always_inline void powerpc_excp (CPUState *env,
         /* XXX: To be removed */
         if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
             env->osi_call) {
-            if (env->osi_call(env) != 0)
+            if (env->osi_call(env) != 0) {
+                env->exception_index = POWERPC_EXCP_NONE;
+                env->error_code = 0;
                 return;
+            }
         }
         if (loglevel & CPU_LOG_INT) {
             dump_syscall(env);
index 0495879ce31eb5cca7f1227007c927500012b876..0146d33c3dd04a5ff7bad17093405af88ac9b306 100644 (file)
@@ -135,13 +135,6 @@ void OPPROTO op_set_Rc0 (void)
     RETURN();
 }
 
-/* Set Rc1 (for floating point arithmetic) */
-void OPPROTO op_set_Rc1 (void)
-{
-    env->crf[1] = env->fpscr[7];
-    RETURN();
-}
-
 /* Constants load */
 void OPPROTO op_reset_T0 (void)
 {
@@ -552,21 +545,108 @@ void OPPROTO op_store_dbatl (void)
 #endif /* !defined(CONFIG_USER_ONLY) */
 
 /* FPSCR */
-void OPPROTO op_load_fpscr (void)
+#ifdef CONFIG_SOFTFLOAT
+void OPPROTO op_reset_fpstatus (void)
 {
-    do_load_fpscr();
+    env->fp_status.float_exception_flags = 0;
     RETURN();
 }
+#endif
 
-void OPPROTO op_store_fpscr (void)
+void OPPROTO op_compute_fprf (void)
 {
-    do_store_fpscr(PARAM1);
+    do_compute_fprf(PARAM1);
     RETURN();
 }
 
-void OPPROTO op_reset_scrfx (void)
+#ifdef CONFIG_SOFTFLOAT
+void OPPROTO op_float_check_status (void)
 {
-    env->fpscr[7] &= ~0x8;
+    do_float_check_status();
+    RETURN();
+}
+#else
+void OPPROTO op_float_check_status (void)
+{
+    if (env->exception_index == POWERPC_EXCP_PROGRAM &&
+        (env->error_code & POWERPC_EXCP_FP)) {
+        /* Differred floating-point exception after target FPR update */
+        if (msr_fe0 != 0 || msr_fe1 != 0)
+            do_raise_exception_err(env->exception_index, env->error_code);
+    }
+    RETURN();
+}
+#endif
+
+#if defined(WORDS_BIGENDIAN)
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+void OPPROTO op_load_fpscr_FT0 (void)
+{
+    /* The 32 MSB of the target fpr are undefined.
+     * They'll be zero...
+     */
+    union {
+        float64 d;
+        struct {
+            uint32_t u[2];
+        } s;
+    } u;
+
+    u.s.u[WORD0] = 0;
+    u.s.u[WORD1] = env->fpscr;
+    FT0 = u.d;
+    RETURN();
+}
+
+void OPPROTO op_set_FT0 (void)
+{
+    union {
+        float64 d;
+        struct {
+            uint32_t u[2];
+        } s;
+    } u;
+
+    u.s.u[WORD0] = 0;
+    u.s.u[WORD1] = PARAM1;
+    FT0 = u.d;
+    RETURN();
+}
+#undef WORD0
+#undef WORD1
+
+void OPPROTO op_load_fpscr_T0 (void)
+{
+    T0 = (env->fpscr >> PARAM1) & 0xF;
+    RETURN();
+}
+
+void OPPROTO op_load_fpcc (void)
+{
+    T0 = fpscr_fpcc;
+    RETURN();
+}
+
+void OPPROTO op_fpscr_resetbit (void)
+{
+    env->fpscr &= PARAM1;
+    RETURN();
+}
+
+void OPPROTO op_fpscr_setbit (void)
+{
+    do_fpscr_setbit(PARAM1);
+    RETURN();
+}
+
+void OPPROTO op_store_fpscr (void)
+{
+    do_store_fpscr(PARAM1);
     RETURN();
 }
 
@@ -1702,28 +1782,44 @@ void OPPROTO op_srli_T1_64 (void)
 /* fadd - fadd. */
 void OPPROTO op_fadd (void)
 {
+#if USE_PRECISE_EMULATION
+    do_fadd();
+#else
     FT0 = float64_add(FT0, FT1, &env->fp_status);
+#endif
     RETURN();
 }
 
 /* fsub - fsub. */
 void OPPROTO op_fsub (void)
 {
+#if USE_PRECISE_EMULATION
+    do_fsub();
+#else
     FT0 = float64_sub(FT0, FT1, &env->fp_status);
+#endif
     RETURN();
 }
 
 /* fmul - fmul. */
 void OPPROTO op_fmul (void)
 {
+#if USE_PRECISE_EMULATION
+    do_fmul();
+#else
     FT0 = float64_mul(FT0, FT1, &env->fp_status);
+#endif
     RETURN();
 }
 
 /* fdiv - fdiv. */
 void OPPROTO op_fdiv (void)
 {
+#if USE_PRECISE_EMULATION
+    do_fdiv();
+#else
     FT0 = float64_div(FT0, FT1, &env->fp_status);
+#endif
     RETURN();
 }
 
@@ -1805,7 +1901,11 @@ void OPPROTO op_fnmsub (void)
 /* frsp - frsp. */
 void OPPROTO op_frsp (void)
 {
+#if USE_PRECISE_EMULATION
+    do_frsp();
+#else
     FT0 = float64_to_float32(FT0, &env->fp_status);
+#endif
     RETURN();
 }
 
index 06a5a561caed28b7c8d5add60cac724bf028bc11..81827735641b1182433ccf01714716d6b97c9810 100644 (file)
@@ -51,14 +51,6 @@ void do_raise_exception_err (uint32_t exception, int error_code)
 #if 0
     printf("Raise exception %3x code : %d\n", exception, error_code);
 #endif
-    switch (exception) {
-    case POWERPC_EXCP_PROGRAM:
-        if (error_code == POWERPC_EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0)
-            return;
-        break;
-    default:
-        break;
-    }
     env->exception_index = exception;
     env->error_code = error_code;
     cpu_loop_exit();
@@ -107,77 +99,6 @@ void do_store_pri (int prio)
 }
 #endif
 
-void do_load_fpscr (void)
-{
-    /* The 32 MSB of the target fpr are undefined.
-     * They'll be zero...
-     */
-    union {
-        float64 d;
-        struct {
-            uint32_t u[2];
-        } s;
-    } u;
-    int i;
-
-#if defined(WORDS_BIGENDIAN)
-#define WORD0 0
-#define WORD1 1
-#else
-#define WORD0 1
-#define WORD1 0
-#endif
-    u.s.u[WORD0] = 0;
-    u.s.u[WORD1] = 0;
-    for (i = 0; i < 8; i++)
-        u.s.u[WORD1] |= env->fpscr[i] << (4 * i);
-    FT0 = u.d;
-}
-
-void do_store_fpscr (uint32_t mask)
-{
-    /*
-     * We use only the 32 LSB of the incoming fpr
-     */
-    union {
-        double d;
-        struct {
-            uint32_t u[2];
-        } s;
-    } u;
-    int i, rnd_type;
-
-    u.d = FT0;
-    if (mask & 0x80)
-        env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9);
-    for (i = 1; i < 7; i++) {
-        if (mask & (1 << (7 - i)))
-            env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF;
-    }
-    /* TODO: update FEX & VX */
-    /* Set rounding mode */
-    switch (env->fpscr[0] & 0x3) {
-    case 0:
-        /* Best approximation (round to nearest) */
-        rnd_type = float_round_nearest_even;
-        break;
-    case 1:
-        /* Smaller magnitude (round toward zero) */
-        rnd_type = float_round_to_zero;
-        break;
-    case 2:
-        /* Round toward +infinite */
-        rnd_type = float_round_up;
-        break;
-    default:
-    case 3:
-        /* Round toward -infinite */
-        rnd_type = float_round_down;
-        break;
-    }
-    set_float_rounding_mode(rnd_type, &env->fp_status);
-}
-
 target_ulong ppc_load_dump_spr (int sprn)
 {
     if (loglevel != 0) {
@@ -553,6 +474,538 @@ void do_popcntb_64 (void)
 
 /*****************************************************************************/
 /* Floating point operations helpers */
+static inline int fpisneg (float64 f)
+{
+    union {
+        float64 f;
+        uint64_t u;
+    } u;
+
+    u.f = f;
+
+    return u.u >> 63 != 0;
+}
+
+static inline int isden (float f)
+{
+    union {
+        float64 f;
+        uint64_t u;
+    } u;
+
+    u.f = f;
+
+    return ((u.u >> 52) & 0x7FF) == 0;
+}
+
+static inline int iszero (float64 f)
+{
+    union {
+        float64 f;
+        uint64_t u;
+    } u;
+
+    u.f = f;
+
+    return (u.u & ~0x8000000000000000ULL) == 0;
+}
+
+static inline int isinfinity (float64 f)
+{
+    union {
+        float64 f;
+        uint64_t u;
+    } u;
+
+    u.f = f;
+
+    return ((u.u >> 51) & 0x3FF) == 0x3FF &&
+        (u.u & 0x000FFFFFFFFFFFFFULL) == 0;
+}
+
+void do_compute_fprf (int set_fprf)
+{
+    int isneg;
+
+    isneg = fpisneg(FT0);
+    if (unlikely(float64_is_nan(FT0))) {
+        if (float64_is_signaling_nan(FT0)) {
+            /* Signaling NaN: flags are undefined */
+            T0 = 0x00;
+        } else {
+            /* Quiet NaN */
+            T0 = 0x11;
+        }
+    } else if (unlikely(isinfinity(FT0))) {
+        /* +/- infinity */
+        if (isneg)
+            T0 = 0x09;
+        else
+            T0 = 0x05;
+    } else {
+        if (iszero(FT0)) {
+            /* +/- zero */
+            if (isneg)
+                T0 = 0x12;
+            else
+                T0 = 0x02;
+        } else {
+            if (isden(FT0)) {
+                /* Denormalized numbers */
+                T0 = 0x10;
+            } else {
+                /* Normalized numbers */
+                T0 = 0x00;
+            }
+            if (isneg) {
+                T0 |= 0x08;
+            } else {
+                T0 |= 0x04;
+            }
+        }
+    }
+    if (set_fprf) {
+        /* We update FPSCR_FPRF */
+        env->fpscr &= ~(0x1F << FPSCR_FPRF);
+        env->fpscr |= T0 << FPSCR_FPRF;
+    }
+    /* We just need fpcc to update Rc1 */
+    T0 &= 0xF;
+}
+
+/* Floating-point invalid operations exception */
+static always_inline void fload_invalid_op_excp (int op)
+{
+    int ve;
+
+    ve = fpscr_ve;
+    if (op & POWERPC_EXCP_FP_VXSNAN) {
+        /* Operation on signaling NaN */
+        env->fpscr |= 1 << FPSCR_VXSNAN;
+    }
+    if (op & POWERPC_EXCP_FP_VXSOFT) {
+        /* Software-defined condition */
+        env->fpscr |= 1 << FPSCR_VXSOFT;
+    }
+    switch (op & ~(POWERPC_EXCP_FP_VXSOFT | POWERPC_EXCP_FP_VXSNAN)) {
+    case POWERPC_EXCP_FP_VXISI:
+        /* Magnitude subtraction of infinities */
+        env->fpscr |= 1 << FPSCR_VXISI;
+        goto update_arith;
+    case POWERPC_EXCP_FP_VXIDI:
+        /* Division of infinity by infinity */
+        env->fpscr |= 1 << FPSCR_VXIDI;
+        goto update_arith;
+    case POWERPC_EXCP_FP_VXZDZ:
+        /* Division of zero by zero */
+        env->fpscr |= 1 << FPSCR_VXZDZ;
+        goto update_arith;
+    case POWERPC_EXCP_FP_VXIMZ:
+        /* Multiplication of zero by infinity */
+        env->fpscr |= 1 << FPSCR_VXIMZ;
+        goto update_arith;
+    case POWERPC_EXCP_FP_VXVC:
+        /* Ordered comparison of NaN */
+        env->fpscr |= 1 << FPSCR_VXVC;
+        env->fpscr &= ~(0xF << FPSCR_FPCC);
+        env->fpscr |= 0x11 << FPSCR_FPCC;
+        /* We must update the target FPR before raising the exception */
+        if (ve != 0) {
+            env->exception_index = POWERPC_EXCP_PROGRAM;
+            env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_VXVC;
+            /* Update the floating-point enabled exception summary */
+            env->fpscr |= 1 << FPSCR_FEX;
+            /* Exception is differed */
+            ve = 0;
+        }
+        break;
+    case POWERPC_EXCP_FP_VXSQRT:
+        /* Square root of a negative number */
+        env->fpscr |= 1 << FPSCR_VXSQRT;
+    update_arith:
+        env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+        if (ve == 0) {
+            /* Set the result to quiet NaN */
+            FT0 = (uint64_t)-1;
+            env->fpscr &= ~(0xF << FPSCR_FPCC);
+            env->fpscr |= 0x11 << FPSCR_FPCC;
+        }
+        break;
+    case POWERPC_EXCP_FP_VXCVI:
+        /* Invalid conversion */
+        env->fpscr |= 1 << FPSCR_VXCVI;
+        env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+        if (ve == 0) {
+            /* Set the result to quiet NaN */
+            FT0 = (uint64_t)-1;
+            env->fpscr &= ~(0xF << FPSCR_FPCC);
+            env->fpscr |= 0x11 << FPSCR_FPCC;
+        }
+        break;
+    }
+    /* Update the floating-point invalid operation summary */
+    env->fpscr |= 1 << FPSCR_VX;
+    /* Update the floating-point exception summary */
+    env->fpscr |= 1 << FPSCR_FX;
+    if (ve != 0) {
+        /* Update the floating-point enabled exception summary */
+        env->fpscr |= 1 << FPSCR_FEX;
+        if (msr_fe0 != 0 || msr_fe1 != 0)
+            do_raise_exception_err(POWERPC_EXCP_PROGRAM, POWERPC_EXCP_FP | op);
+    }
+}
+
+static always_inline void float_zero_divide_excp (void)
+{
+    union {
+        float64 f;
+        uint64_t u;
+    } u0, u1;
+    
+
+    env->fpscr |= 1 << FPSCR_ZX;
+    env->fpscr &= ~((1 << FPSCR_FR) | (1 << FPSCR_FI));
+    /* Update the floating-point exception summary */
+    env->fpscr |= 1 << FPSCR_FX;
+    if (fpscr_ze != 0) {
+        /* Update the floating-point enabled exception summary */
+        env->fpscr |= 1 << FPSCR_FEX;
+        if (msr_fe0 != 0 || msr_fe1 != 0) {
+            do_raise_exception_err(POWERPC_EXCP_PROGRAM,
+                                   POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX);
+        }
+    } else {
+        /* Set the result to infinity */
+        u0.f = FT0;
+        u1.f = FT1;
+        u0.u = ((u0.u ^ u1.u) & 0x8000000000000000ULL);
+        u0.u |= 0x3FFULL << 51;
+        FT0 = u0.f;
+    }
+}
+
+static always_inline void float_overflow_excp (void)
+{
+    env->fpscr |= 1 << FPSCR_OX;
+    /* Update the floating-point exception summary */
+    env->fpscr |= 1 << FPSCR_FX;
+    if (fpscr_oe != 0) {
+        /* XXX: should adjust the result */
+        /* Update the floating-point enabled exception summary */
+        env->fpscr |= 1 << FPSCR_FEX;
+        /* We must update the target FPR before raising the exception */
+        env->exception_index = POWERPC_EXCP_PROGRAM;
+        env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX;
+    } else {
+        env->fpscr |= 1 << FPSCR_XX;
+        env->fpscr |= 1 << FPSCR_FI;
+    }
+}
+
+static always_inline void float_underflow_excp (void)
+{
+    env->fpscr |= 1 << FPSCR_UX;
+    /* Update the floating-point exception summary */
+    env->fpscr |= 1 << FPSCR_FX;
+    if (fpscr_ue != 0) {
+        /* XXX: should adjust the result */
+        /* Update the floating-point enabled exception summary */
+        env->fpscr |= 1 << FPSCR_FEX;
+        /* We must update the target FPR before raising the exception */
+        env->exception_index = POWERPC_EXCP_PROGRAM;
+        env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX;
+    }
+}
+
+static always_inline void float_inexact_excp (void)
+{
+    env->fpscr |= 1 << FPSCR_XX;
+    /* Update the floating-point exception summary */
+    env->fpscr |= 1 << FPSCR_FX;
+    if (fpscr_xe != 0) {
+        /* Update the floating-point enabled exception summary */
+        env->fpscr |= 1 << FPSCR_FEX;
+        /* We must update the target FPR before raising the exception */
+        env->exception_index = POWERPC_EXCP_PROGRAM;
+        env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX;
+    }
+}
+
+static always_inline void fpscr_set_rounding_mode (void)
+{
+    int rnd_type;
+
+    /* Set rounding mode */
+    switch (fpscr_rn) {
+    case 0:
+        /* Best approximation (round to nearest) */
+        rnd_type = float_round_nearest_even;
+        break;
+    case 1:
+        /* Smaller magnitude (round toward zero) */
+        rnd_type = float_round_to_zero;
+        break;
+    case 2:
+        /* Round toward +infinite */
+        rnd_type = float_round_up;
+        break;
+    default:
+    case 3:
+        /* Round toward -infinite */
+        rnd_type = float_round_down;
+        break;
+    }
+    set_float_rounding_mode(rnd_type, &env->fp_status);
+}
+
+void do_fpscr_setbit (int bit)
+{
+    int prev;
+
+    prev = (env->fpscr >> bit) & 1;
+    env->fpscr |= 1 << bit;
+    if (prev == 0) {
+        switch (bit) {
+        case FPSCR_VX:
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_ve)
+                goto raise_ve;
+        case FPSCR_OX:
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_oe)
+                goto raise_oe;
+            break;
+        case FPSCR_UX:
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_ue)
+                goto raise_ue;
+            break;
+        case FPSCR_ZX:
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_ze)
+                goto raise_ze;
+            break;
+        case FPSCR_XX:
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_xe)
+                goto raise_xe;
+            break;
+        case FPSCR_VXSNAN:
+        case FPSCR_VXISI:
+        case FPSCR_VXIDI:
+        case FPSCR_VXZDZ:
+        case FPSCR_VXIMZ:
+        case FPSCR_VXVC:
+        case FPSCR_VXSOFT:
+        case FPSCR_VXSQRT:
+        case FPSCR_VXCVI:
+            env->fpscr |= 1 << FPSCR_VX;
+            env->fpscr |= 1 << FPSCR_FX;
+            if (fpscr_ve != 0)
+                goto raise_ve;
+            break;
+        case FPSCR_VE:
+            if (fpscr_vx != 0) {
+            raise_ve:
+                env->error_code = POWERPC_EXCP_FP;
+                if (fpscr_vxsnan)
+                    env->error_code |= POWERPC_EXCP_FP_VXSNAN;
+                if (fpscr_vxisi)
+                    env->error_code |= POWERPC_EXCP_FP_VXISI;
+                if (fpscr_vxidi)
+                    env->error_code |= POWERPC_EXCP_FP_VXIDI;
+                if (fpscr_vxzdz)
+                    env->error_code |= POWERPC_EXCP_FP_VXZDZ;
+                if (fpscr_vximz)
+                    env->error_code |= POWERPC_EXCP_FP_VXIMZ;
+                if (fpscr_vxvc)
+                    env->error_code |= POWERPC_EXCP_FP_VXVC;
+                if (fpscr_vxsoft)
+                    env->error_code |= POWERPC_EXCP_FP_VXSOFT;
+                if (fpscr_vxsqrt)
+                    env->error_code |= POWERPC_EXCP_FP_VXSQRT;
+                if (fpscr_vxcvi)
+                    env->error_code |= POWERPC_EXCP_FP_VXCVI;
+                goto raise_excp;
+            }
+            break;
+        case FPSCR_OE:
+            if (fpscr_ox != 0) {
+            raise_oe:
+                env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_OX;
+                goto raise_excp;
+            }
+            break;
+        case FPSCR_UE:
+            if (fpscr_ux != 0) {
+            raise_ue:
+                env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_UX;
+                goto raise_excp;
+            }
+            break;
+        case FPSCR_ZE:
+            if (fpscr_zx != 0) {
+            raise_ze:
+                env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_ZX;
+                goto raise_excp;
+            }
+            break;
+        case FPSCR_XE:
+            if (fpscr_xx != 0) {
+            raise_xe:
+                env->error_code = POWERPC_EXCP_FP | POWERPC_EXCP_FP_XX;
+                goto raise_excp;
+            }
+            break;
+        case FPSCR_RN1:
+        case FPSCR_RN:
+            fpscr_set_rounding_mode();
+            break;
+        default:
+            break;
+        raise_excp:
+            /* Update the floating-point enabled exception summary */
+            env->fpscr |= 1 << FPSCR_FEX;
+                /* We have to update Rc1 before raising the exception */
+            env->exception_index = POWERPC_EXCP_PROGRAM;
+            break;
+        }
+    }
+}
+
+#if defined(WORDS_BIGENDIAN)
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+void do_store_fpscr (uint32_t mask)
+{
+    /*
+     * We use only the 32 LSB of the incoming fpr
+     */
+    union {
+        double d;
+        struct {
+            uint32_t u[2];
+        } s;
+    } u;
+    uint32_t prev, new;
+    int i;
+
+    u.d = FT0;
+    prev = env->fpscr;
+    new = u.s.u[WORD1];
+    new &= ~0x90000000;
+    new |= prev & 0x90000000;
+    for (i = 0; i < 7; i++) {
+        if (mask & (1 << i)) {
+            env->fpscr &= ~(0xF << (4 * i));
+            env->fpscr |= new & (0xF << (4 * i));
+        }
+    }
+    /* Update VX and FEX */
+    if (fpscr_ix != 0)
+        env->fpscr |= 1 << FPSCR_VX;
+    if ((fpscr_ex & fpscr_eex) != 0) {
+        env->fpscr |= 1 << FPSCR_FEX;
+        env->exception_index = POWERPC_EXCP_PROGRAM;
+        /* XXX: we should compute it properly */
+        env->error_code = POWERPC_EXCP_FP;
+    }
+    fpscr_set_rounding_mode();
+}
+#undef WORD0
+#undef WORD1
+
+#ifdef CONFIG_SOFTFLOAT
+void do_float_check_status (void)
+{
+    if (env->exception_index == POWERPC_EXCP_PROGRAM &&
+        (env->error_code & POWERPC_EXCP_FP)) {
+        /* Differred floating-point exception after target FPR update */
+        if (msr_fe0 != 0 || msr_fe1 != 0)
+            do_raise_exception_err(env->exception_index, env->error_code);
+    } else if (env->fp_status.float_exception_flags & float_flag_overflow) {
+        float_overflow_excp();
+    } else if (env->fp_status.float_exception_flags & float_flag_underflow) {
+        float_underflow_excp();
+    } else if (env->fp_status.float_exception_flags & float_flag_inexact) {
+        float_inexact_excp();
+    }
+}
+#endif
+
+#if USE_PRECISE_EMULATION
+void do_fadd (void)
+{
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1))) {
+        /* sNaN addition */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (likely(isfinite(FT0) || isfinite(FT1) ||
+                      fpisneg(FT0) == fpisneg(FT1))) {
+        FT0 = float64_add(FT0, FT1, &env->fp_status);
+    } else {
+        /* Magnitude subtraction of infinities */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+    }
+}
+
+void do_fsub (void)
+{
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1))) {
+        /* sNaN subtraction */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (likely(isfinite(FT0) || isfinite(FT1) ||
+                      fpisneg(FT0) != fpisneg(FT1))) {
+        FT0 = float64_sub(FT0, FT1, &env->fp_status);
+    } else {
+        /* Magnitude subtraction of infinities */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXISI);
+    }
+}
+
+void do_fmul (void)
+{
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1))) {
+        /* sNaN multiplication */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely((ifinf(FT0) && iszero(FT1)) ||
+                        (inzero(FT0) && isinfinity(FT1)))) {
+        /* Multiplication of zero by infinity */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXIMZ);
+    } else {
+        FT0 = float64_mul(FT0, FT1, &env->fp_status);
+    }
+}
+
+void do_fdiv (void)
+{
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1))) {
+        /* sNaN division */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely(isinfinity(FT0) && isinfinity(FT1))) {
+        /* Division of infinity by infinity */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXIDI);
+    } else if (unlikely(iszero(FT1))) {
+        if (iszero(FT0)) {
+            /* Division of zero by zero */
+            fload_invalid_op_excp(POWERPC_EXCP_FP_VXZDZ);
+        } else {
+            /* Division by zero */
+            float_zero_divide_excp();
+        }
+    } else {
+        FT0 = float64_div(FT0, FT1, &env->fp_status);
+    }
+}
+#endif /* USE_PRECISE_EMULATION */
+
 void do_fctiw (void)
 {
     union {
@@ -560,14 +1013,22 @@ void do_fctiw (void)
         uint64_t i;
     } p;
 
-    p.i = float64_to_int32(FT0, &env->fp_status);
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+    } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) {
+        /* qNan / infinity conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+    } else {
+        p.i = float64_to_int32(FT0, &env->fp_status);
 #if USE_PRECISE_EMULATION
-    /* XXX: higher bits are not supposed to be significant.
-     *     to make tests easier, return the same as a real PowerPC 750 (aka G3)
-     */
-    p.i |= 0xFFF80000ULL << 32;
+        /* XXX: higher bits are not supposed to be significant.
+         *     to make tests easier, return the same as a real PowerPC 750
+         */
+        p.i |= 0xFFF80000ULL << 32;
 #endif
-    FT0 = p.d;
+        FT0 = p.d;
+    }
 }
 
 void do_fctiwz (void)
@@ -577,14 +1038,22 @@ void do_fctiwz (void)
         uint64_t i;
     } p;
 
-    p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status);
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+    } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) {
+        /* qNan / infinity conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+    } else {
+        p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status);
 #if USE_PRECISE_EMULATION
-    /* XXX: higher bits are not supposed to be significant.
-     *     to make tests easier, return the same as a real PowerPC 750 (aka G3)
-     */
-    p.i |= 0xFFF80000ULL << 32;
+        /* XXX: higher bits are not supposed to be significant.
+         *     to make tests easier, return the same as a real PowerPC 750
+         */
+        p.i |= 0xFFF80000ULL << 32;
 #endif
-    FT0 = p.d;
+        FT0 = p.d;
+    }
 }
 
 #if defined(TARGET_PPC64)
@@ -606,8 +1075,16 @@ void do_fctid (void)
         uint64_t i;
     } p;
 
-    p.i = float64_to_int64(FT0, &env->fp_status);
-    FT0 = p.d;
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+    } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) {
+        /* qNan / infinity conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+    } else {
+        p.i = float64_to_int64(FT0, &env->fp_status);
+        FT0 = p.d;
+    }
 }
 
 void do_fctidz (void)
@@ -617,20 +1094,34 @@ void do_fctidz (void)
         uint64_t i;
     } p;
 
-    p.i = float64_to_int64_round_to_zero(FT0, &env->fp_status);
-    FT0 = p.d;
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+    } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) {
+        /* qNan / infinity conversion */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+    } else {
+        p.i = float64_to_int64_round_to_zero(FT0, &env->fp_status);
+        FT0 = p.d;
+    }
 }
 
 #endif
 
 static always_inline void do_fri (int rounding_mode)
 {
-    int curmode;
-
-    curmode = env->fp_status.float_rounding_mode;
-    set_float_rounding_mode(rounding_mode, &env->fp_status);
-    FT0 = float64_round_to_int(FT0, &env->fp_status);
-    set_float_rounding_mode(curmode, &env->fp_status);
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN round */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN | POWERPC_EXCP_FP_VXCVI);
+    } else if (unlikely(float64_is_nan(FT0) || isinfinity(FT0))) {
+        /* qNan / infinity round */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXCVI);
+    } else {
+        set_float_rounding_mode(rounding_mode, &env->fp_status);
+        FT0 = float64_round_to_int(FT0, &env->fp_status);
+        /* Restore rounding mode from FPSCR */
+        fpscr_set_rounding_mode();
+    }
 }
 
 void do_frin (void)
@@ -656,90 +1147,142 @@ void do_frim (void)
 #if USE_PRECISE_EMULATION
 void do_fmadd (void)
 {
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1) ||
+                 float64_is_signaling_nan(FT2))) {
+        /* sNaN operation */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
 #ifdef FLOAT128
-    float128 ft0_128, ft1_128;
-
-    ft0_128 = float64_to_float128(FT0, &env->fp_status);
-    ft1_128 = float64_to_float128(FT1, &env->fp_status);
-    ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
-    ft1_128 = float64_to_float128(FT2, &env->fp_status);
-    ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
-    FT0 = float128_to_float64(ft0_128, &env->fp_status);
+        /* This is the way the PowerPC specification defines it */
+        float128 ft0_128, ft1_128;
+
+        ft0_128 = float64_to_float128(FT0, &env->fp_status);
+        ft1_128 = float64_to_float128(FT1, &env->fp_status);
+        ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+        ft1_128 = float64_to_float128(FT2, &env->fp_status);
+        ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
+        FT0 = float128_to_float64(ft0_128, &env->fp_status);
 #else
-    /* This is OK on x86 hosts */
-    FT0 = (FT0 * FT1) + FT2;
+        /* This is OK on x86 hosts */
+        FT0 = (FT0 * FT1) + FT2;
 #endif
+    }
 }
 
 void do_fmsub (void)
 {
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1) ||
+                 float64_is_signaling_nan(FT2))) {
+        /* sNaN operation */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
 #ifdef FLOAT128
-    float128 ft0_128, ft1_128;
-
-    ft0_128 = float64_to_float128(FT0, &env->fp_status);
-    ft1_128 = float64_to_float128(FT1, &env->fp_status);
-    ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
-    ft1_128 = float64_to_float128(FT2, &env->fp_status);
-    ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
-    FT0 = float128_to_float64(ft0_128, &env->fp_status);
+        /* This is the way the PowerPC specification defines it */
+        float128 ft0_128, ft1_128;
+
+        ft0_128 = float64_to_float128(FT0, &env->fp_status);
+        ft1_128 = float64_to_float128(FT1, &env->fp_status);
+        ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+        ft1_128 = float64_to_float128(FT2, &env->fp_status);
+        ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
+        FT0 = float128_to_float64(ft0_128, &env->fp_status);
 #else
-    /* This is OK on x86 hosts */
-    FT0 = (FT0 * FT1) - FT2;
+        /* This is OK on x86 hosts */
+        FT0 = (FT0 * FT1) - FT2;
 #endif
+    }
 }
 #endif /* USE_PRECISE_EMULATION */
 
 void do_fnmadd (void)
 {
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1) ||
+                 float64_is_signaling_nan(FT2))) {
+        /* sNaN operation */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
 #if USE_PRECISE_EMULATION
 #ifdef FLOAT128
-    float128 ft0_128, ft1_128;
-
-    ft0_128 = float64_to_float128(FT0, &env->fp_status);
-    ft1_128 = float64_to_float128(FT1, &env->fp_status);
-    ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
-    ft1_128 = float64_to_float128(FT2, &env->fp_status);
-    ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
-    FT0 = float128_to_float64(ft0_128, &env->fp_status);
+        /* This is the way the PowerPC specification defines it */
+        float128 ft0_128, ft1_128;
+
+        ft0_128 = float64_to_float128(FT0, &env->fp_status);
+        ft1_128 = float64_to_float128(FT1, &env->fp_status);
+        ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+        ft1_128 = float64_to_float128(FT2, &env->fp_status);
+        ft0_128 = float128_add(ft0_128, ft1_128, &env->fp_status);
+        FT0 = float128_to_float64(ft0_128, &env->fp_status);
 #else
-    /* This is OK on x86 hosts */
-    FT0 = (FT0 * FT1) + FT2;
+        /* This is OK on x86 hosts */
+        FT0 = (FT0 * FT1) + FT2;
 #endif
 #else
-    FT0 = float64_mul(FT0, FT1, &env->fp_status);
-    FT0 = float64_add(FT0, FT2, &env->fp_status);
+        FT0 = float64_mul(FT0, FT1, &env->fp_status);
+        FT0 = float64_add(FT0, FT2, &env->fp_status);
 #endif
-    if (likely(!isnan(FT0)))
-        FT0 = float64_chs(FT0);
+        if (likely(!isnan(FT0)))
+            FT0 = float64_chs(FT0);
+    }
 }
 
 void do_fnmsub (void)
 {
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1) ||
+                 float64_is_signaling_nan(FT2))) {
+        /* sNaN operation */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
 #if USE_PRECISE_EMULATION
 #ifdef FLOAT128
-    float128 ft0_128, ft1_128;
-
-    ft0_128 = float64_to_float128(FT0, &env->fp_status);
-    ft1_128 = float64_to_float128(FT1, &env->fp_status);
-    ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
-    ft1_128 = float64_to_float128(FT2, &env->fp_status);
-    ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
-    FT0 = float128_to_float64(ft0_128, &env->fp_status);
+        /* This is the way the PowerPC specification defines it */
+        float128 ft0_128, ft1_128;
+
+        ft0_128 = float64_to_float128(FT0, &env->fp_status);
+        ft1_128 = float64_to_float128(FT1, &env->fp_status);
+        ft0_128 = float128_mul(ft0_128, ft1_128, &env->fp_status);
+        ft1_128 = float64_to_float128(FT2, &env->fp_status);
+        ft0_128 = float128_sub(ft0_128, ft1_128, &env->fp_status);
+        FT0 = float128_to_float64(ft0_128, &env->fp_status);
 #else
-    /* This is OK on x86 hosts */
-    FT0 = (FT0 * FT1) - FT2;
+        /* This is OK on x86 hosts */
+        FT0 = (FT0 * FT1) - FT2;
 #endif
 #else
-    FT0 = float64_mul(FT0, FT1, &env->fp_status);
-    FT0 = float64_sub(FT0, FT2, &env->fp_status);
+        FT0 = float64_mul(FT0, FT1, &env->fp_status);
+        FT0 = float64_sub(FT0, FT2, &env->fp_status);
 #endif
-    if (likely(!isnan(FT0)))
-        FT0 = float64_chs(FT0);
+        if (likely(!isnan(FT0)))
+            FT0 = float64_chs(FT0);
+    }
 }
 
+#if USE_PRECISE_EMULATION
+void do_frsp (void)
+{
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN square root */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
+        FT0 = float64_to_float32(FT0, &env->fp_status);
+    }
+}
+#endif /* USE_PRECISE_EMULATION */
+
 void do_fsqrt (void)
 {
-    FT0 = float64_sqrt(FT0, &env->fp_status);
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN square root */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely(fpisneg(FT0) && !iszero(FT0))) {
+        /* Square root of a negative nonzero number */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT);
+    } else {
+        FT0 = float64_sqrt(FT0, &env->fp_status);
+    }
 }
 
 void do_fre (void)
@@ -749,7 +1292,13 @@ void do_fre (void)
         uint64_t i;
     } p;
 
-    if (likely(isnormal(FT0))) {
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN reciprocal */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely(iszero(FT0))) {
+        /* Zero reciprocal */
+        float_zero_divide_excp();
+    } else if (likely(isnormal(FT0))) {
         FT0 = float64_div(1.0, FT0, &env->fp_status);
     } else {
         p.d = FT0;
@@ -759,7 +1308,7 @@ void do_fre (void)
             p.i = 0x7FF0000000000000ULL;
         } else if (isnan(FT0)) {
             p.i = 0x7FF8000000000000ULL;
-        } else if (FT0 < 0.0) {
+        } else if (fpisneg(FT0)) {
             p.i = 0x8000000000000000ULL;
         } else {
             p.i = 0x0000000000000000ULL;
@@ -775,7 +1324,13 @@ void do_fres (void)
         uint64_t i;
     } p;
 
-    if (likely(isnormal(FT0))) {
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN reciprocal */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely(iszero(FT0))) {
+        /* Zero reciprocal */
+        float_zero_divide_excp();
+    } else if (likely(isnormal(FT0))) {
 #if USE_PRECISE_EMULATION
         FT0 = float64_div(1.0, FT0, &env->fp_status);
         FT0 = float64_to_float32(FT0, &env->fp_status);
@@ -790,7 +1345,7 @@ void do_fres (void)
             p.i = 0x7FF0000000000000ULL;
         } else if (isnan(FT0)) {
             p.i = 0x7FF8000000000000ULL;
-        } else if (FT0 < 0.0) {
+        } else if (fpisneg(FT0)) {
             p.i = 0x8000000000000000ULL;
         } else {
             p.i = 0x0000000000000000ULL;
@@ -806,7 +1361,13 @@ void do_frsqrte (void)
         uint64_t i;
     } p;
 
-    if (likely(isnormal(FT0) && FT0 > 0.0)) {
+    if (unlikely(float64_is_signaling_nan(FT0))) {
+        /* sNaN reciprocal square root */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else if (unlikely(fpisneg(FT0) && !iszero(FT0))) {
+        /* Reciprocal square root of a negative nonzero number */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSQRT);
+    } else if (likely(isnormal(FT0))) {
         FT0 = float64_sqrt(FT0, &env->fp_status);
         FT0 = float32_div(1.0, FT0, &env->fp_status);
     } else {
@@ -816,9 +1377,8 @@ void do_frsqrte (void)
         } 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 |= 0x000FFFFFFFFFFFFFULL;
+        } else if (fpisneg(FT0)) {
             p.i = 0x7FF8000000000000ULL;
         } else {
             p.i = 0x0000000000000000ULL;
@@ -829,7 +1389,7 @@ void do_frsqrte (void)
 
 void do_fsel (void)
 {
-    if (FT0 >= 0)
+    if (!fpisneg(FT0) || iszero(FT0))
         FT0 = FT1;
     else
         FT0 = FT2;
@@ -837,7 +1397,11 @@ void do_fsel (void)
 
 void do_fcmpu (void)
 {
-    if (likely(!isnan(FT0) && !isnan(FT1))) {
+    if (unlikely(float64_is_signaling_nan(FT0) ||
+                 float64_is_signaling_nan(FT1))) {
+        /* sNaN comparison */
+        fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN);
+    } else {
         if (float64_lt(FT0, FT1, &env->fp_status)) {
             T0 = 0x08UL;
         } else if (!float64_le(FT0, FT1, &env->fp_status)) {
@@ -845,18 +1409,25 @@ void do_fcmpu (void)
         } else {
             T0 = 0x02UL;
         }
-    } else {
-        T0 = 0x01UL;
-        env->fpscr[4] |= 0x1;
-        env->fpscr[6] |= 0x1;
     }
-    env->fpscr[3] = T0;
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);
+    env->fpscr |= T0 << FPSCR_FPRF;
 }
 
 void do_fcmpo (void)
 {
-    env->fpscr[4] &= ~0x1;
-    if (likely(!isnan(FT0) && !isnan(FT1))) {
+    if (unlikely(float64_is_nan(FT0) ||
+                 float64_is_nan(FT1))) {
+        if (float64_is_signaling_nan(FT0) ||
+            float64_is_signaling_nan(FT1)) {
+            /* sNaN comparison */
+            fload_invalid_op_excp(POWERPC_EXCP_FP_VXSNAN |
+                                  POWERPC_EXCP_FP_VXVC);
+        } else {
+            /* qNaN comparison */
+            fload_invalid_op_excp(POWERPC_EXCP_FP_VXVC);
+        }
+    } else {
         if (float64_lt(FT0, FT1, &env->fp_status)) {
             T0 = 0x08UL;
         } else if (!float64_le(FT0, FT1, &env->fp_status)) {
@@ -864,19 +1435,9 @@ void do_fcmpo (void)
         } else {
             T0 = 0x02UL;
         }
-    } else {
-        T0 = 0x01UL;
-        env->fpscr[4] |= 0x1;
-        if (!float64_is_signaling_nan(FT0) || !float64_is_signaling_nan(FT1)) {
-            /* Quiet NaN case */
-            env->fpscr[6] |= 0x1;
-            if (!(env->fpscr[1] & 0x8))
-                env->fpscr[4] |= 0x8;
-        } else {
-            env->fpscr[4] |= 0x8;
-        }
     }
-    env->fpscr[3] = T0;
+    env->fpscr &= ~(0x0F << FPSCR_FPRF);
+    env->fpscr |= T0 << FPSCR_FPRF;
 }
 
 #if !defined (CONFIG_USER_ONLY)
index d8b2f252c89e620030719d67bf25b5cd7535cdd2..6597b3ca58084d7ecbe4790fe3f8ce5f48dd3c92 100644 (file)
@@ -60,7 +60,7 @@ void do_store_cr (uint32_t mask);
 #if defined(TARGET_PPC64)
 void do_store_pri (int prio);
 #endif
-void do_load_fpscr (void);
+void do_fpscr_setbit (int bit);
 void do_store_fpscr (uint32_t mask);
 target_ulong ppc_load_dump_spr (int sprn);
 void ppc_store_dump_spr (int sprn, target_ulong val);
@@ -94,6 +94,16 @@ void do_popcntb_64 (void);
 #endif
 
 /* Floating-point arithmetic helpers */
+void do_compute_fprf (int set_class);
+#ifdef CONFIG_SOFTFLOAT
+void do_float_check_status (void);
+#endif
+#if USE_PRECISE_EMULATION
+void do_fadd (void);
+void do_fsub (void);
+void do_fmul (void);
+void do_fdiv (void);
+#endif
 void do_fsqrt (void);
 void do_fre (void);
 void do_fres (void);
@@ -105,6 +115,9 @@ void do_fmsub (void);
 #endif
 void do_fnmadd (void);
 void do_fnmsub (void);
+#if USE_PRECISE_EMULATION
+void do_frsp (void);
+#endif
 void do_fctiw (void);
 void do_fctiwz (void);
 #if defined(TARGET_PPC64)
index 28dc59c9424fae2bd3bcfd22d067202c0f6d1bb7..bdd8844323b8dc7a5001d3b57390c3091fdc6a2e 100644 (file)
@@ -165,39 +165,6 @@ void OPPROTO glue(op_store_T1_crf_crf, REG) (void)
     RETURN();
 }
 
-/* Floating point condition and status register moves */
-void OPPROTO glue(op_load_fpscr_T0_fpscr, REG) (void)
-{
-    T0 = env->fpscr[REG];
-    RETURN();
-}
-
-#if REG == 0
-void OPPROTO glue(op_store_T0_fpscr_fpscr, REG) (void)
-{
-    env->fpscr[REG] = (env->fpscr[REG] & 0x9) | (T0 & ~0x9);
-    RETURN();
-}
-
-void OPPROTO glue(op_clear_fpscr_fpscr, REG) (void)
-{
-    env->fpscr[REG] = (env->fpscr[REG] & 0x9);
-    RETURN();
-}
-#else
-void OPPROTO glue(op_store_T0_fpscr_fpscr, REG) (void)
-{
-    env->fpscr[REG] = T0;
-    RETURN();
-}
-
-void OPPROTO glue(op_clear_fpscr_fpscr, REG) (void)
-{
-    env->fpscr[REG] = 0x0;
-    RETURN();
-}
-#endif
-
 #endif /* REG <= 7 */
 
 /* floating point registers moves */
index 4c5e8c6b98993e3b38f93f073f11d4610d00dfd5..3c05b2cbb5df8c3e286942fd1d2649b4908c1450 100644 (file)
@@ -32,6 +32,7 @@
 //#define PPC_DEBUG_DISAS
 //#define DEBUG_MEMORY_ACCESSES
 //#define DO_PPC_STATISTICS
+//#define OPTIMIZE_FPRF_UPDATE
 
 /*****************************************************************************/
 /* Code translation helpers                                                  */
@@ -50,6 +51,10 @@ enum {
 
 static uint16_t *gen_opc_ptr;
 static uint32_t *gen_opparam_ptr;
+#if defined(OPTIMIZE_FPRF_UPDATE)
+static uint16_t *gen_fprf_buf[OPC_BUF_SIZE];
+static uint16_t **gen_fprf_ptr;
+#endif
 
 #include "gen-op.h"
 
@@ -117,16 +122,6 @@ GEN8(gen_op_load_crf_T1, gen_op_load_crf_T1_crf);
 GEN8(gen_op_store_T0_crf, gen_op_store_T0_crf_crf);
 GEN8(gen_op_store_T1_crf, gen_op_store_T1_crf_crf);
 
-/* Floating point condition and status register moves */
-GEN8(gen_op_load_fpscr_T0, gen_op_load_fpscr_T0_fpscr);
-GEN8(gen_op_store_T0_fpscr, gen_op_store_T0_fpscr_fpscr);
-GEN8(gen_op_clear_fpscr, gen_op_clear_fpscr_fpscr);
-static always_inline void gen_op_store_T0_fpscri (int n, uint8_t param)
-{
-    gen_op_set_T0(param);
-    gen_op_store_T0_fpscr(n);
-}
-
 /* General purpose registers moves */
 GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr);
 GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr);
@@ -199,6 +194,44 @@ static always_inline void gen_set_Rc0 (DisasContext *ctx)
     gen_op_set_Rc0();
 }
 
+static always_inline void gen_reset_fpstatus (void)
+{
+#ifdef CONFIG_SOFTFLOAT
+    gen_op_reset_fpstatus();
+#endif
+}
+
+static always_inline void gen_compute_fprf (int set_fprf, int set_rc)
+{
+    if (set_fprf != 0) {
+        /* This case might be optimized later */
+#if defined(OPTIMIZE_FPRF_UPDATE)
+        *gen_fprf_ptr++ = gen_opc_ptr;
+#endif
+        gen_op_compute_fprf(1);
+        if (unlikely(set_rc))
+            gen_op_store_T0_crf(1);
+        gen_op_float_check_status();
+    } else if (unlikely(set_rc)) {
+        /* We always need to compute fpcc */
+        gen_op_compute_fprf(0);
+        gen_op_store_T0_crf(1);
+        if (set_fprf)
+            gen_op_float_check_status();
+    }
+}
+
+static always_inline void gen_optimize_fprf (void)
+{
+#if defined(OPTIMIZE_FPRF_UPDATE)
+    uint16_t **ptr;
+
+    for (ptr = gen_fprf_buf; ptr != (gen_fprf_ptr - 1); ptr++)
+        *ptr = INDEX_op_nop1;
+    gen_fprf_ptr = gen_fprf_buf;
+#endif
+}
+
 static always_inline void gen_update_nip (DisasContext *ctx, target_ulong nip)
 {
 #if defined(TARGET_PPC64)
@@ -497,6 +530,8 @@ enum {
     PPC_CACHE_DCBZ    = 0x0000400000000000ULL,
     /* dcbz instruction with tunable cache line size                         */
     PPC_CACHE_DCBZT   = 0x0000800000000000ULL,
+    /* frsqrtes extension                                                    */
+    PPC_FLOAT_FRSQRTES = 0x0001000000000000ULL,
 };
 
 /*****************************************************************************/
@@ -1656,124 +1691,127 @@ __GEN_LOGICAL2(srd, 0x1B, 0x10, PPC_64B);
 #endif
 
 /***                       Floating-Point arithmetic                       ***/
-#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, type)                     \
+#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat, set_fprf, type)           \
 GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, type)                        \
 {                                                                             \
     if (unlikely(!ctx->fpu_enabled)) {                                        \
         GEN_EXCP_NO_FP(ctx);                                                  \
         return;                                                               \
     }                                                                         \
-    gen_op_reset_scrfx();                                                     \
     gen_op_load_fpr_FT0(rA(ctx->opcode));                                     \
     gen_op_load_fpr_FT1(rC(ctx->opcode));                                     \
     gen_op_load_fpr_FT2(rB(ctx->opcode));                                     \
+    gen_reset_fpstatus();                                                     \
     gen_op_f##op();                                                           \
     if (isfloat) {                                                            \
         gen_op_frsp();                                                        \
     }                                                                         \
     gen_op_store_FT0_fpr(rD(ctx->opcode));                                    \
-    if (unlikely(Rc(ctx->opcode) != 0))                                       \
-        gen_op_set_Rc1();                                                     \
+    gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0);                         \
 }
 
-#define GEN_FLOAT_ACB(name, op2, type)                                        \
-_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, type);                               \
-_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, type);
+#define GEN_FLOAT_ACB(name, op2, set_fprf, type)                              \
+_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0, set_fprf, type);                     \
+_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1, set_fprf, type);
 
-#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat)                     \
-GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT)                        \
+#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat, set_fprf, type)     \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type)                             \
 {                                                                             \
     if (unlikely(!ctx->fpu_enabled)) {                                        \
         GEN_EXCP_NO_FP(ctx);                                                  \
         return;                                                               \
     }                                                                         \
-    gen_op_reset_scrfx();                                                     \
     gen_op_load_fpr_FT0(rA(ctx->opcode));                                     \
     gen_op_load_fpr_FT1(rB(ctx->opcode));                                     \
+    gen_reset_fpstatus();                                                     \
     gen_op_f##op();                                                           \
     if (isfloat) {                                                            \
         gen_op_frsp();                                                        \
     }                                                                         \
     gen_op_store_FT0_fpr(rD(ctx->opcode));                                    \
-    if (unlikely(Rc(ctx->opcode) != 0))                                       \
-        gen_op_set_Rc1();                                                     \
+    gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0);                         \
 }
-#define GEN_FLOAT_AB(name, op2, inval)                                        \
-_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0);                               \
-_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1);
+#define GEN_FLOAT_AB(name, op2, inval, set_fprf, type)                        \
+_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0, set_fprf, type);               \
+_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
 
-#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat)                     \
-GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT)                        \
+#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat, set_fprf, type)     \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, type)                             \
 {                                                                             \
     if (unlikely(!ctx->fpu_enabled)) {                                        \
         GEN_EXCP_NO_FP(ctx);                                                  \
         return;                                                               \
     }                                                                         \
-    gen_op_reset_scrfx();                                                     \
     gen_op_load_fpr_FT0(rA(ctx->opcode));                                     \
     gen_op_load_fpr_FT1(rC(ctx->opcode));                                     \
+    gen_reset_fpstatus();                                                     \
     gen_op_f##op();                                                           \
     if (isfloat) {                                                            \
         gen_op_frsp();                                                        \
     }                                                                         \
     gen_op_store_FT0_fpr(rD(ctx->opcode));                                    \
-    if (unlikely(Rc(ctx->opcode) != 0))                                       \
-        gen_op_set_Rc1();                                                     \
+    gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0);                         \
 }
-#define GEN_FLOAT_AC(name, op2, inval)                                        \
-_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0);                               \
-_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1);
+#define GEN_FLOAT_AC(name, op2, inval, set_fprf, type)                        \
+_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0, set_fprf, type);               \
+_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1, set_fprf, type);
 
-#define GEN_FLOAT_B(name, op2, op3, type)                                     \
+#define GEN_FLOAT_B(name, op2, op3, set_fprf, type)                           \
 GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, type)                        \
 {                                                                             \
     if (unlikely(!ctx->fpu_enabled)) {                                        \
         GEN_EXCP_NO_FP(ctx);                                                  \
         return;                                                               \
     }                                                                         \
-    gen_op_reset_scrfx();                                                     \
     gen_op_load_fpr_FT0(rB(ctx->opcode));                                     \
+    gen_reset_fpstatus();                                                     \
     gen_op_f##name();                                                         \
     gen_op_store_FT0_fpr(rD(ctx->opcode));                                    \
-    if (unlikely(Rc(ctx->opcode) != 0))                                       \
-        gen_op_set_Rc1();                                                     \
+    gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0);                         \
 }
 
-#define GEN_FLOAT_BS(name, op1, op2, type)                                    \
+#define GEN_FLOAT_BS(name, op1, op2, set_fprf, type)                          \
 GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, type)                        \
 {                                                                             \
     if (unlikely(!ctx->fpu_enabled)) {                                        \
         GEN_EXCP_NO_FP(ctx);                                                  \
         return;                                                               \
     }                                                                         \
-    gen_op_reset_scrfx();                                                     \
     gen_op_load_fpr_FT0(rB(ctx->opcode));                                     \
+    gen_reset_fpstatus();                                                     \
     gen_op_f##name();                                                         \
     gen_op_store_FT0_fpr(rD(ctx->opcode));                                    \
-    if (unlikely(Rc(ctx->opcode) != 0))                                       \
-        gen_op_set_Rc1();                                                     \
+    gen_compute_fprf(set_fprf, Rc(ctx->opcode) != 0);                         \
 }
 
 /* fadd - fadds */
-GEN_FLOAT_AB(add, 0x15, 0x000007C0);
+GEN_FLOAT_AB(add, 0x15, 0x000007C0, 1, PPC_FLOAT);
 /* fdiv - fdivs */
-GEN_FLOAT_AB(div, 0x12, 0x000007C0);
+GEN_FLOAT_AB(div, 0x12, 0x000007C0, 1, PPC_FLOAT);
 /* fmul - fmuls */
-GEN_FLOAT_AC(mul, 0x19, 0x0000F800);
+GEN_FLOAT_AC(mul, 0x19, 0x0000F800, 1, PPC_FLOAT);
 
 /* fre */
-GEN_FLOAT_BS(re, 0x3F, 0x18, PPC_FLOAT_EXT);
+GEN_FLOAT_BS(re, 0x3F, 0x18, 1, PPC_FLOAT_EXT);
 
 /* fres */
-GEN_FLOAT_BS(res, 0x3B, 0x18, PPC_FLOAT_FRES);
+GEN_FLOAT_BS(res, 0x3B, 0x18, 1, PPC_FLOAT_FRES);
 
 /* frsqrte */
-GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, PPC_FLOAT_FRSQRTE);
+GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTE);
+
+/* frsqrtes */
+static always_inline void gen_op_frsqrtes (void)
+{
+    gen_op_frsqrte();
+    gen_op_frsp();
+}
+GEN_FLOAT_BS(rsqrtes, 0x3F, 0x1A, 1, PPC_FLOAT_FRSQRTES);
 
 /* fsel */
-_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, PPC_FLOAT_FSEL);
+_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0, 0, PPC_FLOAT_FSEL);
 /* fsub - fsubs */
-GEN_FLOAT_AB(sub, 0x14, 0x000007C0);
+GEN_FLOAT_AB(sub, 0x14, 0x000007C0, 1, PPC_FLOAT);
 /* Optional: */
 /* fsqrt */
 GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT)
@@ -1782,12 +1820,11 @@ GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_reset_scrfx();
     gen_op_load_fpr_FT0(rB(ctx->opcode));
+    gen_reset_fpstatus();
     gen_op_fsqrt();
     gen_op_store_FT0_fpr(rD(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    gen_compute_fprf(1, Rc(ctx->opcode) != 0);
 }
 
 GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT)
@@ -1796,49 +1833,48 @@ GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_FSQRT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_reset_scrfx();
     gen_op_load_fpr_FT0(rB(ctx->opcode));
+    gen_reset_fpstatus();
     gen_op_fsqrt();
     gen_op_frsp();
     gen_op_store_FT0_fpr(rD(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    gen_compute_fprf(1, Rc(ctx->opcode) != 0);
 }
 
 /***                     Floating-Point multiply-and-add                   ***/
 /* fmadd - fmadds */
-GEN_FLOAT_ACB(madd, 0x1D, PPC_FLOAT);
+GEN_FLOAT_ACB(madd, 0x1D, 1, PPC_FLOAT);
 /* fmsub - fmsubs */
-GEN_FLOAT_ACB(msub, 0x1C, PPC_FLOAT);
+GEN_FLOAT_ACB(msub, 0x1C, 1, PPC_FLOAT);
 /* fnmadd - fnmadds */
-GEN_FLOAT_ACB(nmadd, 0x1F, PPC_FLOAT);
+GEN_FLOAT_ACB(nmadd, 0x1F, 1, PPC_FLOAT);
 /* fnmsub - fnmsubs */
-GEN_FLOAT_ACB(nmsub, 0x1E, PPC_FLOAT);
+GEN_FLOAT_ACB(nmsub, 0x1E, 1, PPC_FLOAT);
 
 /***                     Floating-Point round & convert                    ***/
 /* fctiw */
-GEN_FLOAT_B(ctiw, 0x0E, 0x00, PPC_FLOAT);
+GEN_FLOAT_B(ctiw, 0x0E, 0x00, 0, PPC_FLOAT);
 /* fctiwz */
-GEN_FLOAT_B(ctiwz, 0x0F, 0x00, PPC_FLOAT);
+GEN_FLOAT_B(ctiwz, 0x0F, 0x00, 0, PPC_FLOAT);
 /* frsp */
-GEN_FLOAT_B(rsp, 0x0C, 0x00, PPC_FLOAT);
+GEN_FLOAT_B(rsp, 0x0C, 0x00, 1, PPC_FLOAT);
 #if defined(TARGET_PPC64)
 /* fcfid */
-GEN_FLOAT_B(cfid, 0x0E, 0x1A, PPC_64B);
+GEN_FLOAT_B(cfid, 0x0E, 0x1A, 1, PPC_64B);
 /* fctid */
-GEN_FLOAT_B(ctid, 0x0E, 0x19, PPC_64B);
+GEN_FLOAT_B(ctid, 0x0E, 0x19, 0, PPC_64B);
 /* fctidz */
-GEN_FLOAT_B(ctidz, 0x0F, 0x19, PPC_64B);
+GEN_FLOAT_B(ctidz, 0x0F, 0x19, 0, PPC_64B);
 #endif
 
 /* frin */
-GEN_FLOAT_B(rin, 0x08, 0x0C, PPC_FLOAT_EXT);
+GEN_FLOAT_B(rin, 0x08, 0x0C, 1, PPC_FLOAT_EXT);
 /* friz */
-GEN_FLOAT_B(riz, 0x08, 0x0D, PPC_FLOAT_EXT);
+GEN_FLOAT_B(riz, 0x08, 0x0D, 1, PPC_FLOAT_EXT);
 /* frip */
-GEN_FLOAT_B(rip, 0x08, 0x0E, PPC_FLOAT_EXT);
+GEN_FLOAT_B(rip, 0x08, 0x0E, 1, PPC_FLOAT_EXT);
 /* frim */
-GEN_FLOAT_B(rim, 0x08, 0x0F, PPC_FLOAT_EXT);
+GEN_FLOAT_B(rim, 0x08, 0x0F, 1, PPC_FLOAT_EXT);
 
 /***                         Floating-Point compare                        ***/
 /* fcmpo */
@@ -1848,11 +1884,12 @@ GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_reset_scrfx();
     gen_op_load_fpr_FT0(rA(ctx->opcode));
     gen_op_load_fpr_FT1(rB(ctx->opcode));
+    gen_reset_fpstatus();
     gen_op_fcmpo();
     gen_op_store_T0_crf(crfD(ctx->opcode));
+    gen_op_float_check_status();
 }
 
 /* fcmpu */
@@ -1862,47 +1899,54 @@ GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_reset_scrfx();
     gen_op_load_fpr_FT0(rA(ctx->opcode));
     gen_op_load_fpr_FT1(rB(ctx->opcode));
+    gen_reset_fpstatus();
     gen_op_fcmpu();
     gen_op_store_T0_crf(crfD(ctx->opcode));
+    gen_op_float_check_status();
 }
 
 /***                         Floating-point move                           ***/
 /* fabs */
-GEN_FLOAT_B(abs, 0x08, 0x08, PPC_FLOAT);
+/* XXX: beware that fabs never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(abs, 0x08, 0x08, 0, PPC_FLOAT);
 
 /* fmr  - fmr. */
+/* XXX: beware that fmr never checks for NaNs nor update FPSCR */
 GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT)
 {
     if (unlikely(!ctx->fpu_enabled)) {
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_reset_scrfx();
     gen_op_load_fpr_FT0(rB(ctx->opcode));
     gen_op_store_FT0_fpr(rD(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    gen_compute_fprf(0, Rc(ctx->opcode) != 0);
 }
 
 /* fnabs */
-GEN_FLOAT_B(nabs, 0x08, 0x04, PPC_FLOAT);
+/* XXX: beware that fnabs never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(nabs, 0x08, 0x04, 0, PPC_FLOAT);
 /* fneg */
-GEN_FLOAT_B(neg, 0x08, 0x01, PPC_FLOAT);
+/* XXX: beware that fneg never checks for NaNs nor update FPSCR */
+GEN_FLOAT_B(neg, 0x08, 0x01, 0, PPC_FLOAT);
 
 /***                  Floating-Point status & ctrl register                ***/
 /* mcrfs */
 GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT)
 {
+    int bfa;
+
     if (unlikely(!ctx->fpu_enabled)) {
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_load_fpscr_T0(crfS(ctx->opcode));
+    gen_optimize_fprf();
+    bfa = 4 * (7 - crfS(ctx->opcode));
+    gen_op_load_fpscr_T0(bfa);
     gen_op_store_T0_crf(crfD(ctx->opcode));
-    gen_op_clear_fpscr(crfS(ctx->opcode));
+    gen_op_fpscr_resetbit(~(0xF << bfa));
 }
 
 /* mffs */
@@ -1912,10 +1956,11 @@ GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_load_fpscr();
+    gen_optimize_fprf();
+    gen_reset_fpstatus();
+    gen_op_load_fpscr_FT0();
     gen_op_store_FT0_fpr(rD(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    gen_compute_fprf(0, Rc(ctx->opcode) != 0);
 }
 
 /* mtfsb0 */
@@ -1927,12 +1972,15 @@ GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    crb = crbD(ctx->opcode) >> 2;
-    gen_op_load_fpscr_T0(crb);
-    gen_op_andi_T0(~(1 << (crbD(ctx->opcode) & 0x03)));
-    gen_op_store_T0_fpscr(crb);
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    crb = 32 - (crbD(ctx->opcode) >> 2);
+    gen_optimize_fprf();
+    gen_reset_fpstatus();
+    if (likely(crb != 30 && crb != 29))
+        gen_op_fpscr_resetbit(~(1 << crb));
+    if (unlikely(Rc(ctx->opcode) != 0)) {
+        gen_op_load_fpcc();
+        gen_op_set_Rc0();
+    }
 }
 
 /* mtfsb1 */
@@ -1944,12 +1992,18 @@ GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    crb = crbD(ctx->opcode) >> 2;
-    gen_op_load_fpscr_T0(crb);
-    gen_op_ori(1 << (crbD(ctx->opcode) & 0x03));
-    gen_op_store_T0_fpscr(crb);
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    crb = 32 - (crbD(ctx->opcode) >> 2);
+    gen_optimize_fprf();
+    gen_reset_fpstatus();
+    /* XXX: we pretend we can only do IEEE floating-point computations */
+    if (likely(crb != FPSCR_FEX && crb != FPSCR_VX && crb != FPSCR_NI))
+        gen_op_fpscr_setbit(crb);
+    if (unlikely(Rc(ctx->opcode) != 0)) {
+        gen_op_load_fpcc();
+        gen_op_set_Rc0();
+    }
+    /* We can raise a differed exception */
+    gen_op_float_check_status();
 }
 
 /* mtfsf */
@@ -1959,22 +2013,39 @@ GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x02010000, PPC_FLOAT)
         GEN_EXCP_NO_FP(ctx);
         return;
     }
+    gen_optimize_fprf();
     gen_op_load_fpr_FT0(rB(ctx->opcode));
+    gen_reset_fpstatus();
     gen_op_store_fpscr(FM(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    if (unlikely(Rc(ctx->opcode) != 0)) {
+        gen_op_load_fpcc();
+        gen_op_set_Rc0();
+    }
+    /* We can raise a differed exception */
+    gen_op_float_check_status();
 }
 
 /* mtfsfi */
 GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006f0800, PPC_FLOAT)
 {
+    int bf, sh;
+
     if (unlikely(!ctx->fpu_enabled)) {
         GEN_EXCP_NO_FP(ctx);
         return;
     }
-    gen_op_store_T0_fpscri(crbD(ctx->opcode) >> 2, FPIMM(ctx->opcode));
-    if (unlikely(Rc(ctx->opcode) != 0))
-        gen_op_set_Rc1();
+    bf = crbD(ctx->opcode) >> 2;
+    sh = 7 - bf;
+    gen_optimize_fprf();
+    gen_op_set_FT0(FPIMM(ctx->opcode) << (4 * sh));
+    gen_reset_fpstatus();
+    gen_op_store_fpscr(1 << sh);
+    if (unlikely(Rc(ctx->opcode) != 0)) {
+        gen_op_load_fpcc();
+        gen_op_set_Rc0();
+    }
+    /* We can raise a differed exception */
+    gen_op_float_check_status();
 }
 
 /***                           Addressing modes                            ***/
@@ -6717,6 +6788,9 @@ static always_inline int gen_intermediate_code_internal (CPUState *env,
     gen_opc_ptr = gen_opc_buf;
     gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
     gen_opparam_ptr = gen_opparam_buf;
+#if defined(OPTIMIZE_FPRF_UPDATE)
+    gen_fprf_ptr = gen_fprf_buf;
+#endif
     nb_gen_labels = 0;
     ctx.nip = pc_start;
     ctx.tb = tb;