From patchwork Sun Jun 21 22:35:06 2015 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 8bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 487082 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id AB7BA140157 for ; Mon, 22 Jun 2015 08:36:33 +1000 (AEST) Received: from localhost ([::1]:37484 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z6nqp-0004Cz-SE for incoming@patchwork.ozlabs.org; Sun, 21 Jun 2015 18:36:31 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:38732) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z6nq8-0003F1-KN for qemu-devel@nongnu.org; Sun, 21 Jun 2015 18:35:55 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1Z6nq1-00065g-CY for qemu-devel@nongnu.org; Sun, 21 Jun 2015 18:35:48 -0400 Received: from smtp4-g21.free.fr ([212.27.42.4]:51707) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1Z6nq0-00065V-Rb for qemu-devel@nongnu.org; Sun, 21 Jun 2015 18:35:41 -0400 Received: from Quad.localdomain (unknown [78.238.229.36]) by smtp4-g21.free.fr (Postfix) with ESMTPS id 66BE14C806E; Mon, 22 Jun 2015 00:35:38 +0200 (CEST) From: Laurent Vivier To: qemu-devel@nongnu.org Date: Mon, 22 Jun 2015 00:35:06 +0200 Message-Id: <1434926106-5905-3-git-send-email-laurent@vivier.eu> X-Mailer: git-send-email 2.4.3 In-Reply-To: <1434926106-5905-1-git-send-email-laurent@vivier.eu> References: <1434926106-5905-1-git-send-email-laurent@vivier.eu> MIME-Version: 1.0 X-detected-operating-system: by eggs.gnu.org: Windows NT kernel [generic] [fuzzy] X-Received-From: 212.27.42.4 Cc: Andreas Schwab , Laurent Vivier , gerg@uclinux.org Subject: [Qemu-devel] [PATCH 2/2] m68k: Implement 680x0 processors family 96 bit FPU X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.14 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Signed-off-by: Laurent Vivier --- configure | 2 +- fpu/softfloat-specialize.h | 48 ++- fpu/softfloat.c | 38 +- gdb-xml/m68k-fp.xml | 21 + include/fpu/softfloat.h | 11 +- target-m68k/cpu.c | 9 + target-m68k/cpu.h | 38 +- target-m68k/helper.c | 868 +++++++++++++++++++++++++++++++++++++++++- target-m68k/helper.h | 44 +++ target-m68k/qregs.def | 5 +- target-m68k/translate.c | 930 ++++++++++++++++++++++++++++++++++++++++++++- 11 files changed, 1967 insertions(+), 47 deletions(-) create mode 100644 gdb-xml/m68k-fp.xml diff --git a/configure b/configure index 4a66b2e..a282979 100755 --- a/configure +++ b/configure @@ -5211,7 +5211,7 @@ case "$target_name" in ;; m68k) bflt="yes" - gdb_xml_files="cf-core.xml cf-fp.xml" + gdb_xml_files="cf-core.xml cf-fp.xml m68k-fp.xml" ;; microblaze|microblazeel) TARGET_ARCH=microblaze diff --git a/fpu/softfloat-specialize.h b/fpu/softfloat-specialize.h index 6dd41d8..1f8d5ea 100644 --- a/fpu/softfloat-specialize.h +++ b/fpu/softfloat-specialize.h @@ -113,7 +113,7 @@ const float16 float16_default_nan = const_float16(0xFE00); #if defined(TARGET_SPARC) const float32 float32_default_nan = const_float32(0x7FFFFFFF); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_XTENSA) || defined(TARGET_S390X) + defined(TARGET_XTENSA) || defined(TARGET_S390X) || defined(TARGET_M68K) const float32 float32_default_nan = const_float32(0x7FC00000); #elif SNAN_BIT_IS_ONE const float32 float32_default_nan = const_float32(0x7FBFFFFF); @@ -127,7 +127,7 @@ const float32 float32_default_nan = const_float32(0xFFC00000); #if defined(TARGET_SPARC) const float64 float64_default_nan = const_float64(LIT64( 0x7FFFFFFFFFFFFFFF )); #elif defined(TARGET_PPC) || defined(TARGET_ARM) || defined(TARGET_ALPHA) || \ - defined(TARGET_S390X) + defined(TARGET_S390X) || defined(TARGET_M68K) const float64 float64_default_nan = const_float64(LIT64( 0x7FF8000000000000 )); #elif SNAN_BIT_IS_ONE const float64 float64_default_nan = const_float64(LIT64(0x7FF7FFFFFFFFFFFF)); @@ -138,7 +138,10 @@ const float64 float64_default_nan = const_float64(LIT64( 0xFFF8000000000000 )); /*---------------------------------------------------------------------------- | The pattern for a default generated extended double-precision NaN. *----------------------------------------------------------------------------*/ -#if SNAN_BIT_IS_ONE +#if defined(TARGET_M68K) +#define floatx80_default_nan_high 0x7FFF +#define floatx80_default_nan_low LIT64(0x4000000000000000) +#elif SNAN_BIT_IS_ONE #define floatx80_default_nan_high 0x7FFF #define floatx80_default_nan_low LIT64(0xBFFFFFFFFFFFFFFF) #else @@ -150,6 +153,21 @@ const floatx80 floatx80_default_nan = make_floatx80_init(floatx80_default_nan_high, floatx80_default_nan_low); /*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision inf. +*----------------------------------------------------------------------------*/ + +#if defined(TARGET_M68K) +#define floatx80_default_inf_high 0x7FFF +#define floatx80_default_inf_low LIT64(0x0000000000000000) +#else +#define floatx80_default_inf_high 0x7FFF +#define floatx80_default_inf_low LIT64(0x8000000000000000) +#endif + +const floatx80 floatx80_default_inf + = make_floatx80_init(floatx80_default_inf_high, floatx80_default_inf_low); + +/*---------------------------------------------------------------------------- | The pattern for a default generated quadruple-precision NaN. The `high' and | `low' values hold the most- and least-significant bits, respectively. *----------------------------------------------------------------------------*/ @@ -475,6 +493,26 @@ static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, return 1; } } +#elif defined(TARGET_M68K) +static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, + flag aIsLargerSignificand) +{ + /* If either operand, but not both operands, of an operation is a + * nonsignaling NAN, then that NAN is returned as the result. If both + * operands are nonsignaling NANs, then the destination operand + * nonsignaling NAN is returned as the result. + */ + + if (aIsSNaN) { + return 0; + } else if (bIsSNaN) { + return 1; + } else if (bIsQNaN) { + return 1; + } else { + return 0; + } +} #else static int pickNaN(flag aIsQNaN, flag aIsSNaN, flag bIsQNaN, flag bIsSNaN, flag aIsLargerSignificand) @@ -974,7 +1012,9 @@ int floatx80_is_signaling_nan( floatx80 a ) floatx80 floatx80_maybe_silence_nan( floatx80 a ) { if (floatx80_is_signaling_nan(a)) { -#if SNAN_BIT_IS_ONE +#if defined(TARGET_M68K) + a.low |= LIT64(0x4000000000000000); +#elif SNAN_BIT_IS_ONE # if defined(TARGET_MIPS) || defined(TARGET_SH4) || defined(TARGET_UNICORE32) a.low = floatx80_default_nan_low; a.high = floatx80_default_nan_high; diff --git a/fpu/softfloat.c b/fpu/softfloat.c index f1170fe..19992a9 100644 --- a/fpu/softfloat.c +++ b/fpu/softfloat.c @@ -902,7 +902,9 @@ static floatx80 roundAndPackFloatx80(int8 roundingPrecision, flag zSign, ) { return packFloatx80( zSign, 0x7FFE, ~ roundMask ); } - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, + floatx80_default_inf_high, + floatx80_default_inf_low); } if ( zExp <= 0 ) { isTiny = @@ -1861,7 +1863,9 @@ floatx80 float32_to_floatx80(float32 a, float_status *status) if (aSig) { return commonNaNToFloatx80(float32ToCommonNaN(a, status), status); } - return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(aSign, + floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) { if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 ); @@ -3633,7 +3637,9 @@ floatx80 float64_to_floatx80(float64 a, float_status *status) if (aSig) { return commonNaNToFloatx80(float64ToCommonNaN(a, status), status); } - return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(aSign, + floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) { if ( aSig == 0 ) return packFloatx80( aSign, 0, 0 ); @@ -4878,8 +4884,8 @@ int64 floatx80_to_int64(floatx80 a, float_status *status) if ( shiftCount ) { float_raise(float_flag_invalid, status); if ( ! aSign - || ( ( aExp == 0x7FFF ) - && ( aSig != LIT64( 0x8000000000000000 ) ) ) + || ((aExp == floatx80_default_inf_high) + && (aSig != floatx80_default_inf_low)) ) { return LIT64( 0x7FFFFFFFFFFFFFFF ); } @@ -5150,7 +5156,9 @@ static floatx80 addFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, if ((uint64_t)(bSig << 1)) { return propagateFloatx80NaN(a, b, status); } - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, + floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) ++expDiff; shift64ExtraRightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); @@ -5228,7 +5236,8 @@ static floatx80 subFloatx80Sigs(floatx80 a, floatx80 b, flag zSign, if ((uint64_t)(bSig << 1)) { return propagateFloatx80NaN(a, b, status); } - return packFloatx80( zSign ^ 1, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign ^ 1, floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) ++expDiff; shift128RightJamming( aSig, 0, - expDiff, &aSig, &zSig1 ); @@ -5322,7 +5331,8 @@ floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status) return propagateFloatx80NaN(a, b, status); } if ( ( bExp | bSig ) == 0 ) goto invalid; - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, floatx80_default_inf_high, + floatx80_default_inf_low); } if ( bExp == 0x7FFF ) { if ((uint64_t)(bSig << 1)) { @@ -5335,7 +5345,8 @@ floatx80 floatx80_mul(floatx80 a, floatx80 b, float_status *status) z.high = floatx80_default_nan_high; return z; } - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) { if ( aSig == 0 ) return packFloatx80( zSign, 0, 0 ); @@ -5386,7 +5397,8 @@ floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status) } goto invalid; } - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, floatx80_default_inf_high, + floatx80_default_inf_low); } if ( bExp == 0x7FFF ) { if ((uint64_t)(bSig << 1)) { @@ -5404,7 +5416,8 @@ floatx80 floatx80_div(floatx80 a, floatx80 b, float_status *status) return z; } float_raise(float_flag_divbyzero, status); - return packFloatx80( zSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(zSign, floatx80_default_inf_high, + floatx80_default_inf_low); } normalizeFloatx80Subnormal( bSig, &bExp, &bSig ); } @@ -6118,7 +6131,8 @@ floatx80 float128_to_floatx80(float128 a, float_status *status) if ( aSig0 | aSig1 ) { return commonNaNToFloatx80(float128ToCommonNaN(a, status), status); } - return packFloatx80( aSign, 0x7FFF, LIT64( 0x8000000000000000 ) ); + return packFloatx80(aSign, floatx80_default_inf_high, + floatx80_default_inf_low); } if ( aExp == 0 ) { if ( ( aSig0 | aSig1 ) == 0 ) return packFloatx80( aSign, 0, 0 ); diff --git a/gdb-xml/m68k-fp.xml b/gdb-xml/m68k-fp.xml new file mode 100644 index 0000000..64290d1 --- /dev/null +++ b/gdb-xml/m68k-fp.xml @@ -0,0 +1,21 @@ + + + + + + + + + + + + + + + , + + diff --git a/include/fpu/softfloat.h b/include/fpu/softfloat.h index ded34eb..ce6e3ed 100644 --- a/include/fpu/softfloat.h +++ b/include/fpu/softfloat.h @@ -605,6 +605,11 @@ float64 floatx80_to_float64(floatx80, float_status *status); float128 floatx80_to_float128(floatx80, float_status *status); /*---------------------------------------------------------------------------- +| The pattern for a default generated extended double-precision inf. +*----------------------------------------------------------------------------*/ +extern const floatx80 floatx80_default_inf; + +/*---------------------------------------------------------------------------- | Software IEC/IEEE extended double-precision operations. *----------------------------------------------------------------------------*/ floatx80 floatx80_round_to_int(floatx80, float_status *status); @@ -643,7 +648,8 @@ static inline floatx80 floatx80_chs(floatx80 a) static inline int floatx80_is_infinity(floatx80 a) { - return (a.high & 0x7fff) == 0x7fff && a.low == 0x8000000000000000LL; + return (a.high & 0x7fff) == floatx80_default_inf.high && + a.low == floatx80_default_inf.low; } static inline int floatx80_is_neg(floatx80 a) @@ -672,6 +678,9 @@ static inline int floatx80_is_any_nan(floatx80 a) #define floatx80_pi make_floatx80(0x4000, 0xc90fdaa22168c235LL) #define floatx80_half make_floatx80(0x3ffe, 0x8000000000000000LL) #define floatx80_infinity make_floatx80(0x7fff, 0x8000000000000000LL) +#define floatx80_e make_floatx80(0x4000, 0xadf85458a2bb4a9aULL) +#define floatx80_log2e make_floatx80(0x3fff, 0xb8aa3b295c17f0bcULL) +#define floatx80_10 make_floatx80(0x4002, 0xa000000000000000ULL) /*---------------------------------------------------------------------------- | The pattern for a default generated extended double-precision NaN. diff --git a/target-m68k/cpu.c b/target-m68k/cpu.c index 4c6760b..c79d289 100644 --- a/target-m68k/cpu.c +++ b/target-m68k/cpu.c @@ -46,6 +46,7 @@ static void m68k_cpu_reset(CPUState *s) M68kCPU *cpu = M68K_CPU(s); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); CPUM68KState *env = &cpu->env; + int i; mcc->parent_reset(s); @@ -55,6 +56,14 @@ static void m68k_cpu_reset(CPUState *s) #endif m68k_switch_sp(env); + for (i = 0; i < 8; i++) { + env->m68k_fregs[i].d = floatx80_default_nan; + } + env->fp0h = floatx80_default_nan.high; + env->fp0l = floatx80_default_nan.low; + env->fp1h = floatx80_default_nan.high; + env->fp1l = floatx80_default_nan.low; + env->cc_op = CC_OP_FLAGS; /* TODO: We should set PC from the interrupt vector. */ env->pc = 0; diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index 7a8eabf..ce2ad0d 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -56,6 +56,23 @@ #define NB_MMU_MODES 2 +typedef uint32_t CPUM68K_SingleU; +typedef uint64_t CPUM68K_DoubleU; + +typedef struct { + uint32_t high; + uint64_t low; +} __attribute__((packed)) CPUM68K_XDoubleU; + +typedef struct { + uint8_t high[2]; + uint8_t low[10]; +} __attribute__((packed)) CPUM68K_PDoubleU; + +typedef CPU_LDoubleU FPReg; +#define PRIxFPH PRIx16 +#define PRIxFPL PRIx64 + typedef struct CPUM68KState { uint32_t dregs[8]; uint32_t aregs[8]; @@ -72,8 +89,11 @@ typedef struct CPUM68KState { uint32_t cc_src; uint32_t cc_x; - float64 fregs[8]; - float64 fp_result; + /* 680x0 */ + FPReg m68k_fregs[8]; + /* ColdFire */ + float64 cf_fregs[8]; + float64 cf_fp_result; uint32_t fpcr; uint32_t fpsr; float_status fp_status; @@ -86,6 +106,13 @@ typedef struct CPUM68KState { uint32_t macsr; uint32_t mac_mask; + /* Temporary storage for FPU */ + + uint32_t fp0h; + uint64_t fp0l; + uint32_t fp1h; + uint64_t fp1l; + /* Temporary storage for DIV helpers. */ uint32_t div1; uint32_t div2; @@ -167,6 +194,13 @@ typedef enum { #define M68K_SSP 0 #define M68K_USP 1 +#define FCCF_SHIFT 24 +#define FCCF_MASK (0xff << FCCF_SHIFT) +#define FCCF_A (0x01 << FCCF_SHIFT) /* Not-A-Number */ +#define FCCF_I (0x02 << FCCF_SHIFT) /* Infinity */ +#define FCCF_Z (0x04 << FCCF_SHIFT) /* Zero */ +#define FCCF_N (0x08 << FCCF_SHIFT) /* Negative */ + /* CACR fields are implementation defined, but some bits are common. */ #define M68K_CACR_EUSP 0x10 diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 1b92b50..208c06f 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -22,6 +22,24 @@ #include "exec/gdbstub.h" #include "exec/helper-proto.h" +#include + +#if 0 +#define DBG_FPUH(...) do { fprintf(stderr, "0x%08x: ", env->pc); \ + fprintf(stderr, __VA_ARGS__); } while (0) +#define DBG_FPU(...) do { fprintf(stderr, __VA_ARGS__); } while (0) +#else +#define DBG_FPUH(...) +#define DBG_FPU(...) +#endif +static inline float FLOAT(float32 x) +{ + return *(float *)&x; +} +static inline double DOUBLE(float64 x) +{ + return *(double *)&x; +} #define SIGNBIT (1u << 31) @@ -116,10 +134,10 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) g_slist_free(list); } -static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) { if (n < 8) { - stfq_p(mem_buf, env->fregs[n]); + stfq_p(mem_buf, env->cf_fregs[n]); return 8; } if (n < 11) { @@ -130,12 +148,54 @@ static int fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) return 0; } -static int fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) { if (n < 8) { - env->fregs[n] = ldfq_p(mem_buf); + env->cf_fregs[n] = ldfq_p(mem_buf); return 8; } + switch (n) { + case 8: + env->fpcr = ldl_be_p(mem_buf); + return 4; + case 9: + env->fpsr = ldl_be_p(mem_buf); + return 4; + case 10: + return 4; + } + return 0; +} + +static int m68k_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +{ + if (n < 8) { + stw_be_p(mem_buf, env->m68k_fregs[n].l.upper); + memset(mem_buf + 2, 0, 2); + stq_be_p(mem_buf + 4, env->m68k_fregs[n].l.lower); + return 12; + } + switch (n) { + case 8: + stl_be_p(mem_buf, env->fpcr); + return 4; + case 9: + stl_be_p(mem_buf, env->fpsr); + return 4; + case 10: + memset(mem_buf, 0, 4); + return 4; + } + return 0; +} + +static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) +{ + if (n < 8) { + env->m68k_fregs[n].l.upper = lduw_be_p(mem_buf); + env->m68k_fregs[n].l.lower = ldq_be_p(mem_buf + 4); + return 12; + } if (n < 11) { /* FP control registers (not implemented) */ return 4; @@ -169,8 +229,11 @@ void m68k_cpu_init_gdb(M68kCPU *cpu) CPUM68KState *env = &cpu->env; if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { - gdb_register_coprocessor(cs, fpu_gdb_get_reg, fpu_gdb_set_reg, + gdb_register_coprocessor(cs, cf_fpu_gdb_get_reg, cf_fpu_gdb_set_reg, 11, "cf-fp.xml", 18); + } else if (m68k_feature(env, M68K_FEATURE_FPU)) { + gdb_register_coprocessor(cs, m68k_fpu_gdb_get_reg, + m68k_fpu_gdb_set_reg, 11, "m68k-fp.xml", 18); } /* TODO: Add [E]MAC registers. */ } @@ -864,7 +927,7 @@ HELPER_ROXL(uint8_t, 8) HELPER_ROXL(uint16_t, 16) HELPER_ROXL(uint32_t, 32) -/* FPU helpers. */ +/* ColdFire FPU helpers. */ uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val) { return float64_to_int32(val, &env->fp_status); @@ -953,6 +1016,799 @@ uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val) return float64_compare_quiet(val, float64_zero, &env->fp_status); } +/* 680x0 FPU helpers. */ + +static const floatx80 fpu_rom[128] = { + [0x00] = floatx80_pi, /* Pi */ + + [0x0b] = { .high = 0x3ffd, .low = 0x9a209a84fbcff798ULL }, /* Log10(2) */ + [0x0c] = floatx80_e, /* e */ + [0x0d] = floatx80_log2e, /* Log2(e) */ + [0x0e] = { .high = 0x3ffd, .low = 0xde5bd8a937287195ULL }, /* Log10(e) */ + [0x0f] = floatx80_zero, /* Zero */ + + [0x30] = floatx80_ln2, /* ln(2) */ + [0x31] = { .high = 0x4000, .low = 0x935d8dddaaa8ac17ULL }, /* ln(10) */ + [0x32] = floatx80_one, /* 10^0 */ + [0x33] = floatx80_10, /* 10^1 */ + [0x34] = { .high = 0x4005, .low = 0xc800000000000000ULL }, /* 10^2 */ + [0x35] = { .high = 0x400c, .low = 0x9c40000000000000ULL }, /* 10^4 */ + [0x36] = { .high = 0x4019, .low = 0xbebc200000000000ULL }, /* 10^8 */ + [0x37] = { .high = 0x4034, .low = 0x8e1bc9bf04000000ULL }, /* 10^16 */ + [0x38] = { .high = 0x4069, .low = 0x9dc5ada82b70b59eULL }, /* 10^32 */ + [0x39] = { .high = 0x40d3, .low = 0xc2781f49ffcfa6d5ULL }, /* 10^64 */ + [0x3a] = { .high = 0x41a8, .low = 0x93ba47c980e98ce0ULL }, /* 10^128 */ + [0x3b] = { .high = 0x4351, .low = 0xaa7eebfb9df9de8eULL }, /* 10^256 */ + [0x3c] = { .high = 0x46a3, .low = 0xe319a0aea60e91c7ULL }, /* 10^512 */ + [0x3d] = { .high = 0x4d48, .low = 0xc976758681750c17ULL }, /* 10^1024 */ + [0x3e] = { .high = 0x5a92, .low = 0x9e8b3b5dc53d5de5ULL }, /* 10^2048 */ + [0x3f] = { .high = 0x7525, .low = 0xc46052028a20979bULL }, /* 10^4096 */ +}; + +static inline floatx80 FP0_to_floatx80(CPUM68KState *env) +{ + floatx80 res; + + res.high = env->fp0h; + res.low = env->fp0l; + + return res; +} + +static inline void floatx80_to_FP0(CPUM68KState *env, floatx80 res) +{ + env->fp0h = res.high; + env->fp0l = res.low; +} + +static inline void floatx80_to_FP1(CPUM68KState *env, floatx80 res) +{ + env->fp1h = res.high; + env->fp1l = res.low; +} + +static inline int32_t FP0_to_int32(CPUM68KState *env) +{ + return env->fp0h; +} + +static inline void int32_to_FP0(CPUM68KState *env, int32_t val) +{ + env->fp0h = val; +} + +static inline float32 FP0_to_float32(CPUM68KState *env) +{ + return *(float32 *)&env->fp0h; +} + +static inline void float32_to_FP0(CPUM68KState *env, float32 val) +{ + + env->fp0h = *(uint32_t *)&val; +} + +static inline float64 FP0_to_float64(CPUM68KState *env) +{ + return *(float64 *)&env->fp0l; +} + +static inline void float64_to_FP0(CPUM68KState *env, float64 val) +{ + env->fp0l = *(uint64_t *)&val; +} + +static inline floatx80 FP1_to_floatx80(CPUM68KState *env) +{ + floatx80 res; + + res.high = env->fp1h; + res.low = env->fp1l; + + return res; +} + +static inline long double floatx80_to_ldouble(floatx80 val) +{ + if (floatx80_is_infinity(val)) { + if (floatx80_is_neg(val)) { + return -__builtin_infl(); + } + return __builtin_infl(); + } + if (floatx80_is_any_nan(val)) { + char low[20]; + sprintf(low, "0x%016"PRIx64, val.low); + + return nanl(low); + } + + return *(long double *)&val; +} + +static inline floatx80 ldouble_to_floatx80(long double val) +{ + floatx80 res; + + if (isinf(val)) { + res.high = floatx80_default_nan.high; + res.low = 0; + } + if (isinf(val) < 0) { + res.high |= 0x8000; + } + if (isnan(val)) { + res.high = floatx80_default_nan.high; + res.low = *(uint64_t *)((char *)&val + 4); + } + return *(floatx80 *)&val; +} + +void HELPER(const_FP0)(CPUM68KState *env, uint32_t offset) +{ + env->fp0h = fpu_rom[offset].high; + env->fp0l = fpu_rom[offset].low; + DBG_FPUH("ROM[0x%02x] %"PRIxFPH" %"PRIxFPL" %.17Lg\n", + offset, env->fp0h, env->fp0l, + floatx80_to_ldouble(FP0_to_floatx80(env))); +} + +static inline void restore_precision_mode(CPUM68KState *env) +{ + int rounding_precision; + + rounding_precision = (env->fpcr >> 6) & 0x03; + + switch (rounding_precision) { + case 0: /* extended */ + set_floatx80_rounding_precision(80, &env->fp_status); + break; + case 1: /* single */ + set_floatx80_rounding_precision(32, &env->fp_status); + break; + case 2: /* double */ + set_floatx80_rounding_precision(64, &env->fp_status); + break; + case 3: /* reserved */ + default: + break; + } +} + +static inline void restore_rounding_mode(CPUM68KState *env) +{ + int rounding_mode; + + rounding_mode = (env->fpcr >> 4) & 0x03; + + switch (rounding_mode) { + case 0: /* round to nearest */ + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + break; + case 1: /* round to zero */ + set_float_rounding_mode(float_round_to_zero, &env->fp_status); + break; + case 2: /* round toward minus infinity */ + set_float_rounding_mode(float_round_down, &env->fp_status); + break; + case 3: /* round toward positive infinity */ + set_float_rounding_mode(float_round_up, &env->fp_status); + break; + } +} + +void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) +{ + DBG_FPUH("set_fpcr %04x\n", val); + + env->fpcr = val & 0xffff; + + restore_precision_mode(env); + restore_rounding_mode(env); +} + +void HELPER(exts32_FP0)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("exts32_FP0 %d", FP0_to_int32(env)); + + res = int32_to_floatx80(FP0_to_int32(env), &env->fp_status); + + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + floatx80_to_FP0(env, res); +} + +void HELPER(extf32_FP0)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("extf32_FP0 %f %08x", FLOAT(FP0_to_float32(env)), + FP0_to_int32(env)); + res = float32_to_floatx80(FP0_to_float32(env), &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(extf64_FP0)(CPUM68KState *env) +{ + floatx80 res; + uint64_t val; + + val = FP0_to_float64(env); + DBG_FPUH("extf64_FP0 0x%016"PRIx64", %g", val, *(double *)&val); + res = float64_to_floatx80(val, &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(extp96_FP0)(CPUM68KState *env) +{ +} + +void HELPER(reds32_FP0)(CPUM68KState *env) +{ + floatx80 val; + int32_t res; + + val = FP0_to_floatx80(env); + DBG_FPUH("reds32_FP0 %Lg (%08x %016"PRIx64")", + floatx80_to_ldouble(val), env->fp0h, env->fp0l); + res = floatx80_to_int32(val, &env->fp_status); + DBG_FPU(" = %d\n", res); + + int32_to_FP0(env, res); +} + +void HELPER(redf32_FP0)(CPUM68KState *env) +{ + floatx80 val; + float32 res; + + val = FP0_to_floatx80(env); + DBG_FPUH("redf32_FP0 %Lg", floatx80_to_ldouble(val)); + res = floatx80_to_float32(val, &env->fp_status); + DBG_FPU(" = %f\n", FLOAT(res)); + + float32_to_FP0(env, res); +} + +void HELPER(redf64_FP0)(CPUM68KState *env) +{ + floatx80 val; + float64 res; + + val = FP0_to_floatx80(env); + DBG_FPUH("redf64_FP0 %Lg", floatx80_to_ldouble(val)); + res = floatx80_to_float64(val, &env->fp_status); + DBG_FPU(" = %g\n", *(double *)&res); + + float64_to_FP0(env, res); +} + +void HELPER(redp96_FP0)(CPUM68KState *env) +{ + DBG_FPUH("redp96_FP0\n"); +} + +void HELPER(iround_FP0)(CPUM68KState *env) +{ + floatx80 res; + + res = FP0_to_floatx80(env); + + DBG_FPUH("iround_FP0 %Lg", floatx80_to_ldouble(res)); + + res = floatx80_round_to_int(res, &env->fp_status); + + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(sinh_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("sinh_FP0 %Lg", val); + val = sinhl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(itrunc_FP0)(CPUM68KState *env) +{ + floatx80 res; + + res = FP0_to_floatx80(env); + DBG_FPUH("itrunc_FP0 %Lg", floatx80_to_ldouble(res)); + + set_float_rounding_mode(float_round_to_zero, &env->fp_status); + res = floatx80_round_to_int(res, &env->fp_status); + restore_rounding_mode(env); + + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(sqrt_FP0)(CPUM68KState *env) +{ + floatx80 res; + + res = FP0_to_floatx80(env); + DBG_FPUH("sqrt_FP0 %Lg", floatx80_to_ldouble(res)); + res = floatx80_sqrt(res, &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(lognp1_FP0)(CPUM68KState *env) +{ + floatx80 val; + long double res; + + val = FP0_to_floatx80(env); + DBG_FPUH("lognp1_FP0 %Lg", floatx80_to_ldouble(val)); + res = logl(floatx80_to_ldouble(val) + 1.0); + DBG_FPU(" = %Lg\n", res); + + floatx80_to_FP0(env, ldouble_to_floatx80(res)); +} + +void HELPER(ln_FP0)(CPUM68KState *env) +{ + floatx80 val; + long double res; + + val = FP0_to_floatx80(env); + DBG_FPUH("ln_FP0 %Lg", floatx80_to_ldouble(val)); + res = logl(floatx80_to_ldouble(val)); + DBG_FPU(" = %Lg\n", res); + + floatx80_to_FP0(env, ldouble_to_floatx80(res)); +} + +void HELPER(log10_FP0)(CPUM68KState *env) +{ + floatx80 val; + long double res; + + val = FP0_to_floatx80(env); + DBG_FPUH("log10_FP0 %Lg", floatx80_to_ldouble(val)); + res = log10l(floatx80_to_ldouble(val)); + DBG_FPU(" = %Lg\n", res); + + floatx80_to_FP0(env, ldouble_to_floatx80(res)); +} + +void HELPER(atan_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("atan_FP0 %Lg", val); + val = atanl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(asin_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("asin_FP0 %Lg", val); + val = asinl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(atanh_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("atanh_FP0 %Lg", val); + val = atanhl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(sin_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("sin_FP0 %Lg", val); + val = sinl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(tanh_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("tanh_FP0 %Lg", val); + val = tanhl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(tan_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("tan_FP0 %Lg", val); + val = tanl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(exp_FP0)(CPUM68KState *env) +{ + floatx80 f; + long double res; + + f = FP0_to_floatx80(env); + + DBG_FPUH("exp_FP0 %Lg", floatx80_to_ldouble(f)); + + res = expl(floatx80_to_ldouble(f)); + + DBG_FPU(" = %Lg\n", res); + floatx80_to_FP0(env, ldouble_to_floatx80(res)); +} + +void HELPER(exp2_FP0)(CPUM68KState *env) +{ + floatx80 f; + long double res; + + f = FP0_to_floatx80(env); + + DBG_FPUH("exp2_FP0 %Lg", floatx80_to_ldouble(f)); + + res = exp2l(floatx80_to_ldouble(f)); + + DBG_FPU(" = %Lg\n", res); + floatx80_to_FP0(env, ldouble_to_floatx80(res)); +} + +void HELPER(exp10_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("exp2_FP0 %Lg", val); + val = exp10l(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(abs_FP0)(CPUM68KState *env) +{ + floatx80 res; + + res = FP0_to_floatx80(env); + DBG_FPUH("abs_FP0 %Lg", floatx80_to_ldouble(res)); + res = floatx80_abs(res); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(cosh_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("cosh_FP0 %Lg", val); + val = coshl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(chs_FP0)(CPUM68KState *env) +{ + floatx80 res; + + res = FP0_to_floatx80(env); + DBG_FPUH("chs_FP0 %Lg", floatx80_to_ldouble(res)); + res = floatx80_chs(res); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(acos_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("acos_FP0 %Lg", val); + val = acosl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(cos_FP0)(CPUM68KState *env) +{ + floatx80 res; + long double val; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("cos_FP0 %Lg", val); + val = cosl(val); + DBG_FPU(" = %Lg", val); + res = ldouble_to_floatx80(val); + floatx80_to_FP0(env, res); +} + +void HELPER(getexp_FP0)(CPUM68KState *env) +{ + int32_t exp; + floatx80 res; + + DBG_FPUH("getexp_FP0 %Lg", floatx80_to_ldouble(FP0_to_floatx80(env))); + + DBG_FPU(" fp0h 0x%08x fp0l 0x%016" PRIx64, env->fp0h, env->fp0l); + + exp = (env->fp0h & 0x7fff) - 0x3fff; + + res = int32_to_floatx80(exp, &env->fp_status); + + DBG_FPU(" = %Lg", floatx80_to_ldouble(res)); + floatx80_to_FP0(env, res); +} + +void HELPER(scale_FP0_FP1)(CPUM68KState *env) +{ + int32_t scale; + int32_t exp; + + DBG_FPUH("scale_FP0 %Lg", floatx80_to_ldouble(FP0_to_floatx80(env))); + + DBG_FPU(" fp0h 0x%08x fp0l 0x%016" PRIx64, env->fp0h, env->fp0l); + + scale = floatx80_to_int32(FP0_to_floatx80(env), &env->fp_status); + + exp = (env->fp1h & 0x7fff) + scale; + + env->fp0h = (env->fp1h & 0x8000) | (exp & 0x7fff); + env->fp0l = env->fp1l; + DBG_FPU(" = %Lg", floatx80_to_ldouble(FP0_to_floatx80(env))); +} + +void HELPER(add_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("add_FP0_FP1 %Lg %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)), + floatx80_to_ldouble(FP1_to_floatx80(env))); + res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(sub_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("sub_FP0 %Lg %Lg", floatx80_to_ldouble(FP0_to_floatx80(env)), + floatx80_to_ldouble(FP1_to_floatx80(env))); + res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env), + &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(mul_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("mul_FP0_FP1 %Lg %Lg", + floatx80_to_ldouble(FP0_to_floatx80(env)), + floatx80_to_ldouble(FP1_to_floatx80(env))); + res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(div_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + + DBG_FPUH("div_FP0_FP1 %Lg %Lg", + floatx80_to_ldouble(FP0_to_floatx80(env)), + floatx80_to_ldouble(FP1_to_floatx80(env))); + res = floatx80_div(FP1_to_floatx80(env), FP0_to_floatx80(env), + &env->fp_status); + DBG_FPU(" = %Lg\n", floatx80_to_ldouble(res)); + + floatx80_to_FP0(env, res); +} + +void HELPER(mod_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + long double src, dst; + + src = floatx80_to_ldouble(FP0_to_floatx80(env)); + dst = floatx80_to_ldouble(FP1_to_floatx80(env)); + + DBG_FPUH("mod_FP0_FP1 %Lg %Lg", src, dst); + dst = fmodl(dst, src); + DBG_FPU(" = %Lg\n", dst); + + res = ldouble_to_floatx80(dst); + floatx80_to_FP0(env, res); +} + +void HELPER(sincos_FP0_FP1)(CPUM68KState *env) +{ + floatx80 res; + long double val, valsin, valcos; + + res = FP0_to_floatx80(env); + val = floatx80_to_ldouble(res); + + DBG_FPUH("sincos_FP0 %Lg", val); + sincosl(val, &valsin, &valcos); + DBG_FPU(" = %Lg, %Lg", valsin, valcos); + res = ldouble_to_floatx80(valsin); + floatx80_to_FP0(env, res); + res = ldouble_to_floatx80(valcos); + floatx80_to_FP1(env, res); +} + +static void set_fpcc(CPUM68KState *env, floatx80 val) +{ + uint32_t fpcc = 0; + + if (floatx80_is_any_nan(val)) { + fpcc |= FCCF_A; + } + if (floatx80_is_infinity(val)) { + fpcc |= FCCF_I; + } + if (floatx80_is_neg(val)) { + fpcc |= FCCF_N; + } + if (floatx80_is_zero(val)) { + fpcc |= FCCF_Z; + } + + DBG_FPU("FPCC 0x%02x %c%c%c%c\n", fpcc >> FCCF_SHIFT, + fpcc & FCCF_N ? 'N' : '-', + fpcc & FCCF_Z ? 'Z' : '-', + fpcc & FCCF_I ? 'I' : '-', + fpcc & FCCF_A ? 'A' : '-'); + env->fpsr = (env->fpsr & ~FCCF_MASK) | fpcc; +} + +void HELPER(fcmp_FP0_FP1)(CPUM68KState *env) +{ + /* ??? This may incorrectly raise exceptions. */ + /* ??? Should flush denormals to zero. */ + floatx80 res; + DBG_FPU("cmp_FP0_FP1 %Lg %Lg\n", floatx80_to_ldouble(FP0_to_floatx80(env)), + floatx80_to_ldouble(FP1_to_floatx80(env))); + res = floatx80_sub(FP1_to_floatx80(env), FP0_to_floatx80(env), + &env->fp_status); + if (floatx80_is_any_nan(res)) { + /* +/-inf compares equal against itself, but sub returns nan. */ + if (!floatx80_is_any_nan(FP0_to_floatx80(env)) + && !floatx80_is_any_nan(FP1_to_floatx80(env))) { + res = floatx80_zero; + if (floatx80_lt_quiet(FP1_to_floatx80(env), res, &env->fp_status)) { + res = floatx80_chs(res); + } + } + } + + set_fpcc(env, res); +} + +void HELPER(compare_FP0)(CPUM68KState *env) +{ + DBG_FPU("compare_FP0 %Lg\n", floatx80_to_ldouble(FP0_to_floatx80(env))); + set_fpcc(env, FP0_to_floatx80(env)); +} + +void HELPER(update_fpsr)(CPUM68KState *env) +{ + int eflags; + + DBG_FPU("update_fpsr"); + + env->fpsr &= 0xff00; + + eflags = get_float_exception_flags(&env->fp_status); + + if (eflags & float_flag_invalid) { + env->fpsr |= 0x0080; + env->fpsr |= 0x2000; + } + if (eflags & float_flag_divbyzero) { + env->fpsr |= 0x0010; + env->fpsr |= 0x0400; + } + if (eflags & float_flag_overflow) { + env->fpsr |= 0x0040; + env->fpsr |= 0x1000; + } + if (eflags & float_flag_underflow) { + env->fpsr |= 0x0020; + env->fpsr |= 0x0800; + } + if (eflags & float_flag_inexact) { + env->fpsr |= 0x0008; + env->fpsr |= 0x0200; + } + set_float_exception_flags(0, &env->fp_status); +} + +void HELPER(fmovem)(CPUM68KState *env, uint32_t opsize, uint32_t mode, + uint32_t mask) +{ + fprintf(stderr, "MISSING HELPER fmovem\n"); +} + /* MAC unit. */ /* FIXME: The MAC unit implementation is a bit of a mess. Some helpers take values, others take register numbers and manipulate the contents diff --git a/target-m68k/helper.h b/target-m68k/helper.h index 5db4278..eaa34c9 100644 --- a/target-m68k/helper.h +++ b/target-m68k/helper.h @@ -63,6 +63,50 @@ DEF_HELPER_3(mul_f64, f64, env, f64, f64) DEF_HELPER_3(div_f64, f64, env, f64, f64) DEF_HELPER_3(sub_cmp_f64, f64, env, f64, f64) DEF_HELPER_2(compare_f64, i32, env, f64) +DEF_HELPER_1(exts32_FP0, void, env) +DEF_HELPER_1(extf32_FP0, void, env) +DEF_HELPER_1(extf64_FP0, void, env) +DEF_HELPER_1(redf32_FP0, void, env) +DEF_HELPER_1(redf64_FP0, void, env) +DEF_HELPER_1(extp96_FP0, void, env) +DEF_HELPER_1(reds32_FP0, void, env) +DEF_HELPER_1(redp96_FP0, void, env) + +DEF_HELPER_4(fmovem, void, env, i32, i32, i32) +DEF_HELPER_2(set_fpcr, void, env, i32) +DEF_HELPER_2(const_FP0, void, env, i32) +DEF_HELPER_1(iround_FP0, void, env) +DEF_HELPER_1(sinh_FP0, void, env) +DEF_HELPER_1(itrunc_FP0, void, env) +DEF_HELPER_1(sqrt_FP0, void, env) +DEF_HELPER_1(lognp1_FP0, void, env) +DEF_HELPER_1(atan_FP0, void, env) +DEF_HELPER_1(asin_FP0, void, env) +DEF_HELPER_1(atanh_FP0, void, env) +DEF_HELPER_1(sin_FP0, void, env) +DEF_HELPER_1(tanh_FP0, void, env) +DEF_HELPER_1(tan_FP0, void, env) +DEF_HELPER_1(exp_FP0, void, env) +DEF_HELPER_1(exp2_FP0, void, env) +DEF_HELPER_1(exp10_FP0, void, env) +DEF_HELPER_1(ln_FP0, void, env) +DEF_HELPER_1(log10_FP0, void, env) +DEF_HELPER_1(abs_FP0, void, env) +DEF_HELPER_1(cosh_FP0, void, env) +DEF_HELPER_1(chs_FP0, void, env) +DEF_HELPER_1(acos_FP0, void, env) +DEF_HELPER_1(cos_FP0, void, env) +DEF_HELPER_1(getexp_FP0, void, env) +DEF_HELPER_1(scale_FP0_FP1, void, env) +DEF_HELPER_1(add_FP0_FP1, void, env) +DEF_HELPER_1(sub_FP0_FP1, void, env) +DEF_HELPER_1(mul_FP0_FP1, void, env) +DEF_HELPER_1(div_FP0_FP1, void, env) +DEF_HELPER_1(mod_FP0_FP1, void, env) +DEF_HELPER_1(sincos_FP0_FP1, void, env) +DEF_HELPER_1(fcmp_FP0_FP1, void, env) +DEF_HELPER_1(compare_FP0, void, env) +DEF_HELPER_1(update_fpsr, void, env) DEF_HELPER_3(mac_move, void, env, i32, i32) DEF_HELPER_3(macmulf, i64, env, i32, i32) diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index aba6c9a..af37602 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -1,4 +1,6 @@ -DEFF64(FP_RESULT, fp_result) +DEFF64(FP_RESULT, cf_fp_result) +DEFF96(FP0, fp0) +DEFF96(FP1, fp1) DEFO32(PC, pc) DEFO32(SR, sr) DEFO32(CC_OP, cc_op) @@ -10,3 +12,4 @@ DEFO32(DIV2, div2) DEFO32(QUADH, quadh) DEFO32(MACSR, macsr) DEFO32(MAC_MASK, mac_mask) +DEFO32(FPSR, fpsr) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index a5e68cf..05222fb 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -32,7 +32,7 @@ //#define DEBUG_DISPATCH 1 -/* Fake floating point. */ +/* ColdFire: Fake floating point. */ #define tcg_gen_mov_f64 tcg_gen_mov_i64 #define tcg_gen_qemu_ldf64 tcg_gen_qemu_ld64 #define tcg_gen_qemu_stf64 tcg_gen_qemu_st64 @@ -40,10 +40,14 @@ #define DEFO32(name, offset) static TCGv QREG_##name; #define DEFO64(name, offset) static TCGv_i64 QREG_##name; #define DEFF64(name, offset) static TCGv_i64 QREG_##name; +#define DEFF96(name, offset) \ + static TCGv_i32 QREG_##name##H; \ + static TCGv_i64 QREG_##name##L; #include "qregs.def" #undef DEFO32 #undef DEFO64 #undef DEFF64 +#undef DEFF96 static TCGv_i32 cpu_halted; static TCGv_i32 cpu_exception_index; @@ -55,6 +59,8 @@ static TCGv cpu_dregs[8]; static TCGv cpu_aregs[8]; static TCGv_i64 cpu_fregs[8]; static TCGv_i64 cpu_macc[4]; +static TCGv QEMU_FPSR; +static TCGv QEMU_FPCR; #define REG(insn, pos) (((insn) >> (pos)) & 7) #define DREG(insn, pos) cpu_dregs[REG(insn, pos)] @@ -80,10 +86,17 @@ void m68k_tcg_init(void) #define DEFO32(name, offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUM68KState, offset), #name); #define DEFO64(name, offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUM68KState, offset), #name); #define DEFF64(name, offset) DEFO64(name, offset) +#define DEFF96(name, offset) do { \ +QREG_##name##H = tcg_global_mem_new_i32(TCG_AREG0, \ + offsetof(CPUM68KState, offset##h), #name); \ +QREG_##name##L = tcg_global_mem_new_i64(TCG_AREG0, \ + offsetof(CPUM68KState, offset##l), #name); \ +} while (0); #include "qregs.def" #undef DEFO32 #undef DEFO64 #undef DEFF64 +#undef DEFF96 cpu_halted = tcg_global_mem_new_i32(TCG_AREG0, -offsetof(M68kCPU, env) + @@ -107,7 +120,7 @@ void m68k_tcg_init(void) p += 3; sprintf(p, "F%d", i); cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUM68KState, fregs[i]), p); + offsetof(CPUM68KState, cf_fregs[i]), p); p += 3; } for (i = 0; i < 4; i++) { @@ -117,6 +130,11 @@ void m68k_tcg_init(void) p += 5; } + QEMU_FPSR = tcg_global_mem_new(TCG_AREG0, offsetof(CPUM68KState, fpsr), + "FPSR"); + QEMU_FPCR = tcg_global_mem_new(TCG_AREG0, offsetof(CPUM68KState, fpcr), + "FPCR"); + NULL_QREG = tcg_global_mem_new(TCG_AREG0, -4, "NULL"); store_dummy = tcg_global_mem_new(TCG_AREG0, -8, "NULL"); } @@ -246,6 +264,46 @@ static inline void gen_addr_fault(DisasContext *s) gen_exception(s, s->insn_pc, EXCP_ADDRESS); } +static void gen_op_load_fpr_FP0(int freg) +{ + tcg_gen_ld16u_i32(QREG_FP0H, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_ld_i64(QREG_FP0L, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.low)); +} + +static void gen_op_store_fpr_FP0(int freg) +{ + tcg_gen_st16_i32(QREG_FP0H, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_st_i64(QREG_FP0L, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.low)); +} + +static void gen_op_store_fpr_FP1(int freg) +{ + tcg_gen_st16_i32(QREG_FP1H, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_st_i64(QREG_FP1L, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.low)); +} + +static void gen_op_load_fpr_FP1(int freg) +{ + tcg_gen_ld16u_i32(QREG_FP1H, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_ld_i64(QREG_FP1L, cpu_env, + offsetof(CPUM68KState, m68k_fregs[freg]) + + offsetof(FPReg, d.low)); +} + /* Generate a load from the specified address. Narrow values are sign extended to full register width. */ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) @@ -835,6 +893,320 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, return NULL_QREG; } +static inline void gen_extend_FP0(int opsize) +{ + switch (opsize) { + case OS_BYTE: + tcg_gen_ext8s_i32(QREG_FP0H, QREG_FP0H); + gen_helper_exts32_FP0(cpu_env); + break; + case OS_WORD: + tcg_gen_ext16s_i32(QREG_FP0H, QREG_FP0H); + gen_helper_exts32_FP0(cpu_env); + break; + case OS_LONG: + gen_helper_exts32_FP0(cpu_env); + break; + case OS_SINGLE: + gen_helper_extf32_FP0(cpu_env); + break; + case OS_DOUBLE: + gen_helper_extf64_FP0(cpu_env); + break; + case OS_EXTENDED: + tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16); + break; + case OS_PACKED: + gen_helper_extp96_FP0(cpu_env); + break; + default: + g_assert_not_reached(); + } +} + +static inline void gen_reduce_FP0(int opsize) +{ + switch (opsize) { + case OS_BYTE: + case OS_WORD: + case OS_LONG: + gen_helper_reds32_FP0(cpu_env); + break; + case OS_SINGLE: + gen_helper_redf32_FP0(cpu_env); + break; + case OS_DOUBLE: + gen_helper_redf64_FP0(cpu_env); + break; + case OS_EXTENDED: + tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16); + break; + case OS_PACKED: + gen_helper_redp96_FP0(cpu_env); + break; + default: + g_assert_not_reached(); + } +} + +static inline void gen_load_FP0(DisasContext *s, int opsize, TCGv addr) +{ + TCGv tmp; + int index = IS_USER(s); + s->is_mem = 1; + switch (opsize) { + case OS_BYTE: + tcg_gen_qemu_ld8s(QREG_FP0H, addr, index); + gen_helper_exts32_FP0(cpu_env); + break; + case OS_WORD: + tcg_gen_qemu_ld16s(QREG_FP0H, addr, index); + gen_helper_exts32_FP0(cpu_env); + break; + case OS_LONG: + tcg_gen_qemu_ld32u(QREG_FP0H, addr, index); + gen_helper_exts32_FP0(cpu_env); + break; + case OS_SINGLE: + tcg_gen_qemu_ld32u(QREG_FP0H, addr, index); + gen_helper_extf32_FP0(cpu_env); + break; + case OS_DOUBLE: + tcg_gen_qemu_ld64(QREG_FP0L, addr, index); + gen_helper_extf64_FP0(cpu_env); + break; + case OS_EXTENDED: + tcg_gen_qemu_ld32u(QREG_FP0H, addr, index); + tcg_gen_shri_i32(QREG_FP0H, QREG_FP0H, 16); + tmp = tcg_temp_new(); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_qemu_ld64(QREG_FP0L, tmp, index); + tcg_temp_free(tmp); + break; + case OS_PACKED: + tcg_gen_qemu_ld32u(QREG_FP0H, addr, index); + tmp = tcg_temp_new(); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_qemu_ld64(QREG_FP0L, tmp, index); + tcg_temp_free(tmp); + gen_helper_extp96_FP0(cpu_env); + break; + default: + g_assert_not_reached(); + } + gen_throws_exception = gen_last_qop; +} + +static inline void gen_store_FP0(DisasContext *s, int opsize, TCGv addr) +{ + TCGv tmp; + int index = IS_USER(s); + s->is_mem = 1; + switch (opsize) { + case OS_BYTE: + gen_helper_reds32_FP0(cpu_env); + tcg_gen_qemu_st8(QREG_FP0H, addr, index); + break; + case OS_WORD: + gen_helper_reds32_FP0(cpu_env); + tcg_gen_qemu_st16(QREG_FP0H, addr, index); + break; + case OS_LONG: + gen_helper_reds32_FP0(cpu_env); + tcg_gen_qemu_st32(QREG_FP0H, addr, index); + break; + case OS_SINGLE: + gen_helper_redf32_FP0(cpu_env); + tcg_gen_qemu_st32(QREG_FP0H, addr, index); + break; + case OS_DOUBLE: + gen_helper_redf64_FP0(cpu_env); + tcg_gen_qemu_st64(QREG_FP0L, addr, index); + break; + case OS_EXTENDED: + tcg_gen_shli_i32(QREG_FP0H, QREG_FP0H, 16); + tcg_gen_qemu_st32(QREG_FP0H, addr, index); + tmp = tcg_temp_new(); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_qemu_st64(QREG_FP0L, tmp, index); + tcg_temp_free(tmp); + break; + case OS_PACKED: + gen_helper_redp96_FP0(cpu_env); + tcg_gen_qemu_st32(QREG_FP0H, addr, index); + tmp = tcg_temp_new(); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_qemu_st64(QREG_FP0L, tmp, index); + tcg_temp_free(tmp); + break; + default: + g_assert_not_reached(); + } + gen_throws_exception = gen_last_qop; +} + +static void gen_op_load_ea_FP0(CPUM68KState *env, DisasContext *s, + uint16_t insn, int opsize) +{ + TCGv reg; + TCGv addr; + uint64_t val; + + switch ((insn >> 3) & 7) { + case 0: /* Data register direct. */ + tcg_gen_mov_i32(QREG_FP0H, DREG(insn, 0)); + gen_extend_FP0(opsize); + break; + case 1: /* Address register direct. */ + gen_addr_fault(s); + break; + case 2: /* Indirect register */ + gen_load_FP0(s, opsize, AREG(insn, 0)); + break; + case 3: /* Indirect postincrement. */ + reg = AREG(insn, 0); + gen_load_FP0(s, opsize, reg); + tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize)); + break; + case 4: /* Indirect predecrememnt. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_load_FP0(s, opsize, addr); + tcg_gen_mov_i32(AREG(insn, 0), addr); + break; + case 5: /* Indirect displacement. */ + case 6: /* Indirect index + displacement. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_load_FP0(s, opsize, addr); + break; + case 7: /* Other */ + switch (insn & 7) { + case 0: /* Absolute short. */ + case 1: /* Absolute long. */ + case 2: /* pc displacement */ + case 3: /* pc index+displacement. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_load_FP0(s, opsize, addr); + break; + case 4: /* Immediate. */ + switch (opsize) { + case OS_BYTE: + val = read_im8(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_WORD: + val = read_im16(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_LONG: + val = read_im32(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_SINGLE: + val = read_im32(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_DOUBLE: + val = read_im64(env, s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + case OS_EXTENDED: + val = read_im32(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + val = read_im64(env, s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + case OS_PACKED: + val = read_im32(env, s); + tcg_gen_movi_i32(QREG_FP0H, val); + val = read_im64(env, s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + default: + g_assert_not_reached(); + } + gen_extend_FP0(opsize); + break; + default: + g_assert_not_reached(); + } + } +} + +static void gen_op_store_ea_FP0(CPUM68KState *env, DisasContext *s, + uint16_t insn, int opsize) +{ + TCGv reg; + TCGv addr; + + switch ((insn >> 3) & 7) { + case 0: /* Data register direct. */ + gen_reduce_FP0(opsize); + tcg_gen_mov_i32(DREG(insn, 0), QREG_FP0H); + break; + case 1: /* Address register direct. */ + gen_addr_fault(s); + break; + case 2: /* Indirect register */ + reg = AREG(insn, 0); + gen_store_FP0(s, opsize, reg); + break; + case 3: /* Indirect postincrement. */ + reg = AREG(insn, 0); + gen_store_FP0(s, opsize, reg); + tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize)); + break; + case 4: /* Indirect predecrememnt. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_store_FP0(s, opsize, addr); + tcg_gen_mov_i32(AREG(insn, 0), addr); + break; + case 5: /* Indirect displacement. */ + case 6: /* Indirect index + displacement. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_store_FP0(s, opsize, addr); + break; + case 7: /* Other */ + switch (insn & 7) { + case 0: /* Absolute short. */ + case 1: /* Absolute long. */ + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + gen_store_FP0(s, opsize, addr); + break; + case 2: /* pc displacement */ + case 3: /* pc index+displacement. */ + case 4: /* Immediate. */ + gen_addr_fault(s); + break; + default: + g_assert_not_reached(); + } + } +} + /* This generates a conditional branch, clobbering all temporaries. */ static void gen_jmpcc(DisasContext *s, int cond, TCGLabel *l1) { @@ -3386,7 +3758,7 @@ DISAS_INSN(trap) /* ??? FP exceptions are not implemented. Most exceptions are deferred until immediately before the next FP instruction is executed. */ -DISAS_INSN(fpu) +DISAS_INSN(cf_fpu) { uint16_t ext; int32_t offset; @@ -3672,7 +4044,7 @@ undef: disas_undef_fpu(env, s, insn); } -DISAS_INSN(fbcc) +DISAS_INSN(cf_fbcc) { uint32_t offset; uint32_t addr; @@ -3748,7 +4120,494 @@ DISAS_INSN(fbcc) gen_jmp_tb(s, 1, addr + offset); } -DISAS_INSN(frestore) +static void gen_op_fmovem(CPUM68KState *env, DisasContext *s, + uint32_t insn, uint32_t ext) +{ + int opsize; + uint16_t mask; + int i; + uint32_t mode; + int32_t incr; + TCGv addr, tmp; + int is_load; + + if (m68k_feature(s->env, M68K_FEATURE_FPU)) { + opsize = OS_EXTENDED; + } else { + opsize = OS_DOUBLE; /* FIXME */ + } + + mode = (ext >> 11) & 0x3; + if ((mode & 0x1) == 1) { + gen_helper_fmovem(cpu_env, tcg_const_i32(opsize), + tcg_const_i32(mode), DREG(ext, 0)); + return; + } + + tmp = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(tmp)) { + gen_addr_fault(s); + return; + } + + addr = tcg_temp_new(); + tcg_gen_mov_i32(addr, tmp); + is_load = ((ext & 0x2000) == 0); + incr = opsize_bytes(opsize); + mask = ext & 0x00FF; + + if (!is_load && (mode & 2) == 0) { + for (i = 7; i >= 0; i--, mask <<= 1) { + if (mask & 0x80) { + gen_op_load_fpr_FP0(i); + gen_store_FP0(s, opsize, addr); + if ((mask & 0xff) != 0x80) { + tcg_gen_subi_i32(addr, addr, incr); + } + } + } + tcg_gen_mov_i32(AREG(insn, 0), addr); + } else { + for (i = 0; i < 8; i++, mask <<= 1) { + if (mask & 0x80) { + if (is_load) { + gen_load_FP0(s, opsize, addr); + gen_op_store_fpr_FP0(i); + } else { + gen_op_load_fpr_FP0(i); + gen_store_FP0(s, opsize, addr); + } + tcg_gen_addi_i32(addr, addr, incr); + } + } + if ((insn & 070) == 030) { + tcg_gen_mov_i32(AREG(insn, 0), addr); + } + } + tcg_temp_free_i32(addr); +} + +DISAS_INSN(m68k_fpu) +{ + int ctrl; + uint16_t ext; + uint8_t rom_offset; + int opmode; + int round; + int set_dest; + int opsize; + TCGv val; + + ext = read_im16(env, s); + opmode = ext & 0x7f; + switch ((ext >> 13) & 7) { + case 0: + break; + case 1: + goto undef; + case 2: + if (insn == 0xf200 && (ext & 0xfc00) == 0x5c00) { + /* fmovecr */ + rom_offset = ext & 0x7f; + gen_helper_const_FP0(cpu_env, tcg_const_i32(rom_offset)); + gen_op_store_fpr_FP0(REG(ext, 7)); + return; + } + break; + case 3: /* fmove out */ + opsize = ext_opsize(ext, 10); + gen_op_load_fpr_FP0(REG(ext, 7)); + gen_helper_compare_FP0(cpu_env); + gen_op_store_ea_FP0(env, s, insn, opsize); + return; + case 4: /* fmove to control register. */ + ctrl = (ext >> 10) & 7; + if (ctrl & 4) { /* FPCR */ + SRC_EA(env, val, OS_LONG, 0, NULL); + gen_helper_set_fpcr(cpu_env, val); + } + if (ctrl & 2) { /* FPSR */ + SRC_EA(env, QEMU_FPSR, OS_LONG, 0, NULL); + } + if (ctrl & 1) { /* FPIAR */ + SRC_EA(env, val, OS_LONG, 0, NULL); + } + return; + case 5: /* fmove from control register. */ + ctrl = (ext >> 10) & 7; + if (ctrl & 4) { /* FPCR */ + DEST_EA(env, insn, OS_LONG, QEMU_FPCR, NULL); + } + if (ctrl & 2) { /* FPSR */ + gen_helper_update_fpsr(cpu_env); + DEST_EA(env, insn, OS_LONG, QEMU_FPSR, NULL); + } + if (ctrl & 1) { /* FPIAR */ + TCGv tmp = tcg_temp_new_i32(); + DEST_EA(env, insn, OS_LONG, tmp, NULL); + tcg_temp_free_i32(tmp); + } + return; + case 6: /* fmovem */ + case 7: + if ((ext & 0xf00) != 0 || (ext & 0xff) == 0) { + goto undef; + } + if ((ext & 0x1000) == 0 && !m68k_feature(s->env, M68K_FEATURE_FPU)) { + goto undef; + } + gen_op_fmovem(env, s, insn, ext); + return; + } + if (ext & (1 << 14)) { + opsize = ext_opsize(ext, 10); + gen_op_load_ea_FP0(env, s, insn, opsize); + } else { + /* Source register. */ + opsize = OS_EXTENDED; + gen_op_load_fpr_FP0(REG(ext, 10)); + } + round = 1; + set_dest = 1; + switch (opmode) { + case 0: case 0x40: case 0x44: /* fmove */ + break; + case 1: /* fint */ + gen_helper_iround_FP0(cpu_env); + round = 0; + break; + case 2: /* fsinh */ + gen_helper_sinh_FP0(cpu_env); + break; + case 3: /* fintrz */ + gen_helper_itrunc_FP0(cpu_env); + round = 0; + break; + case 4: case 0x41: case 0x45: /* fsqrt */ + gen_helper_sqrt_FP0(cpu_env); + break; + case 6: /* flognp1 */ + gen_helper_lognp1_FP0(cpu_env); + break; + case 0x09: /* ftanh */ + gen_helper_tanh_FP0(cpu_env); + break; + case 0x0a: /* fatan */ + gen_helper_atan_FP0(cpu_env); + break; + case 0x0c: /* fasin */ + gen_helper_asin_FP0(cpu_env); + break; + case 0x0d: /* fatanh */ + gen_helper_atanh_FP0(cpu_env); + break; + case 0x0e: /* fsin */ + gen_helper_sin_FP0(cpu_env); + break; + case 0x0f: /* ftan */ + gen_helper_tan_FP0(cpu_env); + break; + case 0x10: /* fetox */ + gen_helper_exp_FP0(cpu_env); + break; + case 0x11: /* ftwotox */ + gen_helper_exp2_FP0(cpu_env); + break; + case 0x12: /* ftentox */ + gen_helper_exp10_FP0(cpu_env); + break; + case 0x14: /* flogn */ + gen_helper_ln_FP0(cpu_env); + break; + case 0x15: /* flog10 */ + gen_helper_log10_FP0(cpu_env); + break; + case 0x18: case 0x58: case 0x5c: /* fabs */ + gen_helper_abs_FP0(cpu_env); + break; + case 0x19: + gen_helper_cosh_FP0(cpu_env); + break; + case 0x1a: case 0x5a: case 0x5e: /* fneg */ + gen_helper_chs_FP0(cpu_env); + break; + case 0x1c: /* facos */ + gen_helper_acos_FP0(cpu_env); + break; + case 0x1d: /* fcos */ + gen_helper_cos_FP0(cpu_env); + break; + case 0x1e: /* fgetexp */ + gen_helper_getexp_FP0(cpu_env); + break; + case 0x20: case 0x60: case 0x64: /* fdiv */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_div_FP0_FP1(cpu_env); + break; + case 0x21: /* fmod */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_mod_FP0_FP1(cpu_env); + break; + case 0x22: case 0x62: case 0x66: /* fadd */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_add_FP0_FP1(cpu_env); + break; + case 0x23: case 0x63: case 0x67: /* fmul */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_mul_FP0_FP1(cpu_env); + break; + case 0x24: /* fsgldiv */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_div_FP0_FP1(cpu_env); + break; + case 0x26: /* fscale */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_scale_FP0_FP1(cpu_env); + break; + case 0x27: /* fsglmul */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_mul_FP0_FP1(cpu_env); + break; + case 0x28: case 0x68: case 0x6c: /* fsub */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_sub_FP0_FP1(cpu_env); + break; + case 0x30: case 0x31: case 0x32: + case 0x33: case 0x34: case 0x35: + case 0x36: case 0x37: + gen_helper_sincos_FP0_FP1(cpu_env); + gen_op_store_fpr_FP0(REG(ext, 7)); /* sin */ + gen_op_store_fpr_FP1(REG(ext, 0)); /* cos */ + break; + case 0x38: /* fcmp */ + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_fcmp_FP0_FP1(cpu_env); + return; + case 0x3a: /* ftst */ + set_dest = 0; + round = 0; + break; + default: + goto undef; + } + gen_helper_compare_FP0(cpu_env); + if (round) { + if (opmode & 0x40) { + if ((opmode & 0x4) != 0) { + round = 0; + } + } else if ((s->fpcr & M68K_FPCR_PREC) == 0) { + round = 0; + } + } + if (round) { +#if 0 + TCGv tmp = tcg_temp_new_i32(); + gen_helper_f64_to_f32(tmp, cpu_env, res); + gen_helper_f32_to_f64(res, cpu_env, tmp); + tcg_temp_free_i32(tmp); +#endif + } + if (set_dest) { + gen_op_store_fpr_FP0(REG(ext, 7)); + } + return; +undef: + /* FIXME: Is this right for offset addressing modes? */ + s->pc -= 2; + disas_undef_fpu(env, s, insn); +} + +static void gen_fjmpcc(DisasContext *s, int cond, TCGLabel *l1) +{ + TCGv tmp; + + /* TODO: Raise BSUN exception. */ + + /* Jump to l1 if condition is true. */ + switch (cond) { + case 0: /* False */ + case 16: /* Signaling false */ + break; + case 1: /* Equal Z */ + case 17: /* Signaling Equal Z */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 2: /* Ordered Greater Than !(A || Z || N) */ + case 18: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, + FCCF_A | FCCF_Z | FCCF_N); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + break; + case 3: /* Ordered Greater Than or Equal Z || !(A || N) */ + case 19: + assert(FCCF_A == (FCCF_N >> 3)); + tmp = tcg_temp_new(); + tcg_gen_shli_i32(tmp, QREG_FPSR, 3); + tcg_gen_or_i32(tmp, tmp, QREG_FPSR); + tcg_gen_xori_i32(tmp, tmp, FCCF_N); + tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 4: /* Ordered Less Than !(!N || A || Z); */ + case 20: + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_N); + tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_A | FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + break; + case 5: /* Ordered Less Than or Equal Z || (N && !A) */ + case 21: + assert(FCCF_A == (FCCF_N >> 3)); + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_A); + tcg_gen_shli_i32(tmp, tmp, 3); + tcg_gen_ori_i32(tmp, tmp, FCCF_Z); + tcg_gen_and_i32(tmp, tmp, QREG_FPSR); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 6: /* Ordered Greater or Less Than !(A || Z) */ + case 22: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + break; + case 7: /* Ordered !A */ + case 23: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + break; + case 8: /* Unordered A */ + case 24: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 9: /* Unordered or Equal A || Z */ + case 25: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 10: /* Unordered or Greater Than A || !(N || Z)) */ + case 26: + assert(FCCF_Z == (FCCF_N >> 1)); + tmp = tcg_temp_new(); + tcg_gen_shli_i32(tmp, QREG_FPSR, 1); + tcg_gen_or_i32(tmp, tmp, QREG_FPSR); + tcg_gen_xori_i32(tmp, tmp, FCCF_N); + tcg_gen_andi_i32(tmp, tmp, FCCF_N | FCCF_A); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 11: /* Unordered or Greater or Equal A || Z || N */ + case 13: /* Unordered or Less or Equal A || Z || N */ + case 29: + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z | FCCF_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 27: /* Not Less Than A || Z || !N */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_A | FCCF_Z | FCCF_N); + tcg_gen_xori_i32(tmp, tmp, FCCF_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 12: /* Unordered or Less Than A || (N && !Z) */ + case 28: + assert(FCCF_Z == (FCCF_N >> 1)); + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, QREG_FPSR, FCCF_Z); + tcg_gen_shli_i32(tmp, tmp, 1); + tcg_gen_ori_i32(tmp, tmp, FCCF_A); + tcg_gen_and_i32(tmp, tmp, QREG_FPSR); + tcg_gen_andi_i32(tmp, tmp, FCCF_A | FCCF_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); + break; + case 14: /* Not Equal !Z */ + case 30: /* Signaling Not Equal !Z */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, QREG_FPSR, FCCF_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); + break; + case 15: /* True */ + case 31: /* Signaling True */ + tcg_gen_br(l1); + break; + } +} + +DISAS_INSN(m68k_fbcc) +{ + uint32_t offset; + uint32_t addr; + TCGLabel *l1; + + addr = s->pc; + offset = cpu_ldsw_code(env, s->pc); + s->pc += 2; + if (insn & (1 << 6)) { + offset = (offset << 16) | cpu_lduw_code(env, s->pc); + s->pc += 2; + } + + l1 = gen_new_label(); + gen_fjmpcc(s, insn & 0x3f, l1); + update_cc_op(s); + gen_jmp_tb(s, 0, s->pc); + gen_set_label(l1); + update_cc_op(s); + gen_jmp_tb(s, 1, addr + offset); +} + +DISAS_INSN(fscc_mem) +{ + TCGLabel *l1, *l2; + TCGv taddr; + TCGv addr; + uint16_t ext; + + ext = read_im16(env, s); + + taddr = gen_lea(env, s, insn, OS_BYTE); + if (IS_NULL_QREG(taddr)) { + gen_addr_fault(s); + return; + } + addr = tcg_temp_local_new(); + tcg_gen_mov_i32(addr, taddr); + l1 = gen_new_label(); + l2 = gen_new_label(); + gen_fjmpcc(s, ext & 0x3f, l1); + gen_store(s, OS_BYTE, addr, tcg_const_i32(0x00)); + tcg_gen_br(l2); + gen_set_label(l1); + gen_store(s, OS_BYTE, addr, tcg_const_i32(0xff)); + gen_set_label(l2); + tcg_temp_free(addr); +} + +DISAS_INSN(fscc_reg) +{ + TCGLabel *l1; + TCGv reg; + uint16_t ext; + + ext = read_im16(env, s); + + reg = DREG(insn, 0); + + l1 = gen_new_label(); + tcg_gen_ori_i32(reg, reg, 0x000000ff); + gen_fjmpcc(s, ext & 0x3f, l1); + tcg_gen_andi_i32(reg, reg, 0xffffff00); + gen_set_label(l1); +} + +DISAS_INSN(cf_frestore) { M68kCPU *cpu = m68k_env_get_cpu(env); @@ -3756,7 +4615,7 @@ DISAS_INSN(frestore) cpu_abort(CPU(cpu), "FRESTORE not implemented"); } -DISAS_INSN(fsave) +DISAS_INSN(cf_fsave) { M68kCPU *cpu = m68k_env_get_cpu(env); @@ -3764,6 +4623,21 @@ DISAS_INSN(fsave) cpu_abort(CPU(cpu), "FSAVE not implemented"); } +DISAS_INSN(m68k_frestore) +{ + TCGv addr; + + SRC_EA(env, addr, OS_LONG, 0, NULL); + /* FIXME: check the state frame */ +} + +DISAS_INSN(m68k_fsave) +{ + /* always write IDLE */ + /* FIXME: 68040 only */ + DEST_EA(env, insn, OS_LONG, tcg_const_i32(0x41000000), NULL); +} + static inline TCGv gen_mac_extract_word(DisasContext *s, TCGv val, int upper) { TCGv tmp = tcg_temp_new(); @@ -4331,10 +5205,16 @@ void register_m68k_insns (CPUM68KState *env) INSN(bitfield_reg, e8c0, f8f8, BITFIELD); INSN(undef_fpu, f000, f000, CF_ISA_A); INSN(undef_fpu, f000, f000, M68000); - INSN(fpu, f200, ffc0, CF_FPU); - INSN(fbcc, f280, ffc0, CF_FPU); - INSN(frestore, f340, ffc0, CF_FPU); - INSN(fsave, f340, ffc0, CF_FPU); + INSN(cf_fpu, f200, ffc0, CF_FPU); + INSN(cf_fbcc, f280, ffc0, CF_FPU); + INSN(cf_frestore, f340, ffc0, CF_FPU); + INSN(cf_fsave, f340, ffc0, CF_FPU); + INSN(m68k_fpu, f200, ffc0, FPU); + INSN(fscc_mem, f240, ffc0, FPU); + INSN(fscc_reg, f240, fff8, FPU); + INSN(m68k_fbcc, f280, ffc0, FPU); + INSN(m68k_frestore, f140, f1c0, FPU); + INSN(m68k_fsave, f100, f1c0, FPU); INSN(intouch, f340, ffc0, CF_ISA_A); INSN(cpushl, f428, ff38, CF_ISA_A); INSN(wddata, fb00, ff00, CF_ISA_A); @@ -4496,20 +5376,30 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, CPUM68KState *env = &cpu->env; int i; uint16_t sr; - CPU_DoubleU u; for (i = 0; i < 8; i++) { - u.d = env->fregs[i]; - cpu_fprintf (f, "D%d = %08x A%d = %08x F%d = %08x%08x (%12g)\n", - i, env->dregs[i], i, env->aregs[i], - i, u.l.upper, u.l.lower, *(double *)&u.d); + cpu_fprintf(f, "D%d = %08x A%d = %08x ", + i, env->dregs[i], i, env->aregs[i]); + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { + CPU_DoubleU u; + u.d = env->cf_fregs[i]; + cpu_fprintf(f, "F%d = %08x%08x (%12g)\n", + i, u.l.upper, u.l.lower, *(double *)&u.d); + } + if (m68k_feature(env, M68K_FEATURE_FPU)) { + cpu_fprintf(f, "F%d = %" PRIxFPH " %" PRIxFPL "\n", + i, env->m68k_fregs[i].d.high, + env->m68k_fregs[i].d.low); + } } - cpu_fprintf (f, "PC = %08x ", env->pc); + cpu_fprintf(f, "PC = %08x ", env->pc); sr = env->sr; - cpu_fprintf (f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-', - (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', - (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); - cpu_fprintf (f, "FPRESULT = %12g\n", *(double *)&env->fp_result); + cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & 0x10) ? 'X' : '-', + (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', + (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { + cpu_fprintf(f, "FPRESULT = %12g\n", *(double *)&env->cf_fp_result); + } } void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, int pc_pos)