From patchwork Wed May 4 21:08:40 2016 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Laurent Vivier X-Patchwork-Id: 618672 Return-Path: X-Original-To: incoming@patchwork.ozlabs.org Delivered-To: patchwork-incoming@bilbo.ozlabs.org Received: from lists.gnu.org (lists.gnu.org [IPv6:2001:4830:134:3::11]) (using TLSv1 with cipher AES256-SHA (256/256 bits)) (No client certificate requested) by ozlabs.org (Postfix) with ESMTPS id 3r0W283MzTz9sp7 for ; Thu, 5 May 2016 07:12:00 +1000 (AEST) Received: from localhost ([::1]:50298 helo=lists.gnu.org) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ay45K-0002F7-UA for incoming@patchwork.ozlabs.org; Wed, 04 May 2016 17:11:54 -0400 Received: from eggs.gnu.org ([2001:4830:134:3::10]:45852) by lists.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ay44Y-0000dN-Fi for qemu-devel@nongnu.org; Wed, 04 May 2016 17:11:13 -0400 Received: from Debian-exim by eggs.gnu.org with spam-scanned (Exim 4.71) (envelope-from ) id 1ay44L-0000eJ-TR for qemu-devel@nongnu.org; Wed, 04 May 2016 17:11:01 -0400 Received: from smtp1-g21.free.fr ([212.27.42.1]:32897) by eggs.gnu.org with esmtp (Exim 4.71) (envelope-from ) id 1ay44L-0000Xm-78 for qemu-devel@nongnu.org; Wed, 04 May 2016 17:10:53 -0400 Received: from Quad.localdomain (unknown [IPv6:2a01:e34:eeee:5240:12c3:7bff:fe6b:9a76]) by smtp1-g21.free.fr (Postfix) with ESMTPS id 53153B004D2; Wed, 4 May 2016 21:04:15 +0200 (CEST) From: Laurent Vivier To: qemu-devel@nongnu.org Date: Wed, 4 May 2016 23:08:40 +0200 Message-Id: <1462396135-20925-5-git-send-email-laurent@vivier.eu> X-Mailer: git-send-email 2.5.5 In-Reply-To: <1462396135-20925-1-git-send-email-laurent@vivier.eu> References: <1462392752-17703-1-git-send-email-laurent@vivier.eu> <1462396135-20925-1-git-send-email-laurent@vivier.eu> X-detected-operating-system: by eggs.gnu.org: Windows NT kernel [generic] [fuzzy] X-Received-From: 212.27.42.1 Subject: [Qemu-devel] [PATCH 37/52] target-m68k: add cas/cas2 ops X-BeenThere: qemu-devel@nongnu.org X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , Cc: Laurent Vivier , gerg@uclinux.org, schwab@linux-m68k.org, agraf@suse.de, rth@twiddle.net Errors-To: qemu-devel-bounces+incoming=patchwork.ozlabs.org@nongnu.org Sender: "Qemu-devel" Signed-off-by: Laurent Vivier --- linux-user/main.c | 193 ++++++++++++++++++++++++++++++++++++++++++++++++ target-m68k/cpu.h | 9 +++ target-m68k/qregs.def | 5 ++ target-m68k/translate.c | 175 +++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 382 insertions(+) diff --git a/linux-user/main.c b/linux-user/main.c index 74b02c7..3c51afe 100644 --- a/linux-user/main.c +++ b/linux-user/main.c @@ -2994,6 +2994,194 @@ void cpu_loop(CPUMBState *env) #ifdef TARGET_M68K +static int do_cas(CPUM68KState *env) +{ + int size, is_cas; + int cmp1_reg, upd1_reg; + int cmp2_reg, upd2_reg; + uint32_t dest1, cmp1, addr1; + uint32_t dest2, cmp2, addr2; + int segv = 0; + int z; + + start_exclusive(); + + /* cas_param bits + * 31 -> CAS(0) / CAS2(1) + * 11:13 -> update reg 2 + * 8:10 -> cmp reg 2 + * 5:7 -> update reg 1 + * 2:4 -> cmp reg 1 + * 0:1 -> opsize + */ + + is_cas = (env->cas_param & 0x80000000) == 0; + + size = env->cas_param & 0x3; + + cmp1_reg = (env->cas_param >> 2) & 7; + upd1_reg = (env->cas_param >> 5) & 7; + cmp2_reg = (env->cas_param >> 8) & 7; + upd2_reg = (env->cas_param >> 11) & 7; + + addr1 = env->cas_addr1; + addr2 = env->cas_addr2; + + switch (size) { + case OS_BYTE: + segv = get_user_u8(dest1, addr1); + cmp1 = (uint8_t)env->dregs[cmp1_reg]; + break; + case OS_WORD: + segv = get_user_u16(dest1, addr1); + cmp1 = (uint16_t)env->dregs[cmp1_reg]; + break; + case OS_LONG: + default: + segv = get_user_u32(dest1, addr1); + cmp1 = env->dregs[cmp1_reg]; + break; + } + if (segv) { + env->mmu.ar = addr1; + goto done; + } + env->cc_n = dest1; + env->cc_v = cmp1; + z = dest1 - cmp1; + env->cc_op = CC_OP_CMPB + size; + + if (is_cas) { + /* CAS */ + + /* if (addr1) == cmp1 then (addr1) = upd1 */ + + if (z == 0) { + switch (size) { + case OS_BYTE: + segv = put_user_u8(env->dregs[upd1_reg], addr1); + break; + case OS_WORD: + segv = put_user_u16(env->dregs[upd1_reg], addr1); + break; + case OS_LONG: + segv = put_user_u32(env->dregs[upd1_reg], addr1); + break; + default: + break; + } + if (segv) { + env->mmu.ar = addr1; + } + goto done; + } + /* else cmp1 = (addr1) */ + switch (size) { + case OS_BYTE: + env->dregs[cmp1_reg] = deposit32(env->dregs[cmp1_reg], + 0, 8, dest1); + break; + case OS_WORD: + env->dregs[cmp1_reg] = deposit32(env->dregs[cmp1_reg], + 0, 16, dest1); + break; + case OS_LONG: + env->dregs[cmp1_reg] = dest1; + break; + default: + break; + } + } else { + /* CAS2 */ + switch (size) { + case OS_BYTE: + segv = get_user_u8(dest2, addr2); + cmp2 = (uint8_t)env->dregs[cmp2_reg]; + break; + case OS_WORD: + segv = get_user_u16(dest2, addr2); + cmp2 = (uint16_t)env->dregs[cmp2_reg]; + break; + case OS_LONG: + default: + segv = get_user_u32(dest2, addr2); + cmp2 = env->dregs[cmp2_reg]; + break; + } + if (segv) { + env->mmu.ar = addr2; + goto done; + } + /* if (addr1) == cmp1 && (addr2) == cmp2 then + * (addr1) = upd1, (addr2) = udp2 + */ + if (z == 0) { + z = dest2 - cmp2; + } + if (z == 0) { + switch (size) { + case OS_BYTE: + segv = put_user_u8(env->dregs[upd1_reg], addr1); + break; + case OS_WORD: + segv = put_user_u16(env->dregs[upd1_reg], addr1); + break; + case OS_LONG: + segv = put_user_u32(env->dregs[upd1_reg], addr1); + break; + default: + break; + } + if (segv) { + env->mmu.ar = addr1; + } + switch (size) { + case OS_BYTE: + segv = put_user_u8(env->dregs[upd2_reg], addr2); + break; + case OS_WORD: + segv = put_user_u16(env->dregs[upd2_reg], addr2); + break; + case OS_LONG: + segv = put_user_u32(env->dregs[upd2_reg], addr2); + break; + default: + break; + } + if (segv) { + env->mmu.ar = addr2; + } + goto done; + } + /* else cmp1 = (addr1), cmp2 = (addr2) */ + switch (size) { + case OS_BYTE: + env->dregs[cmp1_reg] = deposit32(env->dregs[cmp1_reg], + 0, 8, dest1); + env->dregs[cmp2_reg] = deposit32(env->dregs[cmp2_reg], + 0, 8, dest2); + break; + case OS_WORD: + env->dregs[cmp1_reg] = deposit32(env->dregs[cmp1_reg], + 0, 16, dest1); + env->dregs[cmp2_reg] = deposit32(env->dregs[cmp2_reg], + 0, 16, dest2); + break; + case OS_LONG: + env->dregs[cmp1_reg] = dest1; + env->dregs[cmp2_reg] = dest2; + break; + default: + break; + } + } + +done: + end_exclusive(); + + return segv; +} + void cpu_loop(CPUM68KState *env) { CPUState *cs = CPU(m68k_env_get_cpu(env)); @@ -3060,6 +3248,11 @@ void cpu_loop(CPUM68KState *env) case EXCP_INTERRUPT: /* just indicate that signals should be handled asap */ break; + case EXCP_CAS: + if (!do_cas(env)) { + break; + } + /* fall through for segv */ case EXCP_ACCESS: { info.si_signo = TARGET_SIGSEGV; diff --git a/target-m68k/cpu.h b/target-m68k/cpu.h index b300a92..5ce77e4 100644 --- a/target-m68k/cpu.h +++ b/target-m68k/cpu.h @@ -58,6 +58,7 @@ #define EXCP_RTE 0x100 #define EXCP_HALT_INSN 0x101 +#define EXCP_CAS 0x102 #define NB_MMU_MODES 2 #define TARGET_INSN_START_EXTRA_WORDS 1 @@ -110,6 +111,14 @@ typedef struct CPUM68KState { uint32_t qregs[MAX_QREGS]; +#ifdef CONFIG_USER_ONLY + /* CAS/CAS2 parameters */ + + uint32_t cas_param; + uint32_t cas_addr1; + uint32_t cas_addr2; +#endif + CPU_COMMON /* Fields from here on are preserved across CPU reset. */ diff --git a/target-m68k/qregs.def b/target-m68k/qregs.def index 51ff43b..c4581b1 100644 --- a/target-m68k/qregs.def +++ b/target-m68k/qregs.def @@ -9,3 +9,8 @@ DEFO32(CC_V, cc_v) DEFO32(CC_Z, cc_z) DEFO32(MACSR, macsr) DEFO32(MAC_MASK, mac_mask) +#ifdef CONFIG_USER_ONLY +DEFO32(CAS_PARAM, cas_param) +DEFO32(CAS_ADDR1, cas_addr1) +DEFO32(CAS_ADDR2, cas_addr2) +#endif diff --git a/target-m68k/translate.c b/target-m68k/translate.c index d48ab66..80033fc 100644 --- a/target-m68k/translate.c +++ b/target-m68k/translate.c @@ -1846,6 +1846,179 @@ DISAS_INSN(arith_im) } } +DISAS_INSN(cas) +{ + int opsize; + TCGv addr; + uint16_t ext; + + switch ((insn >> 9) & 3) { + case 1: + opsize = OS_BYTE; + break; + case 2: + opsize = OS_WORD; + break; + case 3: + opsize = OS_LONG; + break; + default: + abort(); + } + + ext = read_im16(env, s); + + addr = gen_lea(env, s, insn, opsize); + if (IS_NULL_QREG(addr)) { + gen_addr_fault(s); + return; + } + +#ifdef CONFIG_USER_ONLY + tcg_gen_mov_i32(QREG_CAS_ADDR1, addr); + tcg_gen_movi_i32(QREG_CAS_PARAM, + (REG(ext, 6) << 5) | (REG(ext, 0) << 2) | + opsize); + gen_exception(s, s->pc, EXCP_CAS); + s->is_jmp = DISAS_JUMP; +#else + TCGv dest; + TCGv res; + TCGv cmp; + TCGv zero; + + dest = gen_load(s, opsize, addr, 0); + + zero = tcg_const_i32(0); + cmp = gen_extend(DREG(ext, 0), opsize, 0); + + /* if dest - cmp == 0 */ + + res = tcg_temp_new(); + tcg_gen_sub_i32(res, dest, cmp); + + /* then dest = update1 */ + + tcg_gen_movcond_i32(TCG_COND_EQ, dest, + res, zero, + DREG(ext, 6), dest); + + /* else cmp = dest */ + + tcg_gen_movcond_i32(TCG_COND_NE, cmp, + res, zero, + dest, cmp); + + gen_partset_reg(opsize, DREG(ext, 0), cmp); + gen_store(s, opsize, addr, dest); + gen_logic_cc(s, res, opsize); + + tcg_temp_free_i32(res); + tcg_temp_free_i32(zero); +#endif +} + +DISAS_INSN(cas2) +{ + int opsize; + uint16_t ext1, ext2; + TCGv addr1, addr2; + + switch ((insn >> 9) & 3) { + case 1: + opsize = OS_BYTE; + break; + case 2: + opsize = OS_WORD; + break; + case 3: + opsize = OS_LONG; + break; + default: + abort(); + } + + ext1 = read_im16(env, s); + + if (ext1 & 0x8000) { + /* Address Register */ + addr1 = AREG(ext1, 12); + } else { + /* Data Register */ + addr1 = DREG(ext1, 12); + } + + ext2 = read_im16(env, s); + if (ext2 & 0x8000) { + /* Address Register */ + addr2 = AREG(ext2, 12); + } else { + /* Data Register */ + addr2 = DREG(ext2, 12); + } +#ifdef CONFIG_USER_ONLY + tcg_gen_mov_i32(QREG_CAS_ADDR1, addr1); + tcg_gen_mov_i32(QREG_CAS_ADDR2, addr2); + tcg_gen_movi_i32(QREG_CAS_PARAM, + (REG(ext2, 6) << 11) | (REG(ext2, 0) << 8) | + (REG(ext1, 6) << 5) | (REG(ext1, 0) << 2) | + 0x80000000 | opsize); + gen_exception(s, s->pc, EXCP_CAS); + s->is_jmp = DISAS_JUMP; +#else + TCGv cmp1, cmp2; + TCGv dest1, dest2; + TCGv res1, res2; + TCGv zero; + zero = tcg_const_i32(0); + dest1 = gen_load(s, opsize, addr1, 0); + cmp1 = gen_extend(DREG(ext1, 0), opsize, 0); + + res1 = tcg_temp_new(); + tcg_gen_sub_i32(res1, dest1, cmp1); + dest2 = gen_load(s, opsize, addr2, 0); + cmp2 = gen_extend(DREG(ext2, 0), opsize, 0); + + res2 = tcg_temp_new(); + tcg_gen_sub_i32(res2, dest2, cmp2); + + /* if dest1 - cmp1 == 0 and dest2 - cmp2 == 0 */ + + tcg_gen_movcond_i32(TCG_COND_EQ, res1, + res1, zero, + res2, res1); + + /* then dest1 = update1, dest2 = update2 */ + + tcg_gen_movcond_i32(TCG_COND_EQ, dest1, + res1, zero, + DREG(ext1, 6), dest1); + tcg_gen_movcond_i32(TCG_COND_EQ, dest2, + res1, zero, + DREG(ext2, 6), dest2); + + /* else cmp1 = dest1, cmp2 = dest2 */ + + tcg_gen_movcond_i32(TCG_COND_NE, cmp1, + res1, zero, + dest1, cmp1); + tcg_gen_movcond_i32(TCG_COND_NE, cmp2, + res1, zero, + dest2, cmp2); + + gen_partset_reg(opsize, DREG(ext1, 0), cmp1); + gen_partset_reg(opsize, DREG(ext2, 0), cmp2); + gen_store(s, opsize, addr1, dest1); + gen_store(s, opsize, addr2, dest2); + + gen_logic_cc(s, res1, opsize); + + tcg_temp_free_i32(res2); + tcg_temp_free_i32(res1); + tcg_temp_free_i32(zero); +#endif +} + DISAS_INSN(byterev) { TCGv reg; @@ -4457,6 +4630,8 @@ void register_m68k_insns (CPUM68KState *env) INSN(arith_im, 0680, fff8, CF_ISA_A); INSN(arith_im, 0c00, ff38, CF_ISA_A); INSN(arith_im, 0c00, ff00, M68000); + INSN(cas, 08c0, f9c0, CAS); + INSN(cas2, 08fc, f9ff, CAS); BASE(bitop_im, 0800, ffc0); BASE(bitop_im, 0840, ffc0); BASE(bitop_im, 0880, ffc0);