+ return 1;
+ }
+ } else { /* (insn & 0x00380080) == 0 */
+ int invert;
+
+ op = (insn >> 8) & 0xf;
+ /* One register and immediate. */
+ imm = (u << 7) | ((insn >> 12) & 0x70) | (insn & 0xf);
+ invert = (insn & (1 << 5)) != 0;
+ switch (op) {
+ case 0: case 1:
+ /* no-op */
+ break;
+ case 2: case 3:
+ imm <<= 8;
+ break;
+ case 4: case 5:
+ imm <<= 16;
+ break;
+ case 6: case 7:
+ imm <<= 24;
+ break;
+ case 8: case 9:
+ imm |= imm << 16;
+ break;
+ case 10: case 11:
+ imm = (imm << 8) | (imm << 24);
+ break;
+ case 12:
+ imm = (imm < 8) | 0xff;
+ break;
+ case 13:
+ imm = (imm << 16) | 0xffff;
+ break;
+ case 14:
+ imm |= (imm << 8) | (imm << 16) | (imm << 24);
+ if (invert)
+ imm = ~imm;
+ break;
+ case 15:
+ imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19)
+ | ((imm & 0x40) ? (0x1f << 25) : (1 << 30));
+ break;
+ }
+ if (invert)
+ imm = ~imm;
+
+ if (op != 14 || !invert)
+ gen_op_movl_T1_im(imm);
+
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ if (op & 1 && op < 12) {
+ NEON_GET_REG(T0, rd, pass);
+ if (invert) {
+ /* The immediate value has already been inverted, so
+ BIC becomes AND. */
+ gen_op_andl_T0_T1();
+ } else {
+ gen_op_orl_T0_T1();
+ }
+ NEON_SET_REG(T0, rd, pass);
+ } else {
+ if (op == 14 && invert) {
+ uint32_t tmp;
+ tmp = 0;
+ for (n = 0; n < 4; n++) {
+ if (imm & (1 << (n + (pass & 1) * 4)))
+ tmp |= 0xff << (n * 8);
+ }
+ gen_op_movl_T1_im(tmp);
+ }
+ /* VMOV, VMVN. */
+ NEON_SET_REG(T1, rd, pass);
+ }
+ }
+ }
+ } else { /* (insn & 0x00800010 == 0x00800010) */
+ if (size != 3) {
+ op = (insn >> 8) & 0xf;
+ if ((insn & (1 << 6)) == 0) {
+ /* Three registers of different lengths. */
+ int src1_wide;
+ int src2_wide;
+ int prewiden;
+ /* prewiden, src1_wide, src2_wide */
+ static const int neon_3reg_wide[16][3] = {
+ {1, 0, 0}, /* VADDL */
+ {1, 1, 0}, /* VADDW */
+ {1, 0, 0}, /* VSUBL */
+ {1, 1, 0}, /* VSUBW */
+ {0, 1, 1}, /* VADDHN */
+ {0, 0, 0}, /* VABAL */
+ {0, 1, 1}, /* VSUBHN */
+ {0, 0, 0}, /* VABDL */
+ {0, 0, 0}, /* VMLAL */
+ {0, 0, 0}, /* VQDMLAL */
+ {0, 0, 0}, /* VMLSL */
+ {0, 0, 0}, /* VQDMLSL */
+ {0, 0, 0}, /* Integer VMULL */
+ {0, 0, 0}, /* VQDMULL */
+ {0, 0, 0} /* Polynomial VMULL */
+ };
+
+ prewiden = neon_3reg_wide[op][0];
+ src1_wide = neon_3reg_wide[op][1];
+ src2_wide = neon_3reg_wide[op][2];
+
+ /* Avoid overlapping operands. Wide source operands are
+ always aligned so will never overlap with wide
+ destinations in problematic ways. */
+ if (rd == rm) {
+ NEON_GET_REG(T2, rm, 1);
+ } else if (rd == rn) {
+ NEON_GET_REG(T2, rn, 1);
+ }
+ for (pass = 0; pass < 2; pass++) {
+ /* Load the second operand into env->vfp.scratch.
+ Also widen narrow operands. */
+ if (pass == 1 && rd == rm) {
+ if (prewiden) {
+ gen_op_movl_T0_T2();
+ } else {
+ gen_op_movl_T1_T2();
+ }
+ } else {
+ if (src2_wide) {
+ NEON_GET_REG(T0, rm, pass * 2);
+ NEON_GET_REG(T1, rm, pass * 2 + 1);
+ } else {
+ if (prewiden) {
+ NEON_GET_REG(T0, rm, pass);
+ } else {
+ NEON_GET_REG(T1, rm, pass);
+ }
+ }
+ }
+ if (prewiden && !src2_wide) {
+ GEN_NEON_INTEGER_OP(widen);
+ }
+ if (prewiden || src2_wide) {
+ gen_neon_movl_scratch_T0(0);
+ gen_neon_movl_scratch_T1(1);
+ }
+
+ /* Load the first operand. */
+ if (pass == 1 && rd == rn) {
+ gen_op_movl_T0_T2();
+ } else {
+ if (src1_wide) {
+ NEON_GET_REG(T0, rn, pass * 2);
+ NEON_GET_REG(T1, rn, pass * 2 + 1);
+ } else {
+ NEON_GET_REG(T0, rn, pass);
+ }
+ }
+ if (prewiden && !src1_wide) {
+ GEN_NEON_INTEGER_OP(widen);
+ }
+ switch (op) {
+ case 0: case 1: case 4: /* VADDL, VADDW, VADDHN, VRADDHN */
+ switch (size) {
+ case 0: gen_op_neon_addl_u16(); break;
+ case 1: gen_op_neon_addl_u32(); break;
+ case 2: gen_op_neon_addl_u64(); break;
+ default: abort();
+ }
+ break;
+ case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHL, VRSUBHL */
+ switch (size) {
+ case 0: gen_op_neon_subl_u16(); break;
+ case 1: gen_op_neon_subl_u32(); break;
+ case 2: gen_op_neon_subl_u64(); break;
+ default: abort();
+ }
+ break;
+ case 5: case 7: /* VABAL, VABDL */
+ switch ((size << 1) | u) {
+ case 0: gen_op_neon_abdl_s16(); break;
+ case 1: gen_op_neon_abdl_u16(); break;
+ case 2: gen_op_neon_abdl_s32(); break;
+ case 3: gen_op_neon_abdl_u32(); break;
+ case 4: gen_op_neon_abdl_s64(); break;
+ case 5: gen_op_neon_abdl_u64(); break;
+ default: abort();
+ }
+ break;
+ case 8: case 9: case 10: case 11: case 12: case 13:
+ /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */
+ switch ((size << 1) | u) {
+ case 0: gen_op_neon_mull_s8(); break;
+ case 1: gen_op_neon_mull_u8(); break;
+ case 2: gen_op_neon_mull_s16(); break;
+ case 3: gen_op_neon_mull_u16(); break;
+ case 4: gen_op_imull_T0_T1(); break;
+ case 5: gen_op_mull_T0_T1(); break;
+ default: abort();
+ }
+ break;
+ case 14: /* Polynomial VMULL */
+ cpu_abort(env, "Polynomial VMULL not implemented");
+
+ default: /* 15 is RESERVED. */
+ return 1;
+ }
+ if (op == 5 || op == 13 || (op >= 8 && op <= 11)) {
+ /* Accumulate. */
+ if (op == 10 || op == 11) {
+ switch (size) {
+ case 0: gen_op_neon_negl_u16(); break;
+ case 1: gen_op_neon_negl_u32(); break;
+ case 2: gen_op_neon_negl_u64(); break;
+ default: abort();
+ }
+ }
+
+ gen_neon_movl_scratch_T0(0);
+ gen_neon_movl_scratch_T1(1);
+
+ if (op != 13) {
+ NEON_GET_REG(T0, rd, pass * 2);
+ NEON_GET_REG(T1, rd, pass * 2 + 1);
+ }
+
+ switch (op) {
+ case 5: case 8: case 10: /* VABAL, VMLAL, VMLSL */
+ switch (size) {
+ case 0: gen_op_neon_addl_u16(); break;
+ case 1: gen_op_neon_addl_u32(); break;
+ case 2: gen_op_neon_addl_u64(); break;
+ default: abort();
+ }
+ break;
+ case 9: case 11: /* VQDMLAL, VQDMLSL */
+ switch (size) {
+ case 1: gen_op_neon_addl_saturate_s32(); break;
+ case 2: gen_op_neon_addl_saturate_s64(); break;
+ default: abort();
+ }
+ /* Fall through. */
+ case 13: /* VQDMULL */
+ switch (size) {
+ case 1: gen_op_neon_addl_saturate_s32(); break;
+ case 2: gen_op_neon_addl_saturate_s64(); break;
+ default: abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ NEON_SET_REG(T0, rd, pass * 2);
+ NEON_SET_REG(T1, rd, pass * 2 + 1);
+ } else if (op == 4 || op == 6) {
+ /* Narrowing operation. */
+ if (u) {
+ switch (size) {
+ case 0: gen_op_neon_narrow_high_u8(); break;
+ case 1: gen_op_neon_narrow_high_u16(); break;
+ case 2: gen_op_movl_T0_T1(); break;
+ default: abort();
+ }
+ } else {
+ switch (size) {
+ case 0: gen_op_neon_narrow_high_round_u8(); break;
+ case 1: gen_op_neon_narrow_high_round_u16(); break;
+ case 2: gen_op_neon_narrow_high_round_u32(); break;
+ default: abort();
+ }
+ }
+ NEON_SET_REG(T0, rd, pass);
+ } else {
+ /* Write back the result. */
+ NEON_SET_REG(T0, rd, pass * 2);
+ NEON_SET_REG(T1, rd, pass * 2 + 1);
+ }
+ }
+ } else {
+ /* Two registers and a scalar. */
+ switch (op) {
+ case 0: /* Integer VMLA scalar */
+ case 1: /* Float VMLA scalar */
+ case 4: /* Integer VMLS scalar */
+ case 5: /* Floating point VMLS scalar */
+ case 8: /* Integer VMUL scalar */
+ case 9: /* Floating point VMUL scalar */
+ case 12: /* VQDMULH scalar */
+ case 13: /* VQRDMULH scalar */
+ gen_neon_get_scalar(size, rm);
+ gen_op_movl_T2_T0();
+ for (pass = 0; pass < (u ? 4 : 2); pass++) {
+ if (pass != 0)
+ gen_op_movl_T0_T2();
+ NEON_GET_REG(T1, rn, pass);
+ if (op == 12) {
+ if (size == 1) {
+ gen_op_neon_qdmulh_s16();
+ } else {
+ gen_op_neon_qdmulh_s32();
+ }
+ } else if (op == 13) {
+ if (size == 1) {
+ gen_op_neon_qrdmulh_s16();
+ } else {
+ gen_op_neon_qrdmulh_s32();
+ }
+ } else if (op & 1) {
+ gen_op_neon_mul_f32();
+ } else {
+ switch (size) {
+ case 0: gen_op_neon_mul_u8(); break;
+ case 1: gen_op_neon_mul_u16(); break;
+ case 2: gen_op_mul_T0_T1(); break;
+ default: return 1;
+ }
+ }
+ if (op < 8) {
+ /* Accumulate. */
+ NEON_GET_REG(T1, rd, pass);
+ switch (op) {
+ case 0:
+ gen_neon_add(size);
+ break;
+ case 1:
+ gen_op_neon_add_f32();
+ break;
+ case 4:
+ switch (size) {
+ case 0: gen_op_neon_rsb_u8(); break;
+ case 1: gen_op_neon_rsb_u16(); break;
+ case 2: gen_op_rsbl_T0_T1(); break;
+ default: return 1;
+ }
+ break;
+ case 5:
+ gen_op_neon_rsb_f32();
+ break;
+ default:
+ abort();
+ }
+ }
+ NEON_SET_REG(T0, rd, pass);
+ }
+ break;
+ case 2: /* VMLAL sclar */
+ case 3: /* VQDMLAL scalar */
+ case 6: /* VMLSL scalar */
+ case 7: /* VQDMLSL scalar */
+ case 10: /* VMULL scalar */
+ case 11: /* VQDMULL scalar */
+ if (rd == rn) {
+ /* Save overlapping operands before they are
+ clobbered. */
+ NEON_GET_REG(T0, rn, 1);
+ gen_neon_movl_scratch_T0(2);
+ }
+ gen_neon_get_scalar(size, rm);
+ gen_op_movl_T2_T0();
+ for (pass = 0; pass < 2; pass++) {
+ if (pass != 0) {
+ gen_op_movl_T0_T2();
+ }
+ if (pass != 0 && rd == rn) {
+ gen_neon_movl_T1_scratch(2);
+ } else {
+ NEON_GET_REG(T1, rn, pass);
+ }
+ switch ((size << 1) | u) {
+ case 0: gen_op_neon_mull_s8(); break;
+ case 1: gen_op_neon_mull_u8(); break;
+ case 2: gen_op_neon_mull_s16(); break;
+ case 3: gen_op_neon_mull_u16(); break;
+ case 4: gen_op_imull_T0_T1(); break;
+ case 5: gen_op_mull_T0_T1(); break;
+ default: abort();
+ }
+ if (op == 6 || op == 7) {
+ switch (size) {
+ case 0: gen_op_neon_negl_u16(); break;
+ case 1: gen_op_neon_negl_u32(); break;
+ case 2: gen_op_neon_negl_u64(); break;
+ default: abort();
+ }
+ }
+ gen_neon_movl_scratch_T0(0);
+ gen_neon_movl_scratch_T1(1);
+ NEON_GET_REG(T0, rd, pass * 2);
+ NEON_GET_REG(T1, rd, pass * 2 + 1);
+ switch (op) {
+ case 2: case 6:
+ switch (size) {
+ case 0: gen_op_neon_addl_u16(); break;
+ case 1: gen_op_neon_addl_u32(); break;
+ case 2: gen_op_neon_addl_u64(); break;
+ default: abort();
+ }
+ break;
+ case 3: case 7:
+ switch (size) {
+ case 1:
+ gen_op_neon_addl_saturate_s32();
+ gen_op_neon_addl_saturate_s32();
+ break;
+ case 2:
+ gen_op_neon_addl_saturate_s64();
+ gen_op_neon_addl_saturate_s64();
+ break;
+ default: abort();
+ }
+ break;
+ case 10:
+ /* no-op */
+ break;
+ case 11:
+ switch (size) {
+ case 1: gen_op_neon_addl_saturate_s32(); break;
+ case 2: gen_op_neon_addl_saturate_s64(); break;
+ default: abort();
+ }
+ break;
+ default:
+ abort();
+ }
+ NEON_SET_REG(T0, rd, pass * 2);
+ NEON_SET_REG(T1, rd, pass * 2 + 1);
+ }
+ break;
+ default: /* 14 and 15 are RESERVED */
+ return 1;
+ }
+ }
+ } else { /* size == 3 */
+ if (!u) {
+ /* Extract. */
+ int reg;
+ imm = (insn >> 8) & 0xf;
+ reg = rn;
+ count = q ? 4 : 2;
+ n = imm >> 2;
+ NEON_GET_REG(T0, reg, n);
+ for (pass = 0; pass < count; pass++) {
+ n++;
+ if (n > count) {
+ reg = rm;
+ n -= count;
+ }
+ if (imm & 3) {
+ NEON_GET_REG(T1, reg, n);
+ gen_op_neon_extract((insn << 3) & 0x1f);
+ }
+ /* ??? This is broken if rd and rm overlap */
+ NEON_SET_REG(T0, rd, pass);
+ if (imm & 3) {
+ gen_op_movl_T0_T1();
+ } else {
+ NEON_GET_REG(T0, reg, n);
+ }
+ }
+ } else if ((insn & (1 << 11)) == 0) {
+ /* Two register misc. */
+ op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
+ size = (insn >> 18) & 3;
+ switch (op) {
+ case 0: /* VREV64 */
+ if (size == 3)
+ return 1;
+ for (pass = 0; pass < (q ? 2 : 1); pass++) {
+ NEON_GET_REG(T0, rm, pass * 2);
+ NEON_GET_REG(T1, rm, pass * 2 + 1);
+ switch (size) {
+ case 0: gen_op_rev_T0(); break;
+ case 1: gen_op_revh_T0(); break;
+ case 2: /* no-op */ break;
+ default: abort();
+ }
+ NEON_SET_REG(T0, rd, pass * 2 + 1);
+ if (size == 2) {
+ NEON_SET_REG(T1, rd, pass * 2);
+ } else {
+ gen_op_movl_T0_T1();
+ switch (size) {
+ case 0: gen_op_rev_T0(); break;
+ case 1: gen_op_revh_T0(); break;
+ default: abort();
+ }
+ NEON_SET_REG(T0, rd, pass * 2);
+ }
+ }
+ break;
+ case 4: case 5: /* VPADDL */
+ case 12: case 13: /* VPADAL */
+ if (size < 2)
+ goto elementwise;
+ if (size == 3)
+ return 1;
+ for (pass = 0; pass < (q ? 2 : 1); pass++) {
+ NEON_GET_REG(T0, rm, pass * 2);
+ NEON_GET_REG(T1, rm, pass * 2 + 1);
+ if (op & 1)
+ gen_op_neon_paddl_u32();
+ else
+ gen_op_neon_paddl_s32();
+ if (op >= 12) {
+ /* Accumulate. */
+ gen_neon_movl_scratch_T0(0);
+ gen_neon_movl_scratch_T1(1);
+
+ NEON_GET_REG(T0, rd, pass * 2);
+ NEON_GET_REG(T1, rd, pass * 2 + 1);
+ gen_op_neon_addl_u64();
+ }
+ NEON_SET_REG(T0, rd, pass * 2);
+ NEON_SET_REG(T1, rd, pass * 2 + 1);
+ }
+ break;
+ case 33: /* VTRN */
+ if (size == 2) {
+ for (n = 0; n < (q ? 4 : 2); n += 2) {
+ NEON_GET_REG(T0, rm, n);
+ NEON_GET_REG(T1, rd, n + 1);
+ NEON_SET_REG(T1, rm, n);
+ NEON_SET_REG(T0, rd, n + 1);
+ }
+ } else {
+ goto elementwise;
+ }
+ break;
+ case 34: /* VUZP */
+ /* Reg Before After
+ Rd A3 A2 A1 A0 B2 B0 A2 A0
+ Rm B3 B2 B1 B0 B3 B1 A3 A1
+ */
+ if (size == 3)
+ return 1;
+ gen_neon_unzip(rd, q, 0, size);
+ gen_neon_unzip(rm, q, 4, size);
+ if (q) {
+ static int unzip_order_q[8] =
+ {0, 2, 4, 6, 1, 3, 5, 7};
+ for (n = 0; n < 8; n++) {
+ int reg = (n < 4) ? rd : rm;
+ gen_neon_movl_T0_scratch(unzip_order_q[n]);
+ NEON_SET_REG(T0, reg, n % 4);
+ }
+ } else {
+ static int unzip_order[4] =
+ {0, 4, 1, 5};
+ for (n = 0; n < 4; n++) {
+ int reg = (n < 2) ? rd : rm;
+ gen_neon_movl_T0_scratch(unzip_order[n]);
+ NEON_SET_REG(T0, reg, n % 2);
+ }
+ }
+ break;
+ case 35: /* VZIP */
+ /* Reg Before After
+ Rd A3 A2 A1 A0 B1 A1 B0 A0
+ Rm B3 B2 B1 B0 B3 A3 B2 A2
+ */
+ if (size == 3)
+ return 1;
+ count = (q ? 4 : 2);
+ for (n = 0; n < count; n++) {
+ NEON_GET_REG(T0, rd, n);
+ NEON_GET_REG(T1, rd, n);
+ switch (size) {
+ case 0: gen_op_neon_zip_u8(); break;
+ case 1: gen_op_neon_zip_u16(); break;
+ case 2: /* no-op */; break;
+ default: abort();
+ }
+ gen_neon_movl_scratch_T0(n * 2);
+ gen_neon_movl_scratch_T1(n * 2 + 1);
+ }
+ for (n = 0; n < count * 2; n++) {
+ int reg = (n < count) ? rd : rm;
+ gen_neon_movl_T0_scratch(n);
+ NEON_SET_REG(T0, reg, n % count);
+ }
+ break;
+ case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */
+ for (pass = 0; pass < 2; pass++) {
+ if (rd == rm + 1) {
+ n = 1 - pass;
+ } else {
+ n = pass;
+ }
+ NEON_GET_REG(T0, rm, n * 2);
+ NEON_GET_REG(T1, rm, n * 2 + 1);
+ if (op == 36 && q == 0) {
+ switch (size) {
+ case 0: gen_op_neon_narrow_u8(); break;
+ case 1: gen_op_neon_narrow_u16(); break;
+ case 2: /* no-op */ break;
+ default: return 1;
+ }
+ } else if (q) {
+ switch (size) {
+ case 0: gen_op_neon_narrow_sat_u8(); break;
+ case 1: gen_op_neon_narrow_sat_u16(); break;
+ case 2: gen_op_neon_narrow_sat_u32(); break;
+ default: return 1;
+ }
+ } else {
+ switch (size) {
+ case 0: gen_op_neon_narrow_sat_s8(); break;
+ case 1: gen_op_neon_narrow_sat_s16(); break;
+ case 2: gen_op_neon_narrow_sat_s32(); break;
+ default: return 1;
+ }
+ }
+ NEON_SET_REG(T0, rd, n);
+ }
+ break;
+ case 38: /* VSHLL */
+ if (q)
+ return 1;
+ if (rm == rd) {
+ NEON_GET_REG(T2, rm, 1);
+ }
+ for (pass = 0; pass < 2; pass++) {
+ if (pass == 1 && rm == rd) {
+ gen_op_movl_T0_T2();
+ } else {
+ NEON_GET_REG(T0, rm, pass);
+ }
+ switch (size) {
+ case 0: gen_op_neon_widen_high_u8(); break;
+ case 1: gen_op_neon_widen_high_u16(); break;
+ case 2:
+ gen_op_movl_T1_T0();
+ gen_op_movl_T0_im(0);
+ break;
+ default: return 1;
+ }
+ NEON_SET_REG(T0, rd, pass * 2);
+ NEON_SET_REG(T1, rd, pass * 2 + 1);
+ }
+ break;
+ default:
+ elementwise:
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ if (op == 30 || op == 31 || op >= 58) {
+ gen_op_vfp_getreg_F0s(neon_reg_offset(rm, pass));
+ } else {
+ NEON_GET_REG(T0, rm, pass);
+ }
+ switch (op) {
+ case 1: /* VREV32 */
+ switch (size) {
+ case 0: gen_op_rev_T0(); break;
+ case 1: gen_op_revh_T0(); break;
+ default: return 1;
+ }
+ break;
+ case 2: /* VREV16 */
+ if (size != 0)
+ return 1;
+ gen_op_rev16_T0();
+ break;
+ case 4: case 5: /* VPADDL */
+ case 12: case 13: /* VPADAL */
+ switch ((size << 1) | (op & 1)) {
+ case 0: gen_op_neon_paddl_s8(); break;
+ case 1: gen_op_neon_paddl_u8(); break;
+ case 2: gen_op_neon_paddl_s16(); break;
+ case 3: gen_op_neon_paddl_u16(); break;
+ default: abort();
+ }
+ if (op >= 12) {
+ /* Accumulate */
+ NEON_GET_REG(T1, rd, pass);
+ switch (size) {
+ case 0: gen_op_neon_add_u16(); break;
+ case 1: gen_op_addl_T0_T1(); break;
+ default: abort();
+ }
+ }
+ break;
+ case 8: /* CLS */
+ switch (size) {
+ case 0: gen_op_neon_cls_s8(); break;
+ case 1: gen_op_neon_cls_s16(); break;
+ case 2: gen_op_neon_cls_s32(); break;
+ default: return 1;
+ }
+ break;
+ case 9: /* CLZ */
+ switch (size) {
+ case 0: gen_op_neon_clz_u8(); break;
+ case 1: gen_op_neon_clz_u16(); break;
+ case 2: gen_op_clz_T0(); break;
+ default: return 1;
+ }
+ break;
+ case 10: /* CNT */
+ if (size != 0)
+ return 1;
+ gen_op_neon_cnt_u8();
+ break;
+ case 11: /* VNOT */
+ if (size != 0)
+ return 1;
+ gen_op_notl_T0();
+ break;
+ case 14: /* VQABS */
+ switch (size) {
+ case 0: gen_op_neon_qabs_s8(); break;
+ case 1: gen_op_neon_qabs_s16(); break;
+ case 2: gen_op_neon_qabs_s32(); break;
+ default: return 1;
+ }
+ break;
+ case 15: /* VQNEG */
+ switch (size) {
+ case 0: gen_op_neon_qneg_s8(); break;
+ case 1: gen_op_neon_qneg_s16(); break;
+ case 2: gen_op_neon_qneg_s32(); break;
+ default: return 1;
+ }
+ break;
+ case 16: case 19: /* VCGT #0, VCLE #0 */
+ gen_op_movl_T1_im(0);
+ switch(size) {
+ case 0: gen_op_neon_cgt_s8(); break;
+ case 1: gen_op_neon_cgt_s16(); break;
+ case 2: gen_op_neon_cgt_s32(); break;
+ default: return 1;
+ }
+ if (op == 19)
+ gen_op_notl_T0();
+ break;
+ case 17: case 20: /* VCGE #0, VCLT #0 */
+ gen_op_movl_T1_im(0);
+ switch(size) {
+ case 0: gen_op_neon_cge_s8(); break;
+ case 1: gen_op_neon_cge_s16(); break;
+ case 2: gen_op_neon_cge_s32(); break;
+ default: return 1;
+ }
+ if (op == 20)
+ gen_op_notl_T0();
+ break;
+ case 18: /* VCEQ #0 */
+ gen_op_movl_T1_im(0);
+ switch(size) {
+ case 0: gen_op_neon_ceq_u8(); break;
+ case 1: gen_op_neon_ceq_u16(); break;
+ case 2: gen_op_neon_ceq_u32(); break;
+ default: return 1;
+ }
+ break;
+ case 22: /* VABS */
+ switch(size) {
+ case 0: gen_op_neon_abs_s8(); break;
+ case 1: gen_op_neon_abs_s16(); break;
+ case 2: gen_op_neon_abs_s32(); break;
+ default: return 1;
+ }
+ break;
+ case 23: /* VNEG */
+ gen_op_movl_T1_im(0);
+ switch(size) {
+ case 0: gen_op_neon_rsb_u8(); break;
+ case 1: gen_op_neon_rsb_u16(); break;
+ case 2: gen_op_rsbl_T0_T1(); break;
+ default: return 1;
+ }
+ break;
+ case 24: case 27: /* Float VCGT #0, Float VCLE #0 */
+ gen_op_movl_T1_im(0);
+ gen_op_neon_cgt_f32();
+ if (op == 27)
+ gen_op_notl_T0();
+ break;
+ case 25: case 28: /* Float VCGE #0, Float VCLT #0 */
+ gen_op_movl_T1_im(0);
+ gen_op_neon_cge_f32();
+ if (op == 28)
+ gen_op_notl_T0();
+ break;
+ case 26: /* Float VCEQ #0 */
+ gen_op_movl_T1_im(0);
+ gen_op_neon_ceq_f32();
+ break;
+ case 30: /* Float VABS */
+ gen_op_vfp_abss();
+ break;
+ case 31: /* Float VNEG */
+ gen_op_vfp_negs();
+ break;
+ case 32: /* VSWP */
+ NEON_GET_REG(T1, rd, pass);
+ NEON_SET_REG(T1, rm, pass);
+ break;
+ case 33: /* VTRN */
+ NEON_GET_REG(T1, rd, pass);
+ switch (size) {
+ case 0: gen_op_neon_trn_u8(); break;
+ case 1: gen_op_neon_trn_u16(); break;
+ case 2: abort();
+ default: return 1;
+ }
+ NEON_SET_REG(T1, rm, pass);
+ break;
+ case 56: /* Integer VRECPE */
+ gen_op_neon_recpe_u32();
+ break;
+ case 57: /* Integer VRSQRTE */
+ gen_op_neon_rsqrte_u32();
+ break;
+ case 58: /* Float VRECPE */
+ gen_op_neon_recpe_f32();
+ break;
+ case 59: /* Float VRSQRTE */
+ gen_op_neon_rsqrte_f32();
+ break;
+ case 60: /* VCVT.F32.S32 */
+ gen_op_vfp_tosizs();
+ break;
+ case 61: /* VCVT.F32.U32 */
+ gen_op_vfp_touizs();
+ break;
+ case 62: /* VCVT.S32.F32 */
+ gen_op_vfp_sitos();
+ break;
+ case 63: /* VCVT.U32.F32 */
+ gen_op_vfp_uitos();
+ break;
+ default:
+ /* Reserved: 21, 29, 39-56 */
+ return 1;
+ }
+ if (op == 30 || op == 31 || op >= 58) {
+ gen_op_vfp_setreg_F0s(neon_reg_offset(rm, pass));
+ } else {
+ NEON_SET_REG(T0, rd, pass);
+ }
+ }
+ break;
+ }
+ } else if ((insn & (1 << 10)) == 0) {
+ /* VTBL, VTBX. */
+ n = (insn >> 5) & 0x18;
+ NEON_GET_REG(T1, rm, 0);
+ if (insn & (1 << 6)) {
+ NEON_GET_REG(T0, rd, 0);
+ } else {
+ gen_op_movl_T0_im(0);
+ }
+ gen_op_neon_tbl(rn, n);
+ gen_op_movl_T2_T0();
+ NEON_GET_REG(T1, rm, 1);
+ if (insn & (1 << 6)) {
+ NEON_GET_REG(T0, rd, 0);
+ } else {
+ gen_op_movl_T0_im(0);
+ }
+ gen_op_neon_tbl(rn, n);
+ NEON_SET_REG(T2, rd, 0);
+ NEON_SET_REG(T0, rd, 1);
+ } else if ((insn & 0x380) == 0) {
+ /* VDUP */
+ if (insn & (1 << 19)) {
+ NEON_SET_REG(T0, rm, 1);
+ } else {
+ NEON_SET_REG(T0, rm, 0);
+ }
+ if (insn & (1 << 16)) {
+ gen_op_neon_dup_u8(((insn >> 17) & 3) * 8);
+ } else if (insn & (1 << 17)) {
+ if ((insn >> 18) & 1)
+ gen_op_neon_dup_high16();
+ else
+ gen_op_neon_dup_low16();
+ }
+ for (pass = 0; pass < (q ? 4 : 2); pass++) {
+ NEON_SET_REG(T0, rd, pass);
+ }
+ } else {
+ return 1;
+ }
+ }
+ }
+ return 0;
+}
+
+static int disas_coproc_insn(CPUState * env, DisasContext *s, uint32_t insn)
+{
+ int cpnum;
+
+ cpnum = (insn >> 8) & 0xf;
+ if (arm_feature(env, ARM_FEATURE_XSCALE)
+ && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum)))
+ return 1;
+
+ switch (cpnum) {
+ case 0:
+ case 1:
+ if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
+ return disas_iwmmxt_insn(env, s, insn);
+ } else if (arm_feature(env, ARM_FEATURE_XSCALE)) {
+ return disas_dsp_insn(env, s, insn);
+ }
+ return 1;
+ case 10:
+ case 11:
+ return disas_vfp_insn (env, s, insn);
+ case 15:
+ return disas_cp15_insn (env, s, insn);
+ default:
+ /* Unknown coprocessor. See if the board has hooked it. */
+ return disas_cp_insn (env, s, insn);
+ }
+}
+
+static void disas_arm_insn(CPUState * env, DisasContext *s)
+{
+ unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
+
+ 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 & 0x0d70f000) == 0x0550f000)
+ return; /* PLD */
+ else 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_op_clrex();
+ 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 */
+ uint32_t offset;
+ if (IS_USER(s))
+ goto illegal_op;
+ ARCH(6);
+ op1 = (insn & 0x1f);
+ if (op1 == (env->uncached_cpsr & CPSR_M)) {
+ gen_movl_T1_reg(s, 13);
+ } else {
+ gen_op_movl_T1_r13_banked(op1);
+ }
+ i = (insn >> 23) & 3;
+ switch (i) {
+ case 0: offset = -4; break; /* DA */
+ case 1: offset = -8; break; /* DB */
+ case 2: offset = 0; break; /* IA */
+ case 3: offset = 4; break; /* IB */
+ default: abort();
+ }
+ if (offset)
+ gen_op_addl_T1_im(offset);
+ gen_movl_T0_reg(s, 14);
+ gen_ldst(stl, s);
+ gen_op_movl_T0_cpsr();
+ gen_op_addl_T1_im(4);
+ gen_ldst(stl, s);
+ 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)
+ gen_op_addl_T1_im(offset);
+ if (op1 == (env->uncached_cpsr & CPSR_M)) {
+ gen_movl_reg_T1(s, 13);
+ } else {
+ gen_op_movl_r13_T1_banked(op1);
+ }
+ }
+ } else if ((insn & 0x0e5fffe0) == 0x081d0a00) {
+ /* rfe */
+ uint32_t offset;
+ if (IS_USER(s))
+ goto illegal_op;
+ ARCH(6);
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T1_reg(s, rn);
+ i = (insn >> 23) & 3;
+ switch (i) {
+ case 0: offset = 0; break; /* DA */
+ case 1: offset = -4; break; /* DB */
+ case 2: offset = 4; break; /* IA */
+ case 3: offset = 8; break; /* IB */
+ default: abort();
+ }
+ if (offset)
+ gen_op_addl_T1_im(offset);
+ /* Load CPSR into T2 and PC into T0. */
+ gen_ldst(ldl, s);
+ gen_op_movl_T2_T0();
+ gen_op_addl_T1_im(-4);
+ gen_ldst(ldl, s);
+ if (insn & (1 << 21)) {
+ /* Base writeback. */
+ switch (i) {
+ case 0: offset = -4; break;
+ case 1: offset = 0; break;
+ case 2: offset = 8; break;
+ case 3: offset = 4; break;
+ default: abort();
+ }
+ if (offset)
+ gen_op_addl_T1_im(offset);
+ gen_movl_reg_T1(s, rn);
+ }
+ gen_rfe(s);
+ } else if ((insn & 0x0e000000) == 0x0a000000) {
+ /* branch link and change to thumb (blx <offset>) */
+ int32_t offset;
+
+ val = (uint32_t)s->pc;
+ gen_op_movl_T0_im(val);
+ gen_movl_reg_T0(s, 14);
+ /* 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_op_movl_T0_im(val);
+ gen_bx(s);
+ 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 & 0x0ff10010) == 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 << 14)) {
+ mask |= CPSR_M;
+ val |= (insn & 0x1f);
+ }
+ if (mask) {
+ gen_op_movl_T0_im(val);
+ gen_set_psr_T0(s, mask, 0);
+ }
+ 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 */
+ gen_op_movl_T0_im(val);
+ } else {
+ /* MOVT */
+ gen_movl_T0_reg(s, rd);
+ gen_op_movl_T1_im(0xffff);
+ gen_op_andl_T0_T1();
+ gen_op_movl_T1_im(val << 16);
+ gen_op_orl_T0_T1();
+ }
+ gen_movl_reg_T0(s, rd);
+ } 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));
+ gen_op_movl_T0_im(val);
+ i = ((insn & (1 << 22)) != 0);
+ if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
+ 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 */
+ gen_movl_T0_reg(s, rm);
+ i = ((op1 & 2) != 0);
+ if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
+ goto illegal_op;
+ } else {
+ /* reg = PSR */
+ rd = (insn >> 12) & 0xf;
+ if (op1 & 2) {
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_movl_T0_spsr();
+ } else {
+ gen_op_movl_T0_cpsr();
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+ case 0x1:
+ if (op1 == 1) {
+ /* branch/exchange thumb (bx). */
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ } else if (op1 == 3) {
+ /* clz */
+ rd = (insn >> 12) & 0xf;
+ gen_movl_T0_reg(s, rm);
+ gen_op_clz_T0();
+ gen_movl_reg_T0(s, rd);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x2:
+ if (op1 == 1) {
+ ARCH(5J); /* bxj */
+ /* Trivial implementation equivalent to bx. */
+ gen_movl_T0_reg(s, rm);
+ gen_bx(s);
+ } else {
+ goto illegal_op;
+ }
+ break;
+ case 0x3:
+ if (op1 != 1)
+ goto illegal_op;
+
+ /* branch link/exchange thumb (blx) */
+ val = (uint32_t)s->pc;
+ gen_op_movl_T1_im(val);
+ gen_movl_T0_reg(s, rm);
+ gen_movl_reg_T1(s, 14);
+ gen_bx(s);
+ break;
+ case 0x5: /* saturating add/subtract */
+ rd = (insn >> 12) & 0xf;
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rn);
+ if (op1 & 2)
+ gen_op_double_T1_saturate();
+ if (op1 & 1)
+ gen_op_subl_T0_T1_saturate();
+ else
+ gen_op_addl_T0_T1_saturate();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 7: /* bkpt */
+ gen_set_condexec(s);
+ gen_op_movl_T0_im((long)s->pc - 4);
+ gen_op_movl_reg_TN[0][15]();
+ gen_op_bkpt();
+ s->is_jmp = DISAS_JUMP;
+ 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 */
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rs);
+ if (sh & 4)
+ gen_op_sarl_T1_im(16);
+ else
+ gen_op_sxth_T1();
+ gen_op_imulw_T0_T1();
+ if ((sh & 2) == 0) {
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1_setq();
+ }
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* 16 * 16 */
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rs);
+ gen_mulxy(sh & 2, sh & 4);
+ if (op1 == 2) {
+ gen_op_signbit_T1_T0();
+ gen_op_addq_T0_T1(rn, rd);
+ gen_movl_reg_T0(s, rn);
+ gen_movl_reg_T1(s, rd);
+ } else {
+ if (op1 == 0) {
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1_setq();
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ }
+ 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));
+ gen_op_movl_T1_im(val);
+ if (logic_cc && shift)
+ gen_op_mov_CF_T1();
+ } else {
+ /* register */
+ rm = (insn) & 0xf;
+ gen_movl_T1_reg(s, rm);
+ shiftop = (insn >> 5) & 3;
+ if (!(insn & (1 << 4))) {
+ shift = (insn >> 7) & 0x1f;
+ if (shift != 0) {
+ if (logic_cc) {
+ gen_shift_T1_im_cc[shiftop](shift);
+ } else {
+ gen_shift_T1_im[shiftop](shift);
+ }
+ } else if (shiftop != 0) {
+ if (logic_cc) {
+ gen_shift_T1_0_cc[shiftop]();
+ } else {
+ gen_shift_T1_0[shiftop]();
+ }
+ }
+ } else {
+ rs = (insn >> 8) & 0xf;
+ gen_movl_T0_reg(s, rs);
+ if (logic_cc) {
+ gen_shift_T1_T0_cc[shiftop]();
+ } else {
+ gen_shift_T1_T0[shiftop]();
+ }
+ }
+ }
+ if (op1 != 0x0f && op1 != 0x0d) {
+ rn = (insn >> 16) & 0xf;
+ gen_movl_T0_reg(s, rn);
+ }
+ rd = (insn >> 12) & 0xf;
+ switch(op1) {
+ case 0x00:
+ gen_op_andl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x01:
+ gen_op_xorl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x02:
+ if (set_cc && rd == 15) {
+ /* SUBS r15, ... is used for exception return. */
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_subl_T0_T1_cc();
+ gen_exception_return(s);
+ } else {
+ if (set_cc)
+ gen_op_subl_T0_T1_cc();
+ else
+ gen_op_subl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ }
+ break;
+ case 0x03:
+ if (set_cc)
+ gen_op_rsbl_T0_T1_cc();
+ else
+ gen_op_rsbl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x04:
+ if (set_cc)
+ gen_op_addl_T0_T1_cc();
+ else
+ gen_op_addl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x05:
+ if (set_cc)
+ gen_op_adcl_T0_T1_cc();
+ else
+ gen_op_adcl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x06:
+ if (set_cc)
+ gen_op_sbcl_T0_T1_cc();
+ else
+ gen_op_sbcl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x07:
+ if (set_cc)
+ gen_op_rscl_T0_T1_cc();
+ else
+ gen_op_rscl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ break;
+ case 0x08:
+ if (set_cc) {
+ gen_op_andl_T0_T1();
+ gen_op_logic_T0_cc();
+ }
+ break;
+ case 0x09:
+ if (set_cc) {
+ gen_op_xorl_T0_T1();
+ gen_op_logic_T0_cc();
+ }
+ break;
+ case 0x0a:
+ if (set_cc) {
+ gen_op_subl_T0_T1_cc();
+ }
+ break;
+ case 0x0b:
+ if (set_cc) {
+ gen_op_addl_T0_T1_cc();
+ }
+ break;
+ case 0x0c:
+ gen_op_orl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ case 0x0d:
+ if (logic_cc && rd == 15) {
+ /* MOVS r15, ... is used for exception return. */
+ if (IS_USER(s))
+ goto illegal_op;
+ gen_op_movl_T0_T1();
+ gen_exception_return(s);
+ } else {
+ gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
+ }
+ break;
+ case 0x0e:
+ gen_op_bicl_T0_T1();
+ gen_movl_reg_T0(s, rd);
+ if (logic_cc)
+ gen_op_logic_T0_cc();
+ break;
+ default:
+ case 0x0f:
+ gen_op_notl_T1();
+ gen_movl_reg_T1(s, rd);
+ if (logic_cc)
+ gen_op_logic_T1_cc();
+ break;
+ }
+ } 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 */
+ gen_movl_T0_reg(s, rs);
+ gen_movl_T1_reg(s, rm);
+ gen_op_mul_T0_T1();
+ if (insn & (1 << 22)) {
+ /* Subtract (mls) */
+ ARCH(6T2);
+ gen_movl_T1_reg(s, rn);
+ gen_op_rsbl_T0_T1();
+ } else if (insn & (1 << 21)) {
+ /* Add */
+ gen_movl_T1_reg(s, rn);
+ gen_op_addl_T0_T1();
+ }
+ if (insn & (1 << 20))
+ gen_op_logic_T0_cc();
+ gen_movl_reg_T0(s, rd);
+ break;
+ default:
+ /* 64 bit mul */
+ gen_movl_T0_reg(s, rs);
+ gen_movl_T1_reg(s, rm);
+ if (insn & (1 << 22))
+ gen_op_imull_T0_T1();
+ else
+ gen_op_mull_T0_T1();
+ if (insn & (1 << 21)) /* mult accumulate */
+ gen_op_addq_T0_T1(rn, rd);
+ if (!(insn & (1 << 23))) { /* double accumulate */
+ ARCH(6);
+ gen_op_addq_lo_T0_T1(rn);
+ gen_op_addq_lo_T0_T1(rd);
+ }
+ if (insn & (1 << 20))
+ gen_op_logicq_cc();
+ gen_movl_reg_T0(s, rn);
+ gen_movl_reg_T1(s, rd);
+ break;
+ }
+ } else {
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ if (insn & (1 << 23)) {
+ /* load/store exclusive */
+ gen_movl_T1_reg(s, rn);
+ if (insn & (1 << 20)) {
+ gen_ldst(ldlex, s);
+ } else {
+ rm = insn & 0xf;
+ gen_movl_T0_reg(s, rm);
+ gen_ldst(stlex, s);
+ }
+ gen_movl_reg_T0(s, rd);
+ } else {
+ /* SWP instruction */
+ rm = (insn) & 0xf;
+
+ gen_movl_T0_reg(s, rm);
+ gen_movl_T1_reg(s, rn);
+ if (insn & (1 << 22)) {
+ gen_ldst(swpb, s);
+ } else {
+ gen_ldst(swpl, s);
+ }
+ gen_movl_reg_T0(s, rd);
+ }
+ }
+ } else {
+ int address_offset;
+ int load;
+ /* Misc load/store */
+ rn = (insn >> 16) & 0xf;
+ rd = (insn >> 12) & 0xf;
+ gen_movl_T1_reg(s, rn);
+ if (insn & (1 << 24))
+ gen_add_datah_offset(s, insn, 0);
+ address_offset = 0;
+ if (insn & (1 << 20)) {
+ /* load */
+ switch(sh) {
+ case 1:
+ gen_ldst(lduw, s);
+ break;
+ case 2:
+ gen_ldst(ldsb, s);
+ break;
+ default:
+ case 3:
+ gen_ldst(ldsw, s);
+ break;
+ }
+ load = 1;
+ } else if (sh & 2) {
+ /* doubleword */
+ if (sh & 1) {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stl, s);
+ gen_op_addl_T1_im(4);
+ gen_movl_T0_reg(s, rd + 1);
+ gen_ldst(stl, s);
+ load = 0;
+ } else {
+ /* load */
+ gen_ldst(ldl, s);
+ gen_movl_reg_T0(s, rd);
+ gen_op_addl_T1_im(4);
+ gen_ldst(ldl, s);
+ rd++;
+ load = 1;
+ }
+ address_offset = -4;
+ } else {
+ /* store */
+ gen_movl_T0_reg(s, rd);
+ gen_ldst(stw, 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);
+ gen_movl_reg_T1(s, rn);
+ } else if (insn & (1 << 21)) {
+ if (address_offset)
+ gen_op_addl_T1_im(address_offset);
+ gen_movl_reg_T1(s, rn);
+ }
+ if (load) {
+ /* Complete the load. */
+ gen_movl_reg_T0(s, rd);
+ }
+ }
+ 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;