From: Laurent Vivier <laur...@vivier.eu> - the source register to be divided is a 32bit, so don't extend the 16bit value sign - don't modify the destination operand on overflow - don't modify N and Z flags on overflow (documentation says "undefined" but real 68040 is doing like this)
Signed-off-by: Laurent Vivier <laur...@vivier.eu> --- target-m68k/op_helper.c | 46 ++++++++++++++++++++++++++-------------------- target-m68k/translate.c | 26 +++++++++++++++++--------- 2 files changed, 43 insertions(+), 29 deletions(-) diff --git a/target-m68k/op_helper.c b/target-m68k/op_helper.c index a6c8148..c270259 100644 --- a/target-m68k/op_helper.c +++ b/target-m68k/op_helper.c @@ -213,17 +213,20 @@ void HELPER(divu)(CPUState *env, uint32_t word) /* Avoid using a PARAM1 of zero. This breaks dyngen because it uses the address of a symbol, and gcc knows symbols can't have address zero. */ - if (word && quot > 0xffff) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if ((int32_t)quot < 0) - flags |= CCF_N; - /* Don't modify destination if overflow occured. */ - if ((flags & CCF_V) == 0) { - env->div1 = quot; - env->div2 = rem; + if (word && quot > 0xffff) { + /* real 68040 keep Z and N on overflow, + * whereas documentation says "undefined" + */ + flags |= CCF_V | (env->cc_dest & (CCF_Z|CCF_N)); + } else { + if (quot == 0) + flags |= CCF_Z; + else if ((int16_t)quot < 0) + flags |= CCF_N; } + + env->div1 = quot; + env->div2 = rem; env->cc_dest = flags; } @@ -242,17 +245,20 @@ void HELPER(divs)(CPUState *env, uint32_t word) quot = num / den; rem = num % den; flags = 0; - if (word && quot != (int16_t)quot) - flags |= CCF_V; - if (quot == 0) - flags |= CCF_Z; - else if (quot < 0) - flags |= CCF_N; - /* Don't modify destination if overflow occured. */ - if ((flags & CCF_V) == 0) { - env->div1 = quot; - env->div2 = rem; + if (word && quot != (int16_t)quot) { + /* real 68040 keep Z and N on overflow, + * whereas documentation says "undefined" + */ + flags |= CCF_V | (env->cc_dest & (CCF_Z|CCF_N)); + } else { + if (quot == 0) + flags |= CCF_Z; + else if ((int16_t)quot < 0) + flags |= CCF_N; } + + env->div1 = quot; + env->div2 = rem; env->cc_dest = flags; } diff --git a/target-m68k/translate.c b/target-m68k/translate.c index f5e56bc..52da485 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -993,32 +993,40 @@ DISAS_INSN(mulw) DISAS_INSN(divw) { - TCGv reg; + TCGv dest; TCGv tmp; TCGv src; int sign; + int l1; sign = (insn & 0x100) != 0; - reg = DREG(insn, 9); - if (sign) { - tcg_gen_ext16s_i32(QREG_DIV1, reg); - } else { - tcg_gen_ext16u_i32(QREG_DIV1, reg); - } + + /* dest.l / src.w */ + + dest = DREG(insn, 9); + tcg_gen_mov_i32(QREG_DIV1, dest); + SRC_EA(src, OS_WORD, sign, NULL); tcg_gen_mov_i32(QREG_DIV2, src); + + /* div1 / div2 */ + if (sign) { gen_helper_divs(cpu_env, tcg_const_i32(1)); } else { gen_helper_divu(cpu_env, tcg_const_i32(1)); } + s->cc_op = CC_OP_FLAGS; + + l1 = gen_new_label(); + gen_jmpcc(s, 9 /* V */, l1); tmp = tcg_temp_new(); src = tcg_temp_new(); tcg_gen_ext16u_i32(tmp, QREG_DIV1); tcg_gen_shli_i32(src, QREG_DIV2, 16); - tcg_gen_or_i32(reg, tmp, src); - s->cc_op = CC_OP_FLAGS; + tcg_gen_or_i32(dest, tmp, src); + gen_set_label(l1); } DISAS_INSN(divl) -- 1.7.2.3