Signed-off-by: Laurent Vivier <laur...@vivier.eu> --- target/m68k/cpu.h | 28 +++++++++++++ target/m68k/fpu_helper.c | 107 ++++++++++++++++++++++++++++++++++++++++++++++- target/m68k/helper.h | 1 + target/m68k/translate.c | 27 ++++++++++++ 4 files changed, 162 insertions(+), 1 deletion(-)
diff --git a/target/m68k/cpu.h b/target/m68k/cpu.h index 6b3cb26..7985dc3 100644 --- a/target/m68k/cpu.h +++ b/target/m68k/cpu.h @@ -57,6 +57,15 @@ #define EXCP_TRAP15 47 /* User trap #15. */ #define EXCP_UNSUPPORTED 61 #define EXCP_ICE 13 +#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_RTE 0x100 #define EXCP_HALT_INSN 0x101 @@ -222,6 +231,25 @@ typedef enum { #define FPSR_CC_Z 0x04000000 /* Zero */ #define FPSR_CC_N 0x08000000 /* Negative */ +/* Exception Status */ +#define FPSR_ES_MASK 0x0000ff00 +#define FPSR_ES_BSUN 0x00008000 /* Branch Set on Unordered */ +#define FPSR_ES_SNAN 0x00004000 /* Signaling Not-A-Number */ +#define FPSR_ES_OPERR 0x00002000 /* Operand Error */ +#define FPSR_ES_OVFL 0x00001000 /* Overflow */ +#define FPSR_ES_UNFL 0x00000800 /* Underflow */ +#define FPSR_ES_DZ 0x00000400 /* Divide by Zero */ +#define FPSR_ES_INEX2 0x00000200 /* Inexact operation */ +#define FPSR_ES_INEX 0x00000100 /* Inexact decimal input */ + +/* Accrued Exception */ +#define FPSR_AE_MASK 0x000000ff +#define FPSR_AE_IOP 0x00000080 /* Invalid Operation */ +#define FPSR_AE_OVFL 0x00000040 /* Overflow */ +#define FPSR_AE_UNFL 0x00000020 /* Underflow */ +#define FPSR_AE_DZ 0x00000010 /* Divide by Zero */ +#define FPSR_AE_INEX 0x00000008 /* Inexact */ + /* Quotient */ #define FPSR_QT_MASK 0x00ff0000 diff --git a/target/m68k/fpu_helper.c b/target/m68k/fpu_helper.c index 9d39118..1e68c41 100644 --- a/target/m68k/fpu_helper.c +++ b/target/m68k/fpu_helper.c @@ -177,6 +177,70 @@ static void restore_rounding_mode(CPUM68KState *env) } } +static void set_fpsr_exception(CPUM68KState *env) +{ + uint32_t fpsr = 0; + int flags; + + flags = get_float_exception_flags(&env->fp_status); + if (flags == 0) { + return; + } + set_float_exception_flags(0, &env->fp_status); + + if (flags & float_flag_invalid) { + fpsr |= FPSR_AE_IOP; + } + if (flags & float_flag_divbyzero) { + fpsr |= FPSR_AE_DZ; + } + if (flags & float_flag_overflow) { + fpsr |= FPSR_AE_OVFL; + } + if (flags & float_flag_underflow) { + fpsr |= FPSR_AE_UNFL; + } + if (flags & float_flag_inexact) { + fpsr |= FPSR_AE_INEX; + } + + env->fpsr = (env->fpsr & ~FPSR_AE_MASK) | fpsr; +} + +static void fpu_exception(CPUM68KState *env, uint32_t exception) +{ + CPUState *cs = CPU(m68k_env_get_cpu(env)); + + env->fpsr = (env->fpsr & ~FPSR_ES_MASK) | exception; + if (env->fpcr & exception) { + switch (exception) { + case FPSR_ES_BSUN: + cs->exception_index = EXCP_FP_BSUN; + break; + case FPSR_ES_SNAN: + cs->exception_index = EXCP_FP_SNAN; + break; + case FPSR_ES_OPERR: + cs->exception_index = EXCP_FP_OPERR; + break; + case FPSR_ES_OVFL: + cs->exception_index = EXCP_FP_OVFL; + break; + case FPSR_ES_UNFL: + cs->exception_index = EXCP_FP_UNFL; + break; + case FPSR_ES_DZ: + cs->exception_index = EXCP_FP_DZ; + break; + case FPSR_ES_INEX: + case FPSR_ES_INEX2: + cs->exception_index = EXCP_FP_INEX; + break; + } + cpu_loop_exit_restore(cs, GETPC()); + } +} + void cpu_m68k_set_fpcr(CPUM68KState *env, uint32_t val) { env->fpcr = val & 0xffff; @@ -292,10 +356,16 @@ void HELPER(cmp_FP0_FP1)(CPUM68KState *env) { floatx80 fp0 = FP0_to_floatx80(env); floatx80 fp1 = FP1_to_floatx80(env); - int float_compare; + int flags, float_compare; float_compare = floatx80_compare(fp1, fp0, &env->fp_status); env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | float_comp_to_cc(float_compare); + + flags = get_float_exception_flags(&env->fp_status); + if (flags & float_flag_invalid) { + fpu_exception(env, FPSR_ES_OPERR); + } + set_fpsr_exception(env); } void HELPER(tst_FP0)(CPUM68KState *env) @@ -315,4 +385,39 @@ void HELPER(tst_FP0)(CPUM68KState *env) fpsr |= FPSR_CC_Z; } env->fpsr = (env->fpsr & ~FPSR_CC_MASK) | fpsr; + + set_fpsr_exception(env); +} + +void HELPER(update_fpstatus)(CPUM68KState *env) +{ + int flags = get_float_exception_flags(&env->fp_status); + + if (env->fpsr & FPSR_AE_IOP) { + flags |= float_flag_invalid; + } else { + flags &= ~float_flag_invalid; + } + if (env->fpsr & FPSR_AE_DZ) { + flags |= float_flag_divbyzero; + } else { + flags &= ~float_flag_divbyzero; + } + if (env->fpsr & FPSR_AE_OVFL) { + flags |= float_flag_overflow; + } else { + flags &= ~float_flag_overflow; + } + if (env->fpsr & FPSR_AE_UNFL) { + flags |= float_flag_underflow; + } else { + flags &= ~float_flag_underflow; + } + if (env->fpsr & FPSR_AE_INEX) { + flags |= float_flag_inexact; + } else { + flags &= ~float_flag_inexact; + } + + set_float_exception_flags(flags, &env->fp_status); } diff --git a/target/m68k/helper.h b/target/m68k/helper.h index 03fb268..072a6d0 100644 --- a/target/m68k/helper.h +++ b/target/m68k/helper.h @@ -30,6 +30,7 @@ DEF_HELPER_1(div_FP0_FP1, void, env) DEF_HELPER_1(cmp_FP0_FP1, void, env) DEF_HELPER_2(set_fpcr, void, env, i32) DEF_HELPER_1(tst_FP0, void, env) +DEF_HELPER_1(update_fpstatus, void, env) 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 c9a86ae..030773b 100644 --- a/target/m68k/translate.c +++ b/target/m68k/translate.c @@ -4388,6 +4388,7 @@ static void gen_load_fcr(DisasContext *s, TCGv addr, int reg) switch (reg) { case 0: /* FPSR */ tcg_gen_qemu_ld32u(QEMU_FPSR, addr, index); + gen_helper_update_fpstatus(cpu_env); break; case 1: /* FPIAR */ break; @@ -4431,6 +4432,7 @@ static void gen_op_fmove_fcr(CPUM68KState *env, DisasContext *s, case 2: /* FPSR */ SRC_EA(env, val, OS_LONG, 0, NULL); tcg_gen_mov_i32(QEMU_FPSR, val); + gen_helper_update_fpstatus(cpu_env); break; case 4: /* FPCR */ SRC_EA(env, val, OS_LONG, 0, NULL); @@ -5481,6 +5483,21 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, (env->fpsr & FPSR_CC_I) ? 'I' : '-', (env->fpsr & FPSR_CC_Z) ? 'Z' : '-', (env->fpsr & FPSR_CC_N) ? 'N' : '-'); + cpu_fprintf(f, "%c%c%c%c%c%c%c%c ", + (env->fpsr & FPSR_ES_BSUN) ? 'B' : '-', + (env->fpsr & FPSR_ES_SNAN) ? 'A' : '-', + (env->fpsr & FPSR_ES_OPERR) ? 'O' : '-', + (env->fpsr & FPSR_ES_OVFL) ? 'V' : '-', + (env->fpsr & FPSR_ES_UNFL) ? 'U' : '-', + (env->fpsr & FPSR_ES_DZ) ? 'Z' : '-', + (env->fpsr & FPSR_ES_INEX2) ? '2' : '-', + (env->fpcr & FPSR_ES_INEX) ? 'I' : '-'); + cpu_fprintf(f, "%c%c%c%c%c", + (env->fpsr & FPSR_AE_IOP) ? 'O' : '-', + (env->fpsr & FPSR_AE_OVFL) ? 'V' : '-', + (env->fpsr & FPSR_AE_UNFL) ? 'U' : '-', + (env->fpsr & FPSR_AE_DZ) ? 'Z' : '-', + (env->fpcr & FPSR_ES_INEX) ? 'I' : '-'); cpu_fprintf(f, "\n " "FPCR = %04x ", env->fpcr); switch (env->fpcr & FPCR_PREC_MASK) { @@ -5508,6 +5525,16 @@ void m68k_cpu_dump_state(CPUState *cs, FILE *f, fprintf_function cpu_fprintf, cpu_fprintf(f, "RP "); break; } + /* FPCR exception mask uses the same bitmask as FPSR */ + cpu_fprintf(f, "%c%c%c%c%c%c%c%c\n", + (env->fpcr & FPSR_ES_BSUN) ? 'B' : '-', + (env->fpcr & FPSR_ES_SNAN) ? 'A' : '-', + (env->fpcr & FPSR_ES_OPERR) ? 'O' : '-', + (env->fpcr & FPSR_ES_OVFL) ? 'V' : '-', + (env->fpcr & FPSR_ES_UNFL) ? 'U' : '-', + (env->fpcr & FPSR_ES_DZ) ? 'Z' : '-', + (env->fpcr & FPSR_ES_INEX2) ? '2' : '-', + (env->fpcr & FPSR_ES_INEX) ? 'I' : '-'); } void restore_state_to_opc(CPUM68KState *env, TranslationBlock *tb, -- 2.9.3