From: Laurent Vivier <laur...@vivier.eu> Signed-off-by: Laurent Vivier <laur...@vivier.eu> --- target-m68k/cpu.h | 53 +++- target-m68k/helper.c | 345 +++++++++++++++++---- target-m68k/helpers.h | 39 ++- target-m68k/qregs.def | 3 +- target-m68k/translate.c | 776 +++++++++++++++++++++++++++++++---------------- tests/m68k/Makefile | 15 + tests/m68k/fmove.S | 55 ++++ tests/m68k/fmovecr.S | 29 ++ tests/m68k/fmovem.S | 17 + tests/m68k/trap.i | 5 + 10 files changed, 978 insertions(+), 359 deletions(-) create mode 100644 tests/m68k/Makefile create mode 100644 tests/m68k/fmove.S create mode 100644 tests/m68k/fmovecr.S create mode 100644 tests/m68k/fmovem.S create mode 100644 tests/m68k/trap.i
diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index e316b1e..2587512 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -60,6 +60,36 @@ #define NB_MMU_MODES 2 +#define TARGET_PHYS_ADDR_SPACE_BITS 32 +#define TARGET_VIRT_ADDR_SPACE_BITS 32 + +#ifdef CONFIG_USER_ONLY +/* Linux uses 4k pages. */ +#define TARGET_PAGE_BITS 12 +#else +/* Smallest TLB entry size is 1k. */ +#define TARGET_PAGE_BITS 10 +#endif + +#include "cpu-all.h" + +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]; @@ -76,8 +106,7 @@ typedef struct CPUM68KState { uint32_t cc_src; uint32_t cc_x; - float64 fregs[8]; - float64 fp_result; + FPReg fregs[8]; uint32_t fpcr; uint32_t fpsr; float_status fp_status; @@ -90,6 +119,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; @@ -228,17 +264,6 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf); void register_m68k_insns (CPUM68KState *env); -#ifdef CONFIG_USER_ONLY -/* Linux uses 4k pages. */ -#define TARGET_PAGE_BITS 12 -#else -/* Smallest TLB entry size is 1k. */ -#define TARGET_PAGE_BITS 10 -#endif - -#define TARGET_PHYS_ADDR_SPACE_BITS 32 -#define TARGET_VIRT_ADDR_SPACE_BITS 32 - #define cpu_init cpu_m68k_init #define cpu_exec cpu_m68k_exec #define cpu_gen_code cpu_m68k_gen_code @@ -267,8 +292,6 @@ static inline void cpu_clone_regs(CPUState *env, target_ulong newsp) } #endif -#include "cpu-all.h" - static inline void cpu_get_tb_cpu_state(CPUState *env, target_ulong *pc, target_ulong *cs_base, int *flags) { diff --git a/target-m68k/helper.c b/target-m68k/helper.c index 1bb0cef..081e1d9 100644 --- a/target-m68k/helper.c +++ b/target-m68k/helper.c @@ -23,6 +23,7 @@ #include "config.h" #include "cpu.h" +#include "exec.h" #include "qemu-common.h" #include "gdbstub.h" @@ -115,7 +116,8 @@ void m68k_cpu_list(FILE *f, fprintf_function cpu_fprintf) static int fpu_gdb_get_reg(CPUState *env, uint8_t *mem_buf, int n) { if (n < 8) { - stfq_p(mem_buf, env->fregs[n]); + float_status s; + stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); return 8; } if (n < 11) { @@ -129,7 +131,8 @@ static int fpu_gdb_get_reg(CPUState *env, uint8_t *mem_buf, int n) static int fpu_gdb_set_reg(CPUState *env, uint8_t *mem_buf, int n) { if (n < 8) { - env->fregs[n] = ldfq_p(mem_buf); + float_status s; + env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s); return 8; } if (n < 11) { @@ -980,125 +983,335 @@ HELPER_ROXL(uint32_t, 32) /* FPU helpers. */ static const floatx80 fpu_rom[128] = { - [0x00] = { .high = 0x4000, .low = 0xc90fdaa22168c235ULL }, /* Pi */ - - [0x0b] = { .high = 0x3ffd, .low = 0x9a209a84fbcff798ULL }, /* Log10(2) */ - [0x0c] = { .high = 0x4000, .low = 0xadf85458a2bb4a9aULL }, /* e */ - [0x0d] = { .high = 0x3fff, .low = 0xb8aa3b295c17f0bcULL }, /* Log2(e) */ - [0x0e] = { .high = 0x3ffd, .low = 0xde5bd8a937287195ULL }, /* Log10(e) */ - [0x0f] = { .high = 0x0000, .low = 0x0000000000000000ULL }, /* Zero */ - - [0x30] = { .high = 0x3ffe, .low = 0xb17217f7d1cf79acULL }, /* ln(2) */ - [0x31] = { .high = 0x4000, .low = 0x935d8dddaaa8ac17ULL }, /* ln(10) */ - [0x32] = { .high = 0x3fff, .low = 0x8000000000000000ULL }, /* 10^0 */ - [0x33] = { .high = 0x4002, .low = 0xa000000000000000ULL }, /* 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 */ + [0x00] = floatx80_pi, /* Pi */ + + [0x0b] = { .high = 0x3ffd, .low = 0x9a209a84fbcff798ULL }, /* Log10(2) */ + [0x0c] = { .high = 0x4000, .low = 0xadf85458a2bb4a9aULL }, /* e */ + [0x0d] = { .high = 0x3fff, .low = 0xb8aa3b295c17f0bcULL }, /* 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] = { .high = 0x4002, .low = 0xa000000000000000ULL }, /* 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 */ }; -float64 HELPER(const_f64)(CPUState *env, uint32_t offset) +void HELPER(const_FP0)(CPUState *env, uint32_t offset) { - return floatx80_to_float64(fpu_rom[offset], &env->fp_status); + env->fp0h = fpu_rom[offset].high; + env->fp0l = fpu_rom[offset].low; } -uint32_t HELPER(f64_to_i32)(CPUState *env, float64 val) +static inline floatx80 FP0_to_floatx80(CPUState *env) { - return float64_to_int32(val, &env->fp_status); + floatx80 res; + + res.high = env->fp0h; + res.low = env->fp0l; + + return res; } -float32 HELPER(f64_to_f32)(CPUState *env, float64 val) +static inline void floatx80_to_FP0(CPUState *env, floatx80 res) { - return float64_to_float32(val, &env->fp_status); + env->fp0h = res.high; + env->fp0l = res.low; } -float64 HELPER(i32_to_f64)(CPUState *env, uint32_t val) +static inline int32_t FP0_to_int32(CPUState *env) { - return int32_to_float64(val, &env->fp_status); + return env->fp0h; } -float64 HELPER(f32_to_f64)(CPUState *env, float32 val) +static inline void int32_to_FP0(CPUState *env, int32_t val) { - return float32_to_float64(val, &env->fp_status); + env->fp0h = val; } -float64 HELPER(iround_f64)(CPUState *env, float64 val) +static inline float32 FP0_to_float32(CPUState *env) { - return float64_round_to_int(val, &env->fp_status); + return *(float32*)&env->fp0h; } -float64 HELPER(itrunc_f64)(CPUState *env, float64 val) +static inline void float32_to_FP0(CPUState *env, float32 val) { - return float64_trunc_to_int(val, &env->fp_status); + + env->fp0h = *(uint32_t*)&val; } -float64 HELPER(sqrt_f64)(CPUState *env, float64 val) +static inline float64 FP0_to_float64(CPUState *env) { - return float64_sqrt(val, &env->fp_status); + return *(float64*)&env->fp0l; } -float64 HELPER(abs_f64)(float64 val) +static inline void float64_to_FP0(CPUState *env, float64 val) { - return float64_abs(val); + env->fp0l = *(uint64_t*)&val; } -float64 HELPER(chs_f64)(float64 val) +static inline floatx80 FP1_to_floatx80(CPUState *env) { - return float64_chs(val); + floatx80 res; + + res.high = env->fp1h; + res.low = env->fp1l; + + return res; } -float64 HELPER(add_f64)(CPUState *env, float64 a, float64 b) +static inline void restore_precision_mode(CPUState *env) { - return float64_add(a, b, &env->fp_status); + 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; + } } -float64 HELPER(sub_f64)(CPUState *env, float64 a, float64 b) +static inline void restore_rounding_mode(CPUState *env) { - return float64_sub(a, b, &env->fp_status); + 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; + } } -float64 HELPER(mul_f64)(CPUState *env, float64 a, float64 b) +void HELPER(set_fpcr)(CPUState *env, uint32_t val) { - return float64_mul(a, b, &env->fp_status); + env->fpcr = val & 0xffff; + + restore_precision_mode(env); + restore_rounding_mode(env); } -float64 HELPER(div_f64)(CPUState *env, float64 a, float64 b) +void HELPER(exts32_FP0)(CPUState *env) { - return float64_div(a, b, &env->fp_status); + floatx80 res; + + res = int32_to_floatx80(FP0_to_int32(env), &env->fp_status); + + floatx80_to_FP0(env, res); } -float64 HELPER(sub_cmp_f64)(CPUState *env, float64 a, float64 b) +void HELPER(extf32_FP0)(CPUState *env) +{ + floatx80 res; + + res = float32_to_floatx80(FP0_to_float32(env), &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(extf64_FP0)(CPUState *env) +{ + floatx80 res; + + res = float64_to_floatx80(FP0_to_float64(env), &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(extp96_FP0)(CPUState *env) +{ +} + +void HELPER(reds32_FP0)(CPUState *env) +{ + floatx80 val; + int32_t res; + + val = FP0_to_floatx80(env); + res = floatx80_to_int32(val, &env->fp_status); + + int32_to_FP0(env, res); +} + +void HELPER(redf32_FP0)(CPUState *env) +{ + floatx80 val; + float32 res; + + val = FP0_to_floatx80(env); + res = floatx80_to_float32(val, &env->fp_status); + + float32_to_FP0(env, res); +} + +void HELPER(redf64_FP0)(CPUState *env) +{ + floatx80 val; + float64 res; + + val = FP0_to_floatx80(env); + res = floatx80_to_float64(val, &env->fp_status); + + float64_to_FP0(env, res); +} + +void HELPER(redp96_FP0)(CPUState *env) +{ +} + +void HELPER(iround_FP0)(CPUState *env) +{ + floatx80 res; + + res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(itrunc_FP0)(CPUState *env) +{ + floatx80 res; + + set_float_rounding_mode(float_round_to_zero, &env->fp_status); + res = floatx80_round_to_int(FP0_to_floatx80(env), &env->fp_status); + restore_rounding_mode(env); + + floatx80_to_FP0(env, res); +} + +void HELPER(sqrt_FP0)(CPUState *env) +{ + floatx80 res; + + res = floatx80_sqrt(FP0_to_floatx80(env), &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(abs_FP0)(CPUState *env) +{ + floatx80 res; + + res = floatx80_abs(FP0_to_floatx80(env)); + + floatx80_to_FP0(env, res); +} + +void HELPER(chs_FP0)(CPUState *env) +{ + floatx80 res; + + res = floatx80_chs(FP0_to_floatx80(env)); + + floatx80_to_FP0(env, res); +} + +void HELPER(add_FP0_FP1)(CPUState *env) +{ + floatx80 res; + + res = floatx80_add(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(sub_FP0_FP1)(CPUState *env) +{ + floatx80 res; + + res = floatx80_sub(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(mul_FP0_FP1)(CPUState *env) +{ + floatx80 res; + + res = floatx80_mul(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(div_FP0_FP1)(CPUState *env) +{ + floatx80 res; + + res = floatx80_div(FP0_to_floatx80(env), FP1_to_floatx80(env), + &env->fp_status); + + floatx80_to_FP0(env, res); +} + +void HELPER(fcmp_FP0_FP1)(CPUState *env) { /* ??? This may incorrectly raise exceptions. */ /* ??? Should flush denormals to zero. */ - float64 res; - res = float64_sub(a, b, &env->fp_status); - if (float64_is_quiet_nan(res)) { + floatx80 res; + 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 (!float64_is_quiet_nan(a) - && !float64_is_quiet_nan(b)) { - res = float64_zero; - if (float64_lt_quiet(a, res, &env->fp_status)) - res = float64_chs(res); + if (!floatx80_is_any_nan(FP0_to_floatx80(env)) + && !floatx80_is_any_nan(FP1_to_floatx80(env))) { + if (floatx80_lt_quiet(FP1_to_floatx80(env), floatx80_zero, + &env->fp_status)) + res = floatx80_chs(res); } } - return res; + floatx80_to_FP0(env, res); } -uint32_t HELPER(compare_f64)(CPUState *env, float64 val) +uint32_t HELPER(compare_FP0)(CPUState *env) { - return float64_compare_quiet(val, float64_zero, &env->fp_status); + uint32_t res; + + res = float64_compare_quiet(floatx80_to_float64(FP0_to_floatx80(env), + &env->fp_status), + float64_zero, &env->fp_status); + return res; } +void HELPER(fmovem)(CPUState *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/helpers.h b/target-m68k/helpers.h index aa835eb..6ddd659 100644 --- a/target-m68k/helpers.h +++ b/target-m68k/helpers.h @@ -50,22 +50,29 @@ DEF_HELPER_2(xflag_lt_i32, i32, i32, i32) DEF_HELPER_2(set_sr, void, env, i32) DEF_HELPER_3(movec, void, env, i32, i32) -DEF_HELPER_2(const_f64, f64, env, i32); -DEF_HELPER_2(f64_to_i32, i32, env, f64) -DEF_HELPER_2(f64_to_f32, f32, env, f64) -DEF_HELPER_2(i32_to_f64, f64, env, i32) -DEF_HELPER_2(f32_to_f64, f64, env, f32) -DEF_HELPER_2(iround_f64, f64, env, f64) -DEF_HELPER_2(itrunc_f64, f64, env, f64) -DEF_HELPER_2(sqrt_f64, f64, env, f64) -DEF_HELPER_1(abs_f64, f64, f64) -DEF_HELPER_1(chs_f64, f64, f64) -DEF_HELPER_3(add_f64, f64, env, f64, f64) -DEF_HELPER_3(sub_f64, f64, env, f64, f64) -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(itrunc_FP0, void, env) +DEF_HELPER_1(sqrt_FP0, void, env) +DEF_HELPER_1(abs_FP0, void, env) +DEF_HELPER_1(chs_FP0, 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(fcmp_FP0_FP1, void, env) +DEF_HELPER_1(compare_FP0, i32, 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 76e0236..13a8e5c 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -1,4 +1,5 @@ -DEFF64(FP_RESULT, fp_result) +DEFF96(FP0, fp0) +DEFF96(FP1, fp1) DEFO32(PC, pc) DEFO32(SR, sr) DEFO32(CC_OP, cc_op) diff --git a/target-m68k/translate.c b/target-m68k/translate.c index e394c2d..d4a7074 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -25,6 +25,7 @@ #include "config.h" #include "cpu.h" +#include "exec.h" #include "disas.h" #include "tcg-op.h" #include "qemu-log.h" @@ -35,30 +36,26 @@ //#define DEBUG_DISPATCH 1 -/* 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 - #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_ptr cpu_env; -static char cpu_reg_names[3*8*3 + 5*4]; +static char cpu_reg_names[2*8*3 + 5*4]; 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 DREG(insn, pos) cpu_dregs[((insn) >> (pos)) & 7] -#define AREG(insn, pos) cpu_aregs[((insn) >> (pos)) & 7] -#define FREG(insn, pos) cpu_fregs[((insn) >> (pos)) & 7] +#define REG(insn, pos) (((insn) >> (pos)) & 7) +#define DREG(insn, pos) cpu_dregs[REG(insn, pos)] +#define AREG(insn, pos) cpu_aregs[REG(insn, pos)] #define MACREG(acc) cpu_macc[acc] #define QREG_SP cpu_aregs[7] @@ -76,11 +73,14 @@ void m68k_tcg_init(void) #define DEFO32(name, offset) QREG_##name = tcg_global_mem_new_i32(TCG_AREG0, offsetof(CPUState, offset), #name); #define DEFO64(name, offset) QREG_##name = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, 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(CPUState, offset##h), #name); \ +QREG_##name##L = tcg_global_mem_new_i64(TCG_AREG0, offsetof(CPUState, offset##l), #name); \ +} while (0); #include "qregs.def" #undef DEFO32 #undef DEFO64 -#undef DEFF64 +#undef DEFF96 cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); @@ -94,10 +94,6 @@ void m68k_tcg_init(void) cpu_aregs[i] = tcg_global_mem_new(TCG_AREG0, offsetof(CPUM68KState, aregs[i]), p); p += 3; - sprintf(p, "F%d", i); - cpu_fregs[i] = tcg_global_mem_new_i64(TCG_AREG0, - offsetof(CPUM68KState, fregs[i]), p); - p += 3; } for (i = 0; i < 4; i++) { sprintf(p, "ACC%d", i); @@ -106,6 +102,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"); @@ -150,11 +151,13 @@ typedef struct DisasContext { static void *gen_throws_exception; #define gen_last_qop NULL -#define OS_BYTE 0 -#define OS_WORD 1 -#define OS_LONG 2 -#define OS_SINGLE 4 -#define OS_DOUBLE 5 +#define OS_BYTE 1 +#define OS_WORD 2 +#define OS_LONG 3 +#define OS_SINGLE 4 +#define OS_DOUBLE 5 +#define OS_EXTENDED 6 +#define OS_PACKED 7 typedef void (*disas_proc)(DisasContext *, uint16_t); @@ -170,6 +173,64 @@ typedef void (*disas_proc)(DisasContext *, uint16_t); static void disas_##name (DisasContext *s, uint16_t insn) #endif +/* Update the CPU env CC_OP state. */ +static inline void gen_flush_cc_op(DisasContext *s) +{ + if (s->cc_op != CC_OP_DYNAMIC) + tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); +} + + +/* Generate a jump to an immediate address. */ +static void gen_jmp_im(DisasContext *s, uint32_t dest) +{ + gen_flush_cc_op(s); + tcg_gen_movi_i32(QREG_PC, dest); + s->is_jmp = DISAS_JUMP; +} + +static void gen_exception(DisasContext *s, uint32_t where, int nr) +{ + gen_flush_cc_op(s); + gen_jmp_im(s, where); + gen_helper_raise_exception(tcg_const_i32(nr)); +} + +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, fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_ld_i64(QREG_FP0L, cpu_env, + offsetof(CPUM68KState, 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, fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_st_i64(QREG_FP0L, cpu_env, + offsetof(CPUM68KState, 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, fregs[freg]) + + offsetof(FPReg, d.high)); + tcg_gen_ld_i64(QREG_FP1L, cpu_env, + offsetof(CPUM68KState, 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) @@ -192,7 +253,6 @@ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) tcg_gen_qemu_ld16u(tmp, addr, index); break; case OS_LONG: - case OS_SINGLE: tcg_gen_qemu_ld32u(tmp, addr, index); break; default: @@ -202,17 +262,6 @@ static inline TCGv gen_load(DisasContext * s, int opsize, TCGv addr, int sign) return tmp; } -static inline TCGv_i64 gen_load64(DisasContext * s, TCGv addr) -{ - TCGv_i64 tmp; - int index = IS_USER(s); - s->is_mem = 1; - tmp = tcg_temp_new_i64(); - tcg_gen_qemu_ldf64(tmp, addr, index); - gen_throws_exception = gen_last_qop; - return tmp; -} - /* Generate a store. */ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val) { @@ -226,7 +275,6 @@ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val) tcg_gen_qemu_st16(val, addr, index); break; case OS_LONG: - case OS_SINGLE: tcg_gen_qemu_st32(val, addr, index); break; default: @@ -235,14 +283,6 @@ static inline void gen_store(DisasContext *s, int opsize, TCGv addr, TCGv val) gen_throws_exception = gen_last_qop; } -static inline void gen_store64(DisasContext *s, TCGv addr, TCGv_i64 val) -{ - int index = IS_USER(s); - s->is_mem = 1; - tcg_gen_qemu_stf64(val, addr, index); - gen_throws_exception = gen_last_qop; -} - typedef enum { EA_STORE, EA_LOADU, @@ -282,10 +322,17 @@ static inline uint32_t read_im16(DisasContext *s) static inline uint32_t read_im32(DisasContext *s) { uint32_t im; - im = ((uint32_t)lduw_code(s->pc)) << 16; - s->pc += 2; - im |= lduw_code(s->pc); - s->pc += 2; + im = read_im16(s) << 16; + im |= 0xffff & read_im16(s); + return im; +} + +/* Read a 64-bit immediate constant. */ +static inline uint64_t read_im64(DisasContext *s) +{ + uint64_t im; + im = (uint64_t)read_im32(s) << 32; + im |= (uint64_t)read_im32(s); return im; } @@ -414,13 +461,6 @@ static TCGv gen_lea_indexed(DisasContext *s, int opsize, TCGv base) return add; } -/* Update the CPU env CC_OP state. */ -static inline void gen_flush_cc_op(DisasContext *s) -{ - if (s->cc_op != CC_OP_DYNAMIC) - tcg_gen_movi_i32(QREG_CC_OP, s->cc_op); -} - /* Evaluate all the CC flags. */ static inline void gen_flush_flags(DisasContext *s) { @@ -469,6 +509,8 @@ static inline int opsize_bytes(int opsize) case OS_LONG: return 4; case OS_SINGLE: return 4; case OS_DOUBLE: return 8; + case OS_EXTENDED: return 12; + case OS_PACKED: return 12; default: qemu_assert(0, "bad operand size"); return 0; @@ -485,6 +527,20 @@ static inline int insn_opsize(int insn, int pos) } } +static inline int ext_opsize(int ext, int pos) +{ + switch ((ext >> pos) & 7) { + case 0: return OS_LONG; + case 1: return OS_SINGLE; + case 2: return OS_EXTENDED; + case 3: return OS_PACKED; + case 4: return OS_WORD; + case 5: return OS_DOUBLE; + case 6: return OS_BYTE; + default: abort(); + } +} + /* Assign value to a register. If the width is less than the register width only the low part of the register is set. */ static void gen_partset_reg(int opsize, TCGv reg, TCGv val) @@ -534,7 +590,6 @@ static inline TCGv gen_extend(TCGv val, int opsize, int sign) tcg_gen_ext16u_i32(tmp, val); break; case OS_LONG: - case OS_SINGLE: tmp = val; break; default: @@ -704,7 +759,6 @@ static TCGv gen_ea(DisasContext *s, uint16_t insn, int opsize, TCGv val, s->pc += 2; break; case OS_LONG: - case OS_SINGLE: offset = read_im32(s); break; default: @@ -719,6 +773,294 @@ static TCGv gen_ea(DisasContext *s, uint16_t insn, int opsize, TCGv val, 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: + qemu_assert(0, "FPU: Bad operand size"); + } +} + +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: + qemu_assert(0, "FPU: Bad operand size"); + } +} + +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_ld16u(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: + qemu_assert(0, "Bad operand size"); + } + 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: + qemu_assert(0, "Bad operand size"); + } + gen_throws_exception = gen_last_qop; +} + +static void gen_op_load_ea_FP0(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(s, insn, opsize); + 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(s, insn, opsize); + 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(s, insn, opsize); + gen_load_FP0(s, opsize, addr); + break; + case 4: /* Immediate. */ + switch (opsize) { + case OS_BYTE: + val = read_im8(s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_WORD: + val = read_im16(s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_LONG: + val = read_im32(s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_SINGLE: + val = read_im32(s); + tcg_gen_movi_i32(QREG_FP0H, val); + break; + case OS_DOUBLE: + val = read_im64(s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + case OS_EXTENDED: + val = read_im32(s); + tcg_gen_movi_i32(QREG_FP0H, val); + val = read_im64(s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + case OS_PACKED: + val = read_im32(s); + tcg_gen_movi_i32(QREG_FP0H, val); + val = read_im64(s); + tcg_gen_movi_i64(QREG_FP0L, val); + break; + default: + qemu_assert(0, "Bad immediate operand"); + } + gen_extend_FP0(opsize); + break; + default: + qemu_assert(0, "Bad FP addressing mode"); + } + } +} + +static void gen_op_store_ea_FP0(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(s, insn, opsize); + 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(s, insn, opsize); + gen_store_FP0(s, opsize, addr); + break; + case 7: /* Other */ + switch (insn & 7) { + case 0: /* Absolute short. */ + case 1: /* Absolute long. */ + addr = gen_lea(s, insn, opsize); + 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: + qemu_assert(0, "Bad FP addressing mode"); + } + } +} + /* This generates a conditional branch, clobbering all temporaries. */ static void gen_jmpcc(DisasContext *s, int cond, int l1) { @@ -848,14 +1190,6 @@ static void gen_lookup_tb(DisasContext *s) s->is_jmp = DISAS_UPDATE; } -/* Generate a jump to an immediate address. */ -static void gen_jmp_im(DisasContext *s, uint32_t dest) -{ - gen_flush_cc_op(s); - tcg_gen_movi_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; -} - /* Generate a jump to the address in qreg DEST. */ static void gen_jmp(DisasContext *s, TCGv dest) { @@ -864,18 +1198,6 @@ static void gen_jmp(DisasContext *s, TCGv dest) s->is_jmp = DISAS_JUMP; } -static void gen_exception(DisasContext *s, uint32_t where, int nr) -{ - gen_flush_cc_op(s); - gen_jmp_im(s, where); - gen_helper_raise_exception(tcg_const_i32(nr)); -} - -static inline void gen_addr_fault(DisasContext *s) -{ - gen_exception(s, s->insn_pc, EXCP_ADDRESS); -} - #define SRC_EA(result, opsize, op_sign, addrp) do { \ result = gen_ea(s, insn, opsize, NULL_QREG, addrp, op_sign ? EA_LOADS : EA_LOADU); \ if (IS_NULL_QREG(result)) { \ @@ -3159,6 +3481,70 @@ DISAS_INSN(trap) gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf)); } +static void gen_op_fmovem(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(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 & 1) { + gen_op_load_fpr_FP0(i); + gen_store_FP0(s, opsize, addr); + if (mask != 1) + 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 & 1) { + 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); + } + if (mask != 1 || (insn & 070) == 030) + tcg_gen_addi_i32(addr, addr, incr); + } + } + if ((insn & 070) == 030) + tcg_gen_mov_i32(AREG(insn, 0), addr); + } + tcg_temp_free_i32(addr); +} + /* ??? FP exceptions are not implemented. Most exceptions are deferred until immediately before the next FP instruction is executed. */ DISAS_INSN(fpu) @@ -3166,14 +3552,10 @@ DISAS_INSN(fpu) uint16_t ext; uint8_t rom_offset; int opmode; - TCGv_i64 src; - TCGv_i64 dest; - TCGv_i64 res; - TCGv tmp32; - TCGv reg; int round; int set_dest; int opsize; + TCGv val; ext = read_im16(s); opmode = ext & 0x7f; @@ -3186,55 +3568,21 @@ DISAS_INSN(fpu) if ( insn == 0xf200 && (ext & 0xfc00) == 0x5c00) { /* fmovecr */ rom_offset = ext & 0x7f; - dest = FREG(ext, 7); - gen_helper_const_f64(dest, cpu_env, tcg_const_i32(rom_offset)); + 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 */ - src = FREG(ext, 7); - tmp32 = tcg_temp_new_i32(); - /* fmove */ - /* ??? TODO: Proper behavior on overflow. */ - switch ((ext >> 10) & 7) { - case 0: opsize = OS_LONG; break; - case 1: opsize = OS_SINGLE; break; - case 4: opsize = OS_WORD; break; - case 5: opsize = OS_DOUBLE; break; - case 6: opsize = OS_BYTE; break; - default: - goto undef; - } - if (opsize == OS_DOUBLE) { - tmp32 = gen_lea(s, insn, opsize); - if (IS_NULL_QREG(tmp32)) { - gen_addr_fault(s); - return; - } - gen_store64(s, tmp32, src); - if ( ((insn >> 3) & 7) == 3) { /* post-increment */ - reg = AREG(insn, 0); - tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize)); - } - } else { - switch (opsize) { - case OS_LONG: - case OS_WORD: - case OS_BYTE: - gen_helper_f64_to_i32(tmp32, cpu_env, src); - break; - case OS_SINGLE: - gen_helper_f64_to_f32(tmp32, cpu_env, src); - break; - } - DEST_EA(insn, opsize, tmp32, NULL); - } - tcg_temp_free_i32(tmp32); + opsize = ext_opsize(ext, 10); + gen_op_load_fpr_FP0(REG(ext, 7)); + gen_op_store_ea_FP0(s, insn, opsize); return; case 4: /* fmove to control register. */ switch ((ext >> 10) & 7) { case 4: /* FPCR */ - /* Not implemented. Ignore writes. */ + SRC_EA(val, OS_LONG, 0, NULL); + gen_helper_set_fpcr(cpu_env, val); break; case 1: /* FPIAR */ case 2: /* FPSR */ @@ -3247,187 +3595,95 @@ DISAS_INSN(fpu) switch ((ext >> 10) & 7) { case 4: /* FPCR */ /* Not implemented. Always return zero. */ - tmp32 = tcg_const_i32(0); - break; + DEST_EA(insn, OS_LONG, QEMU_FPCR, NULL); + return; case 1: /* FPIAR */ + cpu_abort(NULL, "Unimplemented: fmove from control FPIAR"); + goto undef; case 2: /* FPSR */ + DEST_EA(insn, OS_LONG, QEMU_FPSR, NULL); + return; default: cpu_abort(NULL, "Unimplemented: fmove from control %d", (ext >> 10) & 7); goto undef; } - DEST_EA(insn, OS_LONG, tmp32, NULL); break; case 6: /* fmovem */ case 7: - { - TCGv addr; - int incr; - uint16_t mask; - int i; - if ((ext & 0xf00) != 0 || (ext & 0xff) == 0) - goto undef; - if ((ext & 0x1000) == 0 && !m68k_feature(s->env, M68K_FEATURE_FPU)) - goto undef; - if ((insn & 070) == 040) - tmp32 = AREG(insn, 0); - else { - tmp32 = gen_lea(s, insn, OS_LONG); - if (IS_NULL_QREG(tmp32)) { - gen_addr_fault(s); - return; - } - } - addr = tcg_temp_new_i32(); - tcg_gen_mov_i32(addr, tmp32); - mask = 0x80; - if (m68k_feature(s->env, M68K_FEATURE_FPU)) - incr = 12; - else - incr = 8; - if ((ext & (1 << 13)) && (insn & 070) == 040) { - for (i = 0; i < 8; i++) { - if (ext & mask) { - s->is_mem = 1; - dest = FREG(i, 7); - tcg_gen_subi_i32(addr, addr, incr); - tcg_gen_mov_i32(AREG(insn, 0), addr); - tcg_gen_qemu_stf64(dest, addr, IS_USER(s)); - } - mask >>= 1; - } - tcg_temp_free_i32(addr); - return; - } - for (i = 0; i < 8; i++) { - if (ext & mask) { - s->is_mem = 1; - dest = FREG(i, 0); - if (ext & (1 << 13)) { - /* store */ - tcg_gen_qemu_stf64(dest, addr, IS_USER(s)); - } else { - /* load */ - tcg_gen_qemu_ldf64(dest, addr, IS_USER(s)); - } - if (ext & (mask - 1) || (insn & 070) == 030) { - tcg_gen_addi_i32(addr, addr, incr); - if ((insn & 070) == 030) - tcg_gen_mov_i32(AREG(insn, 0), addr); - } - } - mask >>= 1; - } - tcg_temp_free_i32(addr); - } + 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(s, insn, ext); return; } + opsize = ext_opsize(ext, 10); if (ext & (1 << 14)) { - /* Source effective address. */ - switch ((ext >> 10) & 7) { - case 0: opsize = OS_LONG; break; - case 1: opsize = OS_SINGLE; break; - case 4: opsize = OS_WORD; break; - case 5: opsize = OS_DOUBLE; break; - case 6: opsize = OS_BYTE; break; - default: - goto undef; - } - if (opsize == OS_DOUBLE) { - if ((insn & 0x3f) == 0x3c) { - src = gen_load64(s, tcg_const_i32(s->pc)); - s->pc += 8; - } else { - tmp32 = gen_lea(s, insn, opsize); - if (IS_NULL_QREG(tmp32)) { - gen_addr_fault(s); - return; - } - src = gen_load64(s, tmp32); - if ( ((insn >> 3) & 7) == 3) { /* post-increment */ - reg = AREG(insn, 0); - tcg_gen_addi_i32(reg, reg, opsize_bytes(opsize)); - } - } - } else { - SRC_EA(tmp32, opsize, 1, NULL); - src = tcg_temp_new_i64(); - switch (opsize) { - case OS_LONG: - case OS_WORD: - case OS_BYTE: - gen_helper_i32_to_f64(src, cpu_env, tmp32); - break; - case OS_SINGLE: - gen_helper_f32_to_f64(src, cpu_env, tmp32); - break; - } - } + gen_op_load_ea_FP0(s, insn, opsize); } else { /* Source register. */ - src = FREG(ext, 10); + gen_op_load_fpr_FP0(REG(ext, 10)); } - dest = FREG(ext, 7); - res = tcg_temp_new_i64(); - if (opmode != 0x3a) - tcg_gen_mov_f64(res, dest); round = 1; set_dest = 1; switch (opmode) { case 0: case 0x40: case 0x44: /* fmove */ - tcg_gen_mov_f64(res, src); break; case 1: /* fint */ - gen_helper_iround_f64(res, cpu_env, src); + gen_helper_iround_FP0(cpu_env); round = 0; break; case 3: /* fintrz */ - gen_helper_itrunc_f64(res, cpu_env, src); + gen_helper_itrunc_FP0(cpu_env); round = 0; break; case 4: case 0x41: case 0x45: /* fsqrt */ - gen_helper_sqrt_f64(res, cpu_env, src); + gen_helper_sqrt_FP0(cpu_env); break; case 0x18: case 0x58: case 0x5c: /* fabs */ - gen_helper_abs_f64(res, src); + gen_helper_abs_FP0(cpu_env); break; case 0x1a: case 0x5a: case 0x5e: /* fneg */ - gen_helper_chs_f64(res, src); + gen_helper_chs_FP0(cpu_env); break; case 0x20: case 0x60: case 0x64: /* fdiv */ - gen_helper_div_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_div_FP0_FP1(cpu_env); break; case 0x22: case 0x62: case 0x66: /* fadd */ - gen_helper_add_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_add_FP0_FP1(cpu_env); break; case 0x23: case 0x63: case 0x67: /* fmul */ - gen_helper_mul_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_mul_FP0_FP1(cpu_env); break; case 0x24: /* fsgldiv */ - gen_helper_div_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_div_FP0_FP1(cpu_env); break; case 0x27: /* fsglmul */ - gen_helper_mul_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_mul_FP0_FP1(cpu_env); break; case 0x28: case 0x68: case 0x6c: /* fsub */ - gen_helper_sub_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_sub_FP0_FP1(cpu_env); break; case 0x38: /* fcmp */ - gen_helper_sub_cmp_f64(res, cpu_env, res, src); + gen_op_load_fpr_FP1(REG(ext, 7)); + gen_helper_fcmp_FP0_FP1(cpu_env); set_dest = 0; round = 0; break; case 0x3a: /* ftst */ - tcg_gen_mov_f64(res, src); set_dest = 0; round = 0; break; default: goto undef; } - if (ext & (1 << 14)) { - tcg_temp_free_i64(src); - } if (round) { if (opmode & 0x40) { if ((opmode & 0x4) != 0) @@ -3437,16 +3693,16 @@ DISAS_INSN(fpu) } } 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 } - tcg_gen_mov_f64(QREG_FP_RESULT, res); if (set_dest) { - tcg_gen_mov_f64(dest, res); + gen_op_store_fpr_FP0(REG(ext, 7)); } - tcg_temp_free_i64(res); return; undef: /* FIXME: Is this right for offset addressing modes? */ @@ -3460,7 +3716,7 @@ static void gen_fjmpcc(DisasContext *s, int cond, int l1) /* TODO: Raise BSUN exception. */ flag = tcg_temp_new(); - gen_helper_compare_f64(flag, cpu_env, QREG_FP_RESULT); + gen_helper_compare_FP0(flag, cpu_env); /* Jump to l1 if condition is true. */ switch (cond) { case 0: /* f */ @@ -3484,12 +3740,12 @@ static void gen_fjmpcc(DisasContext *s, int cond, int l1) tcg_gen_andi_i32(flag, flag, 1); tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1); break; - case 7: /* or (=2) */ - tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(2), l1); - break; - case 8: /* un (<2) */ + case 7: /* or (<2) */ tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(2), l1); break; + case 8: /* un (=2) */ + tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(2), l1); + break; case 9: /* ueq (=0 or =2) */ tcg_gen_andi_i32(flag, flag, 1); tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1); @@ -4337,20 +4593,18 @@ void cpu_dump_state(CPUState *env, FILE *f, fprintf_function cpu_fprintf, { 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", + cpu_fprintf (f, "D%d = %08x A%d = %08x " + "F%d = %" PRIxFPH " %" PRIxFPL "\n", i, env->dregs[i], i, env->aregs[i], - i, u.l.upper, u.l.lower, *(double *)&u.d); + i, env->fregs[i].d.high, env->fregs[i].d.low); } 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); } void restore_state_to_opc(CPUState *env, TranslationBlock *tb, int pc_pos) diff --git a/tests/m68k/Makefile b/tests/m68k/Makefile new file mode 100644 index 0000000..8f09805 --- /dev/null +++ b/tests/m68k/Makefile @@ -0,0 +1,15 @@ +TESTS=fmovecr fmove fmovem + +all: $(TESTS) + +%: %.S + m68k-linux-gnu-gcc -m68040 -nostartfiles -nodefaultlibs -nostdlib -o $@ $< + +fmovecr: fmovecr.S +fmove: fmove.S +fmovem: fmovem.S + +.PHONY: clean + +clean: + rm -f $(TESTS) diff --git a/tests/m68k/fmove.S b/tests/m68k/fmove.S new file mode 100644 index 0000000..3f2daba --- /dev/null +++ b/tests/m68k/fmove.S @@ -0,0 +1,55 @@ + .include "trap.i" + + .data +tmp: .long 0x88776655 +pi: .long 0x40000000 + .long 0xc90fdaa2 + .long 0x2168C235 + + .text + .globl _start +_start: + lea pi,%a0 + move.l (%a0), %d0 + fmove.x (%a0), %fp4 + # Dn + + move.l #-1, %d3 + fmove.b %d3, %fp0 + fmove.w %d3, %fp1 + fmove.l %d3, %fp2 + fmove.s %d3, %fp3 + + move.l #1, %d1 + fmove.b %d1, %fp0 + fmove.w %d1, %fp1 + fmove.l %d1, %fp2 + fmove.s %d1, %fp3 + + move.l #0x11223344, %d1 + fmove.b %d1, %fp1 + fmove.w %d1, %fp2 + fmove.l %d1, %fp3 + fmove.s %d1, %fp4 + + # (A0) + + lea tmp,%a0 + fmove.b (%a0), %fp0 + fmove.w (%a0), %fp1 + fmove.l (%a0), %fp2 + fmove.l (%a0), %fp3 + lea pi,%a0 + fmove.x (%a0), %fp4 + + # immediate values + + fmove.b #0xFF,%fp0 + fmove.w #0xFABC,%fp1 + fmove.l #0xFABCDEFA,%fp2 + fmove.s #0xDEADBEAF,%fp3 + fmove.d #0xFABCDEFADEADBEAF,%fp4 + fmove.x #0xFABCDEFADEADBEAF12345678,%fp5 + fmove.p #0xFABCDEFADEADBEAF12345678,%fp6 + + exit 0 diff --git a/tests/m68k/fmovecr.S b/tests/m68k/fmovecr.S new file mode 100644 index 0000000..c6cd82b --- /dev/null +++ b/tests/m68k/fmovecr.S @@ -0,0 +1,29 @@ + .include "trap.i" + + .text + .globl _start +_start: + fmovecr.x #0x00,%fp0 + fmovecr.x #0x0B,%fp0 + fmovecr.x #0x0C,%fp0 + fmovecr.x #0x0D,%fp0 + fmovecr.x #0x0E,%fp0 + fmovecr.x #0x0F,%fp0 + fmovecr.x #0x30,%fp0 + fmovecr.x #0x31,%fp0 + fmovecr.x #0x32,%fp0 + fmovecr.x #0x33,%fp0 + fmovecr.x #0x34,%fp0 + fmovecr.x #0x35,%fp0 + fmovecr.x #0x36,%fp0 + fmovecr.x #0x37,%fp0 + fmovecr.x #0x38,%fp0 + fmovecr.x #0x39,%fp0 + fmovecr.x #0x3A,%fp0 + fmovecr.x #0x3B,%fp0 + fmovecr.x #0x3c,%fp0 + fmovecr.x #0x3d,%fp0 + fmovecr.x #0x3e,%fp0 + fmovecr.x #0x3f,%fp0 + + exit 0 diff --git a/tests/m68k/fmovem.S b/tests/m68k/fmovem.S new file mode 100644 index 0000000..35da35f --- /dev/null +++ b/tests/m68k/fmovem.S @@ -0,0 +1,17 @@ + .include "trap.i" + + .data +pi: .long 0x40000000 + .long 0xc90fdaa2 + .long 0x2168C235 +ln2: .long 0x3ffe0000 + .long 0xb17217f7 + .long 0xd1cf79ac + + .text + .globl _start +_start: + lea pi,%a0 + fmovem.x (%a0), %fp2-%fp3 + fmovem.x (%a0)+, %fp0-%fp1 + exit 0 diff --git a/tests/m68k/trap.i b/tests/m68k/trap.i new file mode 100644 index 0000000..ce7aa1e --- /dev/null +++ b/tests/m68k/trap.i @@ -0,0 +1,5 @@ +.macro exit value + move.l #\value,%d1 + move.l #1, %d0 + trap #0 +.endm -- 1.7.2.3