static void disas_thumb_insn(CPUARMState *env, DisasContext *s) { uint32_t val, insn, op, rm, rn, rd, shift, cond; int32_t offset; int i; TCGv_i32 tmp; TCGv_i32 tmp2; TCGv_i32 addr; if (s->condexec_mask) { cond = s->condexec_cond; if (cond != 0x0e) { /* Skip conditional when condition is AL. */ s->condlabel = gen_new_label(); arm_gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; } } insn = arm_lduw_code(env, s->pc, s->sctlr_b); s->pc += 2; switch (insn >> 12) { case 0: case 1: rd = insn & 7; op = (insn >> 11) & 3; if (op == 3) { /* add/subtract */ rn = (insn >> 3) & 7; tmp = load_reg(s, rn); if (insn & (1 << 10)) { /* immediate */ tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, (insn >> 6) & 7); } else { /* reg */ rm = (insn >> 6) & 7; tmp2 = load_reg(s, rm); } if (insn & (1 << 9)) { if (s->condexec_mask) tcg_gen_sub_i32(tmp, tmp, tmp2); else gen_sub_CC(tmp, tmp, tmp2); } else { if (s->condexec_mask) tcg_gen_add_i32(tmp, tmp, tmp2); else gen_add_CC(tmp, tmp, tmp2); } tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); } else { /* shift immediate */ rm = (insn >> 3) & 7; shift = (insn >> 6) & 0x1f; tmp = load_reg(s, rm); gen_arm_shift_im(tmp, op, shift, s->condexec_mask == 0); if (!s->condexec_mask) gen_logic_CC(tmp); store_reg(s, rd, tmp); } break; case 2: case 3: /* arithmetic large immediate */ op = (insn >> 11) & 3; rd = (insn >> 8) & 0x7; if (op == 0) { /* mov */ tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, insn & 0xff); if (!s->condexec_mask) gen_logic_CC(tmp); store_reg(s, rd, tmp); } else { tmp = load_reg(s, rd); tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, insn & 0xff); switch (op) { case 1: /* cmp */ gen_sub_CC(tmp, tmp, tmp2); tcg_temp_free_i32(tmp); tcg_temp_free_i32(tmp2); break; case 2: /* add */ if (s->condexec_mask) tcg_gen_add_i32(tmp, tmp, tmp2); else gen_add_CC(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 3: /* sub */ if (s->condexec_mask) tcg_gen_sub_i32(tmp, tmp, tmp2); else gen_sub_CC(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; } } break; case 4: if (insn & (1 << 11)) { rd = (insn >> 8) & 7; /* load pc-relative. Bit 1 of PC is ignored. */ val = s->pc + 2 + ((insn & 0xff) * 4); val &= ~(uint32_t)2; addr = tcg_temp_new_i32(); tcg_gen_movi_i32(addr, val); tmp = tcg_temp_new_i32(); gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(addr); store_reg(s, rd, tmp); break; } if (insn & (1 << 10)) { /* data processing extended or blx */ rd = (insn & 7) | ((insn >> 4) & 8); rm = (insn >> 3) & 0xf; op = (insn >> 8) & 3; switch (op) { case 0: /* add */ tmp = load_reg(s, rd); tmp2 = load_reg(s, rm); tcg_gen_add_i32(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); store_reg(s, rd, tmp); break; case 1: /* cmp */ tmp = load_reg(s, rd); tmp2 = load_reg(s, rm); gen_sub_CC(tmp, tmp, tmp2); tcg_temp_free_i32(tmp2); tcg_temp_free_i32(tmp); break; case 2: /* mov/cpy */ tmp = load_reg(s, rm); store_reg(s, rd, tmp); break; case 3:/* branch [and link] exchange thumb register */ tmp = load_reg(s, rm); if (insn & (1 << 7)) { ARCH(5); val = (uint32_t)s->pc | 1; tmp2 = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp2, val); store_reg(s, 14, tmp2); gen_bx(s, tmp); } else { /* Only BX works as exception-return, not BLX */ gen_bx_excret(s, tmp); } break; } break; } /* data processing register */ rd = insn & 7; rm = (insn >> 3) & 7; op = (insn >> 6) & 0xf; if (op == 2 || op == 3 || op == 4 || op == 7) { /* the shift/rotate ops want the operands backwards */ val = rm; rm = rd; rd = val; val = 1; } else { val = 0; } if (op == 9) { /* neg */ tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, 0); } else if (op != 0xf) { /* mvn doesn't read its first operand */ tmp = load_reg(s, rd); } else { TCGV_UNUSED_I32(tmp); } tmp2 = load_reg(s, rm); switch (op) { case 0x0: /* and */ tcg_gen_and_i32(tmp, tmp, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp); break; case 0x1: /* eor */ tcg_gen_xor_i32(tmp, tmp, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp); break; case 0x2: /* lsl */ if (s->condexec_mask) { gen_shl(tmp2, tmp2, tmp); } else { gen_helper_shl_cc(tmp2, cpu_env, tmp2, tmp); gen_logic_CC(tmp2); } break; case 0x3: /* lsr */ if (s->condexec_mask) { gen_shr(tmp2, tmp2, tmp); } else { gen_helper_shr_cc(tmp2, cpu_env, tmp2, tmp); gen_logic_CC(tmp2); } break; case 0x4: /* asr */ if (s->condexec_mask) { gen_sar(tmp2, tmp2, tmp); } else { gen_helper_sar_cc(tmp2, cpu_env, tmp2, tmp); gen_logic_CC(tmp2); } break; case 0x5: /* adc */ if (s->condexec_mask) { gen_adc(tmp, tmp2); } else { gen_adc_CC(tmp, tmp, tmp2); } break; case 0x6: /* sbc */ if (s->condexec_mask) { gen_sub_carry(tmp, tmp, tmp2); } else { gen_sbc_CC(tmp, tmp, tmp2); } break; case 0x7: /* ror */ if (s->condexec_mask) { tcg_gen_andi_i32(tmp, tmp, 0x1f); tcg_gen_rotr_i32(tmp2, tmp2, tmp); } else { gen_helper_ror_cc(tmp2, cpu_env, tmp2, tmp); gen_logic_CC(tmp2); } break; case 0x8: /* tst */ tcg_gen_and_i32(tmp, tmp, tmp2); gen_logic_CC(tmp); rd = 16; break; case 0x9: /* neg */ if (s->condexec_mask) tcg_gen_neg_i32(tmp, tmp2); else gen_sub_CC(tmp, tmp, tmp2); break; case 0xa: /* cmp */ gen_sub_CC(tmp, tmp, tmp2); rd = 16; break; case 0xb: /* cmn */ gen_add_CC(tmp, tmp, tmp2); rd = 16; break; case 0xc: /* orr */ tcg_gen_or_i32(tmp, tmp, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp); break; case 0xd: /* mul */ tcg_gen_mul_i32(tmp, tmp, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp); break; case 0xe: /* bic */ tcg_gen_andc_i32(tmp, tmp, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp); break; case 0xf: /* mvn */ tcg_gen_not_i32(tmp2, tmp2); if (!s->condexec_mask) gen_logic_CC(tmp2); val = 1; rm = rd; break; } if (rd != 16) { if (val) { store_reg(s, rm, tmp2); if (op != 0xf) tcg_temp_free_i32(tmp); } else { store_reg(s, rd, tmp); tcg_temp_free_i32(tmp2); } } else { tcg_temp_free_i32(tmp); tcg_temp_free_i32(tmp2); } break; case 5: /* load/store register offset. */ rd = insn & 7; rn = (insn >> 3) & 7; rm = (insn >> 6) & 7; op = (insn >> 9) & 7; addr = load_reg(s, rn); tmp = load_reg(s, rm); tcg_gen_add_i32(addr, addr, tmp); tcg_temp_free_i32(tmp); if (op < 3) { /* store */ tmp = load_reg(s, rd); } else { tmp = tcg_temp_new_i32(); } switch (op) { case 0: /* str */ gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 1: /* strh */ gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 2: /* strb */ gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 3: /* ldrsb */ gen_aa32_ld8s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 4: /* ldr */ gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 5: /* ldrh */ gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 6: /* ldrb */ gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; case 7: /* ldrsh */ gen_aa32_ld16s_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); break; } if (op >= 3) { /* load */ store_reg(s, rd, tmp); } else { tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); break; case 6: /* load/store word immediate offset */ rd = insn & 7; rn = (insn >> 3) & 7; addr = load_reg(s, rn); val = (insn >> 4) & 0x7c; tcg_gen_addi_i32(addr, addr, val); if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); break; case 7: /* load/store byte immediate offset */ rd = insn & 7; rn = (insn >> 3) & 7; addr = load_reg(s, rn); val = (insn >> 6) & 0x1f; tcg_gen_addi_i32(addr, addr, val); if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld8u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); gen_aa32_st8_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); break; case 8: /* load/store halfword immediate offset */ rd = insn & 7; rn = (insn >> 3) & 7; addr = load_reg(s, rn); val = (insn >> 5) & 0x3e; tcg_gen_addi_i32(addr, addr, val); if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld16u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); gen_aa32_st16_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); break; case 9: /* load/store from stack */ rd = (insn >> 8) & 7; addr = load_reg(s, 13); val = (insn & 0xff) * 4; tcg_gen_addi_i32(addr, addr, val); if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); store_reg(s, rd, tmp); } else { /* store */ tmp = load_reg(s, rd); gen_aa32_st32_iss(s, tmp, addr, get_mem_index(s), rd | ISSIs16Bit); tcg_temp_free_i32(tmp); } tcg_temp_free_i32(addr); break; case 10: /* add to high reg */ rd = (insn >> 8) & 7; if (insn & (1 << 11)) { /* SP */ tmp = load_reg(s, 13); } else { /* PC. bit 1 is ignored. */ tmp = tcg_temp_new_i32(); tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2); } val = (insn & 0xff) * 4; tcg_gen_addi_i32(tmp, tmp, val); store_reg(s, rd, tmp); break; case 11: /* misc */ op = (insn >> 8) & 0xf; switch (op) { case 0: /* adjust stack pointer */ tmp = load_reg(s, 13); val = (insn & 0x7f) * 4; if (insn & (1 << 7)) val = -(int32_t)val; tcg_gen_addi_i32(tmp, tmp, val); store_reg(s, 13, tmp); break; case 2: /* sign/zero extend. */ ARCH(6); rd = insn & 7; rm = (insn >> 3) & 7; tmp = load_reg(s, rm); switch ((insn >> 6) & 3) { case 0: gen_sxth(tmp); break; case 1: gen_sxtb(tmp); break; case 2: gen_uxth(tmp); break; case 3: gen_uxtb(tmp); break; } store_reg(s, rd, tmp); break; case 4: case 5: case 0xc: case 0xd: /* push/pop */ addr = load_reg(s, 13); if (insn & (1 << 8)) offset = 4; else offset = 0; for (i = 0; i < 8; i++) { if (insn & (1 << i)) offset += 4; } if ((insn & (1 << 11)) == 0) { tcg_gen_addi_i32(addr, addr, -offset); } for (i = 0; i < 8; i++) { if (insn & (1 << i)) { if (insn & (1 << 11)) { /* pop */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); store_reg(s, i, tmp); } else { /* push */ tmp = load_reg(s, i); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address. */ tcg_gen_addi_i32(addr, addr, 4); } } TCGV_UNUSED_I32(tmp); if (insn & (1 << 8)) { if (insn & (1 << 11)) { /* pop pc */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); /* don't set the pc until the rest of the instruction has completed */ } else { /* push lr */ tmp = load_reg(s, 14); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } tcg_gen_addi_i32(addr, addr, 4); } if ((insn & (1 << 11)) == 0) { tcg_gen_addi_i32(addr, addr, -offset); } /* write back the new stack pointer */ store_reg(s, 13, addr); /* set the new PC value */ if ((insn & 0x0900) == 0x0900) { store_reg_from_load(s, 15, tmp); } break; case 1: case 3: case 9: case 11: /* czb */ rm = insn & 7; tmp = load_reg(s, rm); s->condlabel = gen_new_label(); s->condjmp = 1; if (insn & (1 << 11)) tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel); else tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel); tcg_temp_free_i32(tmp); offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3; val = (uint32_t)s->pc + 2; val += offset; gen_jmp(s, val); break; case 15: /* IT, nop-hint. */ if ((insn & 0xf) == 0) { gen_nop_hint(s, (insn >> 4) & 0xf); break; } /* If Then. */ s->condexec_cond = (insn >> 4) & 0xe; s->condexec_mask = insn & 0x1f; /* No actual code generated for this insn, just setup state. */ break; case 0xe: /* bkpt */ { int imm8 = extract32(insn, 0, 8); ARCH(5); gen_exception_insn(s, 2, EXCP_BKPT, syn_aa32_bkpt(imm8, true), default_exception_el(s)); break; } case 0xa: /* rev, and hlt */ { int op1 = extract32(insn, 6, 2); if (op1 == 2) { /* HLT */ int imm6 = extract32(insn, 0, 6); gen_hlt(s, imm6); break; } /* Otherwise this is rev */ ARCH(6); rn = (insn >> 3) & 0x7; rd = insn & 0x7; tmp = load_reg(s, rn); switch (op1) { case 0: tcg_gen_bswap32_i32(tmp, tmp); break; case 1: gen_rev16(tmp); break; case 3: gen_revsh(tmp); break; default: g_assert_not_reached(); } store_reg(s, rd, tmp); break; } case 6: switch ((insn >> 5) & 7) { case 2: /* setend */ ARCH(6); if (((insn >> 3) & 1) != !!(s->be_data == MO_BE)) { gen_helper_setend(cpu_env); s->is_jmp = DISAS_UPDATE; } break; case 3: /* cps */ ARCH(6); if (IS_USER(s)) { break; } if (arm_dc_feature(s, ARM_FEATURE_M)) { tmp = tcg_const_i32((insn & (1 << 4)) != 0); /* FAULTMASK */ if (insn & 1) { addr = tcg_const_i32(19); gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); } /* PRIMASK */ if (insn & 2) { addr = tcg_const_i32(16); gen_helper_v7m_msr(cpu_env, addr, tmp); tcg_temp_free_i32(addr); } tcg_temp_free_i32(tmp); gen_lookup_tb(s); } else { if (insn & (1 << 4)) { shift = CPSR_A | CPSR_I | CPSR_F; } else { shift = 0; } gen_set_psr_im(s, ((insn & 7) << 6), 0, shift); } break; default: goto undef; } break; default: goto undef; } break; case 12: { /* load/store multiple */ TCGv_i32 loaded_var; TCGV_UNUSED_I32(loaded_var); rn = (insn >> 8) & 0x7; addr = load_reg(s, rn); for (i = 0; i < 8; i++) { if (insn & (1 << i)) { if (insn & (1 << 11)) { /* load */ tmp = tcg_temp_new_i32(); gen_aa32_ld32u(s, tmp, addr, get_mem_index(s)); if (i == rn) { loaded_var = tmp; } else { store_reg(s, i, tmp); } } else { /* store */ tmp = load_reg(s, i); gen_aa32_st32(s, tmp, addr, get_mem_index(s)); tcg_temp_free_i32(tmp); } /* advance to the next address */ tcg_gen_addi_i32(addr, addr, 4); } } if ((insn & (1 << rn)) == 0) { /* base reg not in list: base register writeback */ store_reg(s, rn, addr); } else { /* base reg in list: if load, complete it now */ if (insn & (1 << 11)) { store_reg(s, rn, loaded_var); } tcg_temp_free_i32(addr); } break; } case 13: /* conditional branch or swi */ cond = (insn >> 8) & 0xf; if (cond == 0xe) goto undef; if (cond == 0xf) { /* swi */ gen_set_pc_im(s, s->pc); s->svc_imm = extract32(insn, 0, 8); s->is_jmp = DISAS_SWI; break; } /* generate a conditional jump to next instruction */ s->condlabel = gen_new_label(); arm_gen_test_cc(cond ^ 1, s->condlabel); s->condjmp = 1; /* jump to the offset */ val = (uint32_t)s->pc + 2; offset = ((int32_t)insn << 24) >> 24; val += offset << 1; gen_jmp(s, val); break; case 14: if (insn & (1 << 11)) { if (disas_thumb2_insn(env, s, insn)) goto undef32; break; } /* unconditional branch */ val = (uint32_t)s->pc; offset = ((int32_t)insn << 21) >> 21; val += (offset << 1) + 2; gen_jmp(s, val); break; case 15: if (disas_thumb2_insn(env, s, insn)) goto undef32; break; } return; undef32: gen_exception_insn(s, 4, EXCP_UDEF, syn_uncategorized(), default_exception_el(s)); return; illegal_op: undef: gen_exception_insn(s, 2, EXCP_UDEF, syn_uncategorized(), default_exception_el(s)); }