Signed-off-by: Richard Henderson <richard.henderson@linaro.org>
---
target/m68k/helper.h | 2 +
target/m68k/fpu_helper.c | 137 +++++++++++++++++++++++++++++++++++++++
target/m68k/translate.c | 22 ++++---
3 files changed, 151 insertions(+), 10 deletions(-)
diff --git a/target/m68k/helper.h b/target/m68k/helper.h
index 95aa5e53bb..2c71361451 100644
--- a/target/m68k/helper.h
+++ b/target/m68k/helper.h
@@ -126,6 +126,8 @@ DEF_HELPER_FLAGS_4(bfffo_mem, TCG_CALL_NO_WG, i64, env, i32, s32, i32)
DEF_HELPER_3(chk, void, env, s32, s32)
DEF_HELPER_4(chk2, void, env, s32, s32, s32)
+DEF_HELPER_FLAGS_3(load_pdr_to_fx80, TCG_CALL_NO_RWG, void, env, fp, tl)
+
#if !defined(CONFIG_USER_ONLY)
DEF_HELPER_3(ptest, void, env, i32, i32)
DEF_HELPER_3(pflush, void, env, i32, i32)
diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c
index 8314791f50..dd80943153 100644
--- a/target/m68k/fpu_helper.c
+++ b/target/m68k/fpu_helper.c
@@ -749,3 +749,140 @@ void HELPER(fcosh)(CPUM68KState *env, FPReg *res, FPReg *val)
{
res->d = floatx80_cosh(val->d, &env->fp_status);
}
+
+/* From m68k float.h, LDBL_MAX_10_EXP. */
+#define FX80_MAX_10_EXP 4932
+
+/* 10**0 through 10**18 */
+static const int64_t i64_pow10[] = {
+ 1ll,
+ 10ll,
+ 100ll,
+ 1000ll,
+ 10000ll,
+ 100000ll,
+ 1000000ll,
+ 10000000ll,
+ 100000000ll,
+ 1000000000ll,
+ 10000000000ll,
+ 100000000000ll,
+ 1000000000000ll,
+ 10000000000000ll,
+ 100000000000000ll,
+ 1000000000000000ll,
+ 10000000000000000ll,
+ 100000000000000000ll,
+ 1000000000000000000ll,
+};
+
+/* Form 10**exp */
+static floatx80 floatx80_exp10i(int exp, float_status *status)
+{
+ static floatx80 cache[FX80_MAX_10_EXP + 1];
+
+ floatx80 ret;
+
+ assert(exp >= 0 && exp <= FX80_MAX_10_EXP);
+
+ ret = cache[exp];
+ if (ret.high) {
+ if (exp >= ARRAY_SIZE(i64_pow10)) {
+ float_raise(float_flag_inexact, status);
+ }
+ } else {
+ FloatRoundMode old_round = get_float_rounding_mode(status);
+ set_float_rounding_mode(float_round_nearest_even, status);
+
+ if (exp < ARRAY_SIZE(i64_pow10)) {
+ ret = int64_to_floatx80(i64_pow10[exp], status);
+ } else {
+ int e0 = exp / 2;
+ int e1 = exp - e0;
+ ret = floatx80_mul(floatx80_exp10i(e0, status),
+ floatx80_exp10i(e1, status), status);
+ }
+
+ set_float_rounding_mode(old_round, status);
+ cache[exp] = ret;
+ }
+ return ret;
+}
+
+void HELPER(load_pdr_to_fx80)(CPUM68KState *env, FPReg *res, target_ulong addr)
+{
+ uint64_t lo;
+ uint32_t hi;
+ int64_t mant;
+ int exp;
+ floatx80 fexp;
+
+ hi = cpu_ldl_be_data_ra(env, addr, GETPC());
+ lo = cpu_ldq_be_data_ra(env, addr + 4, GETPC());
+
+ if (unlikely((hi & 0x7fff0000) == 0x7fff0000)) {
+ /* NaN or Inf */
+ res->l.lower = lo;
+ res->l.upper = hi >> 16;
+ return;
+ }
+
+ /* Initialize mant with the integer digit. */
+ mant = hi & 0xf;
+ if (!mant && !lo) {
+ /* +/- 0, regardless of exponent. */
+ res->l.lower = 0;
+ res->l.upper = (hi >> 16) & 0x8000;
+ return;
+ }
+
+ /*
+ * Accumulate the 16 decimal fraction digits into mant.
+ * With 17 decimal digits, the maximum value is 10**17 - 1,
+ * which is less than 2**57.
+ */
+ for (int i = 60; i >= 0; i -= 4) {
+ /*
+ * From 1.6.6 Data Format and Type Summary:
+ * The fpu does not detect non-decimal digits in any of the exponent,
+ * integer, or fraction digits. These non-decimal digits are converted
+ * in the same manner as decimal digits; the result is probably useless
+ * although it is repeatable.
+ */
+ mant = mant * 10 + ((lo >> i) & 0xf);
+ }
+
+ /* Apply the mantissa sign. */
+ if (hi & 0x80000000) {
+ mant = -mant;
+ }
+
+ /* Convert the 3 digit decimal exponent to binary. */
+ exp = ((hi >> 24) & 0xf)
+ + ((hi >> 20) & 0xf) * 10
+ + ((hi >> 16) & 0xf) * 100;
+
+ /* Apply the exponent sign. */
+ if (hi & 0x40000000) {
+ exp = -exp;
+ }
+
+ /*
+ * Our representation of mant is integral, whereas the decimal point
+ * belongs between the integer and fractional components.
+ * Adjust the exponent to compensate.
+ */
+ exp -= 16;
+
+ /* Convert mantissa. */
+ res->d = int64_to_floatx80(mant, &env->fp_status);
+
+ /* Apply exponent. */
+ if (exp > 0) {
+ fexp = floatx80_exp10i(exp, &env->fp_status);
+ res->d = floatx80_mul(res->d, fexp, &env->fp_status);
+ } else if (exp < 0) {
+ fexp = floatx80_exp10i(-exp, &env->fp_status);
+ res->d = floatx80_div(res->d, fexp, &env->fp_status);
+ }
+}
diff --git a/target/m68k/translate.c b/target/m68k/translate.c
index 445966fb6a..59e7d27393 100644
--- a/target/m68k/translate.c
+++ b/target/m68k/translate.c
@@ -963,11 +963,11 @@ static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp,
tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower));
break;
case OS_PACKED:
- /*
- * unimplemented data type on 68040/ColdFire
- * FIXME if needed for another FPU
- */
- gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
+ if (!m68k_feature(s->env, M68K_FEATURE_FPU_PACKED_DECIMAL)) {
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
+ break;
+ }
+ gen_helper_load_pdr_to_fx80(tcg_env, fp, addr);
break;
default:
g_assert_not_reached();
@@ -1142,11 +1142,13 @@ static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode,
tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower));
break;
case OS_PACKED:
- /*
- * unimplemented data type on 68040/ColdFire
- * FIXME if needed for another FPU
- */
- gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
+ if (!m68k_feature(s->env, M68K_FEATURE_FPU_PACKED_DECIMAL)) {
+ gen_exception(s, s->base.pc_next, EXCP_FP_UNIMP);
+ break;
+ }
+ tmp = tcg_constant_tl(s->pc);
+ s->pc += 12;
+ gen_helper_load_pdr_to_fx80(tcg_env, fp, tmp);
break;
default:
g_assert_not_reached();
--
2.43.0
© 2016 - 2024 Red Hat, Inc.