diff options
Diffstat (limited to 'src/core/arm')
| -rw-r--r-- | src/core/arm/skyeye_common/vfp/vfpdouble.cpp | 13 | ||||
| -rw-r--r-- | src/core/arm/skyeye_common/vfp/vfpsingle.cpp | 27 |
2 files changed, 38 insertions, 2 deletions
diff --git a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp index 7b035f56a..735cf1a09 100644 --- a/src/core/arm/skyeye_common/vfp/vfpdouble.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpdouble.cpp @@ -134,6 +134,19 @@ u32 vfp_double_normaliseround(ARMul_State* state, int dd, struct vfp_double* vd, #endif if (!(significand & ((1ULL << (VFP_DOUBLE_LOW_BITS + 1)) - 1))) underflow = 0; + + int type = vfp_double_type(vd); + + if ((type & VFP_DENORMAL) && (fpscr & FPSCR_FLUSH_TO_ZERO)) { + // Flush denormal to positive 0 + significand = 0; + + vd->sign = 0; + vd->significand = significand; + + underflow = 0; + exceptions |= FPSCR_UFC; + } } /* diff --git a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp index ae5b325f0..1f9142abc 100644 --- a/src/core/arm/skyeye_common/vfp/vfpsingle.cpp +++ b/src/core/arm/skyeye_common/vfp/vfpsingle.cpp @@ -137,6 +137,19 @@ u32 vfp_single_normaliseround(ARMul_State* state, int sd, struct vfp_single* vs, #endif if (!(significand & ((1 << (VFP_SINGLE_LOW_BITS + 1)) - 1))) underflow = 0; + + int type = vfp_single_type(vs); + + if ((type & VFP_DENORMAL) && (fpscr & FPSCR_FLUSH_TO_ZERO)) { + // Flush denormal to positive 0 + significand = 0; + + vs->sign = 0; + vs->significand = significand; + + underflow = 0; + exceptions |= FPSCR_UFC; + } } /* @@ -1049,12 +1062,22 @@ static u32 vfp_single_fadd(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) static u32 vfp_single_fsub(ARMul_State* state, int sd, int sn, s32 m, u32 fpscr) { LOG_TRACE(Core_ARM11, "s%u = %08x", sn, sd); /* - * Subtraction is addition with one sign inverted. + * Subtraction is addition with one sign inverted. Unpack the second operand to perform FTZ if + * necessary, we can't let fadd do this because a denormal in m might get flushed to +0 in FTZ + * mode, and the resulting sign of 0 OP +0 differs between fadd and fsub. We do not need to do + * this for n because +0 OP 0 is always +0 for both fadd and fsub. */ + struct vfp_single vsm; + u32 exceptions = vfp_single_unpack(&vsm, m, fpscr); + if (exceptions & FPSCR_IDC) { + // The value was flushed to zero, re-pack it. + m = vfp_single_pack(&vsm); + } + if (m != 0x7FC00000) // Only negate if m isn't NaN. m = vfp_single_packed_negate(m); - return vfp_single_fadd(state, sd, sn, m, fpscr); + return vfp_single_fadd(state, sd, sn, m, fpscr) | exceptions; } /* |
