mirror of
https://github.com/yuzu-emu/unicorn.git
synced 2025-01-11 02:15:37 +00:00
target-m68k: Inline shifts
Also manage word and byte operands and fix the computation of overflow in the case of M68000 arithmetic shifts. Backports commit 367790cce8e14131426f5190dfd7d1bdbf656e4d from qemu
This commit is contained in:
parent
0c00b036be
commit
cf9424d60d
|
@ -198,58 +198,6 @@ void HELPER(set_sr)(CPUM68KState *env, uint32_t val)
|
|||
m68k_switch_sp(env);
|
||||
}
|
||||
|
||||
uint32_t HELPER(shl_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint64_t result;
|
||||
|
||||
shift &= 63;
|
||||
result = (uint64_t)val << shift;
|
||||
|
||||
env->cc_c = (result >> 32) & 1;
|
||||
env->cc_n = result;
|
||||
env->cc_z = result;
|
||||
env->cc_v = 0;
|
||||
env->cc_x = shift ? env->cc_c : env->cc_x;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t HELPER(shr_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint64_t temp;
|
||||
uint32_t result;
|
||||
|
||||
shift &= 63;
|
||||
temp = (uint64_t)val << 32 >> shift;
|
||||
result = temp >> 32;
|
||||
|
||||
env->cc_c = (temp >> 31) & 1;
|
||||
env->cc_n = result;
|
||||
env->cc_z = result;
|
||||
env->cc_v = 0;
|
||||
env->cc_x = shift ? env->cc_c : env->cc_x;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
uint32_t HELPER(sar_cc)(CPUM68KState *env, uint32_t val, uint32_t shift)
|
||||
{
|
||||
uint64_t temp;
|
||||
uint32_t result;
|
||||
|
||||
shift &= 63;
|
||||
temp = (int64_t)val << 32 >> shift;
|
||||
result = temp >> 32;
|
||||
|
||||
env->cc_c = (temp >> 31) & 1;
|
||||
env->cc_n = result;
|
||||
env->cc_z = result;
|
||||
env->cc_v = result ^ val;
|
||||
env->cc_x = shift ? env->cc_c : env->cc_x;
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/* FPU helpers. */
|
||||
uint32_t HELPER(f64_to_i32)(CPUM68KState *env, float64 val)
|
||||
{
|
||||
|
|
|
@ -9,9 +9,6 @@ DEF_HELPER_4(divul, void, env, int, int, i32)
|
|||
DEF_HELPER_4(divsl, void, env, int, int, s32)
|
||||
DEF_HELPER_4(divull, void, env, int, int, i32)
|
||||
DEF_HELPER_4(divsll, void, env, int, int, s32)
|
||||
DEF_HELPER_3(shl_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(shr_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_3(sar_cc, i32, env, i32, i32)
|
||||
DEF_HELPER_2(set_sr, void, env, i32)
|
||||
DEF_HELPER_3(movec, void, env, i32, i32)
|
||||
DEF_HELPER_4(cas2w, void, env, i32, i32, i32)
|
||||
|
|
|
@ -2987,50 +2987,238 @@ DISAS_INSN(addx_mem)
|
|||
gen_store(s, opsize, addr_dest, tcg_ctx->QREG_CC_N);
|
||||
}
|
||||
|
||||
/* TODO: This could be implemented without helper functions. */
|
||||
DISAS_INSN(shift_im)
|
||||
static inline void shift_im(DisasContext *s, uint16_t insn, int opsize)
|
||||
{
|
||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||
TCGv reg;
|
||||
int tmp;
|
||||
TCGv shift;
|
||||
TCGv QREG_CC_Z = tcg_ctx->QREG_CC_Z;
|
||||
TCGv QREG_CC_N = tcg_ctx->QREG_CC_N;
|
||||
TCGv QREG_CC_X = tcg_ctx->QREG_CC_X;
|
||||
TCGv QREG_CC_V = tcg_ctx->QREG_CC_V;
|
||||
TCGv QREG_CC_C = tcg_ctx->QREG_CC_C;
|
||||
|
||||
set_cc_op(s, CC_OP_FLAGS);
|
||||
int count = (insn >> 9) & 7;
|
||||
int logical = insn & 8;
|
||||
int left = insn & 0x100;
|
||||
int bits = opsize_bytes(opsize) * 8;
|
||||
TCGv reg = gen_extend(s, DREG(insn, 0), opsize, !logical);
|
||||
|
||||
reg = DREG(insn, 0);
|
||||
tmp = (insn >> 9) & 7;
|
||||
if (tmp == 0)
|
||||
tmp = 8;
|
||||
shift = tcg_const_i32(tcg_ctx, tmp);
|
||||
/* No need to flush flags becuse we know we will set C flag. */
|
||||
if (insn & 0x100) {
|
||||
gen_helper_shl_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
if (count == 0) {
|
||||
count = 8;
|
||||
}
|
||||
|
||||
tcg_gen_movi_i32(tcg_ctx, QREG_CC_V, 0);
|
||||
if (left) {
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_C, reg, bits - count);
|
||||
tcg_gen_shli_i32(tcg_ctx, QREG_CC_N, reg, count);
|
||||
|
||||
/* Note that ColdFire always clears V (done above),
|
||||
while M68000 sets if the most significant bit is changed at
|
||||
any time during the shift operation */
|
||||
if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) {
|
||||
/* if shift count >= bits, V is (reg != 0) */
|
||||
if (count >= bits) {
|
||||
tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, QREG_CC_V, reg, QREG_CC_V);
|
||||
} else {
|
||||
TCGv t0 = tcg_temp_new(tcg_ctx);
|
||||
tcg_gen_sari_i32(tcg_ctx, QREG_CC_V, reg, bits - 1);
|
||||
tcg_gen_sari_i32(tcg_ctx, t0, reg, bits - count - 1);
|
||||
tcg_gen_setcond_i32(tcg_ctx, TCG_COND_NE, QREG_CC_V, QREG_CC_V, t0);
|
||||
tcg_temp_free(tcg_ctx, t0);
|
||||
}
|
||||
tcg_gen_neg_i32(tcg_ctx, QREG_CC_V, QREG_CC_V);
|
||||
}
|
||||
} else {
|
||||
if (insn & 8) {
|
||||
gen_helper_shr_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_C, reg, count - 1);
|
||||
if (logical) {
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_N, reg, count);
|
||||
} else {
|
||||
gen_helper_sar_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
tcg_gen_sari_i32(tcg_ctx, QREG_CC_N, reg, count);
|
||||
}
|
||||
}
|
||||
|
||||
gen_ext(s, QREG_CC_N, QREG_CC_N, opsize, 1);
|
||||
tcg_gen_andi_i32(tcg_ctx, QREG_CC_C, QREG_CC_C, 1);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_Z, QREG_CC_N);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_X, QREG_CC_C);
|
||||
|
||||
gen_partset_reg(s, opsize, DREG(insn, 0), QREG_CC_N);
|
||||
set_cc_op(s, CC_OP_FLAGS);
|
||||
}
|
||||
|
||||
static inline void shift_reg(DisasContext *s, uint16_t insn, int opsize)
|
||||
{
|
||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||
TCGv QREG_CC_Z = tcg_ctx->QREG_CC_Z;
|
||||
TCGv QREG_CC_N = tcg_ctx->QREG_CC_N;
|
||||
TCGv QREG_CC_X = tcg_ctx->QREG_CC_X;
|
||||
TCGv QREG_CC_V = tcg_ctx->QREG_CC_V;
|
||||
TCGv QREG_CC_C = tcg_ctx->QREG_CC_C;
|
||||
|
||||
int logical = insn & 8;
|
||||
int left = insn & 0x100;
|
||||
int bits = opsize_bytes(opsize) * 8;
|
||||
TCGv reg = gen_extend(s, DREG(insn, 0), opsize, !logical);
|
||||
TCGv s32;
|
||||
TCGv_i64 t64, s64;
|
||||
|
||||
t64 = tcg_temp_new_i64(tcg_ctx);
|
||||
s64 = tcg_temp_new_i64(tcg_ctx);
|
||||
s32 = tcg_temp_new(tcg_ctx);
|
||||
|
||||
/* Note that m68k truncates the shift count modulo 64, not 32.
|
||||
In addition, a 64-bit shift makes it easy to find "the last
|
||||
bit shifted out", for the carry flag. */
|
||||
tcg_gen_andi_i32(tcg_ctx, s32, DREG(insn, 9), 63);
|
||||
tcg_gen_extu_i32_i64(tcg_ctx, s64, s32);
|
||||
tcg_gen_extu_i32_i64(tcg_ctx, t64, reg);
|
||||
|
||||
/* Optimistically set V=0. Also used as a zero source below. */
|
||||
tcg_gen_movi_i32(tcg_ctx, QREG_CC_V, 0);
|
||||
if (left) {
|
||||
tcg_gen_shl_i64(tcg_ctx, t64, t64, s64);
|
||||
|
||||
if (opsize == OS_LONG) {
|
||||
tcg_gen_extr_i64_i32(tcg_ctx, QREG_CC_N, QREG_CC_C, t64);
|
||||
/* Note that C=0 if shift count is 0, and we get that for free. */
|
||||
} else {
|
||||
TCGv zero = tcg_const_i32(tcg_ctx, 0);
|
||||
tcg_gen_extrl_i64_i32(tcg_ctx, QREG_CC_N, t64);
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_C, QREG_CC_N, bits);
|
||||
tcg_gen_movcond_i32(tcg_ctx, TCG_COND_EQ, QREG_CC_C,
|
||||
s32, zero, zero, QREG_CC_C);
|
||||
tcg_temp_free(tcg_ctx, zero);
|
||||
}
|
||||
tcg_gen_andi_i32(tcg_ctx, QREG_CC_C, QREG_CC_C, 1);
|
||||
|
||||
/* X = C, but only if the shift count was non-zero. */
|
||||
tcg_gen_movcond_i32(tcg_ctx, TCG_COND_NE, QREG_CC_X, s32, QREG_CC_V,
|
||||
QREG_CC_C, QREG_CC_X);
|
||||
|
||||
/* M68000 sets V if the most significant bit is changed at
|
||||
* any time during the shift operation. Do this via creating
|
||||
* an extension of the sign bit, comparing, and discarding
|
||||
* the bits below the sign bit. I.e.
|
||||
* int64_t s = (intN_t)reg;
|
||||
* int64_t t = (int64_t)(intN_t)reg << count;
|
||||
* V = ((s ^ t) & (-1 << (bits - 1))) != 0
|
||||
*/
|
||||
if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) {
|
||||
TCGv_i64 tt = tcg_const_i64(tcg_ctx, 32);
|
||||
/* if shift is greater than 32, use 32 */
|
||||
tcg_gen_movcond_i64(tcg_ctx, TCG_COND_GT, s64, s64, tt, tt, s64);
|
||||
tcg_temp_free_i64(tcg_ctx, tt);
|
||||
/* Sign extend the input to 64 bits; re-do the shift. */
|
||||
tcg_gen_ext_i32_i64(tcg_ctx, t64, reg);
|
||||
tcg_gen_shl_i64(tcg_ctx, s64, t64, s64);
|
||||
/* Clear all bits that are unchanged. */
|
||||
tcg_gen_xor_i64(tcg_ctx, t64, t64, s64);
|
||||
/* Ignore the bits below the sign bit. */
|
||||
tcg_gen_andi_i64(tcg_ctx, t64, t64, -1ULL << (bits - 1));
|
||||
/* If any bits remain set, we have overflow. */
|
||||
tcg_gen_setcondi_i64(tcg_ctx, TCG_COND_NE, t64, t64, 0);
|
||||
tcg_gen_extrl_i64_i32(tcg_ctx, QREG_CC_V, t64);
|
||||
tcg_gen_neg_i32(tcg_ctx, QREG_CC_V, QREG_CC_V);
|
||||
}
|
||||
} else {
|
||||
tcg_gen_shli_i64(tcg_ctx, t64, t64, 32);
|
||||
if (logical) {
|
||||
tcg_gen_shr_i64(tcg_ctx, t64, t64, s64);
|
||||
} else {
|
||||
tcg_gen_sar_i64(tcg_ctx, t64, t64, s64);
|
||||
}
|
||||
tcg_gen_extr_i64_i32(tcg_ctx, QREG_CC_C, QREG_CC_N, t64);
|
||||
|
||||
/* Note that C=0 if shift count is 0, and we get that for free. */
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_C, QREG_CC_C, 31);
|
||||
|
||||
/* X = C, but only if the shift count was non-zero. */
|
||||
tcg_gen_movcond_i32(tcg_ctx, TCG_COND_NE, QREG_CC_X, s32, QREG_CC_V,
|
||||
QREG_CC_C, QREG_CC_X);
|
||||
}
|
||||
gen_ext(s, QREG_CC_N, QREG_CC_N, opsize, 1);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_Z, QREG_CC_N);
|
||||
|
||||
tcg_temp_free(tcg_ctx, s32);
|
||||
tcg_temp_free_i64(tcg_ctx, s64);
|
||||
tcg_temp_free_i64(tcg_ctx, t64);
|
||||
|
||||
/* Write back the result. */
|
||||
gen_partset_reg(s, opsize, DREG(insn, 0), QREG_CC_N);
|
||||
set_cc_op(s, CC_OP_FLAGS);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift8_im)
|
||||
{
|
||||
shift_im(s, insn, OS_BYTE);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift16_im)
|
||||
{
|
||||
shift_im(s, insn, OS_WORD);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift_im)
|
||||
{
|
||||
shift_im(s, insn, OS_LONG);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift8_reg)
|
||||
{
|
||||
shift_reg(s, insn, OS_BYTE);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift16_reg)
|
||||
{
|
||||
shift_reg(s, insn, OS_WORD);
|
||||
}
|
||||
|
||||
DISAS_INSN(shift_reg)
|
||||
{
|
||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||
TCGv reg;
|
||||
TCGv shift;
|
||||
shift_reg(s, insn, OS_LONG);
|
||||
}
|
||||
|
||||
reg = DREG(insn, 0);
|
||||
shift = DREG(insn, 9);
|
||||
if (insn & 0x100) {
|
||||
gen_helper_shl_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
DISAS_INSN(shift_mem)
|
||||
{
|
||||
TCGContext *tcg_ctx = s->uc->tcg_ctx;
|
||||
TCGv QREG_CC_Z = tcg_ctx->QREG_CC_Z;
|
||||
TCGv QREG_CC_N = tcg_ctx->QREG_CC_N;
|
||||
TCGv QREG_CC_X = tcg_ctx->QREG_CC_X;
|
||||
TCGv QREG_CC_V = tcg_ctx->QREG_CC_V;
|
||||
TCGv QREG_CC_C = tcg_ctx->QREG_CC_C;
|
||||
|
||||
int logical = insn & 8;
|
||||
int left = insn & 0x100;
|
||||
TCGv src;
|
||||
TCGv addr;
|
||||
|
||||
SRC_EA(env, src, OS_WORD, !logical, &addr);
|
||||
tcg_gen_movi_i32(tcg_ctx, QREG_CC_V, 0);
|
||||
if (left) {
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_C, src, 15);
|
||||
tcg_gen_shli_i32(tcg_ctx, QREG_CC_N, src, 1);
|
||||
|
||||
/* Note that ColdFire always clears V,
|
||||
while M68000 sets if the most significant bit is changed at
|
||||
any time during the shift operation */
|
||||
if (!logical && m68k_feature(s->env, M68K_FEATURE_M68000)) {
|
||||
src = gen_extend(s, src, OS_WORD, 1);
|
||||
tcg_gen_xor_i32(tcg_ctx, QREG_CC_V, QREG_CC_N, src);
|
||||
}
|
||||
} else {
|
||||
if (insn & 8) {
|
||||
gen_helper_shr_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_C, src);
|
||||
if (logical) {
|
||||
tcg_gen_shri_i32(tcg_ctx, QREG_CC_N, src, 1);
|
||||
} else {
|
||||
gen_helper_sar_cc(tcg_ctx, reg, tcg_ctx->cpu_env, reg, shift);
|
||||
tcg_gen_sari_i32(tcg_ctx, QREG_CC_N, src, 1);
|
||||
}
|
||||
}
|
||||
|
||||
gen_ext(s, QREG_CC_N, QREG_CC_N, OS_WORD, 1);
|
||||
tcg_gen_andi_i32(tcg_ctx, QREG_CC_C, QREG_CC_C, 1);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_Z, QREG_CC_N);
|
||||
tcg_gen_mov_i32(tcg_ctx, QREG_CC_X, QREG_CC_C);
|
||||
|
||||
DEST_EA(env, insn, OS_WORD, QREG_CC_N, &addr);
|
||||
set_cc_op(s, CC_OP_FLAGS);
|
||||
}
|
||||
|
||||
|
@ -4141,6 +4329,13 @@ void register_m68k_insns (CPUM68KState *env)
|
|||
INSN(adda, d0c0, f0c0, M68000);
|
||||
INSN(shift_im, e080, f0f0, CF_ISA_A);
|
||||
INSN(shift_reg, e0a0, f0f0, CF_ISA_A);
|
||||
INSN(shift8_im, e000, f0f0, M68000);
|
||||
INSN(shift16_im, e040, f0f0, M68000);
|
||||
INSN(shift_im, e080, f0f0, M68000);
|
||||
INSN(shift8_reg, e020, f0f0, M68000);
|
||||
INSN(shift16_reg, e060, f0f0, M68000);
|
||||
INSN(shift_reg, e0a0, f0f0, M68000);
|
||||
INSN(shift_mem, e0c0, fcc0, M68000);
|
||||
INSN(undef_fpu, f000, f000, CF_ISA_A);
|
||||
INSN(fpu, f200, ffc0, CF_FPU);
|
||||
INSN(fbcc, f280, ffc0, CF_FPU);
|
||||
|
|
Loading…
Reference in a new issue