static void disas_arm_insn(CPUState * env, DisasContext *s) { unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh; TCGv tmp; TCGv tmp2; TCGv tmp3; TCGv addr; TCGv_i64 tmp64; insn = ldl_code(s->pc); s->pc += 4; /* M variants do not implement ARM mode. */ if (IS_M(env)) goto illegal_op; cond = insn >> 28; if (cond == 0xf){ /* Unconditional instructions. */ if (((insn >> 25) & 7) == 1) { /* NEON Data processing. */ if (!arm_feature(env, ARM_FEATURE_NEON)) goto illegal_op; if (disas_neon_data_insn(env, s, insn)) goto illegal_op; return; } if ((insn & 0x0f100000) == 0x04000000) { /* NEON load/store. */ if (!arm_feature(env, ARM_FEATURE_NEON)) goto illegal_op; if (disas_neon_ls_insn(env, s, insn)) goto illegal_op; return; } if (((insn & 0x0f30f000) == 0x0510f000) || ((insn & 0x0f30f010) == 0x0710f000)) { if ((insn & (1 << 22)) == 0) { /* PLDW; v7MP */ if (!arm_feature(env, ARM_FEATURE_V7MP)) { goto illegal_op; } } /* Otherwise PLD; v5TE+ */ return; } if (((insn & 0x0f70f000) == 0x0450f000) || ((insn & 0x0f70f010) == 0x0650f000)) { ARCH(7); return; /* PLI; V7 */ } if (((insn & 0x0f700000) == 0x04100000) || ((insn & 0x0f700010) == 0x06100000)) { if (!arm_feature(env, ARM_FEATURE_V7MP)) { goto illegal_op; } return; /* v7MP: Unallocated memory hint: must NOP */ } if ((insn & 0x0ffffdff) == 0x01010000) { ARCH(6); /* setend */ if (insn & (1 << 9)) { /* BE8 mode not implemented. */ goto illegal_op; } return; } else if ((insn & 0x0fffff00) == 0x057ff000) { switch ((insn >> 4) & 0xf) { case 1: /* clrex */ ARCH(6K); gen_clrex(s); return; case 4: /* dsb */ case 5: /* dmb */ case 6: /* isb */ ARCH(7); /* We don't emulate caches so these are a no-op. */ return; default: goto illegal_op; } } else if ((insn & 0x0e5fffe0) == 0x084d0500) { /* srs */ int32_t offset; if (IS_USER(s)) goto illegal_op; ARCH(6); op1 = (insn & 0x1f); addr = tcg_temp_new_i32(); tmp = tcg_const_i32(op1); gen_helper_get_r13_banked(addr, cpu_env, tmp); tcg_temp_free_i32(tmp); i = (insn >> 23) & 3; switch (i) { case 0: offset = -4; break; /* DA */ case 1: offset = 0; break; /* IA */ case 2: offset = -8; break; /* DB */ case 3: offset = 4; break; /* IB */ default: abort(); } if (offset) tcg_gen_addi_i32(addr, addr, offset); tmp = load_reg(s, 14); gen_st32(tmp, addr, 0); tmp = load_cpu_field(spsr); tcg_gen_addi_i32(addr, addr, 4); gen_st32(tmp, addr, 0); if (insn & (1 << 21)) { /* Base writeback. */ switch (i) { case 0: offset = -8; break; case 1: offset = 4; break; case 2: offset = -4; break; case 3: offset = 0; break; default: abort(); } if (offset) tcg_gen_addi_i32(addr, addr, offset); tmp = tcg_const_i32(op1); gen_helper_set_r13_banked(cpu_env, tmp, addr); tcg_temp_free_i32(tmp); tcg_temp_free_i32(addr); } else { tcg_temp_free_i32(addr); } return; } else if ((insn & 0x0e50ffe0) == 0x08100a00) { /* rfe */ int32_t offset; if (IS_USER(s)) goto illegal_op; ARCH(6); rn = (insn >> 16) & 0xf; addr = load_reg(s, rn); i = (insn >> 23) & 3; switch (i) { case 0: offset = -4; break; /* DA */ case 1: offset = 0; break; /* IA */ case 2: offset = -8; break; /* DB */ case 3: offset = 4; break; /* IB */ default: abort(); } if (offset) tcg_gen_addi_i32(addr, addr, offset); /* Load PC into tmp and CPSR into tmp2. */ tmp = gen_ld32(addr, 0); tcg_gen_addi_i32(addr, addr, 4); tmp2 = gen_ld32(addr, 0); if (insn & (1 << 21)) { /* Base writeback. */ switch (i) { case 0: offset = -8; break; case 1: offset = 4; break; case 2: offset = -4; break; case 3: offset = 0; break; default: abort(); } if (offset) tcg_gen_addi_i32(addr, addr, offset); store_reg(s, rn, addr); } else { tcg_temp_free_i32(addr); } gen_rfe(s, tmp, tmp2); return; } else if ((insn & 0x0e000000) == 0x0a000000) { /* branch link and change to thumb (blx <offset>) */ int32_t offset; val = (uint32_t)s->pc; tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); store_reg(s, 14, tmp); /* Sign-extend the 24-bit offset */ offset = (((int32_t)insn) << 8) >> 8; /* offset * 4 + bit24 * 2 + (thumb bit) */ val += (offset << 2) | ((insn >> 23) & 2) | 1; /* pipeline offset */ val += 4; gen_bx_im(s, val); return; } else if ((insn & 0x0e000f00) == 0x0c000100) { if (arm_feature(env, ARM_FEATURE_IWMMXT)) { /* iWMMXt register transfer. */ if (env->cp15.c15_cpar & (1 << 1)) if (!disas_iwmmxt_insn(env, s, insn)) return; } } else if ((insn & 0x0fe00000) == 0x0c400000) { /* Coprocessor double register transfer. */ } else if ((insn & 0x0f000010) == 0x0e000010) { /* Additional coprocessor register transfer. */ } else if ((insn & 0x0ff10020) == 0x01000000) { uint32_t mask; uint32_t val; /* cps (privileged) */ if (IS_USER(s)) return; mask = val = 0; if (insn & (1 << 19)) { if (insn & (1 << 8)) mask |= CPSR_A; if (insn & (1 << 7)) mask |= CPSR_I; if (insn & (1 << 6)) mask |= CPSR_F; if (insn & (1 << 18)) val |= mask; } if (insn & (1 << 17)) { mask |= CPSR_M; val |= (insn & 0x1f); } if (mask) { gen_set_psr_im(s, mask, 0, val); } return; } goto illegal_op; } if (cond != 0xe) { /* if not always execute, we generate a conditional jump to next instruction */ s->condlabel = gen_new_label(); gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; } if ((insn & 0x0f900000) == 0x03000000) { if ((insn & (1 << 21)) == 0) { ARCH(6T2); rd = (insn >> 12) & 0xf; val = ((insn >> 4) & 0xf000) | (insn & 0xfff); if ((insn & (1 << 22)) == 0) { /* MOVW */ tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); } else { /* MOVT */ tmp = load_reg(s, rd); tcg_gen_ext16u_i32(tmp, tmp); tcg_gen_ori_i32(tmp, tmp, val << 16); } store_reg(s, rd, tmp); } else { if (((insn >> 12) & 0xf) != 0xf) goto illegal_op; if (((insn >> 16) & 0xf) == 0) { gen_nop_hint(s, insn & 0xff); } else { /* CPSR = immediate */ val = insn & 0xff; shift = ((insn >> 8) & 0xf) * 2; if (shift) val = (val >> shift) | (val << (32 - shift)); i = ((insn & (1 << 22)) != 0); if (gen_set_psr_im(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, val)) goto illegal_op; } } } else if ((insn & 0x0f900000) == 0x01000000 && (insn & 0x00000090) != 0x00000090) { /* miscellaneous instructions */ op1 = (insn >> 21) & 3; sh = (insn >> 4) & 0xf; rm = insn & 0xf; switch (sh) { case 0x0: /* move program status register */ if (op1 & 1) { /* PSR = reg */ tmp = load_reg(s, rm); i = ((op1 & 2) != 0); if (gen_set_psr(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i, tmp)) goto illegal_op; } else { /* reg = PSR */ rd = (insn >> 12) & 0xf; if (op1 & 2) { if (IS_USER(s)) goto illegal_op; tmp = load_cpu_field(spsr); } else { tmp = tcg_temp_new_i32(); gen_helper_cpsr_read(tmp); } store_reg(s, rd, tmp); } break; case 0x1: if (op1 == 1) { /* branch/exchange thumb (bx). */ tmp = load_reg(s, rm); gen_bx(s, tmp); } else if (op1 == 3) { /* clz */ rd = (insn >> 12) & 0xf; tmp = load_reg(s, rm); gen_helper_clz(tmp, tmp); store_reg(s, rd, tmp); } else { goto illegal_op; } break; case 0x2: if (op1 == 1) { ARCH(5J); /* bxj */ /* Trivial implementation equivalent to bx. */ tmp = load_reg(s, rm); gen_bx(s, tmp); } else { goto illegal_op; } break; case 0x3: if (op1 != 1) goto illegal_op; /* branch link/exchange thumb (blx) */ tmp = load_reg(s, rm); tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, s->pc); store_reg(s, 14, tmp2); gen_bx(s, tmp); break; case 0x5: /* saturating add/subtract */ rd = (insn >> 12) & 0xf; rn = (insn >> 16) & 0xf; tmp = load_reg(s, rm); tmp2 = load_reg(s, rn); if (op1 & 2) gen_helper_double_saturate(tmp2, tmp2); if (op1 & 1) gen_helper_sub_saturate(tmp, tmp, tmp2); else gen_helper_add_saturate(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 7: /* SMC instruction (op1 == 3) and undefined instructions (op1 == 0 || op1 == 2) will trap */ if (op1 != 1) { goto illegal_op; } /* bkpt */ gen_exception_insn(s, 4, EXCP_BKPT); break; case 0x8: /* signed multiply */ case 0xa: case 0xc: case 0xe: rs = (insn >> 8) & 0xf; rn = (insn >> 12) & 0xf; rd = (insn >> 16) & 0xf; if (op1 == 1) { /* (32 * 16) >> 16 */ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); if (sh & 4) tcg_gen_sari_i32(tmp2, tmp2, 16); else gen_sxth(tmp2); tmp64 = gen_muls_i64_i32(tmp, tmp2); tcg_gen_shri_i64(tmp64, tmp64, 16); tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); if ((sh & 2) == 0) { tmp2 = load_reg(s, rn); gen_helper_add_setq(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); } else { /* 16 * 16 */ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); gen_mulxy(tmp, tmp2, sh & 2, sh & 4); tcg_temp_free_i32(tmp2); if (op1 == 2) { tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); tcg_temp_free_i32(tmp); gen_addq(s, tmp64, rn, rd); gen_storeq_reg(s, rn, rd, tmp64); tcg_temp_free_i64(tmp64); } else { if (op1 == 0) { tmp2 = load_reg(s, rn); gen_helper_add_setq(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); } } break; default: goto illegal_op; } } else if (((insn & 0x0e000000) == 0 && (insn & 0x00000090) != 0x90) || ((insn & 0x0e000000) == (1 << 25))) { int set_cc, logic_cc, shiftop; op1 = (insn >> 21) & 0xf; set_cc = (insn >> 20) & 1; logic_cc = table_logic_cc[op1] & set_cc; /* data processing instruction */ if (insn & (1 << 25)) { /* immediate operand */ val = insn & 0xff; shift = ((insn >> 8) & 0xf) * 2; if (shift) { val = (val >> shift) | (val << (32 - shift)); } tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, val); if (logic_cc && shift) { gen_set_CF_bit31(tmp2); } } else { /* register */ rm = (insn) & 0xf; tmp2 = load_reg(s, rm); shiftop = (insn >> 5) & 3; if (!(insn & (1 << 4))) { shift = (insn >> 7) & 0x1f; gen_arm_shift_im(tmp2, shiftop, shift, logic_cc); } else { rs = (insn >> 8) & 0xf; tmp = load_reg(s, rs); gen_arm_shift_reg(tmp2, shiftop, tmp, logic_cc); } } if (op1 != 0x0f && op1 != 0x0d) { rn = (insn >> 16) & 0xf; tmp = load_reg(s, rn); } else { TCGV_UNUSED(tmp); } rd = (insn >> 12) & 0xf; switch(op1) { case 0x00: tcg_gen_and_i32(tmp, tmp, tmp2); if (logic_cc) { gen_logic_CC(tmp); } store_reg_bx(env, s, rd, tmp); break; case 0x01: tcg_gen_xor_i32(tmp, tmp, tmp2); if (logic_cc) { gen_logic_CC(tmp); } store_reg_bx(env, s, rd, tmp); break; case 0x02: if (set_cc && rd == 15) { /* SUBS r15, ... is used for exception return. */ if (IS_USER(s)) { goto illegal_op; } gen_helper_sub_cc(tmp, tmp, tmp2); gen_exception_return(s, tmp); } else { if (set_cc) { gen_helper_sub_cc(tmp, tmp, tmp2); } else { tcg_gen_sub_i32(tmp, tmp, tmp2); } store_reg_bx(env, s, rd, tmp); } break; case 0x03: if (set_cc) { gen_helper_sub_cc(tmp, tmp2, tmp); } else { tcg_gen_sub_i32(tmp, tmp2, tmp); } store_reg_bx(env, s, rd, tmp); break; case 0x04: if (set_cc) { gen_helper_add_cc(tmp, tmp, tmp2); } else { tcg_gen_add_i32(tmp, tmp, tmp2); } store_reg_bx(env, s, rd, tmp); break; case 0x05: if (set_cc) { gen_helper_adc_cc(tmp, tmp, tmp2); } else { gen_add_carry(tmp, tmp, tmp2); } store_reg_bx(env, s, rd, tmp); break; case 0x06: if (set_cc) { gen_helper_sbc_cc(tmp, tmp, tmp2); } else { gen_sub_carry(tmp, tmp, tmp2); } store_reg_bx(env, s, rd, tmp); break; case 0x07: if (set_cc) { gen_helper_sbc_cc(tmp, tmp2, tmp); } else { gen_sub_carry(tmp, tmp2, tmp); } store_reg_bx(env, s, rd, tmp); break; case 0x08: if (set_cc) { tcg_gen_and_i32(tmp, tmp, tmp2); gen_logic_CC(tmp); } tcg_temp_free_i32(tmp); break; case 0x09: if (set_cc) { tcg_gen_xor_i32(tmp, tmp, tmp2); gen_logic_CC(tmp); } tcg_temp_free_i32(tmp); break; case 0x0a: if (set_cc) { gen_helper_sub_cc(tmp, tmp, tmp2); } tcg_temp_free_i32(tmp); break; case 0x0b: if (set_cc) { gen_helper_add_cc(tmp, tmp, tmp2); } tcg_temp_free_i32(tmp); break; case 0x0c: tcg_gen_or_i32(tmp, tmp, tmp2); if (logic_cc) { gen_logic_CC(tmp); } store_reg_bx(env, s, rd, tmp); break; case 0x0d: if (logic_cc && rd == 15) { /* MOVS r15, ... is used for exception return. */ if (IS_USER(s)) { goto illegal_op; } gen_exception_return(s, tmp2); } else { if (logic_cc) { gen_logic_CC(tmp2); } store_reg_bx(env, s, rd, tmp2); } break; case 0x0e: tcg_gen_andc_i32(tmp, tmp, tmp2); if (logic_cc) { gen_logic_CC(tmp); } store_reg_bx(env, s, rd, tmp); break; default: case 0x0f: tcg_gen_not_i32(tmp2, tmp2); if (logic_cc) { gen_logic_CC(tmp2); } store_reg_bx(env, s, rd, tmp2); break; } if (op1 != 0x0f && op1 != 0x0d) { tcg_temp_free_i32(tmp2); } } else { /* other instructions */ op1 = (insn >> 24) & 0xf; switch(op1) { case 0x0: case 0x1: /* multiplies, extra load/stores */ sh = (insn >> 5) & 3; if (sh == 0) { if (op1 == 0x0) { rd = (insn >> 16) & 0xf; rn = (insn >> 12) & 0xf; rs = (insn >> 8) & 0xf; rm = (insn) & 0xf; op1 = (insn >> 20) & 0xf; switch (op1) { case 0: case 1: case 2: case 3: case 6: /* 32 bit mul */ tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); tcg_gen_mul_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* Subtract (mls) */ ARCH(6T2); tmp2 = load_reg(s, rn); tcg_gen_sub_i32(tmp, tmp2, tmp); tcg_temp_free_i32(tmp2); } else if (insn & (1 << 21)) { /* Add */ tmp2 = load_reg(s, rn); tcg_gen_add_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } if (insn & (1 << 20)) gen_logic_CC(tmp); store_reg(s, rd, tmp); break; case 4: /* 64 bit mul double accumulate (UMAAL) */ ARCH(6); tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); tmp64 = gen_mulu_i64_i32(tmp, tmp2); gen_addq_lo(s, tmp64, rn); gen_addq_lo(s, tmp64, rd); gen_storeq_reg(s, rn, rd, tmp64); tcg_temp_free_i64(tmp64); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: /* 64 bit mul: UMULL, UMLAL, SMULL, SMLAL. */ tmp = load_reg(s, rs); tmp2 = load_reg(s, rm); if (insn & (1 << 22)) { tmp64 = gen_muls_i64_i32(tmp, tmp2); } else { tmp64 = gen_mulu_i64_i32(tmp, tmp2); } if (insn & (1 << 21)) { /* mult accumulate */ gen_addq(s, tmp64, rn, rd); } if (insn & (1 << 20)) { gen_logicq_cc(tmp64); } gen_storeq_reg(s, rn, rd, tmp64); tcg_temp_free_i64(tmp64); break; default: goto illegal_op; } } else { rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; if (insn & (1 << 23)) { /* load/store exclusive */ op1 = (insn >> 21) & 0x3; if (op1) ARCH(6K); else ARCH(6); addr = tcg_temp_local_new_i32(); load_reg_var(s, addr, rn); if (insn & (1 << 20)) { switch (op1) { case 0: /* ldrex */ gen_load_exclusive(s, rd, 15, addr, 2); break; case 1: /* ldrexd */ gen_load_exclusive(s, rd, rd + 1, addr, 3); break; case 2: /* ldrexb */ gen_load_exclusive(s, rd, 15, addr, 0); break; case 3: /* ldrexh */ gen_load_exclusive(s, rd, 15, addr, 1); break; default: abort(); } } else { rm = insn & 0xf; switch (op1) { case 0: /* strex */ gen_store_exclusive(s, rd, rm, 15, addr, 2); break; case 1: /* strexd */ gen_store_exclusive(s, rd, rm, rm + 1, addr, 3); break; case 2: /* strexb */ gen_store_exclusive(s, rd, rm, 15, addr, 0); break; case 3: /* strexh */ gen_store_exclusive(s, rd, rm, 15, addr, 1); break; default: abort(); } } tcg_temp_free(addr); } else { /* SWP instruction */ rm = (insn) & 0xf; /* ??? This is not really atomic. However we know we never have multiple CPUs running in parallel, so it is good enough. */ addr = load_reg(s, rn); tmp = load_reg(s, rm); if (insn & (1 << 22)) { tmp2 = gen_ld8u(addr, IS_USER(s)); gen_st8(tmp, addr, IS_USER(s)); } else { tmp2 = gen_ld32(addr, IS_USER(s)); gen_st32(tmp, addr, IS_USER(s)); } tcg_temp_free_i32(addr); store_reg(s, rd, tmp2); } } } else { int address_offset; int load; /* Misc load/store */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; addr = load_reg(s, rn); if (insn & (1 << 24)) gen_add_datah_offset(s, insn, 0, addr); address_offset = 0; if (insn & (1 << 20)) { /* load */ switch(sh) { case 1: tmp = gen_ld16u(addr, IS_USER(s)); break; case 2: tmp = gen_ld8s(addr, IS_USER(s)); break; default: case 3: tmp = gen_ld16s(addr, IS_USER(s)); break; } load = 1; } else if (sh & 2) { /* doubleword */ if (sh & 1) { /* store */ tmp = load_reg(s, rd); gen_st32(tmp, addr, IS_USER(s)); tcg_gen_addi_i32(addr, addr, 4); tmp = load_reg(s, rd + 1); gen_st32(tmp, addr, IS_USER(s)); load = 0; } else { /* load */ tmp = gen_ld32(addr, IS_USER(s)); store_reg(s, rd, tmp); tcg_gen_addi_i32(addr, addr, 4); tmp = gen_ld32(addr, IS_USER(s)); rd++; load = 1; } address_offset = -4; } else { /* store */ tmp = load_reg(s, rd); gen_st16(tmp, addr, IS_USER(s)); load = 0; } /* Perform base writeback before the loaded value to ensure correct behavior with overlapping index registers. ldrd with base writeback is is undefined if the destination and index registers overlap. */ if (!(insn & (1 << 24))) { gen_add_datah_offset(s, insn, address_offset, addr); store_reg(s, rn, addr); } else if (insn & (1 << 21)) { if (address_offset) tcg_gen_addi_i32(addr, addr, address_offset); store_reg(s, rn, addr); } else { tcg_temp_free_i32(addr); } if (load) { /* Complete the load. */ store_reg(s, rd, tmp); } } break; case 0x4: case 0x5: goto do_ldst; case 0x6: case 0x7: if (insn & (1 << 4)) { ARCH(6); /* Armv6 Media instructions. */ rm = insn & 0xf; rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; rs = (insn >> 8) & 0xf; switch ((insn >> 23) & 3) { case 0: /* Parallel add/subtract. */ op1 = (insn >> 20) & 7; tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); sh = (insn >> 5) & 7; if ((op1 & 3) == 0 || sh == 5 || sh == 6) goto illegal_op; gen_arm_parallel_addsub(op1, sh, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 1: if ((insn & 0x00700020) == 0) { /* Halfword pack. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); shift = (insn >> 7) & 0x1f; if (insn & (1 << 6)) { /* pkhtb */ if (shift == 0) shift = 31; tcg_gen_sari_i32(tmp2, tmp2, shift); tcg_gen_andi_i32(tmp, tmp, 0xffff0000); tcg_gen_ext16u_i32(tmp2, tmp2); } else { /* pkhbt */ if (shift) tcg_gen_shli_i32(tmp2, tmp2, shift); tcg_gen_ext16u_i32(tmp, tmp); tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000); } tcg_gen_or_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00200020) == 0x00200000) { /* [us]sat */ tmp = load_reg(s, rm); shift = (insn >> 7) & 0x1f; if (insn & (1 << 6)) { if (shift == 0) shift = 31; tcg_gen_sari_i32(tmp, tmp, shift); } else { tcg_gen_shli_i32(tmp, tmp, shift); } sh = (insn >> 16) & 0x1f; tmp2 = tcg_const_i32(sh); if (insn & (1 << 22)) gen_helper_usat(tmp, tmp, tmp2); else gen_helper_ssat(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00300fe0) == 0x00200f20) { /* [us]sat16 */ tmp = load_reg(s, rm); sh = (insn >> 16) & 0x1f; tmp2 = tcg_const_i32(sh); if (insn & (1 << 22)) gen_helper_usat16(tmp, tmp, tmp2); else gen_helper_ssat16(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x00700fe0) == 0x00000fa0) { /* Select bytes. */ tmp = load_reg(s, rn); tmp2 = load_reg(s, rm); tmp3 = tcg_temp_new_i32(); tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE)); gen_helper_sel_flags(tmp, tmp3, tmp, tmp2); tcg_temp_free_i32(tmp3); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else if ((insn & 0x000003e0) == 0x00000060) { tmp = load_reg(s, rm); shift = (insn >> 10) & 3; /* ??? In many cases it's not neccessary to do a rotate, a shift is sufficient. */ if (shift != 0) tcg_gen_rotri_i32(tmp, tmp, shift * 8); op1 = (insn >> 20) & 7; switch (op1) { case 0: gen_sxtb16(tmp); break; case 2: gen_sxtb(tmp); break; case 3: gen_sxth(tmp); break; case 4: gen_uxtb16(tmp); break; case 6: gen_uxtb(tmp); break; case 7: gen_uxth(tmp); break; default: goto illegal_op; } if (rn != 15) { tmp2 = load_reg(s, rn); if ((op1 & 3) == 0) { gen_add16(tmp, tmp2); } else { tcg_gen_add_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } } store_reg(s, rd, tmp); } else if ((insn & 0x003f0f60) == 0x003f0f20) { /* rev */ tmp = load_reg(s, rm); if (insn & (1 << 22)) { if (insn & (1 << 7)) { gen_revsh(tmp); } else { ARCH(6T2); gen_helper_rbit(tmp, tmp); } } else { if (insn & (1 << 7)) gen_rev16(tmp); else tcg_gen_bswap32_i32(tmp, tmp); } store_reg(s, rd, tmp); } else { goto illegal_op; } break; case 2: /* Multiplies (Type 3). */ tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); if (insn & (1 << 20)) { /* Signed multiply most significant [accumulate]. (SMMUL, SMMLA, SMMLS) */ tmp64 = gen_muls_i64_i32(tmp, tmp2); if (rd != 15) { tmp = load_reg(s, rd); if (insn & (1 << 6)) { tmp64 = gen_subq_msw(tmp64, tmp); } else { tmp64 = gen_addq_msw(tmp64, tmp); } } if (insn & (1 << 5)) { tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u); } tcg_gen_shri_i64(tmp64, tmp64, 32); tmp = tcg_temp_new_i32(); tcg_gen_trunc_i64_i32(tmp, tmp64); tcg_temp_free_i64(tmp64); store_reg(s, rn, tmp); } else { if (insn & (1 << 5)) gen_swap_half(tmp2); gen_smul_dual(tmp, tmp2); if (insn & (1 << 6)) { /* This subtraction cannot overflow. */ tcg_gen_sub_i32(tmp, tmp, tmp2); } else { /* This addition cannot overflow 32 bits; * however it may overflow considered as a signed * operation, in which case we must set the Q flag. */ gen_helper_add_setq(tmp, tmp, tmp2); } tcg_temp_free_i32(tmp2); if (insn & (1 << 22)) { /* smlald, smlsld */ tmp64 = tcg_temp_new_i64(); tcg_gen_ext_i32_i64(tmp64, tmp); tcg_temp_free_i32(tmp); gen_addq(s, tmp64, rd, rn); gen_storeq_reg(s, rd, rn, tmp64); tcg_temp_free_i64(tmp64); } else { /* smuad, smusd, smlad, smlsd */ if (rd != 15) { tmp2 = load_reg(s, rd); gen_helper_add_setq(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } store_reg(s, rn, tmp); } } break; case 3: op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7); switch (op1) { case 0: /* Unsigned sum of absolute differences. */ ARCH(6); tmp = load_reg(s, rm); tmp2 = load_reg(s, rs); gen_helper_usad8(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); if (rd != 15) { tmp2 = load_reg(s, rd); tcg_gen_add_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); } store_reg(s, rn, tmp); break; case 0x20: case 0x24: case 0x28: case 0x2c: /* Bitfield insert/clear. */ ARCH(6T2); shift = (insn >> 7) & 0x1f; i = (insn >> 16) & 0x1f; i = i + 1 - shift; if (rm == 15) { tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else { tmp = load_reg(s, rm); } if (i != 32) { tmp2 = load_reg(s, rd); gen_bfi(tmp, tmp2, tmp, shift, (1u << i) - 1); tcg_temp_free_i32(tmp2); } store_reg(s, rd, tmp); break; case 0x12: case 0x16: case 0x1a: case 0x1e: /* sbfx */ case 0x32: case 0x36: case 0x3a: case 0x3e: /* ubfx */ ARCH(6T2); tmp = load_reg(s, rm); shift = (insn >> 7) & 0x1f; i = ((insn >> 16) & 0x1f) + 1; if (shift + i > 32) goto illegal_op; if (i < 32) { if (op1 & 0x20) { gen_ubfx(tmp, shift, (1u << i) - 1); } else { gen_sbfx(tmp, shift, i); } } store_reg(s, rd, tmp); break; default: goto illegal_op; } break; } break; } do_ldst: /* Check for undefined extension instructions * per the ARM Bible IE: * xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx */ sh = (0xf << 20) | (0xf << 4); if (op1 == 0x7 && ((insn & sh) == sh)) { goto illegal_op; } /* load/store byte/word */ rn = (insn >> 16) & 0xf; rd = (insn >> 12) & 0xf; tmp2 = load_reg(s, rn); i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000); if (insn & (1 << 24)) gen_add_data_offset(s, insn, tmp2); if (insn & (1 << 20)) { /* load */ if (insn & (1 << 22)) { tmp = gen_ld8u(tmp2, i); } else { tmp = gen_ld32(tmp2, i); } } else { /* store */ tmp = load_reg(s, rd); if (insn & (1 << 22)) gen_st8(tmp, tmp2, i); else gen_st32(tmp, tmp2, i); } if (!(insn & (1 << 24))) { gen_add_data_offset(s, insn, tmp2); store_reg(s, rn, tmp2); } else if (insn & (1 << 21)) { store_reg(s, rn, tmp2); } else { tcg_temp_free_i32(tmp2); } if (insn & (1 << 20)) { /* Complete the load. */ if (rd == 15) gen_bx(s, tmp); else store_reg(s, rd, tmp); } break; case 0x08: case 0x09: { int j, n, user, loaded_base; TCGv loaded_var; /* load/store multiple words */ /* XXX: store correct base if write back */ user = 0; if (insn & (1 << 22)) { if (IS_USER(s)) goto illegal_op; /* only usable in supervisor mode */ if ((insn & (1 << 15)) == 0) user = 1; } rn = (insn >> 16) & 0xf; addr = load_reg(s, rn); /* compute total size */ loaded_base = 0; TCGV_UNUSED(loaded_var); n = 0; for(i=0;i<16;i++) { if (insn & (1 << i)) n++; } /* XXX: test invalid n == 0 case ? */ if (insn & (1 << 23)) { if (insn & (1 << 24)) { /* pre increment */ tcg_gen_addi_i32(addr, addr, 4); } else { /* post increment */ } } else { if (insn & (1 << 24)) { /* pre decrement */ tcg_gen_addi_i32(addr, addr, -(n * 4)); } else { /* post decrement */ if (n != 1) tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); } } j = 0; for(i=0;i<16;i++) { if (insn & (1 << i)) { if (insn & (1 << 20)) { /* load */ tmp = gen_ld32(addr, IS_USER(s)); if (i == 15) { gen_bx(s, tmp); } else if (user) { tmp2 = tcg_const_i32(i); gen_helper_set_user_reg(tmp2, tmp); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); } else if (i == rn) { loaded_var = tmp; loaded_base = 1; } else { store_reg(s, i, tmp); } } else { /* store */ if (i == 15) { /* special case: r15 = PC + 8 */ val = (long)s->pc + 4; tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); } else if (user) { tmp = tcg_temp_new_i32(); tmp2 = tcg_const_i32(i); gen_helper_get_user_reg(tmp, tmp2); tcg_temp_free_i32(tmp2); } else { tmp = load_reg(s, i); } gen_st32(tmp, addr, IS_USER(s)); } j++; /* no need to add after the last transfer */ if (j != n) tcg_gen_addi_i32(addr, addr, 4); } } if (insn & (1 << 21)) { /* write back */ if (insn & (1 << 23)) { if (insn & (1 << 24)) { /* pre increment */ } else { /* post increment */ tcg_gen_addi_i32(addr, addr, 4); } } else { if (insn & (1 << 24)) { /* pre decrement */ if (n != 1) tcg_gen_addi_i32(addr, addr, -((n - 1) * 4)); } else { /* post decrement */ tcg_gen_addi_i32(addr, addr, -(n * 4)); } } store_reg(s, rn, addr); } else { tcg_temp_free_i32(addr); } if (loaded_base) { store_reg(s, rn, loaded_var); } if ((insn & (1 << 22)) && !user) { /* Restore CPSR from SPSR. */ tmp = load_cpu_field(spsr); gen_set_cpsr(tmp, 0xffffffff); tcg_temp_free_i32(tmp); s->is_jmp = DISAS_UPDATE; } } break; case 0xa: case 0xb: { int32_t offset; /* branch (and link) */ val = (int32_t)s->pc; if (insn & (1 << 24)) { tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, val); store_reg(s, 14, tmp); } offset = (((int32_t)insn << 8) >> 8); val += (offset << 2) + 4; gen_jmp(s, val); } break; case 0xc: case 0xd: case 0xe: /* Coprocessor. */ if (disas_coproc_insn(env, s, insn)) goto illegal_op; break; case 0xf: /* swi */ gen_set_pc_im(s->pc); s->is_jmp = DISAS_SWI; break; default: illegal_op: gen_exception_insn(s, 4, EXCP_UDEF); break; } } }