diff --git a/target-mips/helper.h b/target-mips/helper.h
index 1aaa316..952af63 100644
--- a/target-mips/helper.h
+++ b/target-mips/helper.h
@@ -254,10 +254,10 @@ FOP_PROTO(recip)
 FOP_PROTO(rint)
 #undef FOP_PROTO
 
-#define FOP_PROTO(op)                       \
-DEF_HELPER_1(float_ ## op ## _s, i32, i32)  \
-DEF_HELPER_1(float_ ## op ## _d, i64, i64)  \
-DEF_HELPER_1(float_ ## op ## _ps, i64, i64)
+#define FOP_PROTO(op)                            \
+DEF_HELPER_2(float_ ## op ## _s, i32, env, i32)  \
+DEF_HELPER_2(float_ ## op ## _d, i64, env, i64)  \
+DEF_HELPER_2(float_ ## op ## _ps, i64, env, i64)
 FOP_PROTO(abs)
 FOP_PROTO(chs)
 #undef FOP_PROTO
@@ -924,6 +924,8 @@ DEF_HELPER_4(msa_pcnt_df, void, env, i32, i32, i32)
 DEF_HELPER_4(msa_nloc_df, void, env, i32, i32, i32)
 DEF_HELPER_4(msa_nlzc_df, void, env, i32, i32, i32)
 
+DEF_HELPER_2(msa_class_s, i32, env, i32)
+DEF_HELPER_2(msa_class_d, i64, env, i64)
 DEF_HELPER_4(msa_fclass_df, void, env, i32, i32, i32)
 DEF_HELPER_4(msa_ftrunc_s_df, void, env, i32, i32, i32)
 DEF_HELPER_4(msa_ftrunc_u_df, void, env, i32, i32, i32)
diff --git a/target-mips/msa_helper.c b/target-mips/msa_helper.c
index 47fbba0..fed430d 100644
--- a/target-mips/msa_helper.c
+++ b/target-mips/msa_helper.c
@@ -2924,19 +2924,67 @@ void helper_msa_fmax_a_df(CPUMIPSState *env, uint32_t df, uint32_t wd,
     msa_move_v(pwd, pwx);
 }
 
+#define MSA_CLASS_SIGNALING_NAN      0x001
+#define MSA_CLASS_QUIET_NAN          0x002
+#define MSA_CLASS_NEGATIVE_INFINITY  0x004
+#define MSA_CLASS_NEGATIVE_NORMAL    0x008
+#define MSA_CLASS_NEGATIVE_SUBNORMAL 0x010
+#define MSA_CLASS_NEGATIVE_ZERO      0x020
+#define MSA_CLASS_POSITIVE_INFINITY  0x040
+#define MSA_CLASS_POSITIVE_NORMAL    0x080
+#define MSA_CLASS_POSITIVE_SUBNORMAL 0x100
+#define MSA_CLASS_POSITIVE_ZERO      0x200
+
+#define MSA_CLASS(name, bits)                                        \
+uint ## bits ## _t helper_msa_ ## name (CPUMIPSState *env,           \
+                                        uint ## bits ## _t arg)      \
+{                                                                    \
+    if (float ## bits ## _is_signaling_nan(arg,                      \
+                &env->active_tc.msa_fp_status)) {                    \
+        return MSA_CLASS_SIGNALING_NAN;                              \
+    } else if (float ## bits ## _is_quiet_nan(arg,                   \
+                    &env->active_tc.msa_fp_status)) {                \
+        return MSA_CLASS_QUIET_NAN;                                  \
+    } else if (float ## bits ## _is_neg(arg)) {                      \
+        if (float ## bits ## _is_infinity(arg)) {                    \
+            return MSA_CLASS_NEGATIVE_INFINITY;                      \
+        } else if (float ## bits ## _is_zero(arg)) {                 \
+            return MSA_CLASS_NEGATIVE_ZERO;                          \
+        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \
+            return MSA_CLASS_NEGATIVE_SUBNORMAL;                     \
+        } else {                                                     \
+            return MSA_CLASS_NEGATIVE_NORMAL;                        \
+        }                                                            \
+    } else {                                                         \
+        if (float ## bits ## _is_infinity(arg)) {                    \
+            return MSA_CLASS_POSITIVE_INFINITY;                      \
+        } else if (float ## bits ## _is_zero(arg)) {                 \
+            return MSA_CLASS_POSITIVE_ZERO;                          \
+        } else if (float ## bits ## _is_zero_or_denormal(arg)) {     \
+            return MSA_CLASS_POSITIVE_SUBNORMAL;                     \
+        } else {                                                     \
+            return MSA_CLASS_POSITIVE_NORMAL;                        \
+        }                                                            \
+    }                                                                \
+}
+
+MSA_CLASS(class_s, 32)
+MSA_CLASS(class_d, 64)
+#undef FLOAT_MSA_CLASS
+
 void helper_msa_fclass_df(CPUMIPSState *env, uint32_t df,
         uint32_t wd, uint32_t ws)
 {
     wr_t *pwd = &(env->active_fpu.fpr[wd].wr);
     wr_t *pws = &(env->active_fpu.fpr[ws].wr);
     if (df == DF_WORD) {
-        pwd->w[0] = helper_float_class_s(env, pws->w[0]);
-        pwd->w[1] = helper_float_class_s(env, pws->w[1]);
-        pwd->w[2] = helper_float_class_s(env, pws->w[2]);
-        pwd->w[3] = helper_float_class_s(env, pws->w[3]);
+        pwd->w[0] = helper_msa_class_s(env, pws->w[0]);
+        pwd->w[1] = helper_msa_class_s(env, pws->w[1]);
+        pwd->w[2] = helper_msa_class_s(env, pws->w[2]);
+        pwd->w[3] = helper_msa_class_s(env, pws->w[3]);
     } else {
-        pwd->d[0] = helper_float_class_d(env, pws->d[0]);
-        pwd->d[1] = helper_float_class_d(env, pws->d[1]);
+        pwd->d[0] = helper_msa_class_d(env, pws->d[0]);
+        pwd->d[1] = helper_msa_class_d(env, pws->d[1]);
     }
 }
 
diff --git a/target-mips/op_helper.c b/target-mips/op_helper.c
index 0d22b25..407d5e0 100644
--- a/target-mips/op_helper.c
+++ b/target-mips/op_helper.c
@@ -2621,9 +2621,23 @@ uint64_t helper_float_cvtl_d(CPUMIPSState *env, uint64_t fdt0)
     uint64_t dt2;
 
     dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                dt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2634,14 +2648,29 @@ uint64_t helper_float_cvtl_s(CPUMIPSState *env, uint32_t fst0)
     uint64_t dt2;
 
     dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                dt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
 }
 
+
 uint64_t helper_float_cvtps_pw(CPUMIPSState *env, uint64_t dt0)
 {
     uint32_t fst2;
@@ -2729,9 +2758,23 @@ uint32_t helper_float_cvtw_s(CPUMIPSState *env, uint32_t fst0)
     uint32_t wt2;
 
     wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                wt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2742,9 +2785,23 @@ uint32_t helper_float_cvtw_d(CPUMIPSState *env, uint64_t fdt0)
     uint32_t wt2;
 
     wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                wt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2757,9 +2814,23 @@ uint64_t helper_float_roundl_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
     dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                dt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2772,9 +2843,23 @@ uint64_t helper_float_roundl_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
     dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                dt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2787,9 +2872,23 @@ uint32_t helper_float_roundw_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
     wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                wt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2802,9 +2901,23 @@ uint32_t helper_float_roundw_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_nearest_even, &env->active_fpu.fp_status);
     wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                wt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2815,9 +2928,23 @@ uint64_t helper_float_truncl_d(CPUMIPSState *env, uint64_t fdt0)
     uint64_t dt2;
 
     dt2 = float64_to_int64_round_to_zero(fdt0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                dt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2828,9 +2955,23 @@ uint64_t helper_float_truncl_s(CPUMIPSState *env, uint32_t fst0)
     uint64_t dt2;
 
     dt2 = float32_to_int64_round_to_zero(fst0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                dt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2841,9 +2982,23 @@ uint32_t helper_float_truncw_d(CPUMIPSState *env, uint64_t fdt0)
     uint32_t wt2;
 
     wt2 = float64_to_int32_round_to_zero(fdt0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                wt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2854,9 +3009,23 @@ uint32_t helper_float_truncw_s(CPUMIPSState *env, uint32_t fst0)
     uint32_t wt2;
 
     wt2 = float32_to_int32_round_to_zero(fst0, &env->active_fpu.fp_status);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                wt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2869,9 +3038,23 @@ uint64_t helper_float_ceill_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
     dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                dt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2884,9 +3067,23 @@ uint64_t helper_float_ceill_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
     dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                dt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2899,9 +3096,23 @@ uint32_t helper_float_ceilw_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
     wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                wt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+               & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2914,9 +3125,23 @@ uint32_t helper_float_ceilw_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_up, &env->active_fpu.fp_status);
     wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                wt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2929,9 +3154,23 @@ uint64_t helper_float_floorl_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
     dt2 = float64_to_int64(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                dt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2944,9 +3183,23 @@ uint64_t helper_float_floorl_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
     dt2 = float32_to_int64(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        dt2 = FP_TO_INT64_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                dt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    dt2 = INT64_MIN;
+                else
+                    dt2 = INT64_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            dt2 = FP_TO_INT64_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return dt2;
@@ -2959,9 +3212,23 @@ uint32_t helper_float_floorw_d(CPUMIPSState *env, uint64_t fdt0)
     set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
     wt2 = float64_to_int32(fdt0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float64_is_any_nan(fdt0)) {
+                wt2 = 0;
+            } else {
+                if (float64_is_neg(fdt0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
@@ -2974,36 +3241,121 @@ uint32_t helper_float_floorw_s(CPUMIPSState *env, uint32_t fst0)
     set_float_rounding_mode(float_round_down, &env->active_fpu.fp_status);
     wt2 = float32_to_int32(fst0, &env->active_fpu.fp_status);
     restore_rounding_mode(env);
-    if (get_float_exception_flags(&env->active_fpu.fp_status)
-        & (float_flag_invalid | float_flag_overflow)) {
-        wt2 = FP_TO_INT32_OVERFLOW;
+    if (env->active_fpu.fcr31 & (1 << FCR31_NAN2008)) {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            if (float32_is_any_nan(fst0)) {
+                wt2 = 0;
+            } else {
+                if (float32_is_neg(fst0))
+                    wt2 = INT32_MIN;
+                else
+                    wt2 = INT32_MAX;
+            }
+        }
+    } else {
+        if (get_float_exception_flags(&env->active_fpu.fp_status)
+                & (float_flag_invalid | float_flag_overflow)) {
+            wt2 = FP_TO_INT32_OVERFLOW;
+        }
     }
     update_fcr31(env, GETPC());
     return wt2;
 }
 
-/* unary operations, not modifying fp status  */
-#define FLOAT_UNOP(name)                                       \
-uint64_t helper_float_ ## name ## _d(uint64_t fdt0)                \
-{                                                              \
-    return float64_ ## name(fdt0);                             \
-}                                                              \
-uint32_t helper_float_ ## name ## _s(uint32_t fst0)                \
-{                                                              \
-    return float32_ ## name(fst0);                             \
-}                                                              \
-uint64_t helper_float_ ## name ## _ps(uint64_t fdt0)               \
-{                                                              \
-    uint32_t wt0;                                              \
-    uint32_t wth0;                                             \
-                                                               \
-    wt0 = float32_ ## name(fdt0 & 0XFFFFFFFF);                 \
-    wth0 = float32_ ## name(fdt0 >> 32);                       \
-    return ((uint64_t)wth0 << 32) | wt0;                       \
-}
-FLOAT_UNOP(abs)
-FLOAT_UNOP(chs)
-#undef FLOAT_UNOP
+uint64_t helper_float_abs_d(CPUMIPSState *env, uint64_t fdt0)
+{
+    uint64_t fdt1;
+
+    if (env->active_fpu.fcr31 & (1 << FCR31_ABS2008)) {
+        fdt1 = float64_abs(fdt0);
+    } else {
+        if (float64_is_neg(fdt0)) {
+            fdt1 = float64_sub(0, fdt0, &env->active_fpu.fp_status);
+        } else {
+            fdt1 = float64_add(0, fdt0, &env->active_fpu.fp_status);
+        }
+        update_fcr31(env, GETPC());
+    }
+    return fdt1;
+}
+
+uint32_t helper_float_abs_s(CPUMIPSState *env, uint32_t fst0)
+{
+    uint32_t fst1;
+
+    if (env->active_fpu.fcr31 & (1 << FCR31_ABS2008)) {
+        fst1 = float32_abs(fst0);
+    } else {
+        if (float32_is_neg(fst0)) {
+            fst1 = float32_sub(0, fst0, &env->active_fpu.fp_status);
+        } else {
+            fst1 = float32_add(0, fst0, &env->active_fpu.fp_status);
+        }
+        update_fcr31(env, GETPC());
+    }
+    return fst1;
+}
+
+uint64_t helper_float_abs_ps(CPUMIPSState *env, uint64_t fpst0)
+{
+    uint32_t fst0 = fpst0 & 0XFFFFFFFF;
+    uint32_t fsth0 = fpst0 >> 32;
+    uint32_t fst1;
+    uint32_t fsth1;
+
+    if (float32_is_neg(fst0)) {
+        fst1 = float32_sub(0, fst0, &env->active_fpu.fp_status);
+    } else {
+        fst1 = float32_add(0, fst0, &env->active_fpu.fp_status);
+    }
+    if (float32_is_neg(fsth0)) {
+        fsth1 = float32_sub(0, fsth0, &env->active_fpu.fp_status);
+    } else {
+        fsth1 = float32_add(0, fsth0, &env->active_fpu.fp_status);
+    }
+    update_fcr31(env, GETPC());
+    return ((uint64_t)fsth1 << 32) | fst1;
+}
+
+uint64_t helper_float_chs_d(CPUMIPSState *env, uint64_t fdt0)
+{
+    uint64_t fdt1;
+
+    if (env->active_fpu.fcr31 & (1 << FCR31_ABS2008)) {
+        fdt1 = float64_chs(fdt0);
+    } else {
+        fdt1 = float64_sub(0, fdt0, &env->active_fpu.fp_status);
+        update_fcr31(env, GETPC());
+    }
+    return fdt1;
+}
+
+uint32_t helper_float_chs_s(CPUMIPSState *env, uint32_t fst0)
+{
+    uint32_t fst1;
+
+    if (env->active_fpu.fcr31 & (1 << FCR31_ABS2008)) {
+        fst1 = float32_chs(fst0);
+    } else {
+        fst1 = float32_sub(0, fst0, &env->active_fpu.fp_status);
+        update_fcr31(env, GETPC());
+    }
+    return fst1;
+}
+
+uint64_t helper_float_chs_ps(CPUMIPSState *env, uint64_t fpst0)
+{
+    uint32_t fst0 = fpst0 & 0XFFFFFFFF;
+    uint32_t fsth0 = fpst0 >> 32;
+    uint32_t fst1;
+    uint32_t fsth1;
+
+    fst1 = float32_sub(0, fst0, &env->active_fpu.fp_status);
+    fsth1 = float32_sub(0, fsth0, &env->active_fpu.fp_status);
+    update_fcr31(env, GETPC());
+    return ((uint64_t)fsth1 << 32) | fst1;
+}
 
 /* MIPS specific unary operations */
 uint64_t helper_float_recip_d(CPUMIPSState *env, uint64_t fdt0)
diff --git a/target-mips/translate.c b/target-mips/translate.c
index 669bd0c..327c532 100644
--- a/target-mips/translate.c
+++ b/target-mips/translate.c
@@ -8792,7 +8792,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i32 fp0 = tcg_temp_new_i32();
 
             gen_load_fpr32(ctx, fp0, fs);
-            gen_helper_float_abs_s(fp0, fp0);
+            gen_helper_float_abs_s(fp0, cpu_env, fp0);
             gen_store_fpr32(ctx, fp0, fd);
             tcg_temp_free_i32(fp0);
         }
@@ -8811,7 +8811,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i32 fp0 = tcg_temp_new_i32();
 
             gen_load_fpr32(ctx, fp0, fs);
-            gen_helper_float_chs_s(fp0, fp0);
+            gen_helper_float_chs_s(fp0, cpu_env, fp0);
             gen_store_fpr32(ctx, fp0, fd);
             tcg_temp_free_i32(fp0);
         }
@@ -9282,7 +9282,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i64 fp0 = tcg_temp_new_i64();
 
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_abs_d(fp0, fp0);
+            gen_helper_float_abs_d(fp0, cpu_env, fp0);
             gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
         }
@@ -9303,7 +9303,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i64 fp0 = tcg_temp_new_i64();
 
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_chs_d(fp0, fp0);
+            gen_helper_float_chs_d(fp0, cpu_env, fp0);
             gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
         }
@@ -9794,7 +9794,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i64 fp0 = tcg_temp_new_i64();
 
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_abs_ps(fp0, fp0);
+            gen_helper_float_abs_ps(fp0, cpu_env, fp0);
             gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
         }
@@ -9815,7 +9815,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
             TCGv_i64 fp0 = tcg_temp_new_i64();
 
             gen_load_fpr64(ctx, fp0, fs);
-            gen_helper_float_chs_ps(fp0, fp0);
+            gen_helper_float_chs_ps(fp0, cpu_env, fp0);
             gen_store_fpr64(ctx, fp0, fd);
             tcg_temp_free_i64(fp0);
         }
@@ -9934,7 +9934,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         break;
     case OPC_CVT_S_PU:
-        check_cp1_64bitmode(ctx);
+        check_ps(ctx);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
 
@@ -9956,7 +9956,7 @@ static void gen_farith (DisasContext *ctx, enum fopcode op1,
         }
         break;
     case OPC_CVT_S_PL:
-        check_cp1_64bitmode(ctx);
+        check_ps(ctx);
         {
             TCGv_i32 fp0 = tcg_temp_new_i32();
 
diff --git a/target-mips/translate_init.c b/target-mips/translate_init.c
index 3e48680..18bc7cb 100644
--- a/target-mips/translate_init.c
+++ b/target-mips/translate_init.c
@@ -273,6 +273,7 @@ static const mips_def_t mips_defs[] =
         .CP0_Status_rw_bitmask = 0x3678FF1F,
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
                     (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID),
+        .CP1_fcr31 = 0,
         .SEGBITS = 32,
         .PABITS = 32,
         .insn_flags = CPU_MIPS32R2 | ASE_MIPS16,
@@ -303,6 +304,7 @@ static const mips_def_t mips_defs[] =
                     (0xff << CP0TCSt_TASID),
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
                     (1 << FCR0_D) | (1 << FCR0_S) | (0x95 << FCR0_PRID),
+        .CP1_fcr31 = 0,
         .CP0_SRSCtl = (0xf << CP0SRSCtl_HSS),
         .CP0_SRSConf0_rw_bitmask = 0x3fffffff,
         .CP0_SRSConf0 = (1U << CP0SRSC0_M) | (0x3fe << CP0SRSC0_SRS3) |
@@ -343,6 +345,7 @@ static const mips_def_t mips_defs[] =
         .CP0_Status_rw_bitmask = 0x3778FF1F,
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_L) | (1 << FCR0_W) |
                     (1 << FCR0_D) | (1 << FCR0_S) | (0x93 << FCR0_PRID),
+        .CP1_fcr31 = 0,
         .SEGBITS = 32,
         .PABITS = 32,
         .insn_flags = CPU_MIPS32R2 | ASE_MIPS16 | ASE_DSP | ASE_DSPR2,
@@ -433,8 +436,7 @@ static const mips_def_t mips_defs[] =
     },
     {
         /* A generic CPU supporting MIPS32 Release 6 ISA.
-           FIXME: Support IEEE 754-2008 FP.
-                  Eventually this should be replaced by a real CPU model. */
+           FIXME: Eventually this should be replaced by a real CPU model. */
         .name = "mips32r6-generic",
         .CP0_PRid = 0x00010000,
         .CP0_Config0 = MIPS_CONFIG0 | (0x2 << CP0C0_AR) |
@@ -484,6 +486,7 @@ static const mips_def_t mips_defs[] =
         .CP0_Status_rw_bitmask = 0x3678FFFF,
         /* The R4000 has a full 64bit FPU but doesn't use the fcr0 bits. */
         .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 40,
         .PABITS = 36,
         .insn_flags = CPU_MIPS3,
@@ -502,6 +505,7 @@ static const mips_def_t mips_defs[] =
         .CP0_Status_rw_bitmask = 0x3678FFFF,
         /* The VR5432 has a full 64bit FPU but doesn't use the fcr0 bits. */
         .CP1_fcr0 = (0x54 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 40,
         .PABITS = 32,
         .insn_flags = CPU_VR54XX,
@@ -547,6 +551,7 @@ static const mips_def_t mips_defs[] =
         /* The 5Kf has F64 / L / W but doesn't use the fcr0 bits. */
         .CP1_fcr0 = (1 << FCR0_D) | (1 << FCR0_S) |
                     (0x81 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 42,
         .PABITS = 36,
         .insn_flags = CPU_MIPS64,
@@ -574,6 +579,7 @@ static const mips_def_t mips_defs[] =
         .CP1_fcr0 = (1 << FCR0_3D) | (1 << FCR0_PS) |
                     (1 << FCR0_D) | (1 << FCR0_S) |
                     (0x82 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 40,
         .PABITS = 36,
         .insn_flags = CPU_MIPS64 | ASE_MIPS3D,
@@ -600,6 +606,7 @@ static const mips_def_t mips_defs[] =
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) |
                     (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) |
                     (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 42,
         .PABITS = 36,
         .insn_flags = CPU_MIPS64R2 | ASE_MIPS3D,
@@ -702,6 +709,7 @@ static const mips_def_t mips_defs[] =
         .CCRes = 2,
         .CP0_Status_rw_bitmask = 0x35D0FFFF,
         .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 40,
         .PABITS = 40,
         .insn_flags = CPU_LOONGSON2E,
@@ -720,6 +728,7 @@ static const mips_def_t mips_defs[] =
         .CCRes = 2,
         .CP0_Status_rw_bitmask = 0xF5D0FF1F,   /* Bits 7:5 not writable.  */
         .CP1_fcr0 = (0x5 << FCR0_PRID) | (0x1 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 40,
         .PABITS = 40,
         .insn_flags = CPU_LOONGSON2F,
@@ -747,6 +756,7 @@ static const mips_def_t mips_defs[] =
         .CP1_fcr0 = (1 << FCR0_F64) | (1 << FCR0_3D) | (1 << FCR0_PS) |
                     (1 << FCR0_L) | (1 << FCR0_W) | (1 << FCR0_D) |
                     (1 << FCR0_S) | (0x00 << FCR0_PRID) | (0x0 << FCR0_REV),
+        .CP1_fcr31 = 0,
         .SEGBITS = 42,
         .PABITS = 36,
         .insn_flags = CPU_MIPS64R2 | ASE_DSP | ASE_DSPR2,
@@ -834,7 +844,11 @@ static void fpu_init (CPUMIPSState *env, const mips_def_t *def)
 
     for (i = 0; i < MIPS_FPU_MAX; i++) {
         env->fpus[i].fcr0 = def->CP1_fcr0;
-        set_snan_bit_is_one(1, &env->fpus[i].fp_status);
+        if (env->insn_flags & ISA_MIPS32R6) {
+            set_snan_bit_is_one(0, &env->fpus[i].fp_status);
+        } else {
+            set_snan_bit_is_one(1, &env->fpus[i].fp_status);
+        }
     }
 
     memcpy(&env->active_fpu, &env->fpus[0], sizeof(env->active_fpu));
@@ -893,5 +907,5 @@ static void msa_reset(CPUMIPSState *env)
     /* clear float_status nan mode */
     set_default_nan_mode(0, &env->active_tc.msa_fp_status);
 
-    set_snan_bit_is_one(1, &env->active_tc.msa_fp_status);
+    set_snan_bit_is_one(0, &env->active_tc.msa_fp_status);
 }
