* [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU @ 2017-06-20 20:51 Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function Laurent Vivier ` (5 more replies) 0 siblings, 6 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier This series modifies the original ColdFire FPU implementation to use floatx80 instead of float64 internally as this is the native datatype for 680x0. I didn't keep the float64 type for ColdFire, but if someone thinks it's required I can update this series in this way. The series also adds the FPU status and control registers. The floatx80 datatype used here is not exactly the same as the one used by 680x0 for its extended precision data type, because normally the signaling bit of 680x0 NAN is the MSB of the mantissa minus one and in floatx80 it is the MSB. We also add the gdb server parts to read the new FPU registers. A strange thing happens here: while the gdb client running remotely from a debian etch-m68k has no issue working with 96bit FPU registers (the 680x0 extended precision data type), new gdbs (from a debian unstable and gdb for cross-compiled environment) don't expect this FPU registers size. But it seems like a bug in gdb, not in this implementation. v5: rebase, some patches are now part of master call cpu_m68k_set_fpcr() in XX_fpu_gdb_set_reg() generate unimplemented data type exception for packed data type, and only on coldfire, for extended data type remove QEMU_FPCR/QEMU_FPSR rework gen_op_fmove_fcr() replace cpu_abort(...) by qemu_log_mask(LOG_UNIMP, ...) add a patch to reset FPU registers add a patch to manage FPU registers in ucontext (linux-user) Note: I will replace floatx80 by a floatx96, later, once we have a working linux-user qemu-m68k. v4: this is only a subset of v3 add a patch to move fmove CR to a function use "FPReg *" instead of FP0/FP1 temporaries fcmp/ftst take old FPSR register from parameters and return the new one add R-b from Richard v3: fix fsave opcode Add comment to define "unnormalized" number Correctly define pickNaN() v2: complete rework of the series force single precision in ColdFire mode add "forced" precision instructions (fsmove, fdmove, fsadd, ...) fixed Fcc. Laurent Vivier (6): target-m68k: move fmove CR to a function target-m68k: initialize FPU registers target-m68k: use floatx80 internally target-m68k: define 96bit FP registers for gdb on 680x0 target-m68k: add FPCR and FPSR target-m68k,linux-user: manage FP registers in ucontext configure | 2 +- gdb-xml/m68k-fp.xml | 21 + linux-user/signal.c | 41 ++ target/m68k/cpu.c | 9 +- target/m68k/cpu.h | 58 ++- target/m68k/fpu_helper.c | 180 ++++++--- target/m68k/helper.c | 77 +++- target/m68k/helper.h | 38 +- target/m68k/qregs.def | 1 - target/m68k/translate.c | 983 ++++++++++++++++++++++++++++++++--------------- 10 files changed, 1025 insertions(+), 385 deletions(-) create mode 100644 gdb-xml/m68k-fp.xml -- 2.9.4 ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 22:50 ` Richard Henderson 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers Laurent Vivier ` (4 subsequent siblings) 5 siblings, 1 reply; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier Move code of fmove to/from control register to a function Signed-off-by: Laurent Vivier <laurent@vivier.eu> --- target/m68k/translate.c | 56 +++++++++++++++++++++++++++---------------------- 1 file changed, 31 insertions(+), 25 deletions(-) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index dfecfb6..c9a5fe4 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -4099,6 +4099,35 @@ DISAS_INSN(trap) gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf)); } +static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, + uint32_t insn, uint32_t ext) +{ + int mask = (ext >> 10) & 7; + int is_write = (ext >> 13) & 1; + TCGv val; + + switch (mask) { + case 1: /* FPIAR */ + case 2: /* FPSR */ + default: + qemu_log_mask(LOG_UNIMP, "Unimplemented: fmove to/from control %d", + mask); + goto undef; + case 4: /* FPCR */ + if (is_write) { + val = tcg_const_i32(0); + DEST_EA(env, insn, OS_LONG, val, NULL); + tcg_temp_free(val); + } + /* Not implemented. Ignore register update */ + break; + } + return; +undef: + s->pc -= 2; + disas_undef_fpu(env, s, insn); +} + /* ??? FP exceptions are not implemented. Most exceptions are deferred until immediately before the next FP instruction is executed. */ DISAS_INSN(fpu) @@ -4177,32 +4206,9 @@ DISAS_INSN(fpu) tcg_temp_free_i32(tmp32); return; case 4: /* fmove to control register. */ - switch ((ext >> 10) & 7) { - case 4: /* FPCR */ - /* Not implemented. Ignore writes. */ - break; - case 1: /* FPIAR */ - case 2: /* FPSR */ - default: - cpu_abort(NULL, "Unimplemented: fmove to control %d", - (ext >> 10) & 7); - } - break; case 5: /* fmove from control register. */ - switch ((ext >> 10) & 7) { - case 4: /* FPCR */ - /* Not implemented. Always return zero. */ - tmp32 = tcg_const_i32(0); - break; - case 1: /* FPIAR */ - case 2: /* FPSR */ - default: - cpu_abort(NULL, "Unimplemented: fmove from control %d", - (ext >> 10) & 7); - goto undef; - } - DEST_EA(env, insn, OS_LONG, tmp32, NULL); - break; + gen_op_fmove_fcr(env, s, insn, ext); + return; case 6: /* fmovem */ case 7: { -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function Laurent Vivier @ 2017-06-20 22:50 ` Richard Henderson 2017-06-21 15:54 ` Philippe Mathieu-Daudé 0 siblings, 1 reply; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:50 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > Move code of fmove to/from control register to a function > > Signed-off-by: Laurent Vivier<laurent@vivier.eu> > --- > target/m68k/translate.c | 56 +++++++++++++++++++++++++++---------------------- > 1 file changed, 31 insertions(+), 25 deletions(-) Reviewed-by: Richard Henderson <rth@twiddle.net> r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function 2017-06-20 22:50 ` Richard Henderson @ 2017-06-21 15:54 ` Philippe Mathieu-Daudé 0 siblings, 0 replies; 22+ messages in thread From: Philippe Mathieu-Daudé @ 2017-06-21 15:54 UTC (permalink / raw) To: Richard Henderson, Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno On 06/20/2017 07:50 PM, Richard Henderson wrote: > On 06/20/2017 01:51 PM, Laurent Vivier wrote: >> Move code of fmove to/from control register to a function >> >> Signed-off-by: Laurent Vivier<laurent@vivier.eu> >> --- >> target/m68k/translate.c | 56 >> +++++++++++++++++++++++++++---------------------- >> 1 file changed, 31 insertions(+), 25 deletions(-) > > Reviewed-by: Richard Henderson <rth@twiddle.net> Reviewed-by: Philippe Mathieu-Daudé <f4bug@amsat.org> > > > r~ > ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 22:44 ` Richard Henderson 2017-06-20 22:51 ` Richard Henderson 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally Laurent Vivier ` (3 subsequent siblings) 5 siblings, 2 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier on reset, set FP registers to NaN and control registers to 0 Signed-off-by: Laurent Vivier <laurent@vivier.eu> --- target/m68k/cpu.c | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index f068922..f2e031f 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -49,6 +49,8 @@ static void m68k_cpu_reset(CPUState *s) M68kCPU *cpu = M68K_CPU(s); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); CPUM68KState *env = &cpu->env; + float64 nan = float64_default_nan(NULL); + int i; mcc->parent_reset(s); @@ -57,7 +59,12 @@ static void m68k_cpu_reset(CPUState *s) env->sr = 0x2700; #endif m68k_switch_sp(env); - /* ??? FP regs should be initialized to NaN. */ + for (i = 0; i < 8; i++) { + env->fregs[i] = nan; + } + env->fpcr = 0; + env->fpsr = 0; + cpu_m68k_set_ccr(env, 0); /* TODO: We should set PC from the interrupt vector. */ env->pc = 0; -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers Laurent Vivier @ 2017-06-20 22:44 ` Richard Henderson 2017-06-20 22:51 ` Richard Henderson 1 sibling, 0 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:44 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > + env->fpcr = 0; You probably want to use cpu_m68k_set_fpcr here too, so that fp_status matches. r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers Laurent Vivier 2017-06-20 22:44 ` Richard Henderson @ 2017-06-20 22:51 ` Richard Henderson 1 sibling, 0 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:51 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > on reset, set FP registers to NaN and control registers to 0 > > Signed-off-by: Laurent Vivier<laurent@vivier.eu> > --- > target/m68k/cpu.c | 9 ++++++++- > 1 file changed, 8 insertions(+), 1 deletion(-) Nevermind the last... it's in 5/6. Reviewed-by: Richard Henderson <rth@twiddle.net> r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 22:50 ` Richard Henderson 2017-06-21 16:18 ` Philippe Mathieu-Daudé 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 4/6] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier ` (2 subsequent siblings) 5 siblings, 2 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier Coldfire uses float64, but 680x0 use floatx80. This patch introduces the use of floatx80 internally and enables 680x0 80bits FPU. Signed-off-by: Laurent Vivier <laurent@vivier.eu> --- target/m68k/cpu.c | 4 +- target/m68k/cpu.h | 15 +- target/m68k/fpu_helper.c | 85 ++++--- target/m68k/helper.c | 12 +- target/m68k/helper.h | 37 +-- target/m68k/qregs.def | 1 - target/m68k/translate.c | 646 ++++++++++++++++++++++++++++++----------------- 7 files changed, 509 insertions(+), 291 deletions(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index f2e031f..435456f 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -49,7 +49,7 @@ static void m68k_cpu_reset(CPUState *s) M68kCPU *cpu = M68K_CPU(s); M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); CPUM68KState *env = &cpu->env; - float64 nan = float64_default_nan(NULL); + floatx80 nan = floatx80_default_nan(NULL); int i; mcc->parent_reset(s); @@ -60,7 +60,7 @@ static void m68k_cpu_reset(CPUState *s) #endif m68k_switch_sp(env); for (i = 0; i < 8; i++) { - env->fregs[i] = nan; + env->fregs[i].d = nan; } env->fpcr = 0; env->fpsr = 0; diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 384ec5d..beb8ebc 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -55,8 +55,15 @@ #define EXCP_UNINITIALIZED 15 #define EXCP_TRAP0 32 /* User trap #0. */ #define EXCP_TRAP15 47 /* User trap #15. */ +#define EXCP_FP_BSUN 48 /* Branch Set on Unordered */ +#define EXCP_FP_INEX 49 /* Inexact result */ +#define EXCP_FP_DZ 50 /* Divide by Zero */ +#define EXCP_FP_UNFL 51 /* Underflow */ +#define EXCP_FP_OPERR 52 /* Operand Error */ +#define EXCP_FP_OVFL 53 /* Overflow */ +#define EXCP_FP_SNAN 54 /* Signaling Not-A-Number */ +#define EXCP_FP_UNIMP 55 /* Unimplemented Data type */ #define EXCP_UNSUPPORTED 61 -#define EXCP_ICE 13 #define EXCP_RTE 0x100 #define EXCP_HALT_INSN 0x101 @@ -64,6 +71,8 @@ #define NB_MMU_MODES 2 #define TARGET_INSN_START_EXTRA_WORDS 1 +typedef CPU_LDoubleU FPReg; + typedef struct CPUM68KState { uint32_t dregs[8]; uint32_t aregs[8]; @@ -82,8 +91,8 @@ typedef struct CPUM68KState { uint32_t cc_c; /* either 0/1, unused, or computed from cc_n and cc_v */ uint32_t cc_z; /* == 0 or unused */ - float64 fregs[8]; - float64 fp_result; + FPReg fregs[8]; + FPReg fp_result; uint32_t fpcr; uint32_t fpsr; float_status fp_status; diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 5bf2576..f4d3821 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -21,92 +21,101 @@ #include "qemu/osdep.h" #include "cpu.h" #include "exec/helper-proto.h" +#include "exec/exec-all.h" -uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val) +int32_t HELPER(reds32)(CPUM68KState *env, FPReg *val) { - return float64_to_int32(val, &env->fp_status); + return floatx80_to_int32(val->d, &env->fp_status); } -float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val) +float32 HELPER(redf32)(CPUM68KState *env, FPReg *val) { - return float64_to_float32(val, &env->fp_status); + return floatx80_to_float32(val->d, &env->fp_status); } -float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val) +void HELPER(exts32)(CPUM68KState *env, FPReg *res, int32_t val) { - return int32_to_float64(val, &env->fp_status); + res->d = int32_to_floatx80(val, &env->fp_status); } -float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val) +void HELPER(extf32)(CPUM68KState *env, FPReg *res, float32 val) { - return float32_to_float64(val, &env->fp_status); + res->d = float32_to_floatx80(val, &env->fp_status); } -float64 HELPER(iround_f64)(CPUM68KState *env, float64 val) +void HELPER(extf64)(CPUM68KState *env, FPReg *res, float64 val) { - return float64_round_to_int(val, &env->fp_status); + res->d = float64_to_floatx80(val, &env->fp_status); } -float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val) +float64 HELPER(redf64)(CPUM68KState *env, FPReg *val) { - return float64_trunc_to_int(val, &env->fp_status); + return floatx80_to_float64(val->d, &env->fp_status); } -float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val) +void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val) { - return float64_sqrt(val, &env->fp_status); + res->d = floatx80_round_to_int(val->d, &env->fp_status); } -float64 HELPER(abs_f64)(float64 val) +void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val) { - return float64_abs(val); + res->d = floatx80_round_to_int(val->d, &env->fp_status); } -float64 HELPER(chs_f64)(float64 val) +void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) { - return float64_chs(val); + res->d = floatx80_sqrt(val->d, &env->fp_status); } -float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b) +void HELPER(fabs)(CPUM68KState *env, FPReg *res, FPReg *val) { - return float64_add(a, b, &env->fp_status); + res->d = floatx80_abs(val->d); } -float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b) +void HELPER(fchs)(CPUM68KState *env, FPReg *res, FPReg *val) { - return float64_sub(a, b, &env->fp_status); + res->d = floatx80_chs(val->d); } -float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b) +void HELPER(fadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { - return float64_mul(a, b, &env->fp_status); + res->d = floatx80_add(val0->d, val1->d, &env->fp_status); } -float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b) +void HELPER(fsub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { - return float64_div(a, b, &env->fp_status); + res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); } -float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b) +void HELPER(fmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) +{ + res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); +} + +void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) +{ + res->d = floatx80_div(val1->d, val0->d, &env->fp_status); +} + +void HELPER(fsub_cmp)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) { /* ??? 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, &env->fp_status)) { + res->d = floatx80_sub(val0->d, val1->d, &env->fp_status); + if (floatx80_is_quiet_nan(res->d, &env->fp_status)) { /* +/-inf compares equal against itself, but sub returns nan. */ - if (!float64_is_quiet_nan(a, &env->fp_status) - && !float64_is_quiet_nan(b, &env->fp_status)) { - res = float64_zero; - if (float64_lt_quiet(a, res, &env->fp_status)) { - res = float64_chs(res); + if (!floatx80_is_quiet_nan(val0->d, &env->fp_status) + && !floatx80_is_quiet_nan(val1->d, &env->fp_status)) { + res->d = floatx80_zero; + if (floatx80_lt_quiet(val0->d, res->d, &env->fp_status)) { + res->d = floatx80_chs(res->d); } } } - return res; } -uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val) +uint32_t HELPER(fcompare)(CPUM68KState *env, FPReg *val) { - return float64_compare_quiet(val, float64_zero, &env->fp_status); + return floatx80_compare_quiet(val->d, floatx80_zero, &env->fp_status); } diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 5ca9911..8bfc881 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -73,10 +73,11 @@ 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]); + float_status s; + stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); return 8; } if (n < 11) { @@ -87,10 +88,11 @@ 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); + float_status s; + env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s); return 8; } if (n < 11) { @@ -126,7 +128,7 @@ 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); } /* TODO: Add [E]MAC registers. */ diff --git a/target/m68k/helper.h b/target/m68k/helper.h index d7a4bf1..d871be6 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -12,21 +12,28 @@ DEF_HELPER_3(movec, void, env, i32, i32) DEF_HELPER_4(cas2w, void, env, i32, i32, i32) DEF_HELPER_4(cas2l, void, env, i32, i32, i32) -DEF_HELPER_2(f64_to_i32, f32, 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) +#define dh_alias_fp ptr +#define dh_ctype_fp FPReg * +#define dh_is_signed_fp dh_is_signed_ptr + +DEF_HELPER_3(exts32, void, env, fp, s32) +DEF_HELPER_3(extf32, void, env, fp, f32) +DEF_HELPER_3(extf64, void, env, fp, f64) +DEF_HELPER_2(redf32, f32, env, fp) +DEF_HELPER_2(redf64, f64, env, fp) +DEF_HELPER_2(reds32, s32, env, fp) + +DEF_HELPER_3(firound, void, env, fp, fp) +DEF_HELPER_3(fitrunc, void, env, fp, fp) +DEF_HELPER_3(fsqrt, void, env, fp, fp) +DEF_HELPER_3(fabs, void, env, fp, fp) +DEF_HELPER_3(fchs, void, env, fp, fp) +DEF_HELPER_4(fadd, void, env, fp, fp, fp) +DEF_HELPER_4(fsub, void, env, fp, fp, fp) +DEF_HELPER_4(fmul, void, env, fp, fp, fp) +DEF_HELPER_4(fdiv, void, env, fp, fp, fp) +DEF_HELPER_4(fsub_cmp, void, env, fp, fp, fp) +DEF_HELPER_2(fcompare, i32, env, fp) 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 51ff43b..1aadc62 100644 --- a/target/m68k/qregs.def +++ b/target/m68k/qregs.def @@ -1,4 +1,3 @@ -DEFF64(FP_RESULT, fp_result) DEFO32(PC, pc) DEFO32(SR, sr) DEFO32(CC_OP, cc_op) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index c9a5fe4..73f691f 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -32,37 +32,27 @@ #include "trace-tcg.h" #include "exec/log.h" - //#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; #include "qregs.def" #undef DEFO32 #undef DEFO64 -#undef DEFF64 static TCGv_i32 cpu_halted; static TCGv_i32 cpu_exception_index; static TCGv_env 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]; #define REG(insn, pos) (((insn) >> (pos)) & 7) #define DREG(insn, pos) cpu_dregs[REG(insn, pos)] #define AREG(insn, pos) get_areg(s, REG(insn, pos)) -#define FREG(insn, pos) cpu_fregs[REG(insn, pos)] #define MACREG(acc) cpu_macc[acc] #define QREG_SP get_areg(s, 7) @@ -87,11 +77,9 @@ void m68k_tcg_init(void) #define DEFO64(name, offset) \ QREG_##name = tcg_global_mem_new_i64(cpu_env, \ offsetof(CPUM68KState, offset), #name); -#define DEFF64(name, offset) DEFO64(name, offset) #include "qregs.def" #undef DEFO32 #undef DEFO64 -#undef DEFF64 cpu_halted = tcg_global_mem_new_i32(cpu_env, -offsetof(M68kCPU, env) + @@ -111,10 +99,6 @@ void m68k_tcg_init(void) cpu_aregs[i] = tcg_global_mem_new(cpu_env, offsetof(CPUM68KState, aregs[i]), p); p += 3; - sprintf(p, "F%d", i); - cpu_fregs[i] = tcg_global_mem_new_i64(cpu_env, - offsetof(CPUM68KState, fregs[i]), p); - p += 3; } for (i = 0; i < 4; i++) { sprintf(p, "ACC%d", i); @@ -265,6 +249,42 @@ static void update_cc_op(DisasContext *s) } } +/* Generate a jump to an immediate address. */ +static void gen_jmp_im(DisasContext *s, uint32_t dest) +{ + update_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) +{ + update_cc_op(s); + tcg_gen_mov_i32(QREG_PC, dest); + s->is_jmp = DISAS_JUMP; +} + +static void gen_raise_exception(int nr) +{ + TCGv_i32 tmp = tcg_const_i32(nr); + + gen_helper_raise_exception(cpu_env, tmp); + tcg_temp_free_i32(tmp); +} + +static void gen_exception(DisasContext *s, uint32_t where, int nr) +{ + update_cc_op(s); + gen_jmp_im(s, where); + gen_raise_exception(nr); +} + +static inline void gen_addr_fault(DisasContext *s) +{ + gen_exception(s, s->insn_pc, EXCP_ADDRESS); +} + /* 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) @@ -286,7 +306,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: @@ -296,16 +315,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); - 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) { @@ -318,7 +327,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: @@ -327,13 +335,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); - tcg_gen_qemu_stf64(val, addr, index); - gen_throws_exception = gen_last_qop; -} - typedef enum { EA_STORE, EA_LOADU, @@ -377,6 +378,15 @@ static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s) return im; } +/* Read a 64-bit immediate constant. */ +static inline uint64_t read_im64(CPUM68KState *env, DisasContext *s) +{ + uint64_t im; + im = (uint64_t)read_im32(env, s) << 32; + im |= (uint64_t)read_im32(env, s); + return im; +} + /* Calculate and address index. */ static TCGv gen_addr_index(DisasContext *s, uint16_t ext, TCGv tmp) { @@ -909,6 +919,304 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, return gen_ea_mode(env, s, mode, reg0, opsize, val, addrp, what); } +static TCGv_ptr gen_fp_ptr(int freg) +{ + TCGv_ptr fp = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fregs[freg])); + return fp; +} + +static TCGv_ptr gen_fp_result_ptr(void) +{ + TCGv_ptr fp = tcg_temp_new_ptr(); + tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fp_result)); + return fp; +} + +static void gen_fp_move(TCGv_ptr dest, TCGv_ptr src) +{ + TCGv t32; + TCGv_i64 t64; + + t32 = tcg_temp_new(); + tcg_gen_ld16u_i32(t32, src, offsetof(FPReg, l.upper)); + tcg_gen_st16_i32(t32, dest, offsetof(FPReg, l.upper)); + tcg_temp_free(t32); + + t64 = tcg_temp_new_i64(); + tcg_gen_ld_i64(t64, src, offsetof(FPReg, l.lower)); + tcg_gen_st_i64(t64, dest, offsetof(FPReg, l.lower)); + tcg_temp_free_i64(t64); +} + +static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp) +{ + TCGv tmp; + TCGv_i64 t64; + int index = IS_USER(s); + + t64 = tcg_temp_new_i64(); + tmp = tcg_temp_new(); + switch (opsize) { + case OS_BYTE: + tcg_gen_qemu_ld8s(tmp, addr, index); + gen_helper_exts32(cpu_env, fp, tmp); + break; + case OS_WORD: + tcg_gen_qemu_ld16s(tmp, addr, index); + gen_helper_exts32(cpu_env, fp, tmp); + break; + case OS_LONG: + tcg_gen_qemu_ld32u(tmp, addr, index); + gen_helper_exts32(cpu_env, fp, tmp); + break; + case OS_SINGLE: + tcg_gen_qemu_ld32u(tmp, addr, index); + gen_helper_extf32(cpu_env, fp, tmp); + break; + case OS_DOUBLE: + tcg_gen_qemu_ld64(t64, addr, index); + gen_helper_extf64(cpu_env, fp, t64); + tcg_temp_free_i64(t64); + break; + case OS_EXTENDED: + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + } + tcg_gen_qemu_ld32u(tmp, addr, index); + tcg_gen_shri_i32(tmp, tmp, 16); + tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_qemu_ld64(t64, tmp, index); + tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); + break; + case OS_PACKED: + /* unimplemented data type on 68040/ColdFire + * FIXME if needed for another FPU + */ + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free(tmp); + tcg_temp_free_i64(t64); + gen_throws_exception = gen_last_qop; +} + +static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp) +{ + TCGv tmp; + TCGv_i64 t64; + int index = IS_USER(s); + + t64 = tcg_temp_new_i64(); + tmp = tcg_temp_new(); + switch (opsize) { + case OS_BYTE: + gen_helper_reds32(tmp, cpu_env, fp); + tcg_gen_qemu_st8(tmp, addr, index); + break; + case OS_WORD: + gen_helper_reds32(tmp, cpu_env, fp); + tcg_gen_qemu_st16(tmp, addr, index); + break; + case OS_LONG: + gen_helper_reds32(tmp, cpu_env, fp); + tcg_gen_qemu_st32(tmp, addr, index); + break; + case OS_SINGLE: + gen_helper_redf32(tmp, cpu_env, fp); + tcg_gen_qemu_st32(tmp, addr, index); + break; + case OS_DOUBLE: + gen_helper_redf64(t64, cpu_env, fp); + tcg_gen_qemu_st64(t64, addr, index); + break; + case OS_EXTENDED: + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + } + tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper)); + tcg_gen_shli_i32(tmp, tmp, 16); + tcg_gen_qemu_st32(tmp, addr, index); + tcg_gen_addi_i32(tmp, addr, 4); + tcg_gen_ld_i64(t64, fp, offsetof(FPReg, l.lower)); + tcg_gen_qemu_st64(t64, tmp, index); + break; + case OS_PACKED: + /* unimplemented data type on 68040/ColdFire + * FIXME if needed for another FPU + */ + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free(tmp); + tcg_temp_free_i64(t64); + gen_throws_exception = gen_last_qop; +} + +static void gen_ldst_fp(DisasContext *s, int opsize, TCGv addr, + TCGv_ptr fp, ea_what what) +{ + if (what == EA_STORE) { + gen_store_fp(s, opsize, addr, fp); + } else { + gen_load_fp(s, opsize, addr, fp); + } +} + +static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, + int reg0, int opsize, TCGv_ptr fp, ea_what what) +{ + TCGv reg, addr, tmp; + TCGv_i64 t64; + + switch (mode) { + case 0: /* Data register direct. */ + reg = cpu_dregs[reg0]; + if (what == EA_STORE) { + switch (opsize) { + case OS_BYTE: + case OS_WORD: + case OS_LONG: + gen_helper_reds32(reg, cpu_env, fp); + break; + case OS_SINGLE: + gen_helper_redf32(reg, cpu_env, fp); + break; + default: + g_assert_not_reached(); + } + } else { + tmp = tcg_temp_new(); + switch (opsize) { + case OS_BYTE: + tcg_gen_ext8s_i32(tmp, reg); + gen_helper_exts32(cpu_env, fp, tmp); + break; + case OS_WORD: + tcg_gen_ext16s_i32(tmp, reg); + gen_helper_exts32(cpu_env, fp, tmp); + break; + case OS_LONG: + gen_helper_exts32(cpu_env, fp, reg); + break; + case OS_SINGLE: + gen_helper_extf32(cpu_env, fp, reg); + break; + default: + g_assert_not_reached(); + } + tcg_temp_free(tmp); + } + return 0; + case 1: /* Address register direct. */ + return -1; + case 2: /* Indirect register */ + addr = get_areg(s, reg0); + gen_ldst_fp(s, opsize, addr, fp, what); + return 0; + case 3: /* Indirect postincrement. */ + addr = cpu_aregs[reg0]; + gen_ldst_fp(s, opsize, addr, fp, what); + tcg_gen_addi_i32(addr, addr, opsize_bytes(opsize)); + return 0; + case 4: /* Indirect predecrememnt. */ + addr = gen_lea_mode(env, s, mode, reg0, opsize); + if (IS_NULL_QREG(addr)) { + return -1; + } + gen_ldst_fp(s, opsize, addr, fp, what); + tcg_gen_mov_i32(cpu_aregs[reg0], addr); + return 0; + case 5: /* Indirect displacement. */ + case 6: /* Indirect index + displacement. */ + do_indirect: + addr = gen_lea_mode(env, s, mode, reg0, opsize); + if (IS_NULL_QREG(addr)) { + return -1; + } + gen_ldst_fp(s, opsize, addr, fp, what); + return 0; + case 7: /* Other */ + switch (reg0) { + case 0: /* Absolute short. */ + case 1: /* Absolute long. */ + case 2: /* pc displacement */ + case 3: /* pc index+displacement. */ + goto do_indirect; + case 4: /* Immediate. */ + if (what == EA_STORE) { + return -1; + } + switch (opsize) { + case OS_BYTE: + tmp = tcg_const_i32((int8_t)read_im8(env, s)); + gen_helper_exts32(cpu_env, fp, tmp); + tcg_temp_free(tmp); + break; + case OS_WORD: + tmp = tcg_const_i32((int16_t)read_im16(env, s)); + gen_helper_exts32(cpu_env, fp, tmp); + tcg_temp_free(tmp); + break; + case OS_LONG: + tmp = tcg_const_i32(read_im32(env, s)); + gen_helper_exts32(cpu_env, fp, tmp); + tcg_temp_free(tmp); + break; + case OS_SINGLE: + tmp = tcg_const_i32(read_im32(env, s)); + gen_helper_extf32(cpu_env, fp, tmp); + tcg_temp_free(tmp); + break; + case OS_DOUBLE: + t64 = tcg_const_i64(read_im64(env, s)); + gen_helper_extf64(cpu_env, fp, t64); + tcg_temp_free_i64(t64); + break; + case OS_EXTENDED: + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + } + tmp = tcg_const_i32(read_im32(env, s) >> 16); + tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); + tcg_temp_free(tmp); + t64 = tcg_const_i64(read_im64(env, s)); + tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); + tcg_temp_free_i64(t64); + break; + case OS_PACKED: + /* unimplemented data type on 68040/ColdFire + * FIXME if needed for another FPU + */ + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); + break; + default: + g_assert_not_reached(); + } + return 0; + default: + return -1; + } + } + return -1; +} + +static int gen_ea_fp(CPUM68KState *env, DisasContext *s, uint16_t insn, + int opsize, TCGv_ptr fp, ea_what what) +{ + int mode = extract32(insn, 3, 3); + int reg0 = REG(insn, 0); + return gen_ea_mode_fp(env, s, mode, reg0, opsize, fp, what); +} + typedef struct { TCGCond tcond; bool g1; @@ -1124,42 +1432,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) -{ - update_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) -{ - update_cc_op(s); - tcg_gen_mov_i32(QREG_PC, dest); - s->is_jmp = DISAS_JUMP; -} - -static void gen_raise_exception(int nr) -{ - TCGv_i32 tmp = tcg_const_i32(nr); - - gen_helper_raise_exception(cpu_env, tmp); - tcg_temp_free_i32(tmp); -} - -static void gen_exception(DisasContext *s, uint32_t where, int nr) -{ - update_cc_op(s); - gen_jmp_im(s, where); - gen_raise_exception(nr); -} - -static inline void gen_addr_fault(DisasContext *s) -{ - gen_exception(s, s->insn_pc, EXCP_ADDRESS); -} - #define SRC_EA(env, result, opsize, op_sign, addrp) do { \ result = gen_ea(env, s, insn, opsize, NULL_QREG, addrp, \ op_sign ? EA_LOADS : EA_LOADU); \ @@ -4133,15 +4405,11 @@ undef: DISAS_INSN(fpu) { uint16_t ext; - int32_t offset; int opmode; - TCGv_i64 src; - TCGv_i64 dest; - TCGv_i64 res; TCGv tmp32; int round; - int set_dest; int opsize; + TCGv_ptr cpu_src, cpu_dest; ext = read_im16(env, s); opmode = ext & 0x7f; @@ -4151,59 +4419,12 @@ DISAS_INSN(fpu) case 1: goto undef; case 3: /* fmove out */ - src = FREG(ext, 7); - tmp32 = tcg_temp_new_i32(); - /* fmove */ - /* ??? TODO: Proper behavior on overflow. */ - + cpu_src = gen_fp_ptr(REG(ext, 7)); opsize = ext_opsize(ext, 10); - switch (opsize) { - case OS_LONG: - gen_helper_f64_to_i32(tmp32, cpu_env, src); - break; - case OS_SINGLE: - gen_helper_f64_to_f32(tmp32, cpu_env, src); - break; - case OS_WORD: - gen_helper_f64_to_i32(tmp32, cpu_env, src); - break; - case OS_DOUBLE: - tcg_gen_mov_i32(tmp32, AREG(insn, 0)); - switch ((insn >> 3) & 7) { - case 2: - case 3: - break; - case 4: - tcg_gen_addi_i32(tmp32, tmp32, -8); - break; - case 5: - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; - tcg_gen_addi_i32(tmp32, tmp32, offset); - break; - default: - goto undef; - } - gen_store64(s, tmp32, src); - switch ((insn >> 3) & 7) { - case 3: - tcg_gen_addi_i32(tmp32, tmp32, 8); - tcg_gen_mov_i32(AREG(insn, 0), tmp32); - break; - case 4: - tcg_gen_mov_i32(AREG(insn, 0), tmp32); - break; - } - tcg_temp_free_i32(tmp32); - return; - case OS_BYTE: - gen_helper_f64_to_i32(tmp32, cpu_env, src); - break; - default: - goto undef; + if (gen_ea_fp(env, s, insn, opsize, cpu_src, EA_STORE) == -1) { + gen_addr_fault(s); } - DEST_EA(env, insn, opsize, tmp32, NULL); - tcg_temp_free_i32(tmp32); + tcg_temp_free_ptr(cpu_src); return; case 4: /* fmove to control register. */ case 5: /* fmove from control register. */ @@ -4213,6 +4434,7 @@ DISAS_INSN(fpu) case 7: { TCGv addr; + TCGv_ptr fp; uint16_t mask; int i; if ((ext & 0x1f00) != 0x1000 || (ext & 0xff) == 0) @@ -4225,136 +4447,86 @@ DISAS_INSN(fpu) addr = tcg_temp_new_i32(); tcg_gen_mov_i32(addr, tmp32); mask = 0x80; + fp = tcg_temp_new_ptr(); for (i = 0; i < 8; i++) { if (ext & mask) { - 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)); - } + tcg_gen_addi_ptr(fp, cpu_env, + offsetof(CPUM68KState, fregs[i])); + gen_ldst_fp(s, OS_DOUBLE, addr, fp, + (ext & (1 << 13)) ? EA_STORE : EA_LOADS); if (ext & (mask - 1)) tcg_gen_addi_i32(addr, addr, 8); } mask >>= 1; } tcg_temp_free_i32(addr); + tcg_temp_free_ptr(fp); } return; } if (ext & (1 << 14)) { /* Source effective address. */ opsize = ext_opsize(ext, 10); - if (opsize == OS_DOUBLE) { - tmp32 = tcg_temp_new_i32(); - tcg_gen_mov_i32(tmp32, AREG(insn, 0)); - switch ((insn >> 3) & 7) { - case 2: - case 3: - break; - case 4: - tcg_gen_addi_i32(tmp32, tmp32, -8); - break; - case 5: - offset = cpu_ldsw_code(env, s->pc); - s->pc += 2; - tcg_gen_addi_i32(tmp32, tmp32, offset); - break; - case 7: - offset = cpu_ldsw_code(env, s->pc); - offset += s->pc - 2; - s->pc += 2; - tcg_gen_addi_i32(tmp32, tmp32, offset); - break; - default: - goto undef; - } - src = gen_load64(s, tmp32); - switch ((insn >> 3) & 7) { - case 3: - tcg_gen_addi_i32(tmp32, tmp32, 8); - tcg_gen_mov_i32(AREG(insn, 0), tmp32); - break; - case 4: - tcg_gen_mov_i32(AREG(insn, 0), tmp32); - break; - } - tcg_temp_free_i32(tmp32); - } else { - SRC_EA(env, 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; - } + cpu_src = gen_fp_result_ptr(); + if (gen_ea_fp(env, s, insn, opsize, cpu_src, EA_LOADS) == -1) { + gen_addr_fault(s); + return; } } else { /* Source register. */ - src = FREG(ext, 10); + opsize = OS_EXTENDED; + cpu_src = gen_fp_ptr(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; + cpu_dest = gen_fp_ptr(REG(ext, 7)); switch (opmode) { case 0: case 0x40: case 0x44: /* fmove */ - tcg_gen_mov_f64(res, src); + gen_fp_move(cpu_dest, cpu_src); break; case 1: /* fint */ - gen_helper_iround_f64(res, cpu_env, src); + gen_helper_firound(cpu_env, cpu_dest, cpu_src); round = 0; break; case 3: /* fintrz */ - gen_helper_itrunc_f64(res, cpu_env, src); + gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); round = 0; break; case 4: case 0x41: case 0x45: /* fsqrt */ - gen_helper_sqrt_f64(res, cpu_env, src); + gen_helper_fsqrt(cpu_env, cpu_dest, cpu_src); break; case 0x18: case 0x58: case 0x5c: /* fabs */ - gen_helper_abs_f64(res, src); + gen_helper_fabs(cpu_env, cpu_dest, cpu_src); break; case 0x1a: case 0x5a: case 0x5e: /* fneg */ - gen_helper_chs_f64(res, src); + gen_helper_fchs(cpu_env, cpu_dest, cpu_src); break; case 0x20: case 0x60: case 0x64: /* fdiv */ - gen_helper_div_f64(res, cpu_env, res, src); + gen_helper_fdiv(cpu_env, cpu_dest, cpu_src, cpu_dest); break; case 0x22: case 0x62: case 0x66: /* fadd */ - gen_helper_add_f64(res, cpu_env, res, src); + gen_helper_fadd(cpu_env, cpu_dest, cpu_src, cpu_dest); break; case 0x23: case 0x63: case 0x67: /* fmul */ - gen_helper_mul_f64(res, cpu_env, res, src); + gen_helper_fmul(cpu_env, cpu_dest, cpu_src, cpu_dest); break; case 0x28: case 0x68: case 0x6c: /* fsub */ - gen_helper_sub_f64(res, cpu_env, res, src); + gen_helper_fsub(cpu_env, cpu_dest, cpu_src, cpu_dest); break; case 0x38: /* fcmp */ - gen_helper_sub_cmp_f64(res, cpu_env, res, src); - set_dest = 0; + tcg_temp_free_ptr(cpu_dest); + cpu_dest = gen_fp_result_ptr(); + gen_helper_fsub_cmp(cpu_env, cpu_dest, cpu_src, cpu_dest); round = 0; break; case 0x3a: /* ftst */ - tcg_gen_mov_f64(res, src); - set_dest = 0; + tcg_temp_free_ptr(cpu_dest); + cpu_dest = gen_fp_result_ptr(); + gen_fp_move(cpu_dest, cpu_src); round = 0; break; default: goto undef; } - if (ext & (1 << 14)) { - tcg_temp_free_i64(src); - } if (round) { if (opmode & 0x40) { if ((opmode & 0x4) != 0) @@ -4364,16 +4536,18 @@ DISAS_INSN(fpu) } } if (round) { - 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); - } - tcg_gen_mov_f64(QREG_FP_RESULT, res); - if (set_dest) { - tcg_gen_mov_f64(dest, res); + TCGv tmp = tcg_temp_new(); + gen_helper_redf32(tmp, cpu_env, cpu_dest); + gen_helper_extf32(cpu_env, cpu_dest, tmp); + tcg_temp_free(tmp); + } else { + TCGv_i64 t64 = tcg_temp_new_i64(); + gen_helper_redf64(t64, cpu_env, cpu_dest); + gen_helper_extf64(cpu_env, cpu_dest, t64); + tcg_temp_free_i64(t64); } - tcg_temp_free_i64(res); + tcg_temp_free_ptr(cpu_src); + tcg_temp_free_ptr(cpu_dest); return; undef: /* FIXME: Is this right for offset addressing modes? */ @@ -4387,6 +4561,7 @@ DISAS_INSN(fbcc) uint32_t addr; TCGv flag; TCGLabel *l1; + TCGv_ptr fp_result; addr = s->pc; offset = cpu_ldsw_code(env, s->pc); @@ -4398,7 +4573,9 @@ DISAS_INSN(fbcc) l1 = gen_new_label(); /* TODO: Raise BSUN exception. */ flag = tcg_temp_new(); - gen_helper_compare_f64(flag, cpu_env, QREG_FP_RESULT); + fp_result = gen_fp_result_ptr(); + gen_helper_fcompare(flag, cpu_env, fp_result); + tcg_temp_free_ptr(fp_result); /* Jump to l1 if condition is true. */ switch (insn & 0xf) { case 0: /* f */ @@ -5028,11 +5205,15 @@ void register_m68k_insns (CPUM68KState *env) INSN(bfop_reg, eec0, fff8, BITFIELD); /* bfset */ INSN(bfop_mem, e8c0, ffc0, BITFIELD); /* bftst */ INSN(bfop_reg, e8c0, fff8, BITFIELD); /* bftst */ - INSN(undef_fpu, f000, f000, CF_ISA_A); + BASE(undef_fpu, f000, f000); 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(fsave, f300, ffc0, CF_FPU); + INSN(fpu, f200, ffc0, FPU); + INSN(fbcc, f280, ff80, FPU); + INSN(frestore, f340, ffc0, FPU); + INSN(fsave, f300, ffc0, FPU); INSN(intouch, f340, ffc0, CF_ISA_A); INSN(cpushl, f428, ff38, CF_ISA_A); INSN(wddata, fb00, ff00, CF_ISA_A); @@ -5158,6 +5339,18 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) tb->icount = num_insns; } +static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) +{ + floatx80 a = { .high = high, .low = low }; + union { + float64 f64; + double d; + } u; + + u.f64 = floatx80_to_float64(a, &env->fp_status); + return u.d; +} + void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, int flags) { @@ -5165,20 +5358,19 @@ 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", + for (i = 0; i < 8; i++) { + cpu_fprintf(f, "D%d = %08x A%d = %08x " + "F%d = %04x %016"PRIx64" (%12g)\n", i, env->dregs[i], i, env->aregs[i], - i, u.l.upper, u.l.lower, *(double *)&u.d); - } + i, env->fregs[i].l.upper, env->fregs[i].l.lower, + floatx80_to_double(env, env->fregs[i].l.upper, + env->fregs[i].l.lower)); + } cpu_fprintf (f, "PC = %08x ", env->pc); sr = env->sr | cpu_m68k_get_ccr(env); cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? '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(CPUM68KState *env, TranslationBlock *tb, -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally Laurent Vivier @ 2017-06-20 22:50 ` Richard Henderson 2017-06-21 16:18 ` Philippe Mathieu-Daudé 1 sibling, 0 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:50 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > Coldfire uses float64, but 680x0 use floatx80. > This patch introduces the use of floatx80 internally > and enables 680x0 80bits FPU. > > Signed-off-by: Laurent Vivier<laurent@vivier.eu> > --- Reviewed-by: Richard Henderson <rth@twiddle.net> r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally Laurent Vivier 2017-06-20 22:50 ` Richard Henderson @ 2017-06-21 16:18 ` Philippe Mathieu-Daudé 2017-06-21 16:37 ` Richard Henderson 1 sibling, 1 reply; 22+ messages in thread From: Philippe Mathieu-Daudé @ 2017-06-21 16:18 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Richard Henderson Hi Laurent, On 06/20/2017 05:51 PM, Laurent Vivier wrote: > Coldfire uses float64, but 680x0 use floatx80. > This patch introduces the use of floatx80 internally > and enables 680x0 80bits FPU. > > Signed-off-by: Laurent Vivier <laurent@vivier.eu> > --- > target/m68k/cpu.c | 4 +- > target/m68k/cpu.h | 15 +- > target/m68k/fpu_helper.c | 85 ++++--- > target/m68k/helper.c | 12 +- > target/m68k/helper.h | 37 +-- > target/m68k/qregs.def | 1 - > target/m68k/translate.c | 646 ++++++++++++++++++++++++++++++----------------- > 7 files changed, 509 insertions(+), 291 deletions(-) > > diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c > index f2e031f..435456f 100644 > --- a/target/m68k/cpu.c > +++ b/target/m68k/cpu.c > @@ -49,7 +49,7 @@ static void m68k_cpu_reset(CPUState *s) > M68kCPU *cpu = M68K_CPU(s); > M68kCPUClass *mcc = M68K_CPU_GET_CLASS(cpu); > CPUM68KState *env = &cpu->env; > - float64 nan = float64_default_nan(NULL); > + floatx80 nan = floatx80_default_nan(NULL); > int i; > > mcc->parent_reset(s); > @@ -60,7 +60,7 @@ static void m68k_cpu_reset(CPUState *s) > #endif > m68k_switch_sp(env); > for (i = 0; i < 8; i++) { > - env->fregs[i] = nan; > + env->fregs[i].d = nan; > } > env->fpcr = 0; > env->fpsr = 0; > diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h > index 384ec5d..beb8ebc 100644 > --- a/target/m68k/cpu.h > +++ b/target/m68k/cpu.h > @@ -55,8 +55,15 @@ > #define EXCP_UNINITIALIZED 15 > #define EXCP_TRAP0 32 /* User trap #0. */ > #define EXCP_TRAP15 47 /* User trap #15. */ > +#define EXCP_FP_BSUN 48 /* Branch Set on Unordered */ > +#define EXCP_FP_INEX 49 /* Inexact result */ > +#define EXCP_FP_DZ 50 /* Divide by Zero */ > +#define EXCP_FP_UNFL 51 /* Underflow */ > +#define EXCP_FP_OPERR 52 /* Operand Error */ > +#define EXCP_FP_OVFL 53 /* Overflow */ > +#define EXCP_FP_SNAN 54 /* Signaling Not-A-Number */ > +#define EXCP_FP_UNIMP 55 /* Unimplemented Data type */ > #define EXCP_UNSUPPORTED 61 > -#define EXCP_ICE 13 > > #define EXCP_RTE 0x100 > #define EXCP_HALT_INSN 0x101 > @@ -64,6 +71,8 @@ > #define NB_MMU_MODES 2 > #define TARGET_INSN_START_EXTRA_WORDS 1 > > +typedef CPU_LDoubleU FPReg; What an awful name... Anyway checking on "qemu/bswap.h" it seems there is some endianess issue with it if your host is little-endian. Do you have a way to run Berkeley TestFloat? I think due to license restrictions we may still be able to add release 2a in the qtest suite. (see the thread "Future of SoftFloat use in QEMU" at https://lists.gnu.org/archive/html/qemu-devel/2017-05/msg01730.html) We could then run some float80 unit-test either endians. > + > typedef struct CPUM68KState { > uint32_t dregs[8]; > uint32_t aregs[8]; > @@ -82,8 +91,8 @@ typedef struct CPUM68KState { > uint32_t cc_c; /* either 0/1, unused, or computed from cc_n and cc_v */ > uint32_t cc_z; /* == 0 or unused */ > > - float64 fregs[8]; > - float64 fp_result; > + FPReg fregs[8]; > + FPReg fp_result; > uint32_t fpcr; > uint32_t fpsr; > float_status fp_status; > diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c > index 5bf2576..f4d3821 100644 > --- a/target/m68k/fpu_helper.c > +++ b/target/m68k/fpu_helper.c > @@ -21,92 +21,101 @@ > #include "qemu/osdep.h" > #include "cpu.h" > #include "exec/helper-proto.h" > +#include "exec/exec-all.h" > > -uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val) > +int32_t HELPER(reds32)(CPUM68KState *env, FPReg *val) > { > - return float64_to_int32(val, &env->fp_status); > + return floatx80_to_int32(val->d, &env->fp_status); > } > > -float32 HELPER(f64_to_f32)(CPUM68KState *env, float64 val) > +float32 HELPER(redf32)(CPUM68KState *env, FPReg *val) > { > - return float64_to_float32(val, &env->fp_status); > + return floatx80_to_float32(val->d, &env->fp_status); > } > > -float64 HELPER(i32_to_f64)(CPUM68KState *env, uint32_t val) > +void HELPER(exts32)(CPUM68KState *env, FPReg *res, int32_t val) > { > - return int32_to_float64(val, &env->fp_status); > + res->d = int32_to_floatx80(val, &env->fp_status); > } > > -float64 HELPER(f32_to_f64)(CPUM68KState *env, float32 val) > +void HELPER(extf32)(CPUM68KState *env, FPReg *res, float32 val) > { > - return float32_to_float64(val, &env->fp_status); > + res->d = float32_to_floatx80(val, &env->fp_status); > } > > -float64 HELPER(iround_f64)(CPUM68KState *env, float64 val) > +void HELPER(extf64)(CPUM68KState *env, FPReg *res, float64 val) > { > - return float64_round_to_int(val, &env->fp_status); > + res->d = float64_to_floatx80(val, &env->fp_status); > } > > -float64 HELPER(itrunc_f64)(CPUM68KState *env, float64 val) > +float64 HELPER(redf64)(CPUM68KState *env, FPReg *val) > { > - return float64_trunc_to_int(val, &env->fp_status); > + return floatx80_to_float64(val->d, &env->fp_status); > } > > -float64 HELPER(sqrt_f64)(CPUM68KState *env, float64 val) > +void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val) > { > - return float64_sqrt(val, &env->fp_status); > + res->d = floatx80_round_to_int(val->d, &env->fp_status); > } > > -float64 HELPER(abs_f64)(float64 val) > +void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val) > { > - return float64_abs(val); > + res->d = floatx80_round_to_int(val->d, &env->fp_status); > } > > -float64 HELPER(chs_f64)(float64 val) > +void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) > { > - return float64_chs(val); > + res->d = floatx80_sqrt(val->d, &env->fp_status); > } > > -float64 HELPER(add_f64)(CPUM68KState *env, float64 a, float64 b) > +void HELPER(fabs)(CPUM68KState *env, FPReg *res, FPReg *val) > { > - return float64_add(a, b, &env->fp_status); > + res->d = floatx80_abs(val->d); > } > > -float64 HELPER(sub_f64)(CPUM68KState *env, float64 a, float64 b) > +void HELPER(fchs)(CPUM68KState *env, FPReg *res, FPReg *val) > { > - return float64_sub(a, b, &env->fp_status); > + res->d = floatx80_chs(val->d); > } > > -float64 HELPER(mul_f64)(CPUM68KState *env, float64 a, float64 b) > +void HELPER(fadd)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) > { > - return float64_mul(a, b, &env->fp_status); > + res->d = floatx80_add(val0->d, val1->d, &env->fp_status); > } > > -float64 HELPER(div_f64)(CPUM68KState *env, float64 a, float64 b) > +void HELPER(fsub)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) > { > - return float64_div(a, b, &env->fp_status); > + res->d = floatx80_sub(val1->d, val0->d, &env->fp_status); > } > > -float64 HELPER(sub_cmp_f64)(CPUM68KState *env, float64 a, float64 b) > +void HELPER(fmul)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) > +{ > + res->d = floatx80_mul(val0->d, val1->d, &env->fp_status); > +} > + > +void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) > +{ > + res->d = floatx80_div(val1->d, val0->d, &env->fp_status); > +} > + > +void HELPER(fsub_cmp)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) > { > /* ??? 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, &env->fp_status)) { > + res->d = floatx80_sub(val0->d, val1->d, &env->fp_status); > + if (floatx80_is_quiet_nan(res->d, &env->fp_status)) { > /* +/-inf compares equal against itself, but sub returns nan. */ > - if (!float64_is_quiet_nan(a, &env->fp_status) > - && !float64_is_quiet_nan(b, &env->fp_status)) { > - res = float64_zero; > - if (float64_lt_quiet(a, res, &env->fp_status)) { > - res = float64_chs(res); > + if (!floatx80_is_quiet_nan(val0->d, &env->fp_status) > + && !floatx80_is_quiet_nan(val1->d, &env->fp_status)) { > + res->d = floatx80_zero; > + if (floatx80_lt_quiet(val0->d, res->d, &env->fp_status)) { > + res->d = floatx80_chs(res->d); > } > } > } > - return res; > } > > -uint32_t HELPER(compare_f64)(CPUM68KState *env, float64 val) > +uint32_t HELPER(fcompare)(CPUM68KState *env, FPReg *val) > { > - return float64_compare_quiet(val, float64_zero, &env->fp_status); > + return floatx80_compare_quiet(val->d, floatx80_zero, &env->fp_status); > } > diff --git a/target/m68k/helper.c b/target/m68k/helper.c > index 5ca9911..8bfc881 100644 > --- a/target/m68k/helper.c > +++ b/target/m68k/helper.c > @@ -73,10 +73,11 @@ 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]); > + float_status s; > + stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); > return 8; > } > if (n < 11) { > @@ -87,10 +88,11 @@ 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); > + float_status s; > + env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s); > return 8; > } > if (n < 11) { > @@ -126,7 +128,7 @@ 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); > } > /* TODO: Add [E]MAC registers. */ > diff --git a/target/m68k/helper.h b/target/m68k/helper.h > index d7a4bf1..d871be6 100644 > --- a/target/m68k/helper.h > +++ b/target/m68k/helper.h > @@ -12,21 +12,28 @@ DEF_HELPER_3(movec, void, env, i32, i32) > DEF_HELPER_4(cas2w, void, env, i32, i32, i32) > DEF_HELPER_4(cas2l, void, env, i32, i32, i32) > > -DEF_HELPER_2(f64_to_i32, f32, 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) > +#define dh_alias_fp ptr > +#define dh_ctype_fp FPReg * > +#define dh_is_signed_fp dh_is_signed_ptr > + > +DEF_HELPER_3(exts32, void, env, fp, s32) > +DEF_HELPER_3(extf32, void, env, fp, f32) > +DEF_HELPER_3(extf64, void, env, fp, f64) > +DEF_HELPER_2(redf32, f32, env, fp) > +DEF_HELPER_2(redf64, f64, env, fp) > +DEF_HELPER_2(reds32, s32, env, fp) > + > +DEF_HELPER_3(firound, void, env, fp, fp) > +DEF_HELPER_3(fitrunc, void, env, fp, fp) > +DEF_HELPER_3(fsqrt, void, env, fp, fp) > +DEF_HELPER_3(fabs, void, env, fp, fp) > +DEF_HELPER_3(fchs, void, env, fp, fp) > +DEF_HELPER_4(fadd, void, env, fp, fp, fp) > +DEF_HELPER_4(fsub, void, env, fp, fp, fp) > +DEF_HELPER_4(fmul, void, env, fp, fp, fp) > +DEF_HELPER_4(fdiv, void, env, fp, fp, fp) > +DEF_HELPER_4(fsub_cmp, void, env, fp, fp, fp) > +DEF_HELPER_2(fcompare, i32, env, fp) > > 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 51ff43b..1aadc62 100644 > --- a/target/m68k/qregs.def > +++ b/target/m68k/qregs.def > @@ -1,4 +1,3 @@ > -DEFF64(FP_RESULT, fp_result) > DEFO32(PC, pc) > DEFO32(SR, sr) > DEFO32(CC_OP, cc_op) > diff --git a/target/m68k/translate.c b/target/m68k/translate.c > index c9a5fe4..73f691f 100644 > --- a/target/m68k/translate.c > +++ b/target/m68k/translate.c > @@ -32,37 +32,27 @@ > #include "trace-tcg.h" > #include "exec/log.h" > > - > //#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; > #include "qregs.def" > #undef DEFO32 > #undef DEFO64 > -#undef DEFF64 > > static TCGv_i32 cpu_halted; > static TCGv_i32 cpu_exception_index; > > static TCGv_env 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]; > > #define REG(insn, pos) (((insn) >> (pos)) & 7) > #define DREG(insn, pos) cpu_dregs[REG(insn, pos)] > #define AREG(insn, pos) get_areg(s, REG(insn, pos)) > -#define FREG(insn, pos) cpu_fregs[REG(insn, pos)] > #define MACREG(acc) cpu_macc[acc] > #define QREG_SP get_areg(s, 7) > > @@ -87,11 +77,9 @@ void m68k_tcg_init(void) > #define DEFO64(name, offset) \ > QREG_##name = tcg_global_mem_new_i64(cpu_env, \ > offsetof(CPUM68KState, offset), #name); > -#define DEFF64(name, offset) DEFO64(name, offset) > #include "qregs.def" > #undef DEFO32 > #undef DEFO64 > -#undef DEFF64 > > cpu_halted = tcg_global_mem_new_i32(cpu_env, > -offsetof(M68kCPU, env) + > @@ -111,10 +99,6 @@ void m68k_tcg_init(void) > cpu_aregs[i] = tcg_global_mem_new(cpu_env, > offsetof(CPUM68KState, aregs[i]), p); > p += 3; > - sprintf(p, "F%d", i); > - cpu_fregs[i] = tcg_global_mem_new_i64(cpu_env, > - offsetof(CPUM68KState, fregs[i]), p); > - p += 3; > } > for (i = 0; i < 4; i++) { > sprintf(p, "ACC%d", i); > @@ -265,6 +249,42 @@ static void update_cc_op(DisasContext *s) > } > } > > +/* Generate a jump to an immediate address. */ > +static void gen_jmp_im(DisasContext *s, uint32_t dest) > +{ > + update_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) > +{ > + update_cc_op(s); > + tcg_gen_mov_i32(QREG_PC, dest); > + s->is_jmp = DISAS_JUMP; > +} > + > +static void gen_raise_exception(int nr) > +{ > + TCGv_i32 tmp = tcg_const_i32(nr); > + > + gen_helper_raise_exception(cpu_env, tmp); > + tcg_temp_free_i32(tmp); > +} > + > +static void gen_exception(DisasContext *s, uint32_t where, int nr) > +{ > + update_cc_op(s); > + gen_jmp_im(s, where); > + gen_raise_exception(nr); > +} > + > +static inline void gen_addr_fault(DisasContext *s) > +{ > + gen_exception(s, s->insn_pc, EXCP_ADDRESS); > +} > + > /* 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) > @@ -286,7 +306,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: > @@ -296,16 +315,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); > - 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) > { > @@ -318,7 +327,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: > @@ -327,13 +335,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); > - tcg_gen_qemu_stf64(val, addr, index); > - gen_throws_exception = gen_last_qop; > -} > - > typedef enum { > EA_STORE, > EA_LOADU, > @@ -377,6 +378,15 @@ static inline uint32_t read_im32(CPUM68KState *env, DisasContext *s) > return im; > } > > +/* Read a 64-bit immediate constant. */ > +static inline uint64_t read_im64(CPUM68KState *env, DisasContext *s) > +{ > + uint64_t im; > + im = (uint64_t)read_im32(env, s) << 32; > + im |= (uint64_t)read_im32(env, s); > + return im; > +} > + > /* Calculate and address index. */ > static TCGv gen_addr_index(DisasContext *s, uint16_t ext, TCGv tmp) > { > @@ -909,6 +919,304 @@ static TCGv gen_ea(CPUM68KState *env, DisasContext *s, uint16_t insn, > return gen_ea_mode(env, s, mode, reg0, opsize, val, addrp, what); > } > > +static TCGv_ptr gen_fp_ptr(int freg) > +{ > + TCGv_ptr fp = tcg_temp_new_ptr(); > + tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fregs[freg])); > + return fp; > +} > + > +static TCGv_ptr gen_fp_result_ptr(void) > +{ > + TCGv_ptr fp = tcg_temp_new_ptr(); > + tcg_gen_addi_ptr(fp, cpu_env, offsetof(CPUM68KState, fp_result)); > + return fp; > +} > + > +static void gen_fp_move(TCGv_ptr dest, TCGv_ptr src) > +{ > + TCGv t32; > + TCGv_i64 t64; > + > + t32 = tcg_temp_new(); > + tcg_gen_ld16u_i32(t32, src, offsetof(FPReg, l.upper)); > + tcg_gen_st16_i32(t32, dest, offsetof(FPReg, l.upper)); > + tcg_temp_free(t32); > + > + t64 = tcg_temp_new_i64(); > + tcg_gen_ld_i64(t64, src, offsetof(FPReg, l.lower)); > + tcg_gen_st_i64(t64, dest, offsetof(FPReg, l.lower)); > + tcg_temp_free_i64(t64); > +} > + > +static void gen_load_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp) > +{ > + TCGv tmp; > + TCGv_i64 t64; > + int index = IS_USER(s); > + > + t64 = tcg_temp_new_i64(); > + tmp = tcg_temp_new(); > + switch (opsize) { > + case OS_BYTE: > + tcg_gen_qemu_ld8s(tmp, addr, index); > + gen_helper_exts32(cpu_env, fp, tmp); > + break; > + case OS_WORD: > + tcg_gen_qemu_ld16s(tmp, addr, index); > + gen_helper_exts32(cpu_env, fp, tmp); > + break; > + case OS_LONG: > + tcg_gen_qemu_ld32u(tmp, addr, index); > + gen_helper_exts32(cpu_env, fp, tmp); > + break; > + case OS_SINGLE: > + tcg_gen_qemu_ld32u(tmp, addr, index); > + gen_helper_extf32(cpu_env, fp, tmp); > + break; > + case OS_DOUBLE: > + tcg_gen_qemu_ld64(t64, addr, index); > + gen_helper_extf64(cpu_env, fp, t64); > + tcg_temp_free_i64(t64); > + break; > + case OS_EXTENDED: > + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + } > + tcg_gen_qemu_ld32u(tmp, addr, index); > + tcg_gen_shri_i32(tmp, tmp, 16); > + tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); > + tcg_gen_addi_i32(tmp, addr, 4); > + tcg_gen_qemu_ld64(t64, tmp, index); > + tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); > + break; > + case OS_PACKED: > + /* unimplemented data type on 68040/ColdFire > + * FIXME if needed for another FPU > + */ > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + default: > + g_assert_not_reached(); > + } > + tcg_temp_free(tmp); > + tcg_temp_free_i64(t64); > + gen_throws_exception = gen_last_qop; > +} > + > +static void gen_store_fp(DisasContext *s, int opsize, TCGv addr, TCGv_ptr fp) > +{ > + TCGv tmp; > + TCGv_i64 t64; > + int index = IS_USER(s); > + > + t64 = tcg_temp_new_i64(); > + tmp = tcg_temp_new(); > + switch (opsize) { > + case OS_BYTE: > + gen_helper_reds32(tmp, cpu_env, fp); > + tcg_gen_qemu_st8(tmp, addr, index); > + break; > + case OS_WORD: > + gen_helper_reds32(tmp, cpu_env, fp); > + tcg_gen_qemu_st16(tmp, addr, index); > + break; > + case OS_LONG: > + gen_helper_reds32(tmp, cpu_env, fp); > + tcg_gen_qemu_st32(tmp, addr, index); > + break; > + case OS_SINGLE: > + gen_helper_redf32(tmp, cpu_env, fp); > + tcg_gen_qemu_st32(tmp, addr, index); > + break; > + case OS_DOUBLE: > + gen_helper_redf64(t64, cpu_env, fp); > + tcg_gen_qemu_st64(t64, addr, index); > + break; > + case OS_EXTENDED: > + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + } > + tcg_gen_ld16u_i32(tmp, fp, offsetof(FPReg, l.upper)); > + tcg_gen_shli_i32(tmp, tmp, 16); > + tcg_gen_qemu_st32(tmp, addr, index); > + tcg_gen_addi_i32(tmp, addr, 4); > + tcg_gen_ld_i64(t64, fp, offsetof(FPReg, l.lower)); > + tcg_gen_qemu_st64(t64, tmp, index); > + break; > + case OS_PACKED: > + /* unimplemented data type on 68040/ColdFire > + * FIXME if needed for another FPU > + */ > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + default: > + g_assert_not_reached(); > + } > + tcg_temp_free(tmp); > + tcg_temp_free_i64(t64); > + gen_throws_exception = gen_last_qop; > +} > + > +static void gen_ldst_fp(DisasContext *s, int opsize, TCGv addr, > + TCGv_ptr fp, ea_what what) > +{ > + if (what == EA_STORE) { > + gen_store_fp(s, opsize, addr, fp); > + } else { > + gen_load_fp(s, opsize, addr, fp); > + } > +} > + > +static int gen_ea_mode_fp(CPUM68KState *env, DisasContext *s, int mode, > + int reg0, int opsize, TCGv_ptr fp, ea_what what) > +{ > + TCGv reg, addr, tmp; > + TCGv_i64 t64; > + > + switch (mode) { > + case 0: /* Data register direct. */ > + reg = cpu_dregs[reg0]; > + if (what == EA_STORE) { > + switch (opsize) { > + case OS_BYTE: > + case OS_WORD: > + case OS_LONG: > + gen_helper_reds32(reg, cpu_env, fp); > + break; > + case OS_SINGLE: > + gen_helper_redf32(reg, cpu_env, fp); > + break; > + default: > + g_assert_not_reached(); > + } > + } else { > + tmp = tcg_temp_new(); > + switch (opsize) { > + case OS_BYTE: > + tcg_gen_ext8s_i32(tmp, reg); > + gen_helper_exts32(cpu_env, fp, tmp); > + break; > + case OS_WORD: > + tcg_gen_ext16s_i32(tmp, reg); > + gen_helper_exts32(cpu_env, fp, tmp); > + break; > + case OS_LONG: > + gen_helper_exts32(cpu_env, fp, reg); > + break; > + case OS_SINGLE: > + gen_helper_extf32(cpu_env, fp, reg); > + break; > + default: > + g_assert_not_reached(); > + } > + tcg_temp_free(tmp); > + } > + return 0; > + case 1: /* Address register direct. */ > + return -1; > + case 2: /* Indirect register */ > + addr = get_areg(s, reg0); > + gen_ldst_fp(s, opsize, addr, fp, what); > + return 0; > + case 3: /* Indirect postincrement. */ > + addr = cpu_aregs[reg0]; > + gen_ldst_fp(s, opsize, addr, fp, what); > + tcg_gen_addi_i32(addr, addr, opsize_bytes(opsize)); > + return 0; > + case 4: /* Indirect predecrememnt. */ > + addr = gen_lea_mode(env, s, mode, reg0, opsize); > + if (IS_NULL_QREG(addr)) { > + return -1; > + } > + gen_ldst_fp(s, opsize, addr, fp, what); > + tcg_gen_mov_i32(cpu_aregs[reg0], addr); > + return 0; > + case 5: /* Indirect displacement. */ > + case 6: /* Indirect index + displacement. */ > + do_indirect: > + addr = gen_lea_mode(env, s, mode, reg0, opsize); > + if (IS_NULL_QREG(addr)) { > + return -1; > + } > + gen_ldst_fp(s, opsize, addr, fp, what); > + return 0; > + case 7: /* Other */ > + switch (reg0) { > + case 0: /* Absolute short. */ > + case 1: /* Absolute long. */ > + case 2: /* pc displacement */ > + case 3: /* pc index+displacement. */ > + goto do_indirect; > + case 4: /* Immediate. */ > + if (what == EA_STORE) { > + return -1; > + } > + switch (opsize) { > + case OS_BYTE: > + tmp = tcg_const_i32((int8_t)read_im8(env, s)); > + gen_helper_exts32(cpu_env, fp, tmp); > + tcg_temp_free(tmp); > + break; > + case OS_WORD: > + tmp = tcg_const_i32((int16_t)read_im16(env, s)); > + gen_helper_exts32(cpu_env, fp, tmp); > + tcg_temp_free(tmp); > + break; > + case OS_LONG: > + tmp = tcg_const_i32(read_im32(env, s)); > + gen_helper_exts32(cpu_env, fp, tmp); > + tcg_temp_free(tmp); > + break; > + case OS_SINGLE: > + tmp = tcg_const_i32(read_im32(env, s)); > + gen_helper_extf32(cpu_env, fp, tmp); > + tcg_temp_free(tmp); > + break; > + case OS_DOUBLE: > + t64 = tcg_const_i64(read_im64(env, s)); > + gen_helper_extf64(cpu_env, fp, t64); > + tcg_temp_free_i64(t64); > + break; > + case OS_EXTENDED: > + if (m68k_feature(s->env, M68K_FEATURE_CF_FPU)) { > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + } > + tmp = tcg_const_i32(read_im32(env, s) >> 16); > + tcg_gen_st16_i32(tmp, fp, offsetof(FPReg, l.upper)); > + tcg_temp_free(tmp); > + t64 = tcg_const_i64(read_im64(env, s)); > + tcg_gen_st_i64(t64, fp, offsetof(FPReg, l.lower)); > + tcg_temp_free_i64(t64); > + break; > + case OS_PACKED: > + /* unimplemented data type on 68040/ColdFire > + * FIXME if needed for another FPU > + */ > + gen_exception(s, s->insn_pc, EXCP_FP_UNIMP); > + break; > + default: > + g_assert_not_reached(); > + } > + return 0; > + default: > + return -1; > + } > + } > + return -1; > +} > + > +static int gen_ea_fp(CPUM68KState *env, DisasContext *s, uint16_t insn, > + int opsize, TCGv_ptr fp, ea_what what) > +{ > + int mode = extract32(insn, 3, 3); > + int reg0 = REG(insn, 0); > + return gen_ea_mode_fp(env, s, mode, reg0, opsize, fp, what); > +} > + > typedef struct { > TCGCond tcond; > bool g1; > @@ -1124,42 +1432,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) > -{ > - update_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) > -{ > - update_cc_op(s); > - tcg_gen_mov_i32(QREG_PC, dest); > - s->is_jmp = DISAS_JUMP; > -} > - > -static void gen_raise_exception(int nr) > -{ > - TCGv_i32 tmp = tcg_const_i32(nr); > - > - gen_helper_raise_exception(cpu_env, tmp); > - tcg_temp_free_i32(tmp); > -} > - > -static void gen_exception(DisasContext *s, uint32_t where, int nr) > -{ > - update_cc_op(s); > - gen_jmp_im(s, where); > - gen_raise_exception(nr); > -} > - > -static inline void gen_addr_fault(DisasContext *s) > -{ > - gen_exception(s, s->insn_pc, EXCP_ADDRESS); > -} > - > #define SRC_EA(env, result, opsize, op_sign, addrp) do { \ > result = gen_ea(env, s, insn, opsize, NULL_QREG, addrp, \ > op_sign ? EA_LOADS : EA_LOADU); \ > @@ -4133,15 +4405,11 @@ undef: > DISAS_INSN(fpu) > { > uint16_t ext; > - int32_t offset; > int opmode; > - TCGv_i64 src; > - TCGv_i64 dest; > - TCGv_i64 res; > TCGv tmp32; > int round; > - int set_dest; > int opsize; > + TCGv_ptr cpu_src, cpu_dest; > > ext = read_im16(env, s); > opmode = ext & 0x7f; > @@ -4151,59 +4419,12 @@ DISAS_INSN(fpu) > case 1: > goto undef; > case 3: /* fmove out */ > - src = FREG(ext, 7); > - tmp32 = tcg_temp_new_i32(); > - /* fmove */ > - /* ??? TODO: Proper behavior on overflow. */ > - > + cpu_src = gen_fp_ptr(REG(ext, 7)); > opsize = ext_opsize(ext, 10); > - switch (opsize) { > - case OS_LONG: > - gen_helper_f64_to_i32(tmp32, cpu_env, src); > - break; > - case OS_SINGLE: > - gen_helper_f64_to_f32(tmp32, cpu_env, src); > - break; > - case OS_WORD: > - gen_helper_f64_to_i32(tmp32, cpu_env, src); > - break; > - case OS_DOUBLE: > - tcg_gen_mov_i32(tmp32, AREG(insn, 0)); > - switch ((insn >> 3) & 7) { > - case 2: > - case 3: > - break; > - case 4: > - tcg_gen_addi_i32(tmp32, tmp32, -8); > - break; > - case 5: > - offset = cpu_ldsw_code(env, s->pc); > - s->pc += 2; > - tcg_gen_addi_i32(tmp32, tmp32, offset); > - break; > - default: > - goto undef; > - } > - gen_store64(s, tmp32, src); > - switch ((insn >> 3) & 7) { > - case 3: > - tcg_gen_addi_i32(tmp32, tmp32, 8); > - tcg_gen_mov_i32(AREG(insn, 0), tmp32); > - break; > - case 4: > - tcg_gen_mov_i32(AREG(insn, 0), tmp32); > - break; > - } > - tcg_temp_free_i32(tmp32); > - return; > - case OS_BYTE: > - gen_helper_f64_to_i32(tmp32, cpu_env, src); > - break; > - default: > - goto undef; > + if (gen_ea_fp(env, s, insn, opsize, cpu_src, EA_STORE) == -1) { > + gen_addr_fault(s); > } > - DEST_EA(env, insn, opsize, tmp32, NULL); > - tcg_temp_free_i32(tmp32); > + tcg_temp_free_ptr(cpu_src); > return; > case 4: /* fmove to control register. */ > case 5: /* fmove from control register. */ > @@ -4213,6 +4434,7 @@ DISAS_INSN(fpu) > case 7: > { > TCGv addr; > + TCGv_ptr fp; > uint16_t mask; > int i; > if ((ext & 0x1f00) != 0x1000 || (ext & 0xff) == 0) > @@ -4225,136 +4447,86 @@ DISAS_INSN(fpu) > addr = tcg_temp_new_i32(); > tcg_gen_mov_i32(addr, tmp32); > mask = 0x80; > + fp = tcg_temp_new_ptr(); > for (i = 0; i < 8; i++) { > if (ext & mask) { > - 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)); > - } > + tcg_gen_addi_ptr(fp, cpu_env, > + offsetof(CPUM68KState, fregs[i])); > + gen_ldst_fp(s, OS_DOUBLE, addr, fp, > + (ext & (1 << 13)) ? EA_STORE : EA_LOADS); > if (ext & (mask - 1)) > tcg_gen_addi_i32(addr, addr, 8); > } > mask >>= 1; > } > tcg_temp_free_i32(addr); > + tcg_temp_free_ptr(fp); > } > return; > } > if (ext & (1 << 14)) { > /* Source effective address. */ > opsize = ext_opsize(ext, 10); > - if (opsize == OS_DOUBLE) { > - tmp32 = tcg_temp_new_i32(); > - tcg_gen_mov_i32(tmp32, AREG(insn, 0)); > - switch ((insn >> 3) & 7) { > - case 2: > - case 3: > - break; > - case 4: > - tcg_gen_addi_i32(tmp32, tmp32, -8); > - break; > - case 5: > - offset = cpu_ldsw_code(env, s->pc); > - s->pc += 2; > - tcg_gen_addi_i32(tmp32, tmp32, offset); > - break; > - case 7: > - offset = cpu_ldsw_code(env, s->pc); > - offset += s->pc - 2; > - s->pc += 2; > - tcg_gen_addi_i32(tmp32, tmp32, offset); > - break; > - default: > - goto undef; > - } > - src = gen_load64(s, tmp32); > - switch ((insn >> 3) & 7) { > - case 3: > - tcg_gen_addi_i32(tmp32, tmp32, 8); > - tcg_gen_mov_i32(AREG(insn, 0), tmp32); > - break; > - case 4: > - tcg_gen_mov_i32(AREG(insn, 0), tmp32); > - break; > - } > - tcg_temp_free_i32(tmp32); > - } else { > - SRC_EA(env, 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; > - } > + cpu_src = gen_fp_result_ptr(); > + if (gen_ea_fp(env, s, insn, opsize, cpu_src, EA_LOADS) == -1) { > + gen_addr_fault(s); > + return; > } > } else { > /* Source register. */ > - src = FREG(ext, 10); > + opsize = OS_EXTENDED; > + cpu_src = gen_fp_ptr(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; > + cpu_dest = gen_fp_ptr(REG(ext, 7)); > switch (opmode) { > case 0: case 0x40: case 0x44: /* fmove */ > - tcg_gen_mov_f64(res, src); > + gen_fp_move(cpu_dest, cpu_src); > break; > case 1: /* fint */ > - gen_helper_iround_f64(res, cpu_env, src); > + gen_helper_firound(cpu_env, cpu_dest, cpu_src); > round = 0; > break; > case 3: /* fintrz */ > - gen_helper_itrunc_f64(res, cpu_env, src); > + gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); > round = 0; > break; > case 4: case 0x41: case 0x45: /* fsqrt */ > - gen_helper_sqrt_f64(res, cpu_env, src); > + gen_helper_fsqrt(cpu_env, cpu_dest, cpu_src); > break; > case 0x18: case 0x58: case 0x5c: /* fabs */ > - gen_helper_abs_f64(res, src); > + gen_helper_fabs(cpu_env, cpu_dest, cpu_src); > break; > case 0x1a: case 0x5a: case 0x5e: /* fneg */ > - gen_helper_chs_f64(res, src); > + gen_helper_fchs(cpu_env, cpu_dest, cpu_src); > break; > case 0x20: case 0x60: case 0x64: /* fdiv */ > - gen_helper_div_f64(res, cpu_env, res, src); > + gen_helper_fdiv(cpu_env, cpu_dest, cpu_src, cpu_dest); > break; > case 0x22: case 0x62: case 0x66: /* fadd */ > - gen_helper_add_f64(res, cpu_env, res, src); > + gen_helper_fadd(cpu_env, cpu_dest, cpu_src, cpu_dest); > break; > case 0x23: case 0x63: case 0x67: /* fmul */ > - gen_helper_mul_f64(res, cpu_env, res, src); > + gen_helper_fmul(cpu_env, cpu_dest, cpu_src, cpu_dest); > break; > case 0x28: case 0x68: case 0x6c: /* fsub */ > - gen_helper_sub_f64(res, cpu_env, res, src); > + gen_helper_fsub(cpu_env, cpu_dest, cpu_src, cpu_dest); > break; > case 0x38: /* fcmp */ > - gen_helper_sub_cmp_f64(res, cpu_env, res, src); > - set_dest = 0; > + tcg_temp_free_ptr(cpu_dest); > + cpu_dest = gen_fp_result_ptr(); > + gen_helper_fsub_cmp(cpu_env, cpu_dest, cpu_src, cpu_dest); > round = 0; > break; > case 0x3a: /* ftst */ > - tcg_gen_mov_f64(res, src); > - set_dest = 0; > + tcg_temp_free_ptr(cpu_dest); > + cpu_dest = gen_fp_result_ptr(); > + gen_fp_move(cpu_dest, cpu_src); > round = 0; > break; > default: > goto undef; > } > - if (ext & (1 << 14)) { > - tcg_temp_free_i64(src); > - } > if (round) { > if (opmode & 0x40) { > if ((opmode & 0x4) != 0) > @@ -4364,16 +4536,18 @@ DISAS_INSN(fpu) > } > } > if (round) { > - 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); > - } > - tcg_gen_mov_f64(QREG_FP_RESULT, res); > - if (set_dest) { > - tcg_gen_mov_f64(dest, res); > + TCGv tmp = tcg_temp_new(); > + gen_helper_redf32(tmp, cpu_env, cpu_dest); > + gen_helper_extf32(cpu_env, cpu_dest, tmp); > + tcg_temp_free(tmp); > + } else { > + TCGv_i64 t64 = tcg_temp_new_i64(); > + gen_helper_redf64(t64, cpu_env, cpu_dest); > + gen_helper_extf64(cpu_env, cpu_dest, t64); > + tcg_temp_free_i64(t64); > } > - tcg_temp_free_i64(res); > + tcg_temp_free_ptr(cpu_src); > + tcg_temp_free_ptr(cpu_dest); > return; > undef: > /* FIXME: Is this right for offset addressing modes? */ > @@ -4387,6 +4561,7 @@ DISAS_INSN(fbcc) > uint32_t addr; > TCGv flag; > TCGLabel *l1; > + TCGv_ptr fp_result; > > addr = s->pc; > offset = cpu_ldsw_code(env, s->pc); > @@ -4398,7 +4573,9 @@ DISAS_INSN(fbcc) > l1 = gen_new_label(); > /* TODO: Raise BSUN exception. */ > flag = tcg_temp_new(); > - gen_helper_compare_f64(flag, cpu_env, QREG_FP_RESULT); > + fp_result = gen_fp_result_ptr(); > + gen_helper_fcompare(flag, cpu_env, fp_result); > + tcg_temp_free_ptr(fp_result); > /* Jump to l1 if condition is true. */ > switch (insn & 0xf) { > case 0: /* f */ > @@ -5028,11 +5205,15 @@ void register_m68k_insns (CPUM68KState *env) > INSN(bfop_reg, eec0, fff8, BITFIELD); /* bfset */ > INSN(bfop_mem, e8c0, ffc0, BITFIELD); /* bftst */ > INSN(bfop_reg, e8c0, fff8, BITFIELD); /* bftst */ > - INSN(undef_fpu, f000, f000, CF_ISA_A); > + BASE(undef_fpu, f000, f000); > 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(fsave, f300, ffc0, CF_FPU); > + INSN(fpu, f200, ffc0, FPU); > + INSN(fbcc, f280, ff80, FPU); > + INSN(frestore, f340, ffc0, FPU); > + INSN(fsave, f300, ffc0, FPU); > INSN(intouch, f340, ffc0, CF_ISA_A); > INSN(cpushl, f428, ff38, CF_ISA_A); > INSN(wddata, fb00, ff00, CF_ISA_A); > @@ -5158,6 +5339,18 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) > tb->icount = num_insns; > } > > +static double floatx80_to_double(CPUM68KState *env, uint16_t high, uint64_t low) > +{ > + floatx80 a = { .high = high, .low = low }; > + union { > + float64 f64; > + double d; > + } u; > + > + u.f64 = floatx80_to_float64(a, &env->fp_status); > + return u.d; > +} > + > void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, > int flags) > { > @@ -5165,20 +5358,19 @@ 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", > + for (i = 0; i < 8; i++) { > + cpu_fprintf(f, "D%d = %08x A%d = %08x " > + "F%d = %04x %016"PRIx64" (%12g)\n", > i, env->dregs[i], i, env->aregs[i], > - i, u.l.upper, u.l.lower, *(double *)&u.d); > - } > + i, env->fregs[i].l.upper, env->fregs[i].l.lower, > + floatx80_to_double(env, env->fregs[i].l.upper, > + env->fregs[i].l.lower)); > + } > cpu_fprintf (f, "PC = %08x ", env->pc); > sr = env->sr | cpu_m68k_get_ccr(env); > cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? '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(CPUM68KState *env, TranslationBlock *tb, > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-21 16:18 ` Philippe Mathieu-Daudé @ 2017-06-21 16:37 ` Richard Henderson 2017-06-21 16:45 ` Laurent Vivier 2017-06-26 4:47 ` Philippe Mathieu-Daudé 0 siblings, 2 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-21 16:37 UTC (permalink / raw) To: Philippe Mathieu-Daudé, Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno On 06/21/2017 09:18 AM, Philippe Mathieu-Daudé wrote: >> +typedef CPU_LDoubleU FPReg; > > What an awful name... Anyway checking on "qemu/bswap.h" it seems there is some > endianess issue with it if your host is little-endian. There is no endian-ness issue because we do not attempt to read that structure from memory as a whole. Instead, Laurent uses two big-endian loads (with appropriate address arithmetic) and stores the result into this host structure in host-endian order. Further, the host routines use the structure members by name and do not assume any particular relationship between them. > Do you have a way to run Berkeley TestFloat? As noted in Laurent's cover message, floatx80 isn't quite right -- that is the x86 data type, and the proper m68k data type is slightly different. I would expect the results from using floatx80 to be Just Good Enough to produce a working m68k user-land. It will produce correct results for normal numbers in arithmetic such as 1.0 + 10.0. But I would expect many of the edge conditions that TestFloat would attempt (especially de-normals and un-normals) would fail. r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-21 16:37 ` Richard Henderson @ 2017-06-21 16:45 ` Laurent Vivier 2017-06-26 4:47 ` Philippe Mathieu-Daudé 1 sibling, 0 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-21 16:45 UTC (permalink / raw) To: Richard Henderson, Philippe Mathieu-Daudé, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno Le 21/06/2017 à 18:37, Richard Henderson a écrit : > On 06/21/2017 09:18 AM, Philippe Mathieu-Daudé wrote: >>> +typedef CPU_LDoubleU FPReg; >> >> What an awful name... Anyway checking on "qemu/bswap.h" it seems there >> is some endianess issue with it if your host is little-endian. > > There is no endian-ness issue because we do not attempt to read that > structure from memory as a whole. Instead, Laurent uses two big-endian > loads (with appropriate address arithmetic) and stores the result into > this host structure in host-endian order. Further, the host routines > use the structure members by name and do not assume any particular > relationship between them. > >> Do you have a way to run Berkeley TestFloat? > > As noted in Laurent's cover message, floatx80 isn't quite right -- that > is the x86 data type, and the proper m68k data type is slightly different. > > I would expect the results from using floatx80 to be Just Good Enough to > produce a working m68k user-land. It will produce correct results for > normal numbers in arithmetic such as 1.0 + 10.0. But I would expect > many of the edge conditions that TestFloat would attempt (especially > de-normals and un-normals) would fail. > Thank you Richard. Yes, with floatx80, we can run m68k user-land. I'm going to post the missing instructions. I've also ported TestFloat and SoftFloat to m68k to check the result, it's not perfect but in a good shape. https://github.com/vivier/m68k-testfloat https://github.com/vivier/m68k-softfloat I can compare result from my reference hardware (a real Quadra 800) and qemu-m68k chroot. But I plan to replace the floatx80 by a floatx96 in a near future... Thanks, Laurent ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally 2017-06-21 16:37 ` Richard Henderson 2017-06-21 16:45 ` Laurent Vivier @ 2017-06-26 4:47 ` Philippe Mathieu-Daudé 1 sibling, 0 replies; 22+ messages in thread From: Philippe Mathieu-Daudé @ 2017-06-26 4:47 UTC (permalink / raw) To: Richard Henderson Cc: Laurent Vivier, qemu-devel@nongnu.org Developers, Riku Voipio, Thomas Huth, Aurelien Jarno On Wed, Jun 21, 2017 at 1:37 PM, Richard Henderson <rth@twiddle.net> wrote: > On 06/21/2017 09:18 AM, Philippe Mathieu-Daudé wrote: >> What an awful name... Anyway checking on "qemu/bswap.h" it seems there is >> some endianess issue with it if your host is little-endian. > > > There is no endian-ness issue because we do not attempt to read that > structure from memory as a whole. Instead, Laurent uses two big-endian > loads (with appropriate address arithmetic) and stores the result into this > host structure in host-endian order. Further, the host routines use the > structure members by name and do not assume any particular relationship > between them. Ok, thank your Richard for this clear explanation. ("Endian-ness" ok, Peter also corrected me, I'll give a try to some speller...) > >> Do you have a way to run Berkeley TestFloat? > > > As noted in Laurent's cover message, floatx80 isn't quite right -- that is > the x86 data type, and the proper m68k data type is slightly different. > > I would expect the results from using floatx80 to be Just Good Enough to > produce a working m68k user-land. It will produce correct results for > normal numbers in arithmetic such as 1.0 + 10.0. But I would expect many of > the edge conditions that TestFloat would attempt (especially de-normals and > un-normals) would fail. Ok. I'm asking thinking about cross unit-tests I can add to our continuous integration system, this might be a candidate. Regards, Phil. ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 4/6] target-m68k: define 96bit FP registers for gdb on 680x0 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier ` (2 preceding siblings ...) 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext Laurent Vivier 5 siblings, 0 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier Signed-off-by: Laurent Vivier <laurent@vivier.eu> Reviewed-by: Richard Henderson <rth@twiddle.net> --- configure | 2 +- gdb-xml/m68k-fp.xml | 21 +++++++++++++++++++++ target/m68k/helper.c | 45 +++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 67 insertions(+), 1 deletion(-) create mode 100644 gdb-xml/m68k-fp.xml diff --git a/configure b/configure index db0798d..4df871c 100755 --- a/configure +++ b/configure @@ -6066,7 +6066,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/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 @@ +<?xml version="1.0"?> +<!-- Copyright (C) 2008 Free Software Foundation, Inc. + + Copying and distribution of this file, with or without modification, + are permitted in any medium without royalty provided the copyright + notice and this notice are preserved. --> +<!DOCTYPE feature SYSTEM "gdb-target.dtd"> +<feature name="org.gnu.gdb.coldfire.fp"> + <reg name="fp0" bitsize="96" type="float" group="float"/> + <reg name="fp1" bitsize="96" type="float" group="float"/> + <reg name="fp2" bitsize="96" type="float" group="float"/> + <reg name="fp3" bitsize="96" type="float" group="float"/> + <reg name="fp4" bitsize="96" type="float" group="float"/> + <reg name="fp5" bitsize="96" type="float" group="float"/> + <reg name="fp6" bitsize="96" type="float" group="float"/> + <reg name="fp7" bitsize="96" type="float" group="float"/> + + <reg name="fpcontrol" bitsize="32" group="float"/> + <reg name="fpstatus" bitsize="32" group="float"/>, + <reg name="fpiaddr" bitsize="32" type="code_ptr" group="float"/> +</feature> diff --git a/target/m68k/helper.c b/target/m68k/helper.c index 8bfc881..f2de6b5 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -102,6 +102,48 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) 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->fregs[n].l.upper); + memset(mem_buf + 2, 0, 2); + stq_be_p(mem_buf + 4, env->fregs[n].l.lower); + return 12; + } + switch (n) { + case 8: /* fpcontrol */ + stl_be_p(mem_buf, env->fpcr); + return 4; + case 9: /* fpstatus */ + stl_be_p(mem_buf, env->fpsr); + return 4; + case 10: /* fpiar, not implemented */ + 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->fregs[n].l.upper = lduw_be_p(mem_buf); + env->fregs[n].l.lower = ldq_be_p(mem_buf + 4); + return 12; + } + switch (n) { + case 8: /* fpcontrol */ + env->fpcr = ldl_p(mem_buf); + return 4; + case 9: /* fpstatus */ + env->fpsr = ldl_p(mem_buf); + return 4; + case 10: /* fpiar, not implemented */ + return 4; + } + return 0; +} + M68kCPU *cpu_m68k_init(const char *cpu_model) { M68kCPU *cpu; @@ -130,6 +172,9 @@ void m68k_cpu_init_gdb(M68kCPU *cpu) if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { 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. */ } -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier ` (3 preceding siblings ...) 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 4/6] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 22:55 ` Richard Henderson 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext Laurent Vivier 5 siblings, 1 reply; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier Signed-off-by: Laurent Vivier <laurent@vivier.eu> --- target/m68k/cpu.c | 2 +- target/m68k/cpu.h | 43 +++++- target/m68k/fpu_helper.c | 117 +++++++++++++--- target/m68k/helper.c | 22 ++- target/m68k/helper.h | 5 +- target/m68k/translate.c | 351 +++++++++++++++++++++++++++++++++++------------ 6 files changed, 421 insertions(+), 119 deletions(-) diff --git a/target/m68k/cpu.c b/target/m68k/cpu.c index 435456f..a14b6dd 100644 --- a/target/m68k/cpu.c +++ b/target/m68k/cpu.c @@ -62,7 +62,7 @@ static void m68k_cpu_reset(CPUState *s) for (i = 0; i < 8; i++) { env->fregs[i].d = nan; } - env->fpcr = 0; + cpu_m68k_set_fpcr(env, 0); env->fpsr = 0; cpu_m68k_set_ccr(env, 0); diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index beb8ebc..38a7e11 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -171,6 +171,7 @@ int cpu_m68k_signal_handler(int host_signum, void *pinfo, void *puc); uint32_t cpu_m68k_get_ccr(CPUM68KState *env); void cpu_m68k_set_ccr(CPUM68KState *env, uint32_t); +void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val); /* Instead of computing the condition codes after each m68k instruction, @@ -215,6 +216,43 @@ typedef enum { #define M68K_SSP 0 #define M68K_USP 1 +#define M68K_FPIAR_SHIFT 0 +#define M68K_FPIAR (1 << M68K_FPIAR_SHIFT) +#define M68K_FPSR_SHIFT 1 +#define M68K_FPSR (1 << M68K_FPSR_SHIFT) +#define M68K_FPCR_SHIFT 2 +#define M68K_FPCR (1 << M68K_FPCR_SHIFT) + +/* Floating-Point Status Register */ + +/* Condition Code */ +#define FPSR_CC_MASK 0x0f000000 +#define FPSR_CC_A 0x01000000 /* Not-A-Number */ +#define FPSR_CC_I 0x02000000 /* Infinity */ +#define FPSR_CC_Z 0x04000000 /* Zero */ +#define FPSR_CC_N 0x08000000 /* Negative */ + +/* Quotient */ + +#define FPSR_QT_MASK 0x00ff0000 + +/* Floating-Point Control Register */ +/* Rounding mode */ +#define FPCR_RND_MASK 0x0030 +#define FPCR_RND_N 0x0000 +#define FPCR_RND_Z 0x0010 +#define FPCR_RND_M 0x0020 +#define FPCR_RND_P 0x0030 + +/* Rounding precision */ +#define FPCR_PREC_MASK 0x00c0 +#define FPCR_PREC_X 0x0000 +#define FPCR_PREC_S 0x0040 +#define FPCR_PREC_D 0x0080 +#define FPCR_PREC_U 0x00c0 + +#define FPCR_EXCP_MASK 0xff00 + /* CACR fields are implementation defined, but some bits are common. */ #define M68K_CACR_EUSP 0x10 @@ -231,8 +269,6 @@ typedef enum { void m68k_set_irq_level(M68kCPU *cpu, int level, uint8_t vector); void m68k_switch_sp(CPUM68KState *env); -#define M68K_FPCR_PREC (1 << 6) - void do_m68k_semihosting(CPUM68KState *env, int nr); /* There are 4 ColdFire core ISA revisions: A, A+, B and C. @@ -310,8 +346,7 @@ static inline void cpu_get_tb_cpu_state(CPUM68KState *env, target_ulong *pc, { *pc = env->pc; *cs_base = 0; - *flags = (env->fpcr & M68K_FPCR_PREC) /* Bit 6 */ - | (env->sr & SR_S) /* Bit 13 */ + *flags = (env->sr & SR_S) /* Bit 13 */ | ((env->macsr >> 4) & 0xf); /* Bits 0-3 */ } diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index f4d3821..a9e17f5 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -58,9 +58,74 @@ void HELPER(firound)(CPUM68KState *env, FPReg *res, FPReg *val) res->d = floatx80_round_to_int(val->d, &env->fp_status); } +static void m68k_restore_precision_mode(CPUM68KState *env) +{ + switch (env->fpcr & FPCR_PREC_MASK) { + case FPCR_PREC_X: /* extended */ + set_floatx80_rounding_precision(80, &env->fp_status); + break; + case FPCR_PREC_S: /* single */ + set_floatx80_rounding_precision(32, &env->fp_status); + break; + case FPCR_PREC_D: /* double */ + set_floatx80_rounding_precision(64, &env->fp_status); + break; + case FPCR_PREC_U: /* undefined */ + default: + break; + } +} + +static void cf_restore_precision_mode(CPUM68KState *env) +{ + if (env->fpcr & FPCR_PREC_S) { /* single */ + set_floatx80_rounding_precision(32, &env->fp_status); + } else { /* double */ + set_floatx80_rounding_precision(64, &env->fp_status); + } +} + +static void restore_rounding_mode(CPUM68KState *env) +{ + switch (env->fpcr & FPCR_RND_MASK) { + case FPCR_RND_N: /* round to nearest */ + set_float_rounding_mode(float_round_nearest_even, &env->fp_status); + break; + case FPCR_RND_Z: /* round to zero */ + set_float_rounding_mode(float_round_to_zero, &env->fp_status); + break; + case FPCR_RND_M: /* round toward minus infinity */ + set_float_rounding_mode(float_round_down, &env->fp_status); + break; + case FPCR_RND_P: /* round toward positive infinity */ + set_float_rounding_mode(float_round_up, &env->fp_status); + break; + } +} + +void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val) +{ + env->fpcr = val & 0xffff; + + if (m68k_feature(env, M68K_FEATURE_CF_FPU)) { + cf_restore_precision_mode(env); + } else { + m68k_restore_precision_mode(env); + } + restore_rounding_mode(env); +} + void HELPER(fitrunc)(CPUM68KState *env, FPReg *res, FPReg *val) { + int rounding_mode = get_float_rounding_mode(&env->fp_status); + set_float_rounding_mode(float_round_to_zero, &env->fp_status); res->d = floatx80_round_to_int(val->d, &env->fp_status); + set_float_rounding_mode(rounding_mode, &env->fp_status); +} + +void HELPER(set_fpcr)(CPUM68KState *env, uint32_t val) +{ + cpu_m68k_set_fpcr(env, val); } void HELPER(fsqrt)(CPUM68KState *env, FPReg *res, FPReg *val) @@ -98,24 +163,44 @@ void HELPER(fdiv)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) res->d = floatx80_div(val1->d, val0->d, &env->fp_status); } -void HELPER(fsub_cmp)(CPUM68KState *env, FPReg *res, FPReg *val0, FPReg *val1) -{ - /* ??? This may incorrectly raise exceptions. */ - /* ??? Should flush denormals to zero. */ - res->d = floatx80_sub(val0->d, val1->d, &env->fp_status); - if (floatx80_is_quiet_nan(res->d, &env->fp_status)) { - /* +/-inf compares equal against itself, but sub returns nan. */ - if (!floatx80_is_quiet_nan(val0->d, &env->fp_status) - && !floatx80_is_quiet_nan(val1->d, &env->fp_status)) { - res->d = floatx80_zero; - if (floatx80_lt_quiet(val0->d, res->d, &env->fp_status)) { - res->d = floatx80_chs(res->d); - } - } +static int float_comp_to_cc(int float_compare) +{ + switch (float_compare) { + case float_relation_equal: + return FPSR_CC_Z; + case float_relation_less: + return FPSR_CC_N; + case float_relation_unordered: + return FPSR_CC_A; + case float_relation_greater: + return 0; + default: + g_assert_not_reached(); } } -uint32_t HELPER(fcompare)(CPUM68KState *env, FPReg *val) +void HELPER(fcmp)(CPUM68KState *env, FPReg *val0, FPReg *val1) { - return floatx80_compare_quiet(val->d, floatx80_zero, &env->fp_status); + int float_compare; + + float_compare = floatx80_compare(val1->d, val0->d, &env->fp_status); + env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); +} + +void HELPER(ftst)(CPUM68KState *env, FPReg *val) +{ + uint32_t cc = 0; + + if (floatx80_is_neg(val->d)) { + cc |= FPSR_CC_N; + } + + if (floatx80_is_any_nan(val->d)) { + cc |= FPSR_CC_A; + } else if (floatx80_is_infinity(val->d)) { + cc |= FPSR_CC_I; + } else if (floatx80_is_zero(val->d)) { + cc |= FPSR_CC_Z; + } + env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | cc; } diff --git a/target/m68k/helper.c b/target/m68k/helper.c index f2de6b5..caae291 100644 --- a/target/m68k/helper.c +++ b/target/m68k/helper.c @@ -80,8 +80,14 @@ static int cf_fpu_gdb_get_reg(CPUM68KState *env, uint8_t *mem_buf, int n) stfq_p(mem_buf, floatx80_to_float64(env->fregs[n].d, &s)); return 8; } - if (n < 11) { - /* FP control registers (not implemented) */ + switch (n) { + case 8: /* fpcontrol */ + stl_be_p(mem_buf, env->fpcr); + return 4; + case 9: /* fpstatus */ + stl_be_p(mem_buf, env->fpsr); + return 4; + case 10: /* fpiar, not implemented */ memset(mem_buf, 0, 4); return 4; } @@ -95,8 +101,14 @@ static int cf_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) env->fregs[n].d = float64_to_floatx80(ldfq_p(mem_buf), &s); return 8; } - if (n < 11) { - /* FP control registers (not implemented) */ + switch (n) { + case 8: /* fpcontrol */ + cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); + return 4; + case 9: /* fpstatus */ + env->fpsr = ldl_p(mem_buf); + return 4; + case 10: /* fpiar, not implemented */ return 4; } return 0; @@ -133,7 +145,7 @@ static int m68k_fpu_gdb_set_reg(CPUM68KState *env, uint8_t *mem_buf, int n) } switch (n) { case 8: /* fpcontrol */ - env->fpcr = ldl_p(mem_buf); + cpu_m68k_set_fpcr(env, ldl_p(mem_buf)); return 4; case 9: /* fpstatus */ env->fpsr = ldl_p(mem_buf); diff --git a/target/m68k/helper.h b/target/m68k/helper.h index d871be6..98cbf18 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -32,8 +32,9 @@ DEF_HELPER_4(fadd, void, env, fp, fp, fp) DEF_HELPER_4(fsub, void, env, fp, fp, fp) DEF_HELPER_4(fmul, void, env, fp, fp, fp) DEF_HELPER_4(fdiv, void, env, fp, fp, fp) -DEF_HELPER_4(fsub_cmp, void, env, fp, fp, fp) -DEF_HELPER_2(fcompare, i32, env, fp) +DEF_HELPER_FLAGS_3(fcmp, TCG_CALL_NO_RWG, void, env, fp, fp) +DEF_HELPER_FLAGS_2(set_fpcr, TCG_CALL_NO_RWG, void, env, i32) +DEF_HELPER_FLAGS_2(ftst, TCG_CALL_NO_RWG, void, env, fp) DEF_HELPER_3(mac_move, void, env, i32, i32) DEF_HELPER_3(macmulf, i64, env, i32, i32) diff --git a/target/m68k/translate.c b/target/m68k/translate.c index 73f691f..7aa0fdc 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -120,7 +120,6 @@ typedef struct DisasContext { CCOp cc_op; /* Current CC operation */ int cc_op_synced; int user; - uint32_t fpcr; struct TranslationBlock *tb; int singlestep_enabled; TCGv_i64 mactmp; @@ -4371,33 +4370,139 @@ DISAS_INSN(trap) gen_exception(s, s->pc - 2, EXCP_TRAP0 + (insn & 0xf)); } +static void gen_load_fcr(DisasContext *s, TCGv res, int reg) +{ + switch (reg) { + case M68K_FPIAR: + tcg_gen_movi_i32(res, 0); + break; + case M68K_FPSR: + tcg_gen_ld_i32(res, cpu_env, offsetof(CPUM68KState, fpsr)); + break; + case M68K_FPCR: + tcg_gen_ld_i32(res, cpu_env, offsetof(CPUM68KState, fpcr)); + break; + } +} + +static void gen_store_fcr(DisasContext *s, TCGv val, int reg) +{ + switch (reg) { + case M68K_FPIAR: + break; + case M68K_FPSR: + tcg_gen_st_i32(val, cpu_env, offsetof(CPUM68KState, fpsr)); + break; + case M68K_FPCR: + gen_helper_set_fpcr(cpu_env, val); + break; + } +} + +static void gen_qemu_store_fcr(DisasContext *s, TCGv addr, int reg) +{ + int index = IS_USER(s); + TCGv tmp; + + tmp = tcg_temp_new(); + gen_load_fcr(s, tmp, reg); + tcg_gen_qemu_st32(tmp, addr, index); + tcg_temp_free(tmp); +} + +static void gen_qemu_load_fcr(DisasContext *s, TCGv addr, int reg) +{ + int index = IS_USER(s); + TCGv tmp; + + tmp = tcg_temp_new(); + tcg_gen_qemu_ld32u(tmp, addr, index); + gen_store_fcr(s, tmp, reg); + tcg_temp_free(tmp); +} + + static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, uint32_t insn, uint32_t ext) { int mask = (ext >> 10) & 7; int is_write = (ext >> 13) & 1; - TCGv val; + int mode = extract32(insn, 3, 3); + int i; + TCGv addr, tmp; - switch (mask) { - case 1: /* FPIAR */ - case 2: /* FPSR */ - default: - qemu_log_mask(LOG_UNIMP, "Unimplemented: fmove to/from control %d", - mask); - goto undef; - case 4: /* FPCR */ + switch (mode) { + case 0: /* Dn */ + if (mask != M68K_FPIAR && mask != M68K_FPSR && mask != M68K_FPCR) { + gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + return; + } if (is_write) { - val = tcg_const_i32(0); - DEST_EA(env, insn, OS_LONG, val, NULL); - tcg_temp_free(val); + gen_load_fcr(s, DREG(insn, 0), mask); + } else { + gen_store_fcr(s, DREG(insn, 0), mask); } - /* Not implemented. Ignore register update */ + return; + case 1: /* An, only with FPIAR */ + if (mask != M68K_FPIAR) { + gen_exception(s, s->insn_pc, EXCP_ILLEGAL); + return; + } + if (is_write) { + gen_load_fcr(s, AREG(insn, 0), mask); + } else { + gen_store_fcr(s, AREG(insn, 0), mask); + } + return; + default: break; } - return; -undef: - s->pc -= 2; - disas_undef_fpu(env, s, insn); + + tmp = gen_lea(env, s, insn, OS_LONG); + if (IS_NULL_QREG(tmp)) { + gen_addr_fault(s); + return; + } + + addr = tcg_temp_new(); + tcg_gen_mov_i32(addr, tmp); + + /* mask: + * + * 0b100 Floating-Point Control Register + * 0b010 Floating-Point Status Register + * 0b001 Floating-Point Instruction Address Register + * + */ + + if (is_write && mode == 4) { + for (i = 2; i >= 0; i--, mask >>= 1) { + if (mask & 1) { + gen_qemu_store_fcr(s, addr, 1 << i); + if (mask != 1) { + tcg_gen_subi_i32(addr, addr, opsize_bytes(OS_LONG)); + } + } + } + tcg_gen_mov_i32(AREG(insn, 0), addr); + } else { + for (i = 0; i < 3; i++, mask >>= 1) { + if (mask & 1) { + if (is_write) { + gen_qemu_store_fcr(s, addr, 1 << i); + } else { + gen_qemu_load_fcr(s, addr, 1 << i); + } + if (mask != 1 || mode == 3) { + tcg_gen_addi_i32(addr, addr, opsize_bytes(OS_LONG)); + } + } + } + if (mode == 3) { + tcg_gen_mov_i32(AREG(insn, 0), addr); + } + } + tcg_temp_free_i32(addr); } /* ??? FP exceptions are not implemented. Most exceptions are deferred until @@ -4407,7 +4512,6 @@ DISAS_INSN(fpu) uint16_t ext; int opmode; TCGv tmp32; - int round; int opsize; TCGv_ptr cpu_src, cpu_dest; @@ -4424,6 +4528,7 @@ DISAS_INSN(fpu) if (gen_ea_fp(env, s, insn, opsize, cpu_src, EA_STORE) == -1) { gen_addr_fault(s); } + gen_helper_ftst(cpu_env, cpu_src); tcg_temp_free_ptr(cpu_src); return; case 4: /* fmove to control register. */ @@ -4477,7 +4582,6 @@ DISAS_INSN(fpu) opsize = OS_EXTENDED; cpu_src = gen_fp_ptr(REG(ext, 10)); } - round = 1; cpu_dest = gen_fp_ptr(REG(ext, 7)); switch (opmode) { case 0: case 0x40: case 0x44: /* fmove */ @@ -4485,11 +4589,9 @@ DISAS_INSN(fpu) break; case 1: /* fint */ gen_helper_firound(cpu_env, cpu_dest, cpu_src); - round = 0; break; case 3: /* fintrz */ gen_helper_fitrunc(cpu_env, cpu_dest, cpu_src); - round = 0; break; case 4: case 0x41: case 0x45: /* fsqrt */ gen_helper_fsqrt(cpu_env, cpu_dest, cpu_src); @@ -4513,40 +4615,16 @@ DISAS_INSN(fpu) gen_helper_fsub(cpu_env, cpu_dest, cpu_src, cpu_dest); break; case 0x38: /* fcmp */ - tcg_temp_free_ptr(cpu_dest); - cpu_dest = gen_fp_result_ptr(); - gen_helper_fsub_cmp(cpu_env, cpu_dest, cpu_src, cpu_dest); - round = 0; - break; + gen_helper_fcmp(cpu_env, cpu_src, cpu_dest); + return; case 0x3a: /* ftst */ - tcg_temp_free_ptr(cpu_dest); - cpu_dest = gen_fp_result_ptr(); - gen_fp_move(cpu_dest, cpu_src); - round = 0; - break; + gen_helper_ftst(cpu_env, cpu_src); + return; default: goto undef; } - if (round) { - if (opmode & 0x40) { - if ((opmode & 0x4) != 0) - round = 0; - } else if ((s->fpcr & M68K_FPCR_PREC) == 0) { - round = 0; - } - } - if (round) { - TCGv tmp = tcg_temp_new(); - gen_helper_redf32(tmp, cpu_env, cpu_dest); - gen_helper_extf32(cpu_env, cpu_dest, tmp); - tcg_temp_free(tmp); - } else { - TCGv_i64 t64 = tcg_temp_new_i64(); - gen_helper_redf64(t64, cpu_env, cpu_dest); - gen_helper_extf64(cpu_env, cpu_dest, t64); - tcg_temp_free_i64(t64); - } tcg_temp_free_ptr(cpu_src); + gen_helper_ftst(cpu_env, cpu_dest); tcg_temp_free_ptr(cpu_dest); return; undef: @@ -4559,9 +4637,8 @@ DISAS_INSN(fbcc) { uint32_t offset; uint32_t addr; - TCGv flag; TCGLabel *l1; - TCGv_ptr fp_result; + TCGv tmp, fpsr; addr = s->pc; offset = cpu_ldsw_code(env, s->pc); @@ -4570,64 +4647,125 @@ DISAS_INSN(fbcc) offset = (offset << 16) | read_im16(env, s); } + fpsr = tcg_temp_new(); + gen_load_fcr(s, fpsr, M68K_FPSR); l1 = gen_new_label(); /* TODO: Raise BSUN exception. */ - flag = tcg_temp_new(); - fp_result = gen_fp_result_ptr(); - gen_helper_fcompare(flag, cpu_env, fp_result); - tcg_temp_free_ptr(fp_result); /* Jump to l1 if condition is true. */ - switch (insn & 0xf) { - case 0: /* f */ + switch (insn & 0x3f) { + case 0: /* False */ + case 16: /* Signaling False */ break; - case 1: /* eq (=0) */ - tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(0), l1); + case 1: /* EQual Z */ + case 17: /* Signaling EQual Z */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 2: /* ogt (=1) */ - tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(1), l1); + case 2: /* Ordered Greater Than !(A || Z || N) */ + case 18: /* Greater Than !(A || Z || N) */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, + FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); break; - case 3: /* oge (=0 or =1) */ - tcg_gen_brcond_i32(TCG_COND_LEU, flag, tcg_const_i32(1), l1); + case 3: /* Ordered Greater than or Equal Z || !(A || N) */ + case 19: /* Greater than or Equal Z || !(A || N) */ + assert(FPSR_CC_A == (FPSR_CC_N >> 3)); + tmp = tcg_temp_new(); + tcg_gen_shli_i32(tmp, fpsr, 3); + tcg_gen_or_i32(tmp, tmp, fpsr); + tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N); + tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 4: /* olt (=-1) */ - tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(0), l1); + case 4: /* Ordered Less Than !(!N || A || Z); */ + case 20: /* Less Than !(!N || A || Z); */ + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, fpsr, FPSR_CC_N); + tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A | FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); break; - case 5: /* ole (=-1 or =0) */ - tcg_gen_brcond_i32(TCG_COND_LE, flag, tcg_const_i32(0), l1); + case 5: /* Ordered Less than or Equal Z || (N && !A) */ + case 21: /* Less than or Equal Z || (N && !A) */ + assert(FPSR_CC_A == (FPSR_CC_N >> 3)); + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, fpsr, FPSR_CC_A); + tcg_gen_shli_i32(tmp, tmp, 3); + tcg_gen_ori_i32(tmp, tmp, FPSR_CC_Z); + tcg_gen_and_i32(tmp, tmp, fpsr); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 6: /* ogl (=-1 or =1) */ - tcg_gen_andi_i32(flag, flag, 1); - tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1); + case 6: /* Ordered Greater or Less than !(A || Z) */ + case 22: /* Greater or Less than !(A || Z) */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A | FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); break; - case 7: /* or (=2) */ - tcg_gen_brcond_i32(TCG_COND_EQ, flag, tcg_const_i32(2), l1); + case 7: /* Ordered !A */ + case 23: /* Greater, Less or Equal !A */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); break; - case 8: /* un (<2) */ - tcg_gen_brcond_i32(TCG_COND_LT, flag, tcg_const_i32(2), l1); + case 8: /* Unordered A */ + case 24: /* Not Greater, Less or Equal A */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, 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); + case 9: /* Unordered or Equal A || Z */ + case 25: /* Not Greater or Less then A || Z */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A | FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 10: /* ugt (>0) */ - tcg_gen_brcond_i32(TCG_COND_GT, flag, tcg_const_i32(0), l1); + case 10: /* Unordered or Greater Than A || !(N || Z)) */ + case 26: /* Not Less or Equal A || !(N || Z)) */ + assert(FPSR_CC_Z == (FPSR_CC_N >> 1)); + tmp = tcg_temp_new(); + tcg_gen_shli_i32(tmp, fpsr, 1); + tcg_gen_or_i32(tmp, tmp, fpsr); + tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N); + tcg_gen_andi_i32(tmp, tmp, FPSR_CC_N | FPSR_CC_A); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 11: /* uge (>=0) */ - tcg_gen_brcond_i32(TCG_COND_GE, flag, tcg_const_i32(0), l1); + case 11: /* Unordered or Greater or Equal A || Z || !N */ + case 27: /* Not Less Than A || Z || !N */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); + tcg_gen_xori_i32(tmp, tmp, FPSR_CC_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 12: /* ult (=-1 or =2) */ - tcg_gen_brcond_i32(TCG_COND_GEU, flag, tcg_const_i32(2), l1); + case 12: /* Unordered or Less Than A || (N && !Z) */ + case 28: /* Not Greater than or Equal A || (N && !Z) */ + assert(FPSR_CC_Z == (FPSR_CC_N >> 1)); + tmp = tcg_temp_new(); + tcg_gen_xori_i32(tmp, fpsr, FPSR_CC_Z); + tcg_gen_shli_i32(tmp, tmp, 1); + tcg_gen_ori_i32(tmp, tmp, FPSR_CC_A); + tcg_gen_and_i32(tmp, tmp, fpsr); + tcg_gen_andi_i32(tmp, tmp, FPSR_CC_A | FPSR_CC_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 13: /* ule (!=1) */ - tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(1), l1); + case 13: /* Unordered or Less or Equal A || Z || N */ + case 29: /* Not Greater Than A || Z || N */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_A | FPSR_CC_Z | FPSR_CC_N); + tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, l1); break; - case 14: /* ne (!=0) */ - tcg_gen_brcond_i32(TCG_COND_NE, flag, tcg_const_i32(0), l1); + case 14: /* Not Equal !Z */ + case 30: /* Signaling Not Equal !Z */ + tmp = tcg_temp_new(); + tcg_gen_andi_i32(tmp, fpsr, FPSR_CC_Z); + tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, l1); break; - case 15: /* t */ + case 15: /* True */ + case 31: /* Signaling True */ tcg_gen_br(l1); break; } + tcg_temp_free(fpsr); gen_jmp_tb(s, 0, s->pc); gen_set_label(l1); gen_jmp_tb(s, 1, addr + offset); @@ -5252,7 +5390,6 @@ void gen_intermediate_code(CPUM68KState *env, TranslationBlock *tb) dc->cc_op = CC_OP_DYNAMIC; dc->cc_op_synced = 1; dc->singlestep_enabled = cs->singlestep_enabled; - dc->fpcr = env->fpcr; dc->user = (env->sr & SR_S) == 0; dc->done_mac = 0; dc->writeback_mask = 0; @@ -5371,6 +5508,38 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "SR = %04x %c%c%c%c%c ", sr, (sr & CCF_X) ? 'X' : '-', (sr & CCF_N) ? 'N' : '-', (sr & CCF_Z) ? 'Z' : '-', (sr & CCF_V) ? 'V' : '-', (sr & CCF_C) ? 'C' : '-'); + cpu_fprintf(f, "FPSR = %08x %c%c%c%c ", env->fpsr, + (env->fpsr & FPSR_CC_A) ? 'A' : '-', + (env->fpsr & FPSR_CC_I) ? 'I' : '-', + (env->fpsr & FPSR_CC_Z) ? 'Z' : '-', + (env->fpsr & FPSR_CC_N) ? 'N' : '-'); + cpu_fprintf(f, "\n " + "FPCR = %04x ", env->fpcr); + switch (env->fpcr & FPCR_PREC_MASK) { + case FPCR_PREC_X: + cpu_fprintf(f, "X "); + break; + case FPCR_PREC_S: + cpu_fprintf(f, "S "); + break; + case FPCR_PREC_D: + cpu_fprintf(f, "D "); + break; + } + switch (env->fpcr & FPCR_RND_MASK) { + case FPCR_RND_N: + cpu_fprintf(f, "RN "); + break; + case FPCR_RND_Z: + cpu_fprintf(f, "RZ "); + break; + case FPCR_RND_M: + cpu_fprintf(f, "RM "); + break; + case FPCR_RND_P: + cpu_fprintf(f, "RP "); + break; + } } void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR Laurent Vivier @ 2017-06-20 22:55 ` Richard Henderson 0 siblings, 0 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:55 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > Signed-off-by: Laurent Vivier<laurent@vivier.eu> > --- > target/m68k/cpu.c | 2 +- > target/m68k/cpu.h | 43 +++++- > target/m68k/fpu_helper.c | 117 +++++++++++++--- > target/m68k/helper.c | 22 ++- > target/m68k/helper.h | 5 +- > target/m68k/translate.c | 351 +++++++++++++++++++++++++++++++++++------------ > 6 files changed, 421 insertions(+), 119 deletions(-) Reviewed-by: Richard Henderson <rth@twiddle.net> r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier ` (4 preceding siblings ...) 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR Laurent Vivier @ 2017-06-20 20:51 ` Laurent Vivier 2017-06-20 22:57 ` Richard Henderson 2017-06-28 20:44 ` Laurent Vivier 5 siblings, 2 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-20 20:51 UTC (permalink / raw) To: qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson, Laurent Vivier Signed-off-by: Laurent Vivier <laurent@vivier.eu> --- linux-user/signal.c | 41 +++++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/linux-user/signal.c b/linux-user/signal.c index 3d18d1b..d3753e4 100644 --- a/linux-user/signal.c +++ b/linux-user/signal.c @@ -5673,6 +5673,24 @@ give_sigsegv: force_sigsegv(sig); } +static inline void target_rt_save_fpu_state(struct target_ucontext *uc, + CPUM68KState *env) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + + __put_user(env->fpcr, &fpregs->f_fpcntl[0]); + __put_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high = env->fregs[i].d.high << 16; + __put_user(high, &fpregs->f_fpregs[i * 3]); + __put_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + static inline int target_rt_setup_ucontext(struct target_ucontext *uc, CPUM68KState *env) { @@ -5699,9 +5717,30 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc, __put_user(env->pc, &gregs[16]); __put_user(sr, &gregs[17]); + target_rt_save_fpu_state(uc, env); + return 0; } +static inline void target_rt_restore_fpu_state(CPUM68KState *env, + struct target_ucontext *uc) +{ + int i; + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; + + __get_user(env->fpcr, &fpregs->f_fpcntl[0]); + __get_user(env->fpsr, &fpregs->f_fpcntl[1]); + /* fpiar is not emulated */ + + for (i = 0; i < 8; i++) { + uint32_t high; + __get_user(high, &fpregs->f_fpregs[i * 3]); + env->fregs[i].d.high = high >> 16; + __get_user(env->fregs[i].d.low, + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); + } +} + static inline int target_rt_restore_ucontext(CPUM68KState *env, struct target_ucontext *uc) { @@ -5733,6 +5772,8 @@ static inline int target_rt_restore_ucontext(CPUM68KState *env, __get_user(temp, &gregs[17]); cpu_m68k_set_ccr(env, temp); + target_rt_restore_fpu_state(env, uc); + return 0; badframe: -- 2.9.4 ^ permalink raw reply related [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext Laurent Vivier @ 2017-06-20 22:57 ` Richard Henderson 2017-06-28 20:44 ` Laurent Vivier 1 sibling, 0 replies; 22+ messages in thread From: Richard Henderson @ 2017-06-20 22:57 UTC (permalink / raw) To: Laurent Vivier, qemu-devel Cc: Riku Voipio, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé On 06/20/2017 01:51 PM, Laurent Vivier wrote: > + __get_user(env->fpcr, &fpregs->f_fpcntl[0]); cpu_m68k_set_fpcr r~ ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext Laurent Vivier 2017-06-20 22:57 ` Richard Henderson @ 2017-06-28 20:44 ` Laurent Vivier 2017-06-29 13:46 ` Riku Voipio 1 sibling, 1 reply; 22+ messages in thread From: Laurent Vivier @ 2017-06-28 20:44 UTC (permalink / raw) To: Riku Voipio Cc: qemu-devel, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson Riku, could you ACK this patch, so I will add it in my next pull request for m68k? Thanks, Laurent Le 20/06/2017 à 22:51, Laurent Vivier a écrit : > Signed-off-by: Laurent Vivier <laurent@vivier.eu> > --- > linux-user/signal.c | 41 +++++++++++++++++++++++++++++++++++++++++ > 1 file changed, 41 insertions(+) > > diff --git a/linux-user/signal.c b/linux-user/signal.c > index 3d18d1b..d3753e4 100644 > --- a/linux-user/signal.c > +++ b/linux-user/signal.c > @@ -5673,6 +5673,24 @@ give_sigsegv: > force_sigsegv(sig); > } > > +static inline void target_rt_save_fpu_state(struct target_ucontext *uc, > + CPUM68KState *env) > +{ > + int i; > + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; > + > + __put_user(env->fpcr, &fpregs->f_fpcntl[0]); > + __put_user(env->fpsr, &fpregs->f_fpcntl[1]); > + /* fpiar is not emulated */ > + > + for (i = 0; i < 8; i++) { > + uint32_t high = env->fregs[i].d.high << 16; > + __put_user(high, &fpregs->f_fpregs[i * 3]); > + __put_user(env->fregs[i].d.low, > + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); > + } > +} > + > static inline int target_rt_setup_ucontext(struct target_ucontext *uc, > CPUM68KState *env) > { > @@ -5699,9 +5717,30 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc, > __put_user(env->pc, &gregs[16]); > __put_user(sr, &gregs[17]); > > + target_rt_save_fpu_state(uc, env); > + > return 0; > } > > +static inline void target_rt_restore_fpu_state(CPUM68KState *env, > + struct target_ucontext *uc) > +{ > + int i; > + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; > + > + __get_user(env->fpcr, &fpregs->f_fpcntl[0]); > + __get_user(env->fpsr, &fpregs->f_fpcntl[1]); > + /* fpiar is not emulated */ > + > + for (i = 0; i < 8; i++) { > + uint32_t high; > + __get_user(high, &fpregs->f_fpregs[i * 3]); > + env->fregs[i].d.high = high >> 16; > + __get_user(env->fregs[i].d.low, > + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); > + } > +} > + > static inline int target_rt_restore_ucontext(CPUM68KState *env, > struct target_ucontext *uc) > { > @@ -5733,6 +5772,8 @@ static inline int target_rt_restore_ucontext(CPUM68KState *env, > __get_user(temp, &gregs[17]); > cpu_m68k_set_ccr(env, temp); > > + target_rt_restore_fpu_state(env, uc); > + > return 0; > > badframe: > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-28 20:44 ` Laurent Vivier @ 2017-06-29 13:46 ` Riku Voipio 2017-06-29 14:11 ` Laurent Vivier 0 siblings, 1 reply; 22+ messages in thread From: Riku Voipio @ 2017-06-29 13:46 UTC (permalink / raw) To: Laurent Vivier Cc: qemu-devel, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson On Wed, Jun 28, 2017 at 10:44:57PM +0200, Laurent Vivier wrote: > Riku, > > could you ACK this patch, so I will add it in my next pull request for m68k? Whatabout Richard's comment about using cpu_m68k_set_fpcr ? Riku > Thanks, > Laurent > > Le 20/06/2017 à 22:51, Laurent Vivier a écrit : > > Signed-off-by: Laurent Vivier <laurent@vivier.eu> > > --- > > linux-user/signal.c | 41 +++++++++++++++++++++++++++++++++++++++++ > > 1 file changed, 41 insertions(+) > > > > diff --git a/linux-user/signal.c b/linux-user/signal.c > > index 3d18d1b..d3753e4 100644 > > --- a/linux-user/signal.c > > +++ b/linux-user/signal.c > > @@ -5673,6 +5673,24 @@ give_sigsegv: > > force_sigsegv(sig); > > } > > > > +static inline void target_rt_save_fpu_state(struct target_ucontext *uc, > > + CPUM68KState *env) > > +{ > > + int i; > > + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; > > + > > + __put_user(env->fpcr, &fpregs->f_fpcntl[0]); > > + __put_user(env->fpsr, &fpregs->f_fpcntl[1]); > > + /* fpiar is not emulated */ > > + > > + for (i = 0; i < 8; i++) { > > + uint32_t high = env->fregs[i].d.high << 16; > > + __put_user(high, &fpregs->f_fpregs[i * 3]); > > + __put_user(env->fregs[i].d.low, > > + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); > > + } > > +} > > + > > static inline int target_rt_setup_ucontext(struct target_ucontext *uc, > > CPUM68KState *env) > > { > > @@ -5699,9 +5717,30 @@ static inline int target_rt_setup_ucontext(struct target_ucontext *uc, > > __put_user(env->pc, &gregs[16]); > > __put_user(sr, &gregs[17]); > > > > + target_rt_save_fpu_state(uc, env); > > + > > return 0; > > } > > > > +static inline void target_rt_restore_fpu_state(CPUM68KState *env, > > + struct target_ucontext *uc) > > +{ > > + int i; > > + target_fpregset_t *fpregs = &uc->tuc_mcontext.fpregs; > > + > > + __get_user(env->fpcr, &fpregs->f_fpcntl[0]); > > + __get_user(env->fpsr, &fpregs->f_fpcntl[1]); > > + /* fpiar is not emulated */ > > + > > + for (i = 0; i < 8; i++) { > > + uint32_t high; > > + __get_user(high, &fpregs->f_fpregs[i * 3]); > > + env->fregs[i].d.high = high >> 16; > > + __get_user(env->fregs[i].d.low, > > + (uint64_t *)&fpregs->f_fpregs[i * 3 + 1]); > > + } > > +} > > + > > static inline int target_rt_restore_ucontext(CPUM68KState *env, > > struct target_ucontext *uc) > > { > > @@ -5733,6 +5772,8 @@ static inline int target_rt_restore_ucontext(CPUM68KState *env, > > __get_user(temp, &gregs[17]); > > cpu_m68k_set_ccr(env, temp); > > > > + target_rt_restore_fpu_state(env, uc); > > + > > return 0; > > > > badframe: > > > ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-29 13:46 ` Riku Voipio @ 2017-06-29 14:11 ` Laurent Vivier 2017-06-29 14:17 ` Laurent Vivier 0 siblings, 1 reply; 22+ messages in thread From: Laurent Vivier @ 2017-06-29 14:11 UTC (permalink / raw) To: Riku Voipio Cc: qemu-devel, Thomas Huth, Aurelien Jarno, Philippe Mathieu-Daudé, Richard Henderson Le 29/06/2017 à 15:46, Riku Voipio a écrit : > On Wed, Jun 28, 2017 at 10:44:57PM +0200, Laurent Vivier wrote: >> Riku, >> >> could you ACK this patch, so I will add it in my next pull request for m68k? > > Whatabout Richard's comment about using cpu_m68k_set_fpcr ? You're right. I've fixed it my tree and forgotten to send it. I'm going to resend it. Thanks, Laurent ^ permalink raw reply [flat|nested] 22+ messages in thread
* Re: [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext 2017-06-29 14:11 ` Laurent Vivier @ 2017-06-29 14:17 ` Laurent Vivier 0 siblings, 0 replies; 22+ messages in thread From: Laurent Vivier @ 2017-06-29 14:17 UTC (permalink / raw) To: Riku Voipio Cc: Thomas Huth, Richard Henderson, qemu-devel, Aurelien Jarno, Philippe Mathieu-Daudé Le 29/06/2017 à 16:11, Laurent Vivier a écrit : > Le 29/06/2017 à 15:46, Riku Voipio a écrit : >> On Wed, Jun 28, 2017 at 10:44:57PM +0200, Laurent Vivier wrote: >>> Riku, >>> >>> could you ACK this patch, so I will add it in my next pull request for m68k? >> >> Whatabout Richard's comment about using cpu_m68k_set_fpcr ? > > You're right. I've fixed it my tree and forgotten to send it. > > I'm going to resend it. In fact the patch has been re-sent out of the series: https://lists.gnu.org/archive/html/qemu-devel/2017-06/msg04911.html Thanks, Laurent ^ permalink raw reply [flat|nested] 22+ messages in thread
end of thread, other threads:[~2017-06-29 14:18 UTC | newest] Thread overview: 22+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2017-06-20 20:51 [Qemu-devel] [PATCH v5 0/6] target-m68k: implement 680x0 FPU Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 1/6] target-m68k: move fmove CR to a function Laurent Vivier 2017-06-20 22:50 ` Richard Henderson 2017-06-21 15:54 ` Philippe Mathieu-Daudé 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 2/6] target-m68k: initialize FPU registers Laurent Vivier 2017-06-20 22:44 ` Richard Henderson 2017-06-20 22:51 ` Richard Henderson 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 3/6] target-m68k: use floatx80 internally Laurent Vivier 2017-06-20 22:50 ` Richard Henderson 2017-06-21 16:18 ` Philippe Mathieu-Daudé 2017-06-21 16:37 ` Richard Henderson 2017-06-21 16:45 ` Laurent Vivier 2017-06-26 4:47 ` Philippe Mathieu-Daudé 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 4/6] target-m68k: define 96bit FP registers for gdb on 680x0 Laurent Vivier 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 5/6] target-m68k: add FPCR and FPSR Laurent Vivier 2017-06-20 22:55 ` Richard Henderson 2017-06-20 20:51 ` [Qemu-devel] [PATCH v5 6/6] target-m68k, linux-user: manage FP registers in ucontext Laurent Vivier 2017-06-20 22:57 ` Richard Henderson 2017-06-28 20:44 ` Laurent Vivier 2017-06-29 13:46 ` Riku Voipio 2017-06-29 14:11 ` Laurent Vivier 2017-06-29 14:17 ` Laurent Vivier
This is an external index of several public inboxes, see mirroring instructions on how to clone and mirror all data and code used by this external index.