blob: adac19a267cca2e50f97a23ae750d0d2cc888dd8 [file] [log] [blame]
bellard2c0262a2003-09-30 20:34:21 +00001/*
2 * ARM translation
ths5fafdf22007-09-16 21:08:06 +00003 *
bellard2c0262a2003-09-30 20:34:21 +00004 * Copyright (c) 2003 Fabrice Bellard
pbrook9ee6e8b2007-11-11 00:04:49 +00005 * Copyright (c) 2005-2007 CodeSourcery
balrog18c9b562007-04-30 02:02:17 +00006 * Copyright (c) 2007 OpenedHand, Ltd.
bellard2c0262a2003-09-30 20:34:21 +00007 *
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU Lesser General Public
10 * License as published by the Free Software Foundation; either
11 * version 2 of the License, or (at your option) any later version.
12 *
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16 * Lesser General Public License for more details.
17 *
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
aurel32fad6cb12009-01-04 22:05:52 +000020 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301 USA
bellard2c0262a2003-09-30 20:34:21 +000021 */
22#include <stdarg.h>
23#include <stdlib.h>
24#include <stdio.h>
25#include <string.h>
26#include <inttypes.h>
27
28#include "cpu.h"
29#include "exec-all.h"
30#include "disas.h"
bellard57fec1f2008-02-01 10:50:11 +000031#include "tcg-op.h"
blueswir179383c92008-08-30 09:51:20 +000032#include "qemu-log.h"
pbrook1497c962008-03-31 03:45:50 +000033
pbrooka7812ae2008-11-17 14:43:54 +000034#include "helpers.h"
pbrook1497c962008-03-31 03:45:50 +000035#define GEN_HELPER 1
pbrookb26eefb2008-03-31 03:44:26 +000036#include "helpers.h"
bellard2c0262a2003-09-30 20:34:21 +000037
pbrook9ee6e8b2007-11-11 00:04:49 +000038#define ENABLE_ARCH_5J 0
39#define ENABLE_ARCH_6 arm_feature(env, ARM_FEATURE_V6)
40#define ENABLE_ARCH_6K arm_feature(env, ARM_FEATURE_V6K)
41#define ENABLE_ARCH_6T2 arm_feature(env, ARM_FEATURE_THUMB2)
42#define ENABLE_ARCH_7 arm_feature(env, ARM_FEATURE_V7)
bellardb5ff1b32005-11-26 10:38:39 +000043
pbrook86753402008-10-22 20:35:54 +000044#define ARCH(x) do { if (!ENABLE_ARCH_##x) goto illegal_op; } while(0)
bellardb5ff1b32005-11-26 10:38:39 +000045
bellard2c0262a2003-09-30 20:34:21 +000046/* internal defines */
47typedef struct DisasContext {
bellard0fa85d42005-01-03 23:43:32 +000048 target_ulong pc;
bellard2c0262a2003-09-30 20:34:21 +000049 int is_jmp;
bellarde50e6a22005-04-26 20:36:11 +000050 /* Nonzero if this instruction has been conditionally skipped. */
51 int condjmp;
52 /* The label that will be jumped to when the instruction is skipped. */
53 int condlabel;
pbrook9ee6e8b2007-11-11 00:04:49 +000054 /* Thumb-2 condtional execution bits. */
55 int condexec_mask;
56 int condexec_cond;
bellard2c0262a2003-09-30 20:34:21 +000057 struct TranslationBlock *tb;
bellard8aaca4c2005-04-23 18:27:52 +000058 int singlestep_enabled;
bellard5899f382005-04-27 20:25:20 +000059 int thumb;
bellardb5ff1b32005-11-26 10:38:39 +000060#if !defined(CONFIG_USER_ONLY)
61 int user;
62#endif
bellard2c0262a2003-09-30 20:34:21 +000063} DisasContext;
64
bellardb5ff1b32005-11-26 10:38:39 +000065#if defined(CONFIG_USER_ONLY)
66#define IS_USER(s) 1
67#else
68#define IS_USER(s) (s->user)
69#endif
70
pbrook9ee6e8b2007-11-11 00:04:49 +000071/* These instructions trap after executing, so defer them until after the
72 conditional executions state has been updated. */
73#define DISAS_WFI 4
74#define DISAS_SWI 5
bellard2c0262a2003-09-30 20:34:21 +000075
pbrooka7812ae2008-11-17 14:43:54 +000076static TCGv_ptr cpu_env;
pbrookad694712008-03-31 03:48:30 +000077/* We reuse the same 64-bit temporaries for efficiency. */
pbrooka7812ae2008-11-17 14:43:54 +000078static TCGv_i64 cpu_V0, cpu_V1, cpu_M0;
pbrookad694712008-03-31 03:48:30 +000079
pbrookb26eefb2008-03-31 03:44:26 +000080/* FIXME: These should be removed. */
pbrook8f8e3aa2008-03-31 03:48:01 +000081static TCGv cpu_T[2];
pbrooka7812ae2008-11-17 14:43:54 +000082static TCGv cpu_F0s, cpu_F1s;
83static TCGv_i64 cpu_F0d, cpu_F1d;
pbrookb26eefb2008-03-31 03:44:26 +000084
pbrook2e70f6e2008-06-29 01:03:05 +000085#define ICOUNT_TEMP cpu_T[0]
86#include "gen-icount.h"
87
pbrookb26eefb2008-03-31 03:44:26 +000088/* initialize TCG globals. */
89void arm_translate_init(void)
90{
pbrooka7812ae2008-11-17 14:43:54 +000091 cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env");
pbrookb26eefb2008-03-31 03:44:26 +000092
pbrooka7812ae2008-11-17 14:43:54 +000093 cpu_T[0] = tcg_global_reg_new_i32(TCG_AREG1, "T0");
94 cpu_T[1] = tcg_global_reg_new_i32(TCG_AREG2, "T1");
95
96#define GEN_HELPER 2
97#include "helpers.h"
pbrookb26eefb2008-03-31 03:44:26 +000098}
99
100/* The code generator doesn't like lots of temporaries, so maintain our own
101 cache for reuse within a function. */
102#define MAX_TEMPS 8
103static int num_temps;
104static TCGv temps[MAX_TEMPS];
105
106/* Allocate a temporary variable. */
pbrooka7812ae2008-11-17 14:43:54 +0000107static TCGv_i32 new_tmp(void)
pbrookb26eefb2008-03-31 03:44:26 +0000108{
109 TCGv tmp;
110 if (num_temps == MAX_TEMPS)
111 abort();
112
pbrooka7812ae2008-11-17 14:43:54 +0000113 if (GET_TCGV_I32(temps[num_temps]))
pbrookb26eefb2008-03-31 03:44:26 +0000114 return temps[num_temps++];
115
pbrooka7812ae2008-11-17 14:43:54 +0000116 tmp = tcg_temp_new_i32();
pbrookb26eefb2008-03-31 03:44:26 +0000117 temps[num_temps++] = tmp;
118 return tmp;
119}
120
121/* Release a temporary variable. */
122static void dead_tmp(TCGv tmp)
123{
124 int i;
125 num_temps--;
126 i = num_temps;
pbrooka7812ae2008-11-17 14:43:54 +0000127 if (TCGV_EQUAL(temps[i], tmp))
pbrookb26eefb2008-03-31 03:44:26 +0000128 return;
129
130 /* Shuffle this temp to the last slot. */
pbrooka7812ae2008-11-17 14:43:54 +0000131 while (!TCGV_EQUAL(temps[i], tmp))
pbrookb26eefb2008-03-31 03:44:26 +0000132 i--;
133 while (i < num_temps) {
134 temps[i] = temps[i + 1];
135 i++;
136 }
137 temps[i] = tmp;
138}
139
pbrookd9ba4832008-03-31 03:46:50 +0000140static inline TCGv load_cpu_offset(int offset)
141{
142 TCGv tmp = new_tmp();
143 tcg_gen_ld_i32(tmp, cpu_env, offset);
144 return tmp;
145}
146
147#define load_cpu_field(name) load_cpu_offset(offsetof(CPUState, name))
148
149static inline void store_cpu_offset(TCGv var, int offset)
150{
151 tcg_gen_st_i32(var, cpu_env, offset);
152 dead_tmp(var);
153}
154
155#define store_cpu_field(var, name) \
156 store_cpu_offset(var, offsetof(CPUState, name))
157
pbrookb26eefb2008-03-31 03:44:26 +0000158/* Set a variable to the value of a CPU register. */
159static void load_reg_var(DisasContext *s, TCGv var, int reg)
160{
161 if (reg == 15) {
162 uint32_t addr;
163 /* normaly, since we updated PC, we need only to add one insn */
164 if (s->thumb)
165 addr = (long)s->pc + 2;
166 else
167 addr = (long)s->pc + 4;
168 tcg_gen_movi_i32(var, addr);
169 } else {
170 tcg_gen_ld_i32(var, cpu_env, offsetof(CPUState, regs[reg]));
171 }
172}
173
174/* Create a new temporary and set it to the value of a CPU register. */
175static inline TCGv load_reg(DisasContext *s, int reg)
176{
177 TCGv tmp = new_tmp();
178 load_reg_var(s, tmp, reg);
179 return tmp;
180}
181
182/* Set a CPU register. The source must be a temporary and will be
183 marked as dead. */
184static void store_reg(DisasContext *s, int reg, TCGv var)
185{
186 if (reg == 15) {
187 tcg_gen_andi_i32(var, var, ~1);
188 s->is_jmp = DISAS_JUMP;
189 }
190 tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, regs[reg]));
191 dead_tmp(var);
192}
193
194
195/* Basic operations. */
196#define gen_op_movl_T0_T1() tcg_gen_mov_i32(cpu_T[0], cpu_T[1])
pbrookb26eefb2008-03-31 03:44:26 +0000197#define gen_op_movl_T0_im(im) tcg_gen_movi_i32(cpu_T[0], im)
198#define gen_op_movl_T1_im(im) tcg_gen_movi_i32(cpu_T[1], im)
pbrookb26eefb2008-03-31 03:44:26 +0000199
200#define gen_op_addl_T1_im(im) tcg_gen_addi_i32(cpu_T[1], cpu_T[1], im)
201#define gen_op_addl_T0_T1() tcg_gen_add_i32(cpu_T[0], cpu_T[0], cpu_T[1])
202#define gen_op_subl_T0_T1() tcg_gen_sub_i32(cpu_T[0], cpu_T[0], cpu_T[1])
203#define gen_op_rsbl_T0_T1() tcg_gen_sub_i32(cpu_T[0], cpu_T[1], cpu_T[0])
204
pbrook8984bd22008-03-31 03:47:48 +0000205#define gen_op_addl_T0_T1_cc() gen_helper_add_cc(cpu_T[0], cpu_T[0], cpu_T[1])
206#define gen_op_adcl_T0_T1_cc() gen_helper_adc_cc(cpu_T[0], cpu_T[0], cpu_T[1])
207#define gen_op_subl_T0_T1_cc() gen_helper_sub_cc(cpu_T[0], cpu_T[0], cpu_T[1])
208#define gen_op_sbcl_T0_T1_cc() gen_helper_sbc_cc(cpu_T[0], cpu_T[0], cpu_T[1])
209#define gen_op_rsbl_T0_T1_cc() gen_helper_sub_cc(cpu_T[0], cpu_T[1], cpu_T[0])
pbrook8984bd22008-03-31 03:47:48 +0000210
pbrookb26eefb2008-03-31 03:44:26 +0000211#define gen_op_andl_T0_T1() tcg_gen_and_i32(cpu_T[0], cpu_T[0], cpu_T[1])
212#define gen_op_xorl_T0_T1() tcg_gen_xor_i32(cpu_T[0], cpu_T[0], cpu_T[1])
213#define gen_op_orl_T0_T1() tcg_gen_or_i32(cpu_T[0], cpu_T[0], cpu_T[1])
214#define gen_op_notl_T0() tcg_gen_not_i32(cpu_T[0], cpu_T[0])
215#define gen_op_notl_T1() tcg_gen_not_i32(cpu_T[1], cpu_T[1])
216#define gen_op_logic_T0_cc() gen_logic_CC(cpu_T[0]);
217#define gen_op_logic_T1_cc() gen_logic_CC(cpu_T[1]);
218
pbrookb26eefb2008-03-31 03:44:26 +0000219#define gen_op_shll_T1_im(im) tcg_gen_shli_i32(cpu_T[1], cpu_T[1], im)
220#define gen_op_shrl_T1_im(im) tcg_gen_shri_i32(cpu_T[1], cpu_T[1], im)
pbrookb26eefb2008-03-31 03:44:26 +0000221
222/* Value extensions. */
pbrook86831432008-05-11 12:22:01 +0000223#define gen_uxtb(var) tcg_gen_ext8u_i32(var, var)
224#define gen_uxth(var) tcg_gen_ext16u_i32(var, var)
pbrookb26eefb2008-03-31 03:44:26 +0000225#define gen_sxtb(var) tcg_gen_ext8s_i32(var, var)
226#define gen_sxth(var) tcg_gen_ext16s_i32(var, var)
227
pbrook1497c962008-03-31 03:45:50 +0000228#define gen_sxtb16(var) gen_helper_sxtb16(var, var)
229#define gen_uxtb16(var) gen_helper_uxtb16(var, var)
pbrook8f012452008-03-31 03:46:03 +0000230
231#define gen_op_mul_T0_T1() tcg_gen_mul_i32(cpu_T[0], cpu_T[0], cpu_T[1])
pbrookb26eefb2008-03-31 03:44:26 +0000232
pbrookd9ba4832008-03-31 03:46:50 +0000233#define gen_set_cpsr(var, mask) gen_helper_cpsr_write(var, tcg_const_i32(mask))
234/* Set NZCV flags from the high 4 bits of var. */
235#define gen_set_nzcv(var) gen_set_cpsr(var, CPSR_NZCV)
236
237static void gen_exception(int excp)
238{
239 TCGv tmp = new_tmp();
240 tcg_gen_movi_i32(tmp, excp);
241 gen_helper_exception(tmp);
242 dead_tmp(tmp);
243}
244
pbrook36706692008-03-31 03:46:19 +0000245static void gen_smul_dual(TCGv a, TCGv b)
246{
247 TCGv tmp1 = new_tmp();
248 TCGv tmp2 = new_tmp();
balrog22478e72008-07-19 10:12:22 +0000249 tcg_gen_ext16s_i32(tmp1, a);
250 tcg_gen_ext16s_i32(tmp2, b);
pbrook36706692008-03-31 03:46:19 +0000251 tcg_gen_mul_i32(tmp1, tmp1, tmp2);
252 dead_tmp(tmp2);
253 tcg_gen_sari_i32(a, a, 16);
254 tcg_gen_sari_i32(b, b, 16);
255 tcg_gen_mul_i32(b, b, a);
256 tcg_gen_mov_i32(a, tmp1);
257 dead_tmp(tmp1);
258}
259
260/* Byteswap each halfword. */
261static void gen_rev16(TCGv var)
262{
263 TCGv tmp = new_tmp();
264 tcg_gen_shri_i32(tmp, var, 8);
265 tcg_gen_andi_i32(tmp, tmp, 0x00ff00ff);
266 tcg_gen_shli_i32(var, var, 8);
267 tcg_gen_andi_i32(var, var, 0xff00ff00);
268 tcg_gen_or_i32(var, var, tmp);
269 dead_tmp(tmp);
270}
271
272/* Byteswap low halfword and sign extend. */
273static void gen_revsh(TCGv var)
274{
275 TCGv tmp = new_tmp();
276 tcg_gen_shri_i32(tmp, var, 8);
277 tcg_gen_andi_i32(tmp, tmp, 0x00ff);
278 tcg_gen_shli_i32(var, var, 8);
279 tcg_gen_ext8s_i32(var, var);
280 tcg_gen_or_i32(var, var, tmp);
281 dead_tmp(tmp);
282}
283
284/* Unsigned bitfield extract. */
285static void gen_ubfx(TCGv var, int shift, uint32_t mask)
286{
287 if (shift)
288 tcg_gen_shri_i32(var, var, shift);
289 tcg_gen_andi_i32(var, var, mask);
290}
291
292/* Signed bitfield extract. */
293static void gen_sbfx(TCGv var, int shift, int width)
294{
295 uint32_t signbit;
296
297 if (shift)
298 tcg_gen_sari_i32(var, var, shift);
299 if (shift + width < 32) {
300 signbit = 1u << (width - 1);
301 tcg_gen_andi_i32(var, var, (1u << width) - 1);
302 tcg_gen_xori_i32(var, var, signbit);
303 tcg_gen_subi_i32(var, var, signbit);
304 }
305}
306
307/* Bitfield insertion. Insert val into base. Clobbers base and val. */
308static void gen_bfi(TCGv dest, TCGv base, TCGv val, int shift, uint32_t mask)
309{
pbrook36706692008-03-31 03:46:19 +0000310 tcg_gen_andi_i32(val, val, mask);
pbrook8f8e3aa2008-03-31 03:48:01 +0000311 tcg_gen_shli_i32(val, val, shift);
312 tcg_gen_andi_i32(base, base, ~(mask << shift));
pbrook36706692008-03-31 03:46:19 +0000313 tcg_gen_or_i32(dest, base, val);
314}
315
pbrookd9ba4832008-03-31 03:46:50 +0000316/* Round the top 32 bits of a 64-bit value. */
317static void gen_roundqd(TCGv a, TCGv b)
pbrook36706692008-03-31 03:46:19 +0000318{
pbrookd9ba4832008-03-31 03:46:50 +0000319 tcg_gen_shri_i32(a, a, 31);
320 tcg_gen_add_i32(a, a, b);
pbrook36706692008-03-31 03:46:19 +0000321}
322
pbrook8f012452008-03-31 03:46:03 +0000323/* FIXME: Most targets have native widening multiplication.
324 It would be good to use that instead of a full wide multiply. */
pbrook5e3f8782008-03-31 03:47:34 +0000325/* 32x32->64 multiply. Marks inputs as dead. */
pbrooka7812ae2008-11-17 14:43:54 +0000326static TCGv_i64 gen_mulu_i64_i32(TCGv a, TCGv b)
pbrook5e3f8782008-03-31 03:47:34 +0000327{
pbrooka7812ae2008-11-17 14:43:54 +0000328 TCGv_i64 tmp1 = tcg_temp_new_i64();
329 TCGv_i64 tmp2 = tcg_temp_new_i64();
pbrook5e3f8782008-03-31 03:47:34 +0000330
331 tcg_gen_extu_i32_i64(tmp1, a);
332 dead_tmp(a);
333 tcg_gen_extu_i32_i64(tmp2, b);
334 dead_tmp(b);
335 tcg_gen_mul_i64(tmp1, tmp1, tmp2);
336 return tmp1;
337}
338
pbrooka7812ae2008-11-17 14:43:54 +0000339static TCGv_i64 gen_muls_i64_i32(TCGv a, TCGv b)
pbrook5e3f8782008-03-31 03:47:34 +0000340{
pbrooka7812ae2008-11-17 14:43:54 +0000341 TCGv_i64 tmp1 = tcg_temp_new_i64();
342 TCGv_i64 tmp2 = tcg_temp_new_i64();
pbrook5e3f8782008-03-31 03:47:34 +0000343
344 tcg_gen_ext_i32_i64(tmp1, a);
345 dead_tmp(a);
346 tcg_gen_ext_i32_i64(tmp2, b);
347 dead_tmp(b);
348 tcg_gen_mul_i64(tmp1, tmp1, tmp2);
349 return tmp1;
350}
351
pbrook8f012452008-03-31 03:46:03 +0000352/* Unsigned 32x32->64 multiply. */
353static void gen_op_mull_T0_T1(void)
354{
pbrooka7812ae2008-11-17 14:43:54 +0000355 TCGv_i64 tmp1 = tcg_temp_new_i64();
356 TCGv_i64 tmp2 = tcg_temp_new_i64();
pbrook8f012452008-03-31 03:46:03 +0000357
358 tcg_gen_extu_i32_i64(tmp1, cpu_T[0]);
359 tcg_gen_extu_i32_i64(tmp2, cpu_T[1]);
360 tcg_gen_mul_i64(tmp1, tmp1, tmp2);
361 tcg_gen_trunc_i64_i32(cpu_T[0], tmp1);
362 tcg_gen_shri_i64(tmp1, tmp1, 32);
363 tcg_gen_trunc_i64_i32(cpu_T[1], tmp1);
364}
365
366/* Signed 32x32->64 multiply. */
pbrookd9ba4832008-03-31 03:46:50 +0000367static void gen_imull(TCGv a, TCGv b)
pbrook8f012452008-03-31 03:46:03 +0000368{
pbrooka7812ae2008-11-17 14:43:54 +0000369 TCGv_i64 tmp1 = tcg_temp_new_i64();
370 TCGv_i64 tmp2 = tcg_temp_new_i64();
pbrook8f012452008-03-31 03:46:03 +0000371
pbrookd9ba4832008-03-31 03:46:50 +0000372 tcg_gen_ext_i32_i64(tmp1, a);
373 tcg_gen_ext_i32_i64(tmp2, b);
pbrook8f012452008-03-31 03:46:03 +0000374 tcg_gen_mul_i64(tmp1, tmp1, tmp2);
pbrookd9ba4832008-03-31 03:46:50 +0000375 tcg_gen_trunc_i64_i32(a, tmp1);
pbrook8f012452008-03-31 03:46:03 +0000376 tcg_gen_shri_i64(tmp1, tmp1, 32);
pbrookd9ba4832008-03-31 03:46:50 +0000377 tcg_gen_trunc_i64_i32(b, tmp1);
378}
pbrookd9ba4832008-03-31 03:46:50 +0000379
pbrook8f012452008-03-31 03:46:03 +0000380/* Swap low and high halfwords. */
381static void gen_swap_half(TCGv var)
382{
383 TCGv tmp = new_tmp();
384 tcg_gen_shri_i32(tmp, var, 16);
385 tcg_gen_shli_i32(var, var, 16);
386 tcg_gen_or_i32(var, var, tmp);
pbrook36706692008-03-31 03:46:19 +0000387 dead_tmp(tmp);
pbrook8f012452008-03-31 03:46:03 +0000388}
389
pbrookb26eefb2008-03-31 03:44:26 +0000390/* Dual 16-bit add. Result placed in t0 and t1 is marked as dead.
391 tmp = (t0 ^ t1) & 0x8000;
392 t0 &= ~0x8000;
393 t1 &= ~0x8000;
394 t0 = (t0 + t1) ^ tmp;
395 */
396
397static void gen_add16(TCGv t0, TCGv t1)
398{
399 TCGv tmp = new_tmp();
400 tcg_gen_xor_i32(tmp, t0, t1);
401 tcg_gen_andi_i32(tmp, tmp, 0x8000);
402 tcg_gen_andi_i32(t0, t0, ~0x8000);
403 tcg_gen_andi_i32(t1, t1, ~0x8000);
404 tcg_gen_add_i32(t0, t0, t1);
405 tcg_gen_xor_i32(t0, t0, tmp);
406 dead_tmp(tmp);
407 dead_tmp(t1);
408}
409
pbrook9a119ff2008-03-31 03:45:35 +0000410#define gen_set_CF(var) tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, CF))
411
pbrookb26eefb2008-03-31 03:44:26 +0000412/* Set CF to the top bit of var. */
413static void gen_set_CF_bit31(TCGv var)
414{
415 TCGv tmp = new_tmp();
416 tcg_gen_shri_i32(tmp, var, 31);
balrog4cc633c2008-12-07 13:32:09 +0000417 gen_set_CF(tmp);
pbrookb26eefb2008-03-31 03:44:26 +0000418 dead_tmp(tmp);
419}
420
421/* Set N and Z flags from var. */
422static inline void gen_logic_CC(TCGv var)
423{
pbrook6fbe23d2008-04-01 17:19:11 +0000424 tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, NF));
425 tcg_gen_st_i32(var, cpu_env, offsetof(CPUState, ZF));
pbrookb26eefb2008-03-31 03:44:26 +0000426}
427
428/* T0 += T1 + CF. */
429static void gen_adc_T0_T1(void)
430{
pbrookd9ba4832008-03-31 03:46:50 +0000431 TCGv tmp;
pbrookb26eefb2008-03-31 03:44:26 +0000432 gen_op_addl_T0_T1();
pbrookd9ba4832008-03-31 03:46:50 +0000433 tmp = load_cpu_field(CF);
pbrookb26eefb2008-03-31 03:44:26 +0000434 tcg_gen_add_i32(cpu_T[0], cpu_T[0], tmp);
435 dead_tmp(tmp);
436}
437
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +0300438/* dest = T0 + T1 + CF. */
439static void gen_add_carry(TCGv dest, TCGv t0, TCGv t1)
440{
441 TCGv tmp;
442 tcg_gen_add_i32(dest, t0, t1);
443 tmp = load_cpu_field(CF);
444 tcg_gen_add_i32(dest, dest, tmp);
445 dead_tmp(tmp);
446}
447
pbrook36706692008-03-31 03:46:19 +0000448/* dest = T0 - T1 + CF - 1. */
449static void gen_sub_carry(TCGv dest, TCGv t0, TCGv t1)
450{
pbrookd9ba4832008-03-31 03:46:50 +0000451 TCGv tmp;
pbrook36706692008-03-31 03:46:19 +0000452 tcg_gen_sub_i32(dest, t0, t1);
pbrookd9ba4832008-03-31 03:46:50 +0000453 tmp = load_cpu_field(CF);
pbrook36706692008-03-31 03:46:19 +0000454 tcg_gen_add_i32(dest, dest, tmp);
455 tcg_gen_subi_i32(dest, dest, 1);
456 dead_tmp(tmp);
457}
458
459#define gen_sbc_T0_T1() gen_sub_carry(cpu_T[0], cpu_T[0], cpu_T[1])
460#define gen_rsc_T0_T1() gen_sub_carry(cpu_T[0], cpu_T[1], cpu_T[0])
461
pbrookb26eefb2008-03-31 03:44:26 +0000462/* T0 &= ~T1. Clobbers T1. */
463/* FIXME: Implement bic natively. */
pbrook8f8e3aa2008-03-31 03:48:01 +0000464static inline void tcg_gen_bic_i32(TCGv dest, TCGv t0, TCGv t1)
465{
466 TCGv tmp = new_tmp();
467 tcg_gen_not_i32(tmp, t1);
468 tcg_gen_and_i32(dest, t0, tmp);
469 dead_tmp(tmp);
470}
pbrookb26eefb2008-03-31 03:44:26 +0000471static inline void gen_op_bicl_T0_T1(void)
472{
473 gen_op_notl_T1();
474 gen_op_andl_T0_T1();
475}
476
477/* FIXME: Implement this natively. */
pbrookad694712008-03-31 03:48:30 +0000478#define tcg_gen_abs_i32(t0, t1) gen_helper_abs(t0, t1)
479
480/* FIXME: Implement this natively. */
pbrookb26eefb2008-03-31 03:44:26 +0000481static void tcg_gen_rori_i32(TCGv t0, TCGv t1, int i)
482{
483 TCGv tmp;
484
485 if (i == 0)
486 return;
487
488 tmp = new_tmp();
489 tcg_gen_shri_i32(tmp, t1, i);
490 tcg_gen_shli_i32(t1, t1, 32 - i);
491 tcg_gen_or_i32(t0, t1, tmp);
492 dead_tmp(tmp);
493}
494
pbrook9a119ff2008-03-31 03:45:35 +0000495static void shifter_out_im(TCGv var, int shift)
pbrookb26eefb2008-03-31 03:44:26 +0000496{
pbrook9a119ff2008-03-31 03:45:35 +0000497 TCGv tmp = new_tmp();
498 if (shift == 0) {
499 tcg_gen_andi_i32(tmp, var, 1);
pbrookb26eefb2008-03-31 03:44:26 +0000500 } else {
pbrook9a119ff2008-03-31 03:45:35 +0000501 tcg_gen_shri_i32(tmp, var, shift);
balrog4cc633c2008-12-07 13:32:09 +0000502 if (shift != 31)
pbrook9a119ff2008-03-31 03:45:35 +0000503 tcg_gen_andi_i32(tmp, tmp, 1);
504 }
505 gen_set_CF(tmp);
506 dead_tmp(tmp);
507}
pbrookb26eefb2008-03-31 03:44:26 +0000508
pbrook9a119ff2008-03-31 03:45:35 +0000509/* Shift by immediate. Includes special handling for shift == 0. */
510static inline void gen_arm_shift_im(TCGv var, int shiftop, int shift, int flags)
511{
512 switch (shiftop) {
513 case 0: /* LSL */
514 if (shift != 0) {
515 if (flags)
516 shifter_out_im(var, 32 - shift);
517 tcg_gen_shli_i32(var, var, shift);
518 }
519 break;
520 case 1: /* LSR */
521 if (shift == 0) {
522 if (flags) {
523 tcg_gen_shri_i32(var, var, 31);
524 gen_set_CF(var);
525 }
526 tcg_gen_movi_i32(var, 0);
527 } else {
528 if (flags)
529 shifter_out_im(var, shift - 1);
530 tcg_gen_shri_i32(var, var, shift);
531 }
532 break;
533 case 2: /* ASR */
534 if (shift == 0)
535 shift = 32;
536 if (flags)
537 shifter_out_im(var, shift - 1);
538 if (shift == 32)
539 shift = 31;
540 tcg_gen_sari_i32(var, var, shift);
541 break;
542 case 3: /* ROR/RRX */
543 if (shift != 0) {
544 if (flags)
545 shifter_out_im(var, shift - 1);
546 tcg_gen_rori_i32(var, var, shift); break;
547 } else {
pbrookd9ba4832008-03-31 03:46:50 +0000548 TCGv tmp = load_cpu_field(CF);
pbrook9a119ff2008-03-31 03:45:35 +0000549 if (flags)
550 shifter_out_im(var, 0);
551 tcg_gen_shri_i32(var, var, 1);
pbrookb26eefb2008-03-31 03:44:26 +0000552 tcg_gen_shli_i32(tmp, tmp, 31);
553 tcg_gen_or_i32(var, var, tmp);
554 dead_tmp(tmp);
pbrookb26eefb2008-03-31 03:44:26 +0000555 }
556 }
557};
558
pbrook8984bd22008-03-31 03:47:48 +0000559static inline void gen_arm_shift_reg(TCGv var, int shiftop,
560 TCGv shift, int flags)
561{
562 if (flags) {
563 switch (shiftop) {
564 case 0: gen_helper_shl_cc(var, var, shift); break;
565 case 1: gen_helper_shr_cc(var, var, shift); break;
566 case 2: gen_helper_sar_cc(var, var, shift); break;
567 case 3: gen_helper_ror_cc(var, var, shift); break;
568 }
569 } else {
570 switch (shiftop) {
571 case 0: gen_helper_shl(var, var, shift); break;
572 case 1: gen_helper_shr(var, var, shift); break;
573 case 2: gen_helper_sar(var, var, shift); break;
574 case 3: gen_helper_ror(var, var, shift); break;
575 }
576 }
577 dead_tmp(shift);
578}
579
pbrook6ddbc6e2008-03-31 03:46:33 +0000580#define PAS_OP(pfx) \
581 switch (op2) { \
582 case 0: gen_pas_helper(glue(pfx,add16)); break; \
583 case 1: gen_pas_helper(glue(pfx,addsubx)); break; \
584 case 2: gen_pas_helper(glue(pfx,subaddx)); break; \
585 case 3: gen_pas_helper(glue(pfx,sub16)); break; \
586 case 4: gen_pas_helper(glue(pfx,add8)); break; \
587 case 7: gen_pas_helper(glue(pfx,sub8)); break; \
588 }
pbrookd9ba4832008-03-31 03:46:50 +0000589static void gen_arm_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
pbrook6ddbc6e2008-03-31 03:46:33 +0000590{
pbrooka7812ae2008-11-17 14:43:54 +0000591 TCGv_ptr tmp;
pbrook9ee6e8b2007-11-11 00:04:49 +0000592
pbrook6ddbc6e2008-03-31 03:46:33 +0000593 switch (op1) {
594#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
595 case 1:
pbrooka7812ae2008-11-17 14:43:54 +0000596 tmp = tcg_temp_new_ptr();
pbrook6ddbc6e2008-03-31 03:46:33 +0000597 tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
598 PAS_OP(s)
599 break;
600 case 5:
pbrooka7812ae2008-11-17 14:43:54 +0000601 tmp = tcg_temp_new_ptr();
pbrook6ddbc6e2008-03-31 03:46:33 +0000602 tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
603 PAS_OP(u)
604 break;
605#undef gen_pas_helper
606#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
607 case 2:
608 PAS_OP(q);
609 break;
610 case 3:
611 PAS_OP(sh);
612 break;
613 case 6:
614 PAS_OP(uq);
615 break;
616 case 7:
617 PAS_OP(uh);
618 break;
619#undef gen_pas_helper
620 }
621}
pbrook9ee6e8b2007-11-11 00:04:49 +0000622#undef PAS_OP
623
pbrook6ddbc6e2008-03-31 03:46:33 +0000624/* For unknown reasons Arm and Thumb-2 use arbitrarily different encodings. */
625#define PAS_OP(pfx) \
626 switch (op2) { \
627 case 0: gen_pas_helper(glue(pfx,add8)); break; \
628 case 1: gen_pas_helper(glue(pfx,add16)); break; \
629 case 2: gen_pas_helper(glue(pfx,addsubx)); break; \
630 case 4: gen_pas_helper(glue(pfx,sub8)); break; \
631 case 5: gen_pas_helper(glue(pfx,sub16)); break; \
632 case 6: gen_pas_helper(glue(pfx,subaddx)); break; \
633 }
pbrookd9ba4832008-03-31 03:46:50 +0000634static void gen_thumb2_parallel_addsub(int op1, int op2, TCGv a, TCGv b)
pbrook6ddbc6e2008-03-31 03:46:33 +0000635{
pbrooka7812ae2008-11-17 14:43:54 +0000636 TCGv_ptr tmp;
pbrook9ee6e8b2007-11-11 00:04:49 +0000637
pbrook6ddbc6e2008-03-31 03:46:33 +0000638 switch (op1) {
639#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b, tmp)
640 case 0:
pbrooka7812ae2008-11-17 14:43:54 +0000641 tmp = tcg_temp_new_ptr();
pbrook6ddbc6e2008-03-31 03:46:33 +0000642 tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
643 PAS_OP(s)
644 break;
645 case 4:
pbrooka7812ae2008-11-17 14:43:54 +0000646 tmp = tcg_temp_new_ptr();
pbrook6ddbc6e2008-03-31 03:46:33 +0000647 tcg_gen_addi_ptr(tmp, cpu_env, offsetof(CPUState, GE));
648 PAS_OP(u)
649 break;
650#undef gen_pas_helper
651#define gen_pas_helper(name) glue(gen_helper_,name)(a, a, b)
652 case 1:
653 PAS_OP(q);
654 break;
655 case 2:
656 PAS_OP(sh);
657 break;
658 case 5:
659 PAS_OP(uq);
660 break;
661 case 6:
662 PAS_OP(uh);
663 break;
664#undef gen_pas_helper
665 }
666}
pbrook9ee6e8b2007-11-11 00:04:49 +0000667#undef PAS_OP
668
pbrookd9ba4832008-03-31 03:46:50 +0000669static void gen_test_cc(int cc, int label)
670{
671 TCGv tmp;
672 TCGv tmp2;
pbrookd9ba4832008-03-31 03:46:50 +0000673 int inv;
674
pbrookd9ba4832008-03-31 03:46:50 +0000675 switch (cc) {
676 case 0: /* eq: Z */
pbrook6fbe23d2008-04-01 17:19:11 +0000677 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000678 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000679 break;
680 case 1: /* ne: !Z */
pbrook6fbe23d2008-04-01 17:19:11 +0000681 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000682 tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000683 break;
684 case 2: /* cs: C */
685 tmp = load_cpu_field(CF);
pbrookcb636692008-05-24 02:22:00 +0000686 tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000687 break;
688 case 3: /* cc: !C */
689 tmp = load_cpu_field(CF);
pbrookcb636692008-05-24 02:22:00 +0000690 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000691 break;
692 case 4: /* mi: N */
pbrook6fbe23d2008-04-01 17:19:11 +0000693 tmp = load_cpu_field(NF);
pbrookcb636692008-05-24 02:22:00 +0000694 tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000695 break;
696 case 5: /* pl: !N */
pbrook6fbe23d2008-04-01 17:19:11 +0000697 tmp = load_cpu_field(NF);
pbrookcb636692008-05-24 02:22:00 +0000698 tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000699 break;
700 case 6: /* vs: V */
701 tmp = load_cpu_field(VF);
pbrookcb636692008-05-24 02:22:00 +0000702 tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000703 break;
704 case 7: /* vc: !V */
705 tmp = load_cpu_field(VF);
pbrookcb636692008-05-24 02:22:00 +0000706 tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000707 break;
708 case 8: /* hi: C && !Z */
709 inv = gen_new_label();
710 tmp = load_cpu_field(CF);
pbrookcb636692008-05-24 02:22:00 +0000711 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
pbrookd9ba4832008-03-31 03:46:50 +0000712 dead_tmp(tmp);
pbrook6fbe23d2008-04-01 17:19:11 +0000713 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000714 tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000715 gen_set_label(inv);
716 break;
717 case 9: /* ls: !C || Z */
718 tmp = load_cpu_field(CF);
pbrookcb636692008-05-24 02:22:00 +0000719 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000720 dead_tmp(tmp);
pbrook6fbe23d2008-04-01 17:19:11 +0000721 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000722 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000723 break;
724 case 10: /* ge: N == V -> N ^ V == 0 */
725 tmp = load_cpu_field(VF);
pbrook6fbe23d2008-04-01 17:19:11 +0000726 tmp2 = load_cpu_field(NF);
pbrookd9ba4832008-03-31 03:46:50 +0000727 tcg_gen_xor_i32(tmp, tmp, tmp2);
728 dead_tmp(tmp2);
pbrookcb636692008-05-24 02:22:00 +0000729 tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000730 break;
731 case 11: /* lt: N != V -> N ^ V != 0 */
732 tmp = load_cpu_field(VF);
pbrook6fbe23d2008-04-01 17:19:11 +0000733 tmp2 = load_cpu_field(NF);
pbrookd9ba4832008-03-31 03:46:50 +0000734 tcg_gen_xor_i32(tmp, tmp, tmp2);
735 dead_tmp(tmp2);
pbrookcb636692008-05-24 02:22:00 +0000736 tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000737 break;
738 case 12: /* gt: !Z && N == V */
739 inv = gen_new_label();
pbrook6fbe23d2008-04-01 17:19:11 +0000740 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000741 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, inv);
pbrookd9ba4832008-03-31 03:46:50 +0000742 dead_tmp(tmp);
743 tmp = load_cpu_field(VF);
pbrook6fbe23d2008-04-01 17:19:11 +0000744 tmp2 = load_cpu_field(NF);
pbrookd9ba4832008-03-31 03:46:50 +0000745 tcg_gen_xor_i32(tmp, tmp, tmp2);
746 dead_tmp(tmp2);
pbrookcb636692008-05-24 02:22:00 +0000747 tcg_gen_brcondi_i32(TCG_COND_GE, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000748 gen_set_label(inv);
749 break;
750 case 13: /* le: Z || N != V */
pbrook6fbe23d2008-04-01 17:19:11 +0000751 tmp = load_cpu_field(ZF);
pbrookcb636692008-05-24 02:22:00 +0000752 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000753 dead_tmp(tmp);
754 tmp = load_cpu_field(VF);
pbrook6fbe23d2008-04-01 17:19:11 +0000755 tmp2 = load_cpu_field(NF);
pbrookd9ba4832008-03-31 03:46:50 +0000756 tcg_gen_xor_i32(tmp, tmp, tmp2);
757 dead_tmp(tmp2);
pbrookcb636692008-05-24 02:22:00 +0000758 tcg_gen_brcondi_i32(TCG_COND_LT, tmp, 0, label);
pbrookd9ba4832008-03-31 03:46:50 +0000759 break;
760 default:
761 fprintf(stderr, "Bad condition code 0x%x\n", cc);
762 abort();
763 }
764 dead_tmp(tmp);
765}
bellard2c0262a2003-09-30 20:34:21 +0000766
blueswir1b1d8e522008-10-26 13:43:07 +0000767static const uint8_t table_logic_cc[16] = {
bellard2c0262a2003-09-30 20:34:21 +0000768 1, /* and */
769 1, /* xor */
770 0, /* sub */
771 0, /* rsb */
772 0, /* add */
773 0, /* adc */
774 0, /* sbc */
775 0, /* rsc */
776 1, /* andl */
777 1, /* xorl */
778 0, /* cmp */
779 0, /* cmn */
780 1, /* orr */
781 1, /* mov */
782 1, /* bic */
783 1, /* mvn */
784};
ths3b46e622007-09-17 08:09:54 +0000785
pbrookd9ba4832008-03-31 03:46:50 +0000786/* Set PC and Thumb state from an immediate address. */
787static inline void gen_bx_im(DisasContext *s, uint32_t addr)
bellard99c475a2005-01-31 20:45:13 +0000788{
pbrookb26eefb2008-03-31 03:44:26 +0000789 TCGv tmp;
bellard99c475a2005-01-31 20:45:13 +0000790
pbrookb26eefb2008-03-31 03:44:26 +0000791 s->is_jmp = DISAS_UPDATE;
792 tmp = new_tmp();
pbrookd9ba4832008-03-31 03:46:50 +0000793 if (s->thumb != (addr & 1)) {
794 tcg_gen_movi_i32(tmp, addr & 1);
795 tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, thumb));
796 }
797 tcg_gen_movi_i32(tmp, addr & ~1);
798 tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, regs[15]));
pbrookb26eefb2008-03-31 03:44:26 +0000799 dead_tmp(tmp);
pbrookd9ba4832008-03-31 03:46:50 +0000800}
801
802/* Set PC and Thumb state from var. var is marked as dead. */
803static inline void gen_bx(DisasContext *s, TCGv var)
804{
805 TCGv tmp;
806
807 s->is_jmp = DISAS_UPDATE;
808 tmp = new_tmp();
809 tcg_gen_andi_i32(tmp, var, 1);
810 store_cpu_field(tmp, thumb);
811 tcg_gen_andi_i32(var, var, ~1);
812 store_cpu_field(var, regs[15]);
813}
814
815/* TODO: This should be removed. Use gen_bx instead. */
816static inline void gen_bx_T0(DisasContext *s)
817{
818 TCGv tmp = new_tmp();
819 tcg_gen_mov_i32(tmp, cpu_T[0]);
820 gen_bx(s, tmp);
pbrookb26eefb2008-03-31 03:44:26 +0000821}
bellardb5ff1b32005-11-26 10:38:39 +0000822
Juha Riihimäki21aeb342009-05-06 09:16:12 +0300823/* Variant of store_reg which uses branch&exchange logic when storing
824 to r15 in ARM architecture v7 and above. The source must be a temporary
825 and will be marked as dead. */
826static inline void store_reg_bx(CPUState *env, DisasContext *s,
827 int reg, TCGv var)
828{
829 if (reg == 15 && ENABLE_ARCH_7) {
830 gen_bx(s, var);
831 } else {
832 store_reg(s, reg, var);
833 }
834}
835
pbrookb0109802008-03-31 03:47:03 +0000836static inline TCGv gen_ld8s(TCGv addr, int index)
837{
838 TCGv tmp = new_tmp();
839 tcg_gen_qemu_ld8s(tmp, addr, index);
840 return tmp;
841}
842static inline TCGv gen_ld8u(TCGv addr, int index)
843{
844 TCGv tmp = new_tmp();
845 tcg_gen_qemu_ld8u(tmp, addr, index);
846 return tmp;
847}
848static inline TCGv gen_ld16s(TCGv addr, int index)
849{
850 TCGv tmp = new_tmp();
851 tcg_gen_qemu_ld16s(tmp, addr, index);
852 return tmp;
853}
854static inline TCGv gen_ld16u(TCGv addr, int index)
855{
856 TCGv tmp = new_tmp();
857 tcg_gen_qemu_ld16u(tmp, addr, index);
858 return tmp;
859}
860static inline TCGv gen_ld32(TCGv addr, int index)
861{
862 TCGv tmp = new_tmp();
863 tcg_gen_qemu_ld32u(tmp, addr, index);
864 return tmp;
865}
866static inline void gen_st8(TCGv val, TCGv addr, int index)
867{
868 tcg_gen_qemu_st8(val, addr, index);
869 dead_tmp(val);
870}
871static inline void gen_st16(TCGv val, TCGv addr, int index)
872{
873 tcg_gen_qemu_st16(val, addr, index);
874 dead_tmp(val);
875}
876static inline void gen_st32(TCGv val, TCGv addr, int index)
877{
878 tcg_gen_qemu_st32(val, addr, index);
879 dead_tmp(val);
880}
bellardb5ff1b32005-11-26 10:38:39 +0000881
bellard2c0262a2003-09-30 20:34:21 +0000882static inline void gen_movl_T0_reg(DisasContext *s, int reg)
883{
pbrookb26eefb2008-03-31 03:44:26 +0000884 load_reg_var(s, cpu_T[0], reg);
bellard2c0262a2003-09-30 20:34:21 +0000885}
886
887static inline void gen_movl_T1_reg(DisasContext *s, int reg)
888{
pbrookb26eefb2008-03-31 03:44:26 +0000889 load_reg_var(s, cpu_T[1], reg);
bellard2c0262a2003-09-30 20:34:21 +0000890}
891
892static inline void gen_movl_T2_reg(DisasContext *s, int reg)
893{
pbrookb26eefb2008-03-31 03:44:26 +0000894 load_reg_var(s, cpu_T[2], reg);
895}
896
pbrook5e3f8782008-03-31 03:47:34 +0000897static inline void gen_set_pc_im(uint32_t val)
898{
899 TCGv tmp = new_tmp();
900 tcg_gen_movi_i32(tmp, val);
901 store_cpu_field(tmp, regs[15]);
902}
903
bellard2c0262a2003-09-30 20:34:21 +0000904static inline void gen_movl_reg_TN(DisasContext *s, int reg, int t)
905{
pbrookb26eefb2008-03-31 03:44:26 +0000906 TCGv tmp;
bellard2c0262a2003-09-30 20:34:21 +0000907 if (reg == 15) {
pbrookb26eefb2008-03-31 03:44:26 +0000908 tmp = new_tmp();
909 tcg_gen_andi_i32(tmp, cpu_T[t], ~1);
910 } else {
911 tmp = cpu_T[t];
912 }
913 tcg_gen_st_i32(tmp, cpu_env, offsetof(CPUState, regs[reg]));
914 if (reg == 15) {
915 dead_tmp(tmp);
bellard2c0262a2003-09-30 20:34:21 +0000916 s->is_jmp = DISAS_JUMP;
917 }
918}
919
920static inline void gen_movl_reg_T0(DisasContext *s, int reg)
921{
922 gen_movl_reg_TN(s, reg, 0);
923}
924
925static inline void gen_movl_reg_T1(DisasContext *s, int reg)
926{
927 gen_movl_reg_TN(s, reg, 1);
928}
929
bellardb5ff1b32005-11-26 10:38:39 +0000930/* Force a TB lookup after an instruction that changes the CPU state. */
931static inline void gen_lookup_tb(DisasContext *s)
932{
933 gen_op_movl_T0_im(s->pc);
934 gen_movl_reg_T0(s, 15);
935 s->is_jmp = DISAS_UPDATE;
936}
937
pbrookb0109802008-03-31 03:47:03 +0000938static inline void gen_add_data_offset(DisasContext *s, unsigned int insn,
939 TCGv var)
bellard2c0262a2003-09-30 20:34:21 +0000940{
bellard1e8d4ee2004-12-08 23:40:14 +0000941 int val, rm, shift, shiftop;
pbrookb26eefb2008-03-31 03:44:26 +0000942 TCGv offset;
bellard2c0262a2003-09-30 20:34:21 +0000943
944 if (!(insn & (1 << 25))) {
945 /* immediate */
946 val = insn & 0xfff;
947 if (!(insn & (1 << 23)))
948 val = -val;
bellard537730b2004-02-22 13:40:57 +0000949 if (val != 0)
pbrookb0109802008-03-31 03:47:03 +0000950 tcg_gen_addi_i32(var, var, val);
bellard2c0262a2003-09-30 20:34:21 +0000951 } else {
952 /* shift/register */
953 rm = (insn) & 0xf;
954 shift = (insn >> 7) & 0x1f;
bellard1e8d4ee2004-12-08 23:40:14 +0000955 shiftop = (insn >> 5) & 3;
pbrookb26eefb2008-03-31 03:44:26 +0000956 offset = load_reg(s, rm);
pbrook9a119ff2008-03-31 03:45:35 +0000957 gen_arm_shift_im(offset, shiftop, shift, 0);
bellard2c0262a2003-09-30 20:34:21 +0000958 if (!(insn & (1 << 23)))
pbrookb0109802008-03-31 03:47:03 +0000959 tcg_gen_sub_i32(var, var, offset);
bellard2c0262a2003-09-30 20:34:21 +0000960 else
pbrookb0109802008-03-31 03:47:03 +0000961 tcg_gen_add_i32(var, var, offset);
pbrookb26eefb2008-03-31 03:44:26 +0000962 dead_tmp(offset);
bellard2c0262a2003-09-30 20:34:21 +0000963 }
964}
965
pbrook191f9a92006-06-14 14:36:07 +0000966static inline void gen_add_datah_offset(DisasContext *s, unsigned int insn,
pbrookb0109802008-03-31 03:47:03 +0000967 int extra, TCGv var)
bellard2c0262a2003-09-30 20:34:21 +0000968{
969 int val, rm;
pbrookb26eefb2008-03-31 03:44:26 +0000970 TCGv offset;
ths3b46e622007-09-17 08:09:54 +0000971
bellard2c0262a2003-09-30 20:34:21 +0000972 if (insn & (1 << 22)) {
973 /* immediate */
974 val = (insn & 0xf) | ((insn >> 4) & 0xf0);
975 if (!(insn & (1 << 23)))
976 val = -val;
pbrook18acad92007-02-14 20:17:03 +0000977 val += extra;
bellard537730b2004-02-22 13:40:57 +0000978 if (val != 0)
pbrookb0109802008-03-31 03:47:03 +0000979 tcg_gen_addi_i32(var, var, val);
bellard2c0262a2003-09-30 20:34:21 +0000980 } else {
981 /* register */
pbrook191f9a92006-06-14 14:36:07 +0000982 if (extra)
pbrookb0109802008-03-31 03:47:03 +0000983 tcg_gen_addi_i32(var, var, extra);
bellard2c0262a2003-09-30 20:34:21 +0000984 rm = (insn) & 0xf;
pbrookb26eefb2008-03-31 03:44:26 +0000985 offset = load_reg(s, rm);
bellard2c0262a2003-09-30 20:34:21 +0000986 if (!(insn & (1 << 23)))
pbrookb0109802008-03-31 03:47:03 +0000987 tcg_gen_sub_i32(var, var, offset);
bellard2c0262a2003-09-30 20:34:21 +0000988 else
pbrookb0109802008-03-31 03:47:03 +0000989 tcg_gen_add_i32(var, var, offset);
pbrookb26eefb2008-03-31 03:44:26 +0000990 dead_tmp(offset);
bellard2c0262a2003-09-30 20:34:21 +0000991 }
992}
993
pbrook4373f3c2008-03-31 03:47:19 +0000994#define VFP_OP2(name) \
995static inline void gen_vfp_##name(int dp) \
996{ \
997 if (dp) \
998 gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, cpu_F1d, cpu_env); \
999 else \
1000 gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, cpu_F1s, cpu_env); \
bellardb7bcbe92005-02-22 19:27:29 +00001001}
1002
pbrook4373f3c2008-03-31 03:47:19 +00001003VFP_OP2(add)
1004VFP_OP2(sub)
1005VFP_OP2(mul)
1006VFP_OP2(div)
bellardb7bcbe92005-02-22 19:27:29 +00001007
pbrook4373f3c2008-03-31 03:47:19 +00001008#undef VFP_OP2
bellardb7bcbe92005-02-22 19:27:29 +00001009
pbrook4373f3c2008-03-31 03:47:19 +00001010static inline void gen_vfp_abs(int dp)
pbrook9ee6e8b2007-11-11 00:04:49 +00001011{
1012 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001013 gen_helper_vfp_absd(cpu_F0d, cpu_F0d);
pbrook9ee6e8b2007-11-11 00:04:49 +00001014 else
pbrook4373f3c2008-03-31 03:47:19 +00001015 gen_helper_vfp_abss(cpu_F0s, cpu_F0s);
pbrook9ee6e8b2007-11-11 00:04:49 +00001016}
1017
pbrook4373f3c2008-03-31 03:47:19 +00001018static inline void gen_vfp_neg(int dp)
1019{
1020 if (dp)
1021 gen_helper_vfp_negd(cpu_F0d, cpu_F0d);
1022 else
1023 gen_helper_vfp_negs(cpu_F0s, cpu_F0s);
1024}
1025
1026static inline void gen_vfp_sqrt(int dp)
1027{
1028 if (dp)
1029 gen_helper_vfp_sqrtd(cpu_F0d, cpu_F0d, cpu_env);
1030 else
1031 gen_helper_vfp_sqrts(cpu_F0s, cpu_F0s, cpu_env);
1032}
1033
1034static inline void gen_vfp_cmp(int dp)
1035{
1036 if (dp)
1037 gen_helper_vfp_cmpd(cpu_F0d, cpu_F1d, cpu_env);
1038 else
1039 gen_helper_vfp_cmps(cpu_F0s, cpu_F1s, cpu_env);
1040}
1041
1042static inline void gen_vfp_cmpe(int dp)
1043{
1044 if (dp)
1045 gen_helper_vfp_cmped(cpu_F0d, cpu_F1d, cpu_env);
1046 else
1047 gen_helper_vfp_cmpes(cpu_F0s, cpu_F1s, cpu_env);
1048}
1049
1050static inline void gen_vfp_F1_ld0(int dp)
1051{
1052 if (dp)
balrog5b340b52008-04-14 02:19:57 +00001053 tcg_gen_movi_i64(cpu_F1d, 0);
pbrook4373f3c2008-03-31 03:47:19 +00001054 else
balrog5b340b52008-04-14 02:19:57 +00001055 tcg_gen_movi_i32(cpu_F1s, 0);
pbrook4373f3c2008-03-31 03:47:19 +00001056}
1057
1058static inline void gen_vfp_uito(int dp)
1059{
1060 if (dp)
1061 gen_helper_vfp_uitod(cpu_F0d, cpu_F0s, cpu_env);
1062 else
1063 gen_helper_vfp_uitos(cpu_F0s, cpu_F0s, cpu_env);
1064}
1065
1066static inline void gen_vfp_sito(int dp)
1067{
1068 if (dp)
balrog66230e02008-04-20 00:58:01 +00001069 gen_helper_vfp_sitod(cpu_F0d, cpu_F0s, cpu_env);
pbrook4373f3c2008-03-31 03:47:19 +00001070 else
balrog66230e02008-04-20 00:58:01 +00001071 gen_helper_vfp_sitos(cpu_F0s, cpu_F0s, cpu_env);
pbrook4373f3c2008-03-31 03:47:19 +00001072}
1073
1074static inline void gen_vfp_toui(int dp)
1075{
1076 if (dp)
1077 gen_helper_vfp_touid(cpu_F0s, cpu_F0d, cpu_env);
1078 else
1079 gen_helper_vfp_touis(cpu_F0s, cpu_F0s, cpu_env);
1080}
1081
1082static inline void gen_vfp_touiz(int dp)
1083{
1084 if (dp)
1085 gen_helper_vfp_touizd(cpu_F0s, cpu_F0d, cpu_env);
1086 else
1087 gen_helper_vfp_touizs(cpu_F0s, cpu_F0s, cpu_env);
1088}
1089
1090static inline void gen_vfp_tosi(int dp)
1091{
1092 if (dp)
1093 gen_helper_vfp_tosid(cpu_F0s, cpu_F0d, cpu_env);
1094 else
1095 gen_helper_vfp_tosis(cpu_F0s, cpu_F0s, cpu_env);
1096}
1097
1098static inline void gen_vfp_tosiz(int dp)
1099{
1100 if (dp)
1101 gen_helper_vfp_tosizd(cpu_F0s, cpu_F0d, cpu_env);
1102 else
1103 gen_helper_vfp_tosizs(cpu_F0s, cpu_F0s, cpu_env);
1104}
1105
1106#define VFP_GEN_FIX(name) \
1107static inline void gen_vfp_##name(int dp, int shift) \
1108{ \
1109 if (dp) \
1110 gen_helper_vfp_##name##d(cpu_F0d, cpu_F0d, tcg_const_i32(shift), cpu_env);\
1111 else \
1112 gen_helper_vfp_##name##s(cpu_F0s, cpu_F0s, tcg_const_i32(shift), cpu_env);\
1113}
1114VFP_GEN_FIX(tosh)
1115VFP_GEN_FIX(tosl)
1116VFP_GEN_FIX(touh)
1117VFP_GEN_FIX(toul)
1118VFP_GEN_FIX(shto)
1119VFP_GEN_FIX(slto)
1120VFP_GEN_FIX(uhto)
1121VFP_GEN_FIX(ulto)
1122#undef VFP_GEN_FIX
1123
bellardb5ff1b32005-11-26 10:38:39 +00001124static inline void gen_vfp_ld(DisasContext *s, int dp)
1125{
1126 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001127 tcg_gen_qemu_ld64(cpu_F0d, cpu_T[1], IS_USER(s));
bellardb5ff1b32005-11-26 10:38:39 +00001128 else
pbrook4373f3c2008-03-31 03:47:19 +00001129 tcg_gen_qemu_ld32u(cpu_F0s, cpu_T[1], IS_USER(s));
bellardb5ff1b32005-11-26 10:38:39 +00001130}
1131
1132static inline void gen_vfp_st(DisasContext *s, int dp)
1133{
1134 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001135 tcg_gen_qemu_st64(cpu_F0d, cpu_T[1], IS_USER(s));
bellardb5ff1b32005-11-26 10:38:39 +00001136 else
pbrook4373f3c2008-03-31 03:47:19 +00001137 tcg_gen_qemu_st32(cpu_F0s, cpu_T[1], IS_USER(s));
bellardb5ff1b32005-11-26 10:38:39 +00001138}
1139
bellard8e960052005-04-07 19:42:46 +00001140static inline long
1141vfp_reg_offset (int dp, int reg)
1142{
1143 if (dp)
1144 return offsetof(CPUARMState, vfp.regs[reg]);
1145 else if (reg & 1) {
1146 return offsetof(CPUARMState, vfp.regs[reg >> 1])
1147 + offsetof(CPU_DoubleU, l.upper);
1148 } else {
1149 return offsetof(CPUARMState, vfp.regs[reg >> 1])
1150 + offsetof(CPU_DoubleU, l.lower);
1151 }
1152}
pbrook9ee6e8b2007-11-11 00:04:49 +00001153
1154/* Return the offset of a 32-bit piece of a NEON register.
1155 zero is the least significant end of the register. */
1156static inline long
1157neon_reg_offset (int reg, int n)
1158{
1159 int sreg;
1160 sreg = reg * 2 + n;
1161 return vfp_reg_offset(0, sreg);
1162}
1163
pbrookad694712008-03-31 03:48:30 +00001164/* FIXME: Remove these. */
1165#define neon_T0 cpu_T[0]
1166#define neon_T1 cpu_T[1]
1167#define NEON_GET_REG(T, reg, n) \
1168 tcg_gen_ld_i32(neon_##T, cpu_env, neon_reg_offset(reg, n))
1169#define NEON_SET_REG(T, reg, n) \
1170 tcg_gen_st_i32(neon_##T, cpu_env, neon_reg_offset(reg, n))
pbrook9ee6e8b2007-11-11 00:04:49 +00001171
pbrook8f8e3aa2008-03-31 03:48:01 +00001172static TCGv neon_load_reg(int reg, int pass)
1173{
1174 TCGv tmp = new_tmp();
1175 tcg_gen_ld_i32(tmp, cpu_env, neon_reg_offset(reg, pass));
1176 return tmp;
1177}
1178
1179static void neon_store_reg(int reg, int pass, TCGv var)
1180{
1181 tcg_gen_st_i32(var, cpu_env, neon_reg_offset(reg, pass));
1182 dead_tmp(var);
1183}
1184
pbrooka7812ae2008-11-17 14:43:54 +00001185static inline void neon_load_reg64(TCGv_i64 var, int reg)
pbrookad694712008-03-31 03:48:30 +00001186{
1187 tcg_gen_ld_i64(var, cpu_env, vfp_reg_offset(1, reg));
1188}
1189
pbrooka7812ae2008-11-17 14:43:54 +00001190static inline void neon_store_reg64(TCGv_i64 var, int reg)
pbrookad694712008-03-31 03:48:30 +00001191{
1192 tcg_gen_st_i64(var, cpu_env, vfp_reg_offset(1, reg));
1193}
1194
pbrook4373f3c2008-03-31 03:47:19 +00001195#define tcg_gen_ld_f32 tcg_gen_ld_i32
1196#define tcg_gen_ld_f64 tcg_gen_ld_i64
1197#define tcg_gen_st_f32 tcg_gen_st_i32
1198#define tcg_gen_st_f64 tcg_gen_st_i64
1199
bellardb7bcbe92005-02-22 19:27:29 +00001200static inline void gen_mov_F0_vreg(int dp, int reg)
1201{
1202 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001203 tcg_gen_ld_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001204 else
pbrook4373f3c2008-03-31 03:47:19 +00001205 tcg_gen_ld_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001206}
1207
1208static inline void gen_mov_F1_vreg(int dp, int reg)
1209{
1210 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001211 tcg_gen_ld_f64(cpu_F1d, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001212 else
pbrook4373f3c2008-03-31 03:47:19 +00001213 tcg_gen_ld_f32(cpu_F1s, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001214}
1215
1216static inline void gen_mov_vreg_F0(int dp, int reg)
1217{
1218 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00001219 tcg_gen_st_f64(cpu_F0d, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001220 else
pbrook4373f3c2008-03-31 03:47:19 +00001221 tcg_gen_st_f32(cpu_F0s, cpu_env, vfp_reg_offset(dp, reg));
bellardb7bcbe92005-02-22 19:27:29 +00001222}
1223
balrog18c9b562007-04-30 02:02:17 +00001224#define ARM_CP_RW_BIT (1 << 20)
1225
pbrooka7812ae2008-11-17 14:43:54 +00001226static inline void iwmmxt_load_reg(TCGv_i64 var, int reg)
pbrooke6771372008-03-31 03:49:05 +00001227{
1228 tcg_gen_ld_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
1229}
1230
pbrooka7812ae2008-11-17 14:43:54 +00001231static inline void iwmmxt_store_reg(TCGv_i64 var, int reg)
pbrooke6771372008-03-31 03:49:05 +00001232{
1233 tcg_gen_st_i64(var, cpu_env, offsetof(CPUState, iwmmxt.regs[reg]));
1234}
1235
1236static inline void gen_op_iwmmxt_movl_wCx_T0(int reg)
1237{
1238 tcg_gen_st_i32(cpu_T[0], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
1239}
1240
1241static inline void gen_op_iwmmxt_movl_T0_wCx(int reg)
1242{
1243 tcg_gen_ld_i32(cpu_T[0], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
1244}
1245
1246static inline void gen_op_iwmmxt_movl_T1_wCx(int reg)
1247{
1248 tcg_gen_ld_i32(cpu_T[1], cpu_env, offsetof(CPUState, iwmmxt.cregs[reg]));
1249}
1250
1251static inline void gen_op_iwmmxt_movq_wRn_M0(int rn)
1252{
1253 iwmmxt_store_reg(cpu_M0, rn);
1254}
1255
1256static inline void gen_op_iwmmxt_movq_M0_wRn(int rn)
1257{
1258 iwmmxt_load_reg(cpu_M0, rn);
1259}
1260
1261static inline void gen_op_iwmmxt_orq_M0_wRn(int rn)
1262{
1263 iwmmxt_load_reg(cpu_V1, rn);
1264 tcg_gen_or_i64(cpu_M0, cpu_M0, cpu_V1);
1265}
1266
1267static inline void gen_op_iwmmxt_andq_M0_wRn(int rn)
1268{
1269 iwmmxt_load_reg(cpu_V1, rn);
1270 tcg_gen_and_i64(cpu_M0, cpu_M0, cpu_V1);
1271}
1272
1273static inline void gen_op_iwmmxt_xorq_M0_wRn(int rn)
1274{
1275 iwmmxt_load_reg(cpu_V1, rn);
1276 tcg_gen_xor_i64(cpu_M0, cpu_M0, cpu_V1);
1277}
1278
1279#define IWMMXT_OP(name) \
1280static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
1281{ \
1282 iwmmxt_load_reg(cpu_V1, rn); \
1283 gen_helper_iwmmxt_##name(cpu_M0, cpu_M0, cpu_V1); \
1284}
1285
1286#define IWMMXT_OP_ENV(name) \
1287static inline void gen_op_iwmmxt_##name##_M0_wRn(int rn) \
1288{ \
1289 iwmmxt_load_reg(cpu_V1, rn); \
1290 gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0, cpu_V1); \
1291}
1292
1293#define IWMMXT_OP_ENV_SIZE(name) \
1294IWMMXT_OP_ENV(name##b) \
1295IWMMXT_OP_ENV(name##w) \
1296IWMMXT_OP_ENV(name##l)
1297
1298#define IWMMXT_OP_ENV1(name) \
1299static inline void gen_op_iwmmxt_##name##_M0(void) \
1300{ \
1301 gen_helper_iwmmxt_##name(cpu_M0, cpu_env, cpu_M0); \
1302}
1303
1304IWMMXT_OP(maddsq)
1305IWMMXT_OP(madduq)
1306IWMMXT_OP(sadb)
1307IWMMXT_OP(sadw)
1308IWMMXT_OP(mulslw)
1309IWMMXT_OP(mulshw)
1310IWMMXT_OP(mululw)
1311IWMMXT_OP(muluhw)
1312IWMMXT_OP(macsw)
1313IWMMXT_OP(macuw)
1314
1315IWMMXT_OP_ENV_SIZE(unpackl)
1316IWMMXT_OP_ENV_SIZE(unpackh)
1317
1318IWMMXT_OP_ENV1(unpacklub)
1319IWMMXT_OP_ENV1(unpackluw)
1320IWMMXT_OP_ENV1(unpacklul)
1321IWMMXT_OP_ENV1(unpackhub)
1322IWMMXT_OP_ENV1(unpackhuw)
1323IWMMXT_OP_ENV1(unpackhul)
1324IWMMXT_OP_ENV1(unpacklsb)
1325IWMMXT_OP_ENV1(unpacklsw)
1326IWMMXT_OP_ENV1(unpacklsl)
1327IWMMXT_OP_ENV1(unpackhsb)
1328IWMMXT_OP_ENV1(unpackhsw)
1329IWMMXT_OP_ENV1(unpackhsl)
1330
1331IWMMXT_OP_ENV_SIZE(cmpeq)
1332IWMMXT_OP_ENV_SIZE(cmpgtu)
1333IWMMXT_OP_ENV_SIZE(cmpgts)
1334
1335IWMMXT_OP_ENV_SIZE(mins)
1336IWMMXT_OP_ENV_SIZE(minu)
1337IWMMXT_OP_ENV_SIZE(maxs)
1338IWMMXT_OP_ENV_SIZE(maxu)
1339
1340IWMMXT_OP_ENV_SIZE(subn)
1341IWMMXT_OP_ENV_SIZE(addn)
1342IWMMXT_OP_ENV_SIZE(subu)
1343IWMMXT_OP_ENV_SIZE(addu)
1344IWMMXT_OP_ENV_SIZE(subs)
1345IWMMXT_OP_ENV_SIZE(adds)
1346
1347IWMMXT_OP_ENV(avgb0)
1348IWMMXT_OP_ENV(avgb1)
1349IWMMXT_OP_ENV(avgw0)
1350IWMMXT_OP_ENV(avgw1)
1351
1352IWMMXT_OP(msadb)
1353
1354IWMMXT_OP_ENV(packuw)
1355IWMMXT_OP_ENV(packul)
1356IWMMXT_OP_ENV(packuq)
1357IWMMXT_OP_ENV(packsw)
1358IWMMXT_OP_ENV(packsl)
1359IWMMXT_OP_ENV(packsq)
1360
1361static inline void gen_op_iwmmxt_muladdsl_M0_T0_T1(void)
1362{
1363 gen_helper_iwmmxt_muladdsl(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
1364}
1365
1366static inline void gen_op_iwmmxt_muladdsw_M0_T0_T1(void)
1367{
1368 gen_helper_iwmmxt_muladdsw(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
1369}
1370
1371static inline void gen_op_iwmmxt_muladdswl_M0_T0_T1(void)
1372{
1373 gen_helper_iwmmxt_muladdswl(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1]);
1374}
1375
1376static inline void gen_op_iwmmxt_align_M0_T0_wRn(int rn)
1377{
1378 iwmmxt_load_reg(cpu_V1, rn);
1379 gen_helper_iwmmxt_align(cpu_M0, cpu_M0, cpu_V1, cpu_T[0]);
1380}
1381
1382static inline void gen_op_iwmmxt_insr_M0_T0_T1(int shift)
1383{
1384 TCGv tmp = tcg_const_i32(shift);
1385 gen_helper_iwmmxt_insr(cpu_M0, cpu_M0, cpu_T[0], cpu_T[1], tmp);
1386}
1387
1388static inline void gen_op_iwmmxt_extrsb_T0_M0(int shift)
1389{
1390 tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
1391 tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
1392 tcg_gen_ext8s_i32(cpu_T[0], cpu_T[0]);
1393}
1394
1395static inline void gen_op_iwmmxt_extrsw_T0_M0(int shift)
1396{
1397 tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
1398 tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
1399 tcg_gen_ext16s_i32(cpu_T[0], cpu_T[0]);
1400}
1401
1402static inline void gen_op_iwmmxt_extru_T0_M0(int shift, uint32_t mask)
1403{
1404 tcg_gen_shri_i64(cpu_M0, cpu_M0, shift);
1405 tcg_gen_trunc_i64_i32(cpu_T[0], cpu_M0);
1406 if (mask != ~0u)
1407 tcg_gen_andi_i32(cpu_T[0], cpu_T[0], mask);
1408}
1409
1410static void gen_op_iwmmxt_set_mup(void)
1411{
1412 TCGv tmp;
1413 tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
1414 tcg_gen_ori_i32(tmp, tmp, 2);
1415 store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
1416}
1417
1418static void gen_op_iwmmxt_set_cup(void)
1419{
1420 TCGv tmp;
1421 tmp = load_cpu_field(iwmmxt.cregs[ARM_IWMMXT_wCon]);
1422 tcg_gen_ori_i32(tmp, tmp, 1);
1423 store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCon]);
1424}
1425
1426static void gen_op_iwmmxt_setpsr_nz(void)
1427{
1428 TCGv tmp = new_tmp();
1429 gen_helper_iwmmxt_setpsr_nz(tmp, cpu_M0);
1430 store_cpu_field(tmp, iwmmxt.cregs[ARM_IWMMXT_wCASF]);
1431}
1432
1433static inline void gen_op_iwmmxt_addl_M0_wRn(int rn)
1434{
1435 iwmmxt_load_reg(cpu_V1, rn);
pbrook86831432008-05-11 12:22:01 +00001436 tcg_gen_ext32u_i64(cpu_V1, cpu_V1);
pbrooke6771372008-03-31 03:49:05 +00001437 tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
1438}
1439
1440
1441static void gen_iwmmxt_movl_T0_T1_wRn(int rn)
1442{
1443 iwmmxt_load_reg(cpu_V0, rn);
1444 tcg_gen_trunc_i64_i32(cpu_T[0], cpu_V0);
1445 tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
1446 tcg_gen_trunc_i64_i32(cpu_T[1], cpu_V0);
1447}
1448
1449static void gen_iwmmxt_movl_wRn_T0_T1(int rn)
1450{
pbrook36aa55d2008-09-21 13:48:32 +00001451 tcg_gen_concat_i32_i64(cpu_V0, cpu_T[0], cpu_T[1]);
pbrooke6771372008-03-31 03:49:05 +00001452 iwmmxt_store_reg(cpu_V0, rn);
1453}
1454
balrog18c9b562007-04-30 02:02:17 +00001455static inline int gen_iwmmxt_address(DisasContext *s, uint32_t insn)
1456{
1457 int rd;
1458 uint32_t offset;
1459
1460 rd = (insn >> 16) & 0xf;
1461 gen_movl_T1_reg(s, rd);
1462
1463 offset = (insn & 0xff) << ((insn >> 7) & 2);
1464 if (insn & (1 << 24)) {
1465 /* Pre indexed */
1466 if (insn & (1 << 23))
1467 gen_op_addl_T1_im(offset);
1468 else
1469 gen_op_addl_T1_im(-offset);
1470
1471 if (insn & (1 << 21))
1472 gen_movl_reg_T1(s, rd);
1473 } else if (insn & (1 << 21)) {
1474 /* Post indexed */
1475 if (insn & (1 << 23))
1476 gen_op_movl_T0_im(offset);
1477 else
1478 gen_op_movl_T0_im(- offset);
1479 gen_op_addl_T0_T1();
1480 gen_movl_reg_T0(s, rd);
1481 } else if (!(insn & (1 << 23)))
1482 return 1;
1483 return 0;
1484}
1485
1486static inline int gen_iwmmxt_shift(uint32_t insn, uint32_t mask)
1487{
1488 int rd = (insn >> 0) & 0xf;
1489
1490 if (insn & (1 << 8))
1491 if (rd < ARM_IWMMXT_wCGR0 || rd > ARM_IWMMXT_wCGR3)
1492 return 1;
1493 else
1494 gen_op_iwmmxt_movl_T0_wCx(rd);
1495 else
pbrooke6771372008-03-31 03:49:05 +00001496 gen_iwmmxt_movl_T0_T1_wRn(rd);
balrog18c9b562007-04-30 02:02:17 +00001497
1498 gen_op_movl_T1_im(mask);
1499 gen_op_andl_T0_T1();
1500 return 0;
1501}
1502
1503/* Disassemble an iwMMXt instruction. Returns nonzero if an error occured
1504 (ie. an undefined instruction). */
1505static int disas_iwmmxt_insn(CPUState *env, DisasContext *s, uint32_t insn)
1506{
1507 int rd, wrd;
1508 int rdhi, rdlo, rd0, rd1, i;
pbrookb0109802008-03-31 03:47:03 +00001509 TCGv tmp;
balrog18c9b562007-04-30 02:02:17 +00001510
1511 if ((insn & 0x0e000e00) == 0x0c000000) {
1512 if ((insn & 0x0fe00ff0) == 0x0c400000) {
1513 wrd = insn & 0xf;
1514 rdlo = (insn >> 12) & 0xf;
1515 rdhi = (insn >> 16) & 0xf;
1516 if (insn & ARM_CP_RW_BIT) { /* TMRRC */
pbrooke6771372008-03-31 03:49:05 +00001517 gen_iwmmxt_movl_T0_T1_wRn(wrd);
balrog18c9b562007-04-30 02:02:17 +00001518 gen_movl_reg_T0(s, rdlo);
1519 gen_movl_reg_T1(s, rdhi);
1520 } else { /* TMCRR */
1521 gen_movl_T0_reg(s, rdlo);
1522 gen_movl_T1_reg(s, rdhi);
pbrooke6771372008-03-31 03:49:05 +00001523 gen_iwmmxt_movl_wRn_T0_T1(wrd);
balrog18c9b562007-04-30 02:02:17 +00001524 gen_op_iwmmxt_set_mup();
1525 }
1526 return 0;
1527 }
1528
1529 wrd = (insn >> 12) & 0xf;
1530 if (gen_iwmmxt_address(s, insn))
1531 return 1;
1532 if (insn & ARM_CP_RW_BIT) {
1533 if ((insn >> 28) == 0xf) { /* WLDRW wCx */
pbrookb0109802008-03-31 03:47:03 +00001534 tmp = gen_ld32(cpu_T[1], IS_USER(s));
1535 tcg_gen_mov_i32(cpu_T[0], tmp);
1536 dead_tmp(tmp);
balrog18c9b562007-04-30 02:02:17 +00001537 gen_op_iwmmxt_movl_wCx_T0(wrd);
1538 } else {
pbrooke6771372008-03-31 03:49:05 +00001539 i = 1;
1540 if (insn & (1 << 8)) {
1541 if (insn & (1 << 22)) { /* WLDRD */
1542 tcg_gen_qemu_ld64(cpu_M0, cpu_T[1], IS_USER(s));
1543 i = 0;
1544 } else { /* WLDRW wRd */
1545 tmp = gen_ld32(cpu_T[1], IS_USER(s));
1546 }
1547 } else {
1548 if (insn & (1 << 22)) { /* WLDRH */
1549 tmp = gen_ld16u(cpu_T[1], IS_USER(s));
1550 } else { /* WLDRB */
1551 tmp = gen_ld8u(cpu_T[1], IS_USER(s));
1552 }
1553 }
1554 if (i) {
1555 tcg_gen_extu_i32_i64(cpu_M0, tmp);
1556 dead_tmp(tmp);
1557 }
balrog18c9b562007-04-30 02:02:17 +00001558 gen_op_iwmmxt_movq_wRn_M0(wrd);
1559 }
1560 } else {
1561 if ((insn >> 28) == 0xf) { /* WSTRW wCx */
1562 gen_op_iwmmxt_movl_T0_wCx(wrd);
pbrookb0109802008-03-31 03:47:03 +00001563 tmp = new_tmp();
1564 tcg_gen_mov_i32(tmp, cpu_T[0]);
1565 gen_st32(tmp, cpu_T[1], IS_USER(s));
balrog18c9b562007-04-30 02:02:17 +00001566 } else {
1567 gen_op_iwmmxt_movq_M0_wRn(wrd);
pbrooke6771372008-03-31 03:49:05 +00001568 tmp = new_tmp();
1569 if (insn & (1 << 8)) {
1570 if (insn & (1 << 22)) { /* WSTRD */
1571 dead_tmp(tmp);
1572 tcg_gen_qemu_st64(cpu_M0, cpu_T[1], IS_USER(s));
1573 } else { /* WSTRW wRd */
1574 tcg_gen_trunc_i64_i32(tmp, cpu_M0);
1575 gen_st32(tmp, cpu_T[1], IS_USER(s));
1576 }
1577 } else {
1578 if (insn & (1 << 22)) { /* WSTRH */
1579 tcg_gen_trunc_i64_i32(tmp, cpu_M0);
1580 gen_st16(tmp, cpu_T[1], IS_USER(s));
1581 } else { /* WSTRB */
1582 tcg_gen_trunc_i64_i32(tmp, cpu_M0);
1583 gen_st8(tmp, cpu_T[1], IS_USER(s));
1584 }
1585 }
balrog18c9b562007-04-30 02:02:17 +00001586 }
1587 }
1588 return 0;
1589 }
1590
1591 if ((insn & 0x0f000000) != 0x0e000000)
1592 return 1;
1593
1594 switch (((insn >> 12) & 0xf00) | ((insn >> 4) & 0xff)) {
1595 case 0x000: /* WOR */
1596 wrd = (insn >> 12) & 0xf;
1597 rd0 = (insn >> 0) & 0xf;
1598 rd1 = (insn >> 16) & 0xf;
1599 gen_op_iwmmxt_movq_M0_wRn(rd0);
1600 gen_op_iwmmxt_orq_M0_wRn(rd1);
1601 gen_op_iwmmxt_setpsr_nz();
1602 gen_op_iwmmxt_movq_wRn_M0(wrd);
1603 gen_op_iwmmxt_set_mup();
1604 gen_op_iwmmxt_set_cup();
1605 break;
1606 case 0x011: /* TMCR */
1607 if (insn & 0xf)
1608 return 1;
1609 rd = (insn >> 12) & 0xf;
1610 wrd = (insn >> 16) & 0xf;
1611 switch (wrd) {
1612 case ARM_IWMMXT_wCID:
1613 case ARM_IWMMXT_wCASF:
1614 break;
1615 case ARM_IWMMXT_wCon:
1616 gen_op_iwmmxt_set_cup();
1617 /* Fall through. */
1618 case ARM_IWMMXT_wCSSF:
1619 gen_op_iwmmxt_movl_T0_wCx(wrd);
1620 gen_movl_T1_reg(s, rd);
1621 gen_op_bicl_T0_T1();
1622 gen_op_iwmmxt_movl_wCx_T0(wrd);
1623 break;
1624 case ARM_IWMMXT_wCGR0:
1625 case ARM_IWMMXT_wCGR1:
1626 case ARM_IWMMXT_wCGR2:
1627 case ARM_IWMMXT_wCGR3:
1628 gen_op_iwmmxt_set_cup();
1629 gen_movl_reg_T0(s, rd);
1630 gen_op_iwmmxt_movl_wCx_T0(wrd);
1631 break;
1632 default:
1633 return 1;
1634 }
1635 break;
1636 case 0x100: /* WXOR */
1637 wrd = (insn >> 12) & 0xf;
1638 rd0 = (insn >> 0) & 0xf;
1639 rd1 = (insn >> 16) & 0xf;
1640 gen_op_iwmmxt_movq_M0_wRn(rd0);
1641 gen_op_iwmmxt_xorq_M0_wRn(rd1);
1642 gen_op_iwmmxt_setpsr_nz();
1643 gen_op_iwmmxt_movq_wRn_M0(wrd);
1644 gen_op_iwmmxt_set_mup();
1645 gen_op_iwmmxt_set_cup();
1646 break;
1647 case 0x111: /* TMRC */
1648 if (insn & 0xf)
1649 return 1;
1650 rd = (insn >> 12) & 0xf;
1651 wrd = (insn >> 16) & 0xf;
1652 gen_op_iwmmxt_movl_T0_wCx(wrd);
1653 gen_movl_reg_T0(s, rd);
1654 break;
1655 case 0x300: /* WANDN */
1656 wrd = (insn >> 12) & 0xf;
1657 rd0 = (insn >> 0) & 0xf;
1658 rd1 = (insn >> 16) & 0xf;
1659 gen_op_iwmmxt_movq_M0_wRn(rd0);
pbrooke6771372008-03-31 03:49:05 +00001660 tcg_gen_neg_i64(cpu_M0, cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00001661 gen_op_iwmmxt_andq_M0_wRn(rd1);
1662 gen_op_iwmmxt_setpsr_nz();
1663 gen_op_iwmmxt_movq_wRn_M0(wrd);
1664 gen_op_iwmmxt_set_mup();
1665 gen_op_iwmmxt_set_cup();
1666 break;
1667 case 0x200: /* WAND */
1668 wrd = (insn >> 12) & 0xf;
1669 rd0 = (insn >> 0) & 0xf;
1670 rd1 = (insn >> 16) & 0xf;
1671 gen_op_iwmmxt_movq_M0_wRn(rd0);
1672 gen_op_iwmmxt_andq_M0_wRn(rd1);
1673 gen_op_iwmmxt_setpsr_nz();
1674 gen_op_iwmmxt_movq_wRn_M0(wrd);
1675 gen_op_iwmmxt_set_mup();
1676 gen_op_iwmmxt_set_cup();
1677 break;
1678 case 0x810: case 0xa10: /* WMADD */
1679 wrd = (insn >> 12) & 0xf;
1680 rd0 = (insn >> 0) & 0xf;
1681 rd1 = (insn >> 16) & 0xf;
1682 gen_op_iwmmxt_movq_M0_wRn(rd0);
1683 if (insn & (1 << 21))
1684 gen_op_iwmmxt_maddsq_M0_wRn(rd1);
1685 else
1686 gen_op_iwmmxt_madduq_M0_wRn(rd1);
1687 gen_op_iwmmxt_movq_wRn_M0(wrd);
1688 gen_op_iwmmxt_set_mup();
1689 break;
1690 case 0x10e: case 0x50e: case 0x90e: case 0xd0e: /* WUNPCKIL */
1691 wrd = (insn >> 12) & 0xf;
1692 rd0 = (insn >> 16) & 0xf;
1693 rd1 = (insn >> 0) & 0xf;
1694 gen_op_iwmmxt_movq_M0_wRn(rd0);
1695 switch ((insn >> 22) & 3) {
1696 case 0:
1697 gen_op_iwmmxt_unpacklb_M0_wRn(rd1);
1698 break;
1699 case 1:
1700 gen_op_iwmmxt_unpacklw_M0_wRn(rd1);
1701 break;
1702 case 2:
1703 gen_op_iwmmxt_unpackll_M0_wRn(rd1);
1704 break;
1705 case 3:
1706 return 1;
1707 }
1708 gen_op_iwmmxt_movq_wRn_M0(wrd);
1709 gen_op_iwmmxt_set_mup();
1710 gen_op_iwmmxt_set_cup();
1711 break;
1712 case 0x10c: case 0x50c: case 0x90c: case 0xd0c: /* WUNPCKIH */
1713 wrd = (insn >> 12) & 0xf;
1714 rd0 = (insn >> 16) & 0xf;
1715 rd1 = (insn >> 0) & 0xf;
1716 gen_op_iwmmxt_movq_M0_wRn(rd0);
1717 switch ((insn >> 22) & 3) {
1718 case 0:
1719 gen_op_iwmmxt_unpackhb_M0_wRn(rd1);
1720 break;
1721 case 1:
1722 gen_op_iwmmxt_unpackhw_M0_wRn(rd1);
1723 break;
1724 case 2:
1725 gen_op_iwmmxt_unpackhl_M0_wRn(rd1);
1726 break;
1727 case 3:
1728 return 1;
1729 }
1730 gen_op_iwmmxt_movq_wRn_M0(wrd);
1731 gen_op_iwmmxt_set_mup();
1732 gen_op_iwmmxt_set_cup();
1733 break;
1734 case 0x012: case 0x112: case 0x412: case 0x512: /* WSAD */
1735 wrd = (insn >> 12) & 0xf;
1736 rd0 = (insn >> 16) & 0xf;
1737 rd1 = (insn >> 0) & 0xf;
1738 gen_op_iwmmxt_movq_M0_wRn(rd0);
1739 if (insn & (1 << 22))
1740 gen_op_iwmmxt_sadw_M0_wRn(rd1);
1741 else
1742 gen_op_iwmmxt_sadb_M0_wRn(rd1);
1743 if (!(insn & (1 << 20)))
1744 gen_op_iwmmxt_addl_M0_wRn(wrd);
1745 gen_op_iwmmxt_movq_wRn_M0(wrd);
1746 gen_op_iwmmxt_set_mup();
1747 break;
1748 case 0x010: case 0x110: case 0x210: case 0x310: /* WMUL */
1749 wrd = (insn >> 12) & 0xf;
1750 rd0 = (insn >> 16) & 0xf;
1751 rd1 = (insn >> 0) & 0xf;
1752 gen_op_iwmmxt_movq_M0_wRn(rd0);
pbrooke6771372008-03-31 03:49:05 +00001753 if (insn & (1 << 21)) {
1754 if (insn & (1 << 20))
1755 gen_op_iwmmxt_mulshw_M0_wRn(rd1);
1756 else
1757 gen_op_iwmmxt_mulslw_M0_wRn(rd1);
1758 } else {
1759 if (insn & (1 << 20))
1760 gen_op_iwmmxt_muluhw_M0_wRn(rd1);
1761 else
1762 gen_op_iwmmxt_mululw_M0_wRn(rd1);
1763 }
balrog18c9b562007-04-30 02:02:17 +00001764 gen_op_iwmmxt_movq_wRn_M0(wrd);
1765 gen_op_iwmmxt_set_mup();
1766 break;
1767 case 0x410: case 0x510: case 0x610: case 0x710: /* WMAC */
1768 wrd = (insn >> 12) & 0xf;
1769 rd0 = (insn >> 16) & 0xf;
1770 rd1 = (insn >> 0) & 0xf;
1771 gen_op_iwmmxt_movq_M0_wRn(rd0);
1772 if (insn & (1 << 21))
1773 gen_op_iwmmxt_macsw_M0_wRn(rd1);
1774 else
1775 gen_op_iwmmxt_macuw_M0_wRn(rd1);
1776 if (!(insn & (1 << 20))) {
pbrooke6771372008-03-31 03:49:05 +00001777 iwmmxt_load_reg(cpu_V1, wrd);
1778 tcg_gen_add_i64(cpu_M0, cpu_M0, cpu_V1);
balrog18c9b562007-04-30 02:02:17 +00001779 }
1780 gen_op_iwmmxt_movq_wRn_M0(wrd);
1781 gen_op_iwmmxt_set_mup();
1782 break;
1783 case 0x006: case 0x406: case 0x806: case 0xc06: /* WCMPEQ */
1784 wrd = (insn >> 12) & 0xf;
1785 rd0 = (insn >> 16) & 0xf;
1786 rd1 = (insn >> 0) & 0xf;
1787 gen_op_iwmmxt_movq_M0_wRn(rd0);
1788 switch ((insn >> 22) & 3) {
1789 case 0:
1790 gen_op_iwmmxt_cmpeqb_M0_wRn(rd1);
1791 break;
1792 case 1:
1793 gen_op_iwmmxt_cmpeqw_M0_wRn(rd1);
1794 break;
1795 case 2:
1796 gen_op_iwmmxt_cmpeql_M0_wRn(rd1);
1797 break;
1798 case 3:
1799 return 1;
1800 }
1801 gen_op_iwmmxt_movq_wRn_M0(wrd);
1802 gen_op_iwmmxt_set_mup();
1803 gen_op_iwmmxt_set_cup();
1804 break;
1805 case 0x800: case 0x900: case 0xc00: case 0xd00: /* WAVG2 */
1806 wrd = (insn >> 12) & 0xf;
1807 rd0 = (insn >> 16) & 0xf;
1808 rd1 = (insn >> 0) & 0xf;
1809 gen_op_iwmmxt_movq_M0_wRn(rd0);
pbrooke6771372008-03-31 03:49:05 +00001810 if (insn & (1 << 22)) {
1811 if (insn & (1 << 20))
1812 gen_op_iwmmxt_avgw1_M0_wRn(rd1);
1813 else
1814 gen_op_iwmmxt_avgw0_M0_wRn(rd1);
1815 } else {
1816 if (insn & (1 << 20))
1817 gen_op_iwmmxt_avgb1_M0_wRn(rd1);
1818 else
1819 gen_op_iwmmxt_avgb0_M0_wRn(rd1);
1820 }
balrog18c9b562007-04-30 02:02:17 +00001821 gen_op_iwmmxt_movq_wRn_M0(wrd);
1822 gen_op_iwmmxt_set_mup();
1823 gen_op_iwmmxt_set_cup();
1824 break;
1825 case 0x802: case 0x902: case 0xa02: case 0xb02: /* WALIGNR */
1826 wrd = (insn >> 12) & 0xf;
1827 rd0 = (insn >> 16) & 0xf;
1828 rd1 = (insn >> 0) & 0xf;
1829 gen_op_iwmmxt_movq_M0_wRn(rd0);
1830 gen_op_iwmmxt_movl_T0_wCx(ARM_IWMMXT_wCGR0 + ((insn >> 20) & 3));
1831 gen_op_movl_T1_im(7);
1832 gen_op_andl_T0_T1();
1833 gen_op_iwmmxt_align_M0_T0_wRn(rd1);
1834 gen_op_iwmmxt_movq_wRn_M0(wrd);
1835 gen_op_iwmmxt_set_mup();
1836 break;
1837 case 0x601: case 0x605: case 0x609: case 0x60d: /* TINSR */
1838 rd = (insn >> 12) & 0xf;
1839 wrd = (insn >> 16) & 0xf;
1840 gen_movl_T0_reg(s, rd);
1841 gen_op_iwmmxt_movq_M0_wRn(wrd);
1842 switch ((insn >> 6) & 3) {
1843 case 0:
1844 gen_op_movl_T1_im(0xff);
1845 gen_op_iwmmxt_insr_M0_T0_T1((insn & 7) << 3);
1846 break;
1847 case 1:
1848 gen_op_movl_T1_im(0xffff);
1849 gen_op_iwmmxt_insr_M0_T0_T1((insn & 3) << 4);
1850 break;
1851 case 2:
1852 gen_op_movl_T1_im(0xffffffff);
1853 gen_op_iwmmxt_insr_M0_T0_T1((insn & 1) << 5);
1854 break;
1855 case 3:
1856 return 1;
1857 }
1858 gen_op_iwmmxt_movq_wRn_M0(wrd);
1859 gen_op_iwmmxt_set_mup();
1860 break;
1861 case 0x107: case 0x507: case 0x907: case 0xd07: /* TEXTRM */
1862 rd = (insn >> 12) & 0xf;
1863 wrd = (insn >> 16) & 0xf;
1864 if (rd == 15)
1865 return 1;
1866 gen_op_iwmmxt_movq_M0_wRn(wrd);
1867 switch ((insn >> 22) & 3) {
1868 case 0:
1869 if (insn & 8)
1870 gen_op_iwmmxt_extrsb_T0_M0((insn & 7) << 3);
1871 else {
pbrooke6771372008-03-31 03:49:05 +00001872 gen_op_iwmmxt_extru_T0_M0((insn & 7) << 3, 0xff);
balrog18c9b562007-04-30 02:02:17 +00001873 }
1874 break;
1875 case 1:
1876 if (insn & 8)
1877 gen_op_iwmmxt_extrsw_T0_M0((insn & 3) << 4);
1878 else {
pbrooke6771372008-03-31 03:49:05 +00001879 gen_op_iwmmxt_extru_T0_M0((insn & 3) << 4, 0xffff);
balrog18c9b562007-04-30 02:02:17 +00001880 }
1881 break;
1882 case 2:
pbrooke6771372008-03-31 03:49:05 +00001883 gen_op_iwmmxt_extru_T0_M0((insn & 1) << 5, ~0u);
balrog18c9b562007-04-30 02:02:17 +00001884 break;
1885 case 3:
1886 return 1;
1887 }
pbrookb26eefb2008-03-31 03:44:26 +00001888 gen_movl_reg_T0(s, rd);
balrog18c9b562007-04-30 02:02:17 +00001889 break;
1890 case 0x117: case 0x517: case 0x917: case 0xd17: /* TEXTRC */
1891 if ((insn & 0x000ff008) != 0x0003f000)
1892 return 1;
1893 gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
1894 switch ((insn >> 22) & 3) {
1895 case 0:
1896 gen_op_shrl_T1_im(((insn & 7) << 2) + 0);
1897 break;
1898 case 1:
1899 gen_op_shrl_T1_im(((insn & 3) << 3) + 4);
1900 break;
1901 case 2:
1902 gen_op_shrl_T1_im(((insn & 1) << 4) + 12);
1903 break;
1904 case 3:
1905 return 1;
1906 }
1907 gen_op_shll_T1_im(28);
pbrookd9ba4832008-03-31 03:46:50 +00001908 gen_set_nzcv(cpu_T[1]);
balrog18c9b562007-04-30 02:02:17 +00001909 break;
1910 case 0x401: case 0x405: case 0x409: case 0x40d: /* TBCST */
1911 rd = (insn >> 12) & 0xf;
1912 wrd = (insn >> 16) & 0xf;
1913 gen_movl_T0_reg(s, rd);
1914 switch ((insn >> 6) & 3) {
1915 case 0:
pbrooke6771372008-03-31 03:49:05 +00001916 gen_helper_iwmmxt_bcstb(cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00001917 break;
1918 case 1:
pbrooke6771372008-03-31 03:49:05 +00001919 gen_helper_iwmmxt_bcstw(cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00001920 break;
1921 case 2:
pbrooke6771372008-03-31 03:49:05 +00001922 gen_helper_iwmmxt_bcstl(cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00001923 break;
1924 case 3:
1925 return 1;
1926 }
1927 gen_op_iwmmxt_movq_wRn_M0(wrd);
1928 gen_op_iwmmxt_set_mup();
1929 break;
1930 case 0x113: case 0x513: case 0x913: case 0xd13: /* TANDC */
1931 if ((insn & 0x000ff00f) != 0x0003f000)
1932 return 1;
1933 gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
1934 switch ((insn >> 22) & 3) {
1935 case 0:
1936 for (i = 0; i < 7; i ++) {
1937 gen_op_shll_T1_im(4);
1938 gen_op_andl_T0_T1();
1939 }
1940 break;
1941 case 1:
1942 for (i = 0; i < 3; i ++) {
1943 gen_op_shll_T1_im(8);
1944 gen_op_andl_T0_T1();
1945 }
1946 break;
1947 case 2:
1948 gen_op_shll_T1_im(16);
1949 gen_op_andl_T0_T1();
1950 break;
1951 case 3:
1952 return 1;
1953 }
pbrookd9ba4832008-03-31 03:46:50 +00001954 gen_set_nzcv(cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00001955 break;
1956 case 0x01c: case 0x41c: case 0x81c: case 0xc1c: /* WACC */
1957 wrd = (insn >> 12) & 0xf;
1958 rd0 = (insn >> 16) & 0xf;
1959 gen_op_iwmmxt_movq_M0_wRn(rd0);
1960 switch ((insn >> 22) & 3) {
1961 case 0:
pbrooke6771372008-03-31 03:49:05 +00001962 gen_helper_iwmmxt_addcb(cpu_M0, cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00001963 break;
1964 case 1:
pbrooke6771372008-03-31 03:49:05 +00001965 gen_helper_iwmmxt_addcw(cpu_M0, cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00001966 break;
1967 case 2:
pbrooke6771372008-03-31 03:49:05 +00001968 gen_helper_iwmmxt_addcl(cpu_M0, cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00001969 break;
1970 case 3:
1971 return 1;
1972 }
1973 gen_op_iwmmxt_movq_wRn_M0(wrd);
1974 gen_op_iwmmxt_set_mup();
1975 break;
1976 case 0x115: case 0x515: case 0x915: case 0xd15: /* TORC */
1977 if ((insn & 0x000ff00f) != 0x0003f000)
1978 return 1;
1979 gen_op_iwmmxt_movl_T1_wCx(ARM_IWMMXT_wCASF);
1980 switch ((insn >> 22) & 3) {
1981 case 0:
1982 for (i = 0; i < 7; i ++) {
1983 gen_op_shll_T1_im(4);
1984 gen_op_orl_T0_T1();
1985 }
1986 break;
1987 case 1:
1988 for (i = 0; i < 3; i ++) {
1989 gen_op_shll_T1_im(8);
1990 gen_op_orl_T0_T1();
1991 }
1992 break;
1993 case 2:
1994 gen_op_shll_T1_im(16);
1995 gen_op_orl_T0_T1();
1996 break;
1997 case 3:
1998 return 1;
1999 }
pbrookd9ba4832008-03-31 03:46:50 +00002000 gen_set_nzcv(cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002001 break;
2002 case 0x103: case 0x503: case 0x903: case 0xd03: /* TMOVMSK */
2003 rd = (insn >> 12) & 0xf;
2004 rd0 = (insn >> 16) & 0xf;
2005 if ((insn & 0xf) != 0)
2006 return 1;
2007 gen_op_iwmmxt_movq_M0_wRn(rd0);
2008 switch ((insn >> 22) & 3) {
2009 case 0:
pbrooke6771372008-03-31 03:49:05 +00002010 gen_helper_iwmmxt_msbb(cpu_T[0], cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00002011 break;
2012 case 1:
pbrooke6771372008-03-31 03:49:05 +00002013 gen_helper_iwmmxt_msbw(cpu_T[0], cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00002014 break;
2015 case 2:
pbrooke6771372008-03-31 03:49:05 +00002016 gen_helper_iwmmxt_msbl(cpu_T[0], cpu_M0);
balrog18c9b562007-04-30 02:02:17 +00002017 break;
2018 case 3:
2019 return 1;
2020 }
2021 gen_movl_reg_T0(s, rd);
2022 break;
2023 case 0x106: case 0x306: case 0x506: case 0x706: /* WCMPGT */
2024 case 0x906: case 0xb06: case 0xd06: case 0xf06:
2025 wrd = (insn >> 12) & 0xf;
2026 rd0 = (insn >> 16) & 0xf;
2027 rd1 = (insn >> 0) & 0xf;
2028 gen_op_iwmmxt_movq_M0_wRn(rd0);
2029 switch ((insn >> 22) & 3) {
2030 case 0:
2031 if (insn & (1 << 21))
2032 gen_op_iwmmxt_cmpgtsb_M0_wRn(rd1);
2033 else
2034 gen_op_iwmmxt_cmpgtub_M0_wRn(rd1);
2035 break;
2036 case 1:
2037 if (insn & (1 << 21))
2038 gen_op_iwmmxt_cmpgtsw_M0_wRn(rd1);
2039 else
2040 gen_op_iwmmxt_cmpgtuw_M0_wRn(rd1);
2041 break;
2042 case 2:
2043 if (insn & (1 << 21))
2044 gen_op_iwmmxt_cmpgtsl_M0_wRn(rd1);
2045 else
2046 gen_op_iwmmxt_cmpgtul_M0_wRn(rd1);
2047 break;
2048 case 3:
2049 return 1;
2050 }
2051 gen_op_iwmmxt_movq_wRn_M0(wrd);
2052 gen_op_iwmmxt_set_mup();
2053 gen_op_iwmmxt_set_cup();
2054 break;
2055 case 0x00e: case 0x20e: case 0x40e: case 0x60e: /* WUNPCKEL */
2056 case 0x80e: case 0xa0e: case 0xc0e: case 0xe0e:
2057 wrd = (insn >> 12) & 0xf;
2058 rd0 = (insn >> 16) & 0xf;
2059 gen_op_iwmmxt_movq_M0_wRn(rd0);
2060 switch ((insn >> 22) & 3) {
2061 case 0:
2062 if (insn & (1 << 21))
2063 gen_op_iwmmxt_unpacklsb_M0();
2064 else
2065 gen_op_iwmmxt_unpacklub_M0();
2066 break;
2067 case 1:
2068 if (insn & (1 << 21))
2069 gen_op_iwmmxt_unpacklsw_M0();
2070 else
2071 gen_op_iwmmxt_unpackluw_M0();
2072 break;
2073 case 2:
2074 if (insn & (1 << 21))
2075 gen_op_iwmmxt_unpacklsl_M0();
2076 else
2077 gen_op_iwmmxt_unpacklul_M0();
2078 break;
2079 case 3:
2080 return 1;
2081 }
2082 gen_op_iwmmxt_movq_wRn_M0(wrd);
2083 gen_op_iwmmxt_set_mup();
2084 gen_op_iwmmxt_set_cup();
2085 break;
2086 case 0x00c: case 0x20c: case 0x40c: case 0x60c: /* WUNPCKEH */
2087 case 0x80c: case 0xa0c: case 0xc0c: case 0xe0c:
2088 wrd = (insn >> 12) & 0xf;
2089 rd0 = (insn >> 16) & 0xf;
2090 gen_op_iwmmxt_movq_M0_wRn(rd0);
2091 switch ((insn >> 22) & 3) {
2092 case 0:
2093 if (insn & (1 << 21))
2094 gen_op_iwmmxt_unpackhsb_M0();
2095 else
2096 gen_op_iwmmxt_unpackhub_M0();
2097 break;
2098 case 1:
2099 if (insn & (1 << 21))
2100 gen_op_iwmmxt_unpackhsw_M0();
2101 else
2102 gen_op_iwmmxt_unpackhuw_M0();
2103 break;
2104 case 2:
2105 if (insn & (1 << 21))
2106 gen_op_iwmmxt_unpackhsl_M0();
2107 else
2108 gen_op_iwmmxt_unpackhul_M0();
2109 break;
2110 case 3:
2111 return 1;
2112 }
2113 gen_op_iwmmxt_movq_wRn_M0(wrd);
2114 gen_op_iwmmxt_set_mup();
2115 gen_op_iwmmxt_set_cup();
2116 break;
2117 case 0x204: case 0x604: case 0xa04: case 0xe04: /* WSRL */
2118 case 0x214: case 0x614: case 0xa14: case 0xe14:
2119 wrd = (insn >> 12) & 0xf;
2120 rd0 = (insn >> 16) & 0xf;
2121 gen_op_iwmmxt_movq_M0_wRn(rd0);
2122 if (gen_iwmmxt_shift(insn, 0xff))
2123 return 1;
2124 switch ((insn >> 22) & 3) {
2125 case 0:
2126 return 1;
2127 case 1:
pbrooke6771372008-03-31 03:49:05 +00002128 gen_helper_iwmmxt_srlw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002129 break;
2130 case 2:
pbrooke6771372008-03-31 03:49:05 +00002131 gen_helper_iwmmxt_srll(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002132 break;
2133 case 3:
pbrooke6771372008-03-31 03:49:05 +00002134 gen_helper_iwmmxt_srlq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002135 break;
2136 }
2137 gen_op_iwmmxt_movq_wRn_M0(wrd);
2138 gen_op_iwmmxt_set_mup();
2139 gen_op_iwmmxt_set_cup();
2140 break;
2141 case 0x004: case 0x404: case 0x804: case 0xc04: /* WSRA */
2142 case 0x014: case 0x414: case 0x814: case 0xc14:
2143 wrd = (insn >> 12) & 0xf;
2144 rd0 = (insn >> 16) & 0xf;
2145 gen_op_iwmmxt_movq_M0_wRn(rd0);
2146 if (gen_iwmmxt_shift(insn, 0xff))
2147 return 1;
2148 switch ((insn >> 22) & 3) {
2149 case 0:
2150 return 1;
2151 case 1:
pbrooke6771372008-03-31 03:49:05 +00002152 gen_helper_iwmmxt_sraw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002153 break;
2154 case 2:
pbrooke6771372008-03-31 03:49:05 +00002155 gen_helper_iwmmxt_sral(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002156 break;
2157 case 3:
pbrooke6771372008-03-31 03:49:05 +00002158 gen_helper_iwmmxt_sraq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002159 break;
2160 }
2161 gen_op_iwmmxt_movq_wRn_M0(wrd);
2162 gen_op_iwmmxt_set_mup();
2163 gen_op_iwmmxt_set_cup();
2164 break;
2165 case 0x104: case 0x504: case 0x904: case 0xd04: /* WSLL */
2166 case 0x114: case 0x514: case 0x914: case 0xd14:
2167 wrd = (insn >> 12) & 0xf;
2168 rd0 = (insn >> 16) & 0xf;
2169 gen_op_iwmmxt_movq_M0_wRn(rd0);
2170 if (gen_iwmmxt_shift(insn, 0xff))
2171 return 1;
2172 switch ((insn >> 22) & 3) {
2173 case 0:
2174 return 1;
2175 case 1:
pbrooke6771372008-03-31 03:49:05 +00002176 gen_helper_iwmmxt_sllw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002177 break;
2178 case 2:
pbrooke6771372008-03-31 03:49:05 +00002179 gen_helper_iwmmxt_slll(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002180 break;
2181 case 3:
pbrooke6771372008-03-31 03:49:05 +00002182 gen_helper_iwmmxt_sllq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002183 break;
2184 }
2185 gen_op_iwmmxt_movq_wRn_M0(wrd);
2186 gen_op_iwmmxt_set_mup();
2187 gen_op_iwmmxt_set_cup();
2188 break;
2189 case 0x304: case 0x704: case 0xb04: case 0xf04: /* WROR */
2190 case 0x314: case 0x714: case 0xb14: case 0xf14:
2191 wrd = (insn >> 12) & 0xf;
2192 rd0 = (insn >> 16) & 0xf;
2193 gen_op_iwmmxt_movq_M0_wRn(rd0);
2194 switch ((insn >> 22) & 3) {
2195 case 0:
2196 return 1;
2197 case 1:
2198 if (gen_iwmmxt_shift(insn, 0xf))
2199 return 1;
pbrooke6771372008-03-31 03:49:05 +00002200 gen_helper_iwmmxt_rorw(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002201 break;
2202 case 2:
2203 if (gen_iwmmxt_shift(insn, 0x1f))
2204 return 1;
pbrooke6771372008-03-31 03:49:05 +00002205 gen_helper_iwmmxt_rorl(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002206 break;
2207 case 3:
2208 if (gen_iwmmxt_shift(insn, 0x3f))
2209 return 1;
pbrooke6771372008-03-31 03:49:05 +00002210 gen_helper_iwmmxt_rorq(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002211 break;
2212 }
2213 gen_op_iwmmxt_movq_wRn_M0(wrd);
2214 gen_op_iwmmxt_set_mup();
2215 gen_op_iwmmxt_set_cup();
2216 break;
2217 case 0x116: case 0x316: case 0x516: case 0x716: /* WMIN */
2218 case 0x916: case 0xb16: case 0xd16: case 0xf16:
2219 wrd = (insn >> 12) & 0xf;
2220 rd0 = (insn >> 16) & 0xf;
2221 rd1 = (insn >> 0) & 0xf;
2222 gen_op_iwmmxt_movq_M0_wRn(rd0);
2223 switch ((insn >> 22) & 3) {
2224 case 0:
2225 if (insn & (1 << 21))
2226 gen_op_iwmmxt_minsb_M0_wRn(rd1);
2227 else
2228 gen_op_iwmmxt_minub_M0_wRn(rd1);
2229 break;
2230 case 1:
2231 if (insn & (1 << 21))
2232 gen_op_iwmmxt_minsw_M0_wRn(rd1);
2233 else
2234 gen_op_iwmmxt_minuw_M0_wRn(rd1);
2235 break;
2236 case 2:
2237 if (insn & (1 << 21))
2238 gen_op_iwmmxt_minsl_M0_wRn(rd1);
2239 else
2240 gen_op_iwmmxt_minul_M0_wRn(rd1);
2241 break;
2242 case 3:
2243 return 1;
2244 }
2245 gen_op_iwmmxt_movq_wRn_M0(wrd);
2246 gen_op_iwmmxt_set_mup();
2247 break;
2248 case 0x016: case 0x216: case 0x416: case 0x616: /* WMAX */
2249 case 0x816: case 0xa16: case 0xc16: case 0xe16:
2250 wrd = (insn >> 12) & 0xf;
2251 rd0 = (insn >> 16) & 0xf;
2252 rd1 = (insn >> 0) & 0xf;
2253 gen_op_iwmmxt_movq_M0_wRn(rd0);
2254 switch ((insn >> 22) & 3) {
2255 case 0:
2256 if (insn & (1 << 21))
2257 gen_op_iwmmxt_maxsb_M0_wRn(rd1);
2258 else
2259 gen_op_iwmmxt_maxub_M0_wRn(rd1);
2260 break;
2261 case 1:
2262 if (insn & (1 << 21))
2263 gen_op_iwmmxt_maxsw_M0_wRn(rd1);
2264 else
2265 gen_op_iwmmxt_maxuw_M0_wRn(rd1);
2266 break;
2267 case 2:
2268 if (insn & (1 << 21))
2269 gen_op_iwmmxt_maxsl_M0_wRn(rd1);
2270 else
2271 gen_op_iwmmxt_maxul_M0_wRn(rd1);
2272 break;
2273 case 3:
2274 return 1;
2275 }
2276 gen_op_iwmmxt_movq_wRn_M0(wrd);
2277 gen_op_iwmmxt_set_mup();
2278 break;
2279 case 0x002: case 0x102: case 0x202: case 0x302: /* WALIGNI */
2280 case 0x402: case 0x502: case 0x602: case 0x702:
2281 wrd = (insn >> 12) & 0xf;
2282 rd0 = (insn >> 16) & 0xf;
2283 rd1 = (insn >> 0) & 0xf;
2284 gen_op_iwmmxt_movq_M0_wRn(rd0);
2285 gen_op_movl_T0_im((insn >> 20) & 3);
2286 gen_op_iwmmxt_align_M0_T0_wRn(rd1);
2287 gen_op_iwmmxt_movq_wRn_M0(wrd);
2288 gen_op_iwmmxt_set_mup();
2289 break;
2290 case 0x01a: case 0x11a: case 0x21a: case 0x31a: /* WSUB */
2291 case 0x41a: case 0x51a: case 0x61a: case 0x71a:
2292 case 0x81a: case 0x91a: case 0xa1a: case 0xb1a:
2293 case 0xc1a: case 0xd1a: case 0xe1a: case 0xf1a:
2294 wrd = (insn >> 12) & 0xf;
2295 rd0 = (insn >> 16) & 0xf;
2296 rd1 = (insn >> 0) & 0xf;
2297 gen_op_iwmmxt_movq_M0_wRn(rd0);
2298 switch ((insn >> 20) & 0xf) {
2299 case 0x0:
2300 gen_op_iwmmxt_subnb_M0_wRn(rd1);
2301 break;
2302 case 0x1:
2303 gen_op_iwmmxt_subub_M0_wRn(rd1);
2304 break;
2305 case 0x3:
2306 gen_op_iwmmxt_subsb_M0_wRn(rd1);
2307 break;
2308 case 0x4:
2309 gen_op_iwmmxt_subnw_M0_wRn(rd1);
2310 break;
2311 case 0x5:
2312 gen_op_iwmmxt_subuw_M0_wRn(rd1);
2313 break;
2314 case 0x7:
2315 gen_op_iwmmxt_subsw_M0_wRn(rd1);
2316 break;
2317 case 0x8:
2318 gen_op_iwmmxt_subnl_M0_wRn(rd1);
2319 break;
2320 case 0x9:
2321 gen_op_iwmmxt_subul_M0_wRn(rd1);
2322 break;
2323 case 0xb:
2324 gen_op_iwmmxt_subsl_M0_wRn(rd1);
2325 break;
2326 default:
2327 return 1;
2328 }
2329 gen_op_iwmmxt_movq_wRn_M0(wrd);
2330 gen_op_iwmmxt_set_mup();
2331 gen_op_iwmmxt_set_cup();
2332 break;
2333 case 0x01e: case 0x11e: case 0x21e: case 0x31e: /* WSHUFH */
2334 case 0x41e: case 0x51e: case 0x61e: case 0x71e:
2335 case 0x81e: case 0x91e: case 0xa1e: case 0xb1e:
2336 case 0xc1e: case 0xd1e: case 0xe1e: case 0xf1e:
2337 wrd = (insn >> 12) & 0xf;
2338 rd0 = (insn >> 16) & 0xf;
2339 gen_op_iwmmxt_movq_M0_wRn(rd0);
2340 gen_op_movl_T0_im(((insn >> 16) & 0xf0) | (insn & 0x0f));
pbrooke6771372008-03-31 03:49:05 +00002341 gen_helper_iwmmxt_shufh(cpu_M0, cpu_env, cpu_M0, cpu_T[0]);
balrog18c9b562007-04-30 02:02:17 +00002342 gen_op_iwmmxt_movq_wRn_M0(wrd);
2343 gen_op_iwmmxt_set_mup();
2344 gen_op_iwmmxt_set_cup();
2345 break;
2346 case 0x018: case 0x118: case 0x218: case 0x318: /* WADD */
2347 case 0x418: case 0x518: case 0x618: case 0x718:
2348 case 0x818: case 0x918: case 0xa18: case 0xb18:
2349 case 0xc18: case 0xd18: case 0xe18: case 0xf18:
2350 wrd = (insn >> 12) & 0xf;
2351 rd0 = (insn >> 16) & 0xf;
2352 rd1 = (insn >> 0) & 0xf;
2353 gen_op_iwmmxt_movq_M0_wRn(rd0);
2354 switch ((insn >> 20) & 0xf) {
2355 case 0x0:
2356 gen_op_iwmmxt_addnb_M0_wRn(rd1);
2357 break;
2358 case 0x1:
2359 gen_op_iwmmxt_addub_M0_wRn(rd1);
2360 break;
2361 case 0x3:
2362 gen_op_iwmmxt_addsb_M0_wRn(rd1);
2363 break;
2364 case 0x4:
2365 gen_op_iwmmxt_addnw_M0_wRn(rd1);
2366 break;
2367 case 0x5:
2368 gen_op_iwmmxt_adduw_M0_wRn(rd1);
2369 break;
2370 case 0x7:
2371 gen_op_iwmmxt_addsw_M0_wRn(rd1);
2372 break;
2373 case 0x8:
2374 gen_op_iwmmxt_addnl_M0_wRn(rd1);
2375 break;
2376 case 0x9:
2377 gen_op_iwmmxt_addul_M0_wRn(rd1);
2378 break;
2379 case 0xb:
2380 gen_op_iwmmxt_addsl_M0_wRn(rd1);
2381 break;
2382 default:
2383 return 1;
2384 }
2385 gen_op_iwmmxt_movq_wRn_M0(wrd);
2386 gen_op_iwmmxt_set_mup();
2387 gen_op_iwmmxt_set_cup();
2388 break;
2389 case 0x008: case 0x108: case 0x208: case 0x308: /* WPACK */
2390 case 0x408: case 0x508: case 0x608: case 0x708:
2391 case 0x808: case 0x908: case 0xa08: case 0xb08:
2392 case 0xc08: case 0xd08: case 0xe08: case 0xf08:
2393 wrd = (insn >> 12) & 0xf;
2394 rd0 = (insn >> 16) & 0xf;
2395 rd1 = (insn >> 0) & 0xf;
2396 gen_op_iwmmxt_movq_M0_wRn(rd0);
2397 if (!(insn & (1 << 20)))
2398 return 1;
2399 switch ((insn >> 22) & 3) {
2400 case 0:
2401 return 1;
2402 case 1:
2403 if (insn & (1 << 21))
2404 gen_op_iwmmxt_packsw_M0_wRn(rd1);
2405 else
2406 gen_op_iwmmxt_packuw_M0_wRn(rd1);
2407 break;
2408 case 2:
2409 if (insn & (1 << 21))
2410 gen_op_iwmmxt_packsl_M0_wRn(rd1);
2411 else
2412 gen_op_iwmmxt_packul_M0_wRn(rd1);
2413 break;
2414 case 3:
2415 if (insn & (1 << 21))
2416 gen_op_iwmmxt_packsq_M0_wRn(rd1);
2417 else
2418 gen_op_iwmmxt_packuq_M0_wRn(rd1);
2419 break;
2420 }
2421 gen_op_iwmmxt_movq_wRn_M0(wrd);
2422 gen_op_iwmmxt_set_mup();
2423 gen_op_iwmmxt_set_cup();
2424 break;
2425 case 0x201: case 0x203: case 0x205: case 0x207:
2426 case 0x209: case 0x20b: case 0x20d: case 0x20f:
2427 case 0x211: case 0x213: case 0x215: case 0x217:
2428 case 0x219: case 0x21b: case 0x21d: case 0x21f:
2429 wrd = (insn >> 5) & 0xf;
2430 rd0 = (insn >> 12) & 0xf;
2431 rd1 = (insn >> 0) & 0xf;
2432 if (rd0 == 0xf || rd1 == 0xf)
2433 return 1;
2434 gen_op_iwmmxt_movq_M0_wRn(wrd);
2435 switch ((insn >> 16) & 0xf) {
2436 case 0x0: /* TMIA */
pbrookb26eefb2008-03-31 03:44:26 +00002437 gen_movl_T0_reg(s, rd0);
2438 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002439 gen_op_iwmmxt_muladdsl_M0_T0_T1();
2440 break;
2441 case 0x8: /* TMIAPH */
pbrookb26eefb2008-03-31 03:44:26 +00002442 gen_movl_T0_reg(s, rd0);
2443 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002444 gen_op_iwmmxt_muladdsw_M0_T0_T1();
2445 break;
2446 case 0xc: case 0xd: case 0xe: case 0xf: /* TMIAxy */
pbrookb26eefb2008-03-31 03:44:26 +00002447 gen_movl_T1_reg(s, rd0);
balrog18c9b562007-04-30 02:02:17 +00002448 if (insn & (1 << 16))
2449 gen_op_shrl_T1_im(16);
2450 gen_op_movl_T0_T1();
pbrookb26eefb2008-03-31 03:44:26 +00002451 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002452 if (insn & (1 << 17))
2453 gen_op_shrl_T1_im(16);
2454 gen_op_iwmmxt_muladdswl_M0_T0_T1();
2455 break;
2456 default:
2457 return 1;
2458 }
2459 gen_op_iwmmxt_movq_wRn_M0(wrd);
2460 gen_op_iwmmxt_set_mup();
2461 break;
2462 default:
2463 return 1;
2464 }
2465
2466 return 0;
2467}
2468
2469/* Disassemble an XScale DSP instruction. Returns nonzero if an error occured
2470 (ie. an undefined instruction). */
2471static int disas_dsp_insn(CPUState *env, DisasContext *s, uint32_t insn)
2472{
2473 int acc, rd0, rd1, rdhi, rdlo;
2474
2475 if ((insn & 0x0ff00f10) == 0x0e200010) {
2476 /* Multiply with Internal Accumulate Format */
2477 rd0 = (insn >> 12) & 0xf;
2478 rd1 = insn & 0xf;
2479 acc = (insn >> 5) & 7;
2480
2481 if (acc != 0)
2482 return 1;
2483
2484 switch ((insn >> 16) & 0xf) {
2485 case 0x0: /* MIA */
pbrookb26eefb2008-03-31 03:44:26 +00002486 gen_movl_T0_reg(s, rd0);
2487 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002488 gen_op_iwmmxt_muladdsl_M0_T0_T1();
2489 break;
2490 case 0x8: /* MIAPH */
pbrookb26eefb2008-03-31 03:44:26 +00002491 gen_movl_T0_reg(s, rd0);
2492 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002493 gen_op_iwmmxt_muladdsw_M0_T0_T1();
2494 break;
2495 case 0xc: /* MIABB */
2496 case 0xd: /* MIABT */
2497 case 0xe: /* MIATB */
2498 case 0xf: /* MIATT */
pbrookb26eefb2008-03-31 03:44:26 +00002499 gen_movl_T1_reg(s, rd0);
balrog18c9b562007-04-30 02:02:17 +00002500 if (insn & (1 << 16))
2501 gen_op_shrl_T1_im(16);
2502 gen_op_movl_T0_T1();
pbrookb26eefb2008-03-31 03:44:26 +00002503 gen_movl_T1_reg(s, rd1);
balrog18c9b562007-04-30 02:02:17 +00002504 if (insn & (1 << 17))
2505 gen_op_shrl_T1_im(16);
2506 gen_op_iwmmxt_muladdswl_M0_T0_T1();
2507 break;
2508 default:
2509 return 1;
2510 }
2511
2512 gen_op_iwmmxt_movq_wRn_M0(acc);
2513 return 0;
2514 }
2515
2516 if ((insn & 0x0fe00ff8) == 0x0c400000) {
2517 /* Internal Accumulator Access Format */
2518 rdhi = (insn >> 16) & 0xf;
2519 rdlo = (insn >> 12) & 0xf;
2520 acc = insn & 7;
2521
2522 if (acc != 0)
2523 return 1;
2524
2525 if (insn & ARM_CP_RW_BIT) { /* MRA */
pbrooke6771372008-03-31 03:49:05 +00002526 gen_iwmmxt_movl_T0_T1_wRn(acc);
pbrookb26eefb2008-03-31 03:44:26 +00002527 gen_movl_reg_T0(s, rdlo);
balrog18c9b562007-04-30 02:02:17 +00002528 gen_op_movl_T0_im((1 << (40 - 32)) - 1);
2529 gen_op_andl_T0_T1();
pbrookb26eefb2008-03-31 03:44:26 +00002530 gen_movl_reg_T0(s, rdhi);
balrog18c9b562007-04-30 02:02:17 +00002531 } else { /* MAR */
pbrookb26eefb2008-03-31 03:44:26 +00002532 gen_movl_T0_reg(s, rdlo);
2533 gen_movl_T1_reg(s, rdhi);
pbrooke6771372008-03-31 03:49:05 +00002534 gen_iwmmxt_movl_wRn_T0_T1(acc);
balrog18c9b562007-04-30 02:02:17 +00002535 }
2536 return 0;
2537 }
2538
2539 return 1;
2540}
2541
balrogc1713132007-04-30 01:26:42 +00002542/* Disassemble system coprocessor instruction. Return nonzero if
2543 instruction is not defined. */
2544static int disas_cp_insn(CPUState *env, DisasContext *s, uint32_t insn)
2545{
pbrook8984bd22008-03-31 03:47:48 +00002546 TCGv tmp;
balrogc1713132007-04-30 01:26:42 +00002547 uint32_t rd = (insn >> 12) & 0xf;
2548 uint32_t cp = (insn >> 8) & 0xf;
2549 if (IS_USER(s)) {
2550 return 1;
2551 }
2552
balrog18c9b562007-04-30 02:02:17 +00002553 if (insn & ARM_CP_RW_BIT) {
balrogc1713132007-04-30 01:26:42 +00002554 if (!env->cp[cp].cp_read)
2555 return 1;
pbrook8984bd22008-03-31 03:47:48 +00002556 gen_set_pc_im(s->pc);
2557 tmp = new_tmp();
2558 gen_helper_get_cp(tmp, cpu_env, tcg_const_i32(insn));
2559 store_reg(s, rd, tmp);
balrogc1713132007-04-30 01:26:42 +00002560 } else {
2561 if (!env->cp[cp].cp_write)
2562 return 1;
pbrook8984bd22008-03-31 03:47:48 +00002563 gen_set_pc_im(s->pc);
2564 tmp = load_reg(s, rd);
2565 gen_helper_set_cp(cpu_env, tcg_const_i32(insn), tmp);
balroga60de942008-06-02 02:29:26 +00002566 dead_tmp(tmp);
balrogc1713132007-04-30 01:26:42 +00002567 }
2568 return 0;
2569}
2570
pbrook9ee6e8b2007-11-11 00:04:49 +00002571static int cp15_user_ok(uint32_t insn)
2572{
2573 int cpn = (insn >> 16) & 0xf;
2574 int cpm = insn & 0xf;
2575 int op = ((insn >> 5) & 7) | ((insn >> 18) & 0x38);
2576
2577 if (cpn == 13 && cpm == 0) {
2578 /* TLS register. */
2579 if (op == 2 || (op == 3 && (insn & ARM_CP_RW_BIT)))
2580 return 1;
2581 }
2582 if (cpn == 7) {
2583 /* ISB, DSB, DMB. */
2584 if ((cpm == 5 && op == 4)
2585 || (cpm == 10 && (op == 4 || op == 5)))
2586 return 1;
2587 }
2588 return 0;
2589}
2590
bellardb5ff1b32005-11-26 10:38:39 +00002591/* Disassemble system coprocessor (cp15) instruction. Return nonzero if
2592 instruction is not defined. */
balroga90b7312007-05-01 01:28:01 +00002593static int disas_cp15_insn(CPUState *env, DisasContext *s, uint32_t insn)
bellardb5ff1b32005-11-26 10:38:39 +00002594{
2595 uint32_t rd;
pbrook8984bd22008-03-31 03:47:48 +00002596 TCGv tmp;
bellardb5ff1b32005-11-26 10:38:39 +00002597
pbrook9ee6e8b2007-11-11 00:04:49 +00002598 /* M profile cores use memory mapped registers instead of cp15. */
2599 if (arm_feature(env, ARM_FEATURE_M))
2600 return 1;
2601
2602 if ((insn & (1 << 25)) == 0) {
2603 if (insn & (1 << 20)) {
2604 /* mrrc */
2605 return 1;
2606 }
2607 /* mcrr. Used for block cache operations, so implement as no-op. */
2608 return 0;
2609 }
2610 if ((insn & (1 << 4)) == 0) {
2611 /* cdp */
2612 return 1;
2613 }
2614 if (IS_USER(s) && !cp15_user_ok(insn)) {
bellardb5ff1b32005-11-26 10:38:39 +00002615 return 1;
2616 }
bellard9332f9d2005-11-26 10:46:39 +00002617 if ((insn & 0x0fff0fff) == 0x0e070f90
2618 || (insn & 0x0fff0fff) == 0x0e070f58) {
2619 /* Wait for interrupt. */
pbrook8984bd22008-03-31 03:47:48 +00002620 gen_set_pc_im(s->pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00002621 s->is_jmp = DISAS_WFI;
bellard9332f9d2005-11-26 10:46:39 +00002622 return 0;
2623 }
bellardb5ff1b32005-11-26 10:38:39 +00002624 rd = (insn >> 12) & 0xf;
balrog18c9b562007-04-30 02:02:17 +00002625 if (insn & ARM_CP_RW_BIT) {
pbrook8984bd22008-03-31 03:47:48 +00002626 tmp = new_tmp();
2627 gen_helper_get_cp15(tmp, cpu_env, tcg_const_i32(insn));
bellardb5ff1b32005-11-26 10:38:39 +00002628 /* If the destination register is r15 then sets condition codes. */
2629 if (rd != 15)
pbrook8984bd22008-03-31 03:47:48 +00002630 store_reg(s, rd, tmp);
2631 else
2632 dead_tmp(tmp);
bellardb5ff1b32005-11-26 10:38:39 +00002633 } else {
pbrook8984bd22008-03-31 03:47:48 +00002634 tmp = load_reg(s, rd);
2635 gen_helper_set_cp15(cpu_env, tcg_const_i32(insn), tmp);
2636 dead_tmp(tmp);
balroga90b7312007-05-01 01:28:01 +00002637 /* Normally we would always end the TB here, but Linux
2638 * arch/arm/mach-pxa/sleep.S expects two instructions following
2639 * an MMU enable to execute from cache. Imitate this behaviour. */
2640 if (!arm_feature(env, ARM_FEATURE_XSCALE) ||
2641 (insn & 0x0fff0fff) != 0x0e010f10)
2642 gen_lookup_tb(s);
bellardb5ff1b32005-11-26 10:38:39 +00002643 }
bellardb5ff1b32005-11-26 10:38:39 +00002644 return 0;
2645}
2646
pbrook9ee6e8b2007-11-11 00:04:49 +00002647#define VFP_REG_SHR(x, n) (((n) > 0) ? (x) >> (n) : (x) << -(n))
2648#define VFP_SREG(insn, bigbit, smallbit) \
2649 ((VFP_REG_SHR(insn, bigbit - 1) & 0x1e) | (((insn) >> (smallbit)) & 1))
2650#define VFP_DREG(reg, insn, bigbit, smallbit) do { \
2651 if (arm_feature(env, ARM_FEATURE_VFP3)) { \
2652 reg = (((insn) >> (bigbit)) & 0x0f) \
2653 | (((insn) >> ((smallbit) - 4)) & 0x10); \
2654 } else { \
2655 if (insn & (1 << (smallbit))) \
2656 return 1; \
2657 reg = ((insn) >> (bigbit)) & 0x0f; \
2658 }} while (0)
2659
2660#define VFP_SREG_D(insn) VFP_SREG(insn, 12, 22)
2661#define VFP_DREG_D(reg, insn) VFP_DREG(reg, insn, 12, 22)
2662#define VFP_SREG_N(insn) VFP_SREG(insn, 16, 7)
2663#define VFP_DREG_N(reg, insn) VFP_DREG(reg, insn, 16, 7)
2664#define VFP_SREG_M(insn) VFP_SREG(insn, 0, 5)
2665#define VFP_DREG_M(reg, insn) VFP_DREG(reg, insn, 0, 5)
2666
pbrook4373f3c2008-03-31 03:47:19 +00002667/* Move between integer and VFP cores. */
2668static TCGv gen_vfp_mrs(void)
2669{
2670 TCGv tmp = new_tmp();
2671 tcg_gen_mov_i32(tmp, cpu_F0s);
2672 return tmp;
2673}
2674
2675static void gen_vfp_msr(TCGv tmp)
2676{
2677 tcg_gen_mov_i32(cpu_F0s, tmp);
2678 dead_tmp(tmp);
2679}
2680
pbrook9ee6e8b2007-11-11 00:04:49 +00002681static inline int
2682vfp_enabled(CPUState * env)
2683{
2684 return ((env->vfp.xregs[ARM_VFP_FPEXC] & (1 << 30)) != 0);
2685}
2686
pbrookad694712008-03-31 03:48:30 +00002687static void gen_neon_dup_u8(TCGv var, int shift)
2688{
2689 TCGv tmp = new_tmp();
2690 if (shift)
2691 tcg_gen_shri_i32(var, var, shift);
pbrook86831432008-05-11 12:22:01 +00002692 tcg_gen_ext8u_i32(var, var);
pbrookad694712008-03-31 03:48:30 +00002693 tcg_gen_shli_i32(tmp, var, 8);
2694 tcg_gen_or_i32(var, var, tmp);
2695 tcg_gen_shli_i32(tmp, var, 16);
2696 tcg_gen_or_i32(var, var, tmp);
2697 dead_tmp(tmp);
2698}
2699
2700static void gen_neon_dup_low16(TCGv var)
2701{
2702 TCGv tmp = new_tmp();
pbrook86831432008-05-11 12:22:01 +00002703 tcg_gen_ext16u_i32(var, var);
pbrookad694712008-03-31 03:48:30 +00002704 tcg_gen_shli_i32(tmp, var, 16);
2705 tcg_gen_or_i32(var, var, tmp);
2706 dead_tmp(tmp);
2707}
2708
2709static void gen_neon_dup_high16(TCGv var)
2710{
2711 TCGv tmp = new_tmp();
2712 tcg_gen_andi_i32(var, var, 0xffff0000);
2713 tcg_gen_shri_i32(tmp, var, 16);
2714 tcg_gen_or_i32(var, var, tmp);
2715 dead_tmp(tmp);
2716}
2717
bellardb7bcbe92005-02-22 19:27:29 +00002718/* Disassemble a VFP instruction. Returns nonzero if an error occured
2719 (ie. an undefined instruction). */
2720static int disas_vfp_insn(CPUState * env, DisasContext *s, uint32_t insn)
2721{
2722 uint32_t rd, rn, rm, op, i, n, offset, delta_d, delta_m, bank_mask;
2723 int dp, veclen;
pbrook4373f3c2008-03-31 03:47:19 +00002724 TCGv tmp;
pbrookad694712008-03-31 03:48:30 +00002725 TCGv tmp2;
bellardb7bcbe92005-02-22 19:27:29 +00002726
pbrook40f137e2006-02-20 00:33:36 +00002727 if (!arm_feature(env, ARM_FEATURE_VFP))
2728 return 1;
2729
pbrook9ee6e8b2007-11-11 00:04:49 +00002730 if (!vfp_enabled(env)) {
2731 /* VFP disabled. Only allow fmxr/fmrx to/from some control regs. */
pbrook40f137e2006-02-20 00:33:36 +00002732 if ((insn & 0x0fe00fff) != 0x0ee00a10)
2733 return 1;
2734 rn = (insn >> 16) & 0xf;
pbrook9ee6e8b2007-11-11 00:04:49 +00002735 if (rn != ARM_VFP_FPSID && rn != ARM_VFP_FPEXC
2736 && rn != ARM_VFP_MVFR1 && rn != ARM_VFP_MVFR0)
pbrook40f137e2006-02-20 00:33:36 +00002737 return 1;
2738 }
bellardb7bcbe92005-02-22 19:27:29 +00002739 dp = ((insn & 0xf00) == 0xb00);
2740 switch ((insn >> 24) & 0xf) {
2741 case 0xe:
2742 if (insn & (1 << 4)) {
2743 /* single register transfer */
bellardb7bcbe92005-02-22 19:27:29 +00002744 rd = (insn >> 12) & 0xf;
2745 if (dp) {
pbrook9ee6e8b2007-11-11 00:04:49 +00002746 int size;
2747 int pass;
2748
2749 VFP_DREG_N(rn, insn);
2750 if (insn & 0xf)
bellardb7bcbe92005-02-22 19:27:29 +00002751 return 1;
pbrook9ee6e8b2007-11-11 00:04:49 +00002752 if (insn & 0x00c00060
2753 && !arm_feature(env, ARM_FEATURE_NEON))
2754 return 1;
2755
2756 pass = (insn >> 21) & 1;
2757 if (insn & (1 << 22)) {
2758 size = 0;
2759 offset = ((insn >> 5) & 3) * 8;
2760 } else if (insn & (1 << 5)) {
2761 size = 1;
2762 offset = (insn & (1 << 6)) ? 16 : 0;
2763 } else {
2764 size = 2;
2765 offset = 0;
2766 }
balrog18c9b562007-04-30 02:02:17 +00002767 if (insn & ARM_CP_RW_BIT) {
bellardb7bcbe92005-02-22 19:27:29 +00002768 /* vfp->arm */
pbrookad694712008-03-31 03:48:30 +00002769 tmp = neon_load_reg(rn, pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00002770 switch (size) {
2771 case 0:
pbrook9ee6e8b2007-11-11 00:04:49 +00002772 if (offset)
pbrookad694712008-03-31 03:48:30 +00002773 tcg_gen_shri_i32(tmp, tmp, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00002774 if (insn & (1 << 23))
pbrookad694712008-03-31 03:48:30 +00002775 gen_uxtb(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002776 else
pbrookad694712008-03-31 03:48:30 +00002777 gen_sxtb(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002778 break;
2779 case 1:
pbrook9ee6e8b2007-11-11 00:04:49 +00002780 if (insn & (1 << 23)) {
2781 if (offset) {
pbrookad694712008-03-31 03:48:30 +00002782 tcg_gen_shri_i32(tmp, tmp, 16);
pbrook9ee6e8b2007-11-11 00:04:49 +00002783 } else {
pbrookad694712008-03-31 03:48:30 +00002784 gen_uxth(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002785 }
2786 } else {
2787 if (offset) {
pbrookad694712008-03-31 03:48:30 +00002788 tcg_gen_sari_i32(tmp, tmp, 16);
pbrook9ee6e8b2007-11-11 00:04:49 +00002789 } else {
pbrookad694712008-03-31 03:48:30 +00002790 gen_sxth(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002791 }
2792 }
2793 break;
2794 case 2:
pbrook9ee6e8b2007-11-11 00:04:49 +00002795 break;
2796 }
pbrookad694712008-03-31 03:48:30 +00002797 store_reg(s, rd, tmp);
bellardb7bcbe92005-02-22 19:27:29 +00002798 } else {
2799 /* arm->vfp */
pbrookad694712008-03-31 03:48:30 +00002800 tmp = load_reg(s, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00002801 if (insn & (1 << 23)) {
2802 /* VDUP */
2803 if (size == 0) {
pbrookad694712008-03-31 03:48:30 +00002804 gen_neon_dup_u8(tmp, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00002805 } else if (size == 1) {
pbrookad694712008-03-31 03:48:30 +00002806 gen_neon_dup_low16(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002807 }
pbrookcbbccff2009-03-17 12:19:58 +00002808 for (n = 0; n <= pass * 2; n++) {
2809 tmp2 = new_tmp();
2810 tcg_gen_mov_i32(tmp2, tmp);
2811 neon_store_reg(rn, n, tmp2);
2812 }
2813 neon_store_reg(rn, n, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002814 } else {
2815 /* VMOV */
2816 switch (size) {
2817 case 0:
pbrookad694712008-03-31 03:48:30 +00002818 tmp2 = neon_load_reg(rn, pass);
2819 gen_bfi(tmp, tmp2, tmp, offset, 0xff);
2820 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00002821 break;
2822 case 1:
pbrookad694712008-03-31 03:48:30 +00002823 tmp2 = neon_load_reg(rn, pass);
2824 gen_bfi(tmp, tmp2, tmp, offset, 0xffff);
2825 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00002826 break;
2827 case 2:
pbrook9ee6e8b2007-11-11 00:04:49 +00002828 break;
2829 }
pbrookad694712008-03-31 03:48:30 +00002830 neon_store_reg(rn, pass, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00002831 }
bellardb7bcbe92005-02-22 19:27:29 +00002832 }
pbrook9ee6e8b2007-11-11 00:04:49 +00002833 } else { /* !dp */
2834 if ((insn & 0x6f) != 0x00)
2835 return 1;
2836 rn = VFP_SREG_N(insn);
balrog18c9b562007-04-30 02:02:17 +00002837 if (insn & ARM_CP_RW_BIT) {
bellardb7bcbe92005-02-22 19:27:29 +00002838 /* vfp->arm */
2839 if (insn & (1 << 21)) {
2840 /* system register */
pbrook40f137e2006-02-20 00:33:36 +00002841 rn >>= 1;
pbrook9ee6e8b2007-11-11 00:04:49 +00002842
bellardb7bcbe92005-02-22 19:27:29 +00002843 switch (rn) {
pbrook40f137e2006-02-20 00:33:36 +00002844 case ARM_VFP_FPSID:
pbrook4373f3c2008-03-31 03:47:19 +00002845 /* VFP2 allows access to FSID from userspace.
pbrook9ee6e8b2007-11-11 00:04:49 +00002846 VFP3 restricts all id registers to privileged
2847 accesses. */
2848 if (IS_USER(s)
2849 && arm_feature(env, ARM_FEATURE_VFP3))
2850 return 1;
pbrook4373f3c2008-03-31 03:47:19 +00002851 tmp = load_cpu_field(vfp.xregs[rn]);
pbrook9ee6e8b2007-11-11 00:04:49 +00002852 break;
pbrook40f137e2006-02-20 00:33:36 +00002853 case ARM_VFP_FPEXC:
pbrook9ee6e8b2007-11-11 00:04:49 +00002854 if (IS_USER(s))
2855 return 1;
pbrook4373f3c2008-03-31 03:47:19 +00002856 tmp = load_cpu_field(vfp.xregs[rn]);
pbrook9ee6e8b2007-11-11 00:04:49 +00002857 break;
pbrook40f137e2006-02-20 00:33:36 +00002858 case ARM_VFP_FPINST:
2859 case ARM_VFP_FPINST2:
pbrook9ee6e8b2007-11-11 00:04:49 +00002860 /* Not present in VFP3. */
2861 if (IS_USER(s)
2862 || arm_feature(env, ARM_FEATURE_VFP3))
2863 return 1;
pbrook4373f3c2008-03-31 03:47:19 +00002864 tmp = load_cpu_field(vfp.xregs[rn]);
bellardb7bcbe92005-02-22 19:27:29 +00002865 break;
pbrook40f137e2006-02-20 00:33:36 +00002866 case ARM_VFP_FPSCR:
balrog601d70b2008-04-20 01:03:45 +00002867 if (rd == 15) {
pbrook4373f3c2008-03-31 03:47:19 +00002868 tmp = load_cpu_field(vfp.xregs[ARM_VFP_FPSCR]);
2869 tcg_gen_andi_i32(tmp, tmp, 0xf0000000);
2870 } else {
2871 tmp = new_tmp();
2872 gen_helper_vfp_get_fpscr(tmp, cpu_env);
2873 }
bellardb7bcbe92005-02-22 19:27:29 +00002874 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00002875 case ARM_VFP_MVFR0:
2876 case ARM_VFP_MVFR1:
2877 if (IS_USER(s)
2878 || !arm_feature(env, ARM_FEATURE_VFP3))
2879 return 1;
pbrook4373f3c2008-03-31 03:47:19 +00002880 tmp = load_cpu_field(vfp.xregs[rn]);
pbrook9ee6e8b2007-11-11 00:04:49 +00002881 break;
bellardb7bcbe92005-02-22 19:27:29 +00002882 default:
2883 return 1;
2884 }
2885 } else {
2886 gen_mov_F0_vreg(0, rn);
pbrook4373f3c2008-03-31 03:47:19 +00002887 tmp = gen_vfp_mrs();
bellardb7bcbe92005-02-22 19:27:29 +00002888 }
2889 if (rd == 15) {
bellardb5ff1b32005-11-26 10:38:39 +00002890 /* Set the 4 flag bits in the CPSR. */
pbrook4373f3c2008-03-31 03:47:19 +00002891 gen_set_nzcv(tmp);
2892 dead_tmp(tmp);
2893 } else {
2894 store_reg(s, rd, tmp);
2895 }
bellardb7bcbe92005-02-22 19:27:29 +00002896 } else {
2897 /* arm->vfp */
pbrook4373f3c2008-03-31 03:47:19 +00002898 tmp = load_reg(s, rd);
bellardb7bcbe92005-02-22 19:27:29 +00002899 if (insn & (1 << 21)) {
pbrook40f137e2006-02-20 00:33:36 +00002900 rn >>= 1;
bellardb7bcbe92005-02-22 19:27:29 +00002901 /* system register */
2902 switch (rn) {
pbrook40f137e2006-02-20 00:33:36 +00002903 case ARM_VFP_FPSID:
pbrook9ee6e8b2007-11-11 00:04:49 +00002904 case ARM_VFP_MVFR0:
2905 case ARM_VFP_MVFR1:
bellardb7bcbe92005-02-22 19:27:29 +00002906 /* Writes are ignored. */
2907 break;
pbrook40f137e2006-02-20 00:33:36 +00002908 case ARM_VFP_FPSCR:
pbrook4373f3c2008-03-31 03:47:19 +00002909 gen_helper_vfp_set_fpscr(cpu_env, tmp);
2910 dead_tmp(tmp);
bellardb5ff1b32005-11-26 10:38:39 +00002911 gen_lookup_tb(s);
bellardb7bcbe92005-02-22 19:27:29 +00002912 break;
pbrook40f137e2006-02-20 00:33:36 +00002913 case ARM_VFP_FPEXC:
pbrook9ee6e8b2007-11-11 00:04:49 +00002914 if (IS_USER(s))
2915 return 1;
pbrook4373f3c2008-03-31 03:47:19 +00002916 store_cpu_field(tmp, vfp.xregs[rn]);
pbrook40f137e2006-02-20 00:33:36 +00002917 gen_lookup_tb(s);
2918 break;
2919 case ARM_VFP_FPINST:
2920 case ARM_VFP_FPINST2:
pbrook4373f3c2008-03-31 03:47:19 +00002921 store_cpu_field(tmp, vfp.xregs[rn]);
pbrook40f137e2006-02-20 00:33:36 +00002922 break;
bellardb7bcbe92005-02-22 19:27:29 +00002923 default:
2924 return 1;
2925 }
2926 } else {
pbrook4373f3c2008-03-31 03:47:19 +00002927 gen_vfp_msr(tmp);
bellardb7bcbe92005-02-22 19:27:29 +00002928 gen_mov_vreg_F0(0, rn);
2929 }
2930 }
2931 }
2932 } else {
2933 /* data processing */
2934 /* The opcode is in bits 23, 21, 20 and 6. */
2935 op = ((insn >> 20) & 8) | ((insn >> 19) & 6) | ((insn >> 6) & 1);
2936 if (dp) {
2937 if (op == 15) {
2938 /* rn is opcode */
2939 rn = ((insn >> 15) & 0x1e) | ((insn >> 7) & 1);
2940 } else {
2941 /* rn is register number */
pbrook9ee6e8b2007-11-11 00:04:49 +00002942 VFP_DREG_N(rn, insn);
bellardb7bcbe92005-02-22 19:27:29 +00002943 }
2944
2945 if (op == 15 && (rn == 15 || rn > 17)) {
2946 /* Integer or single precision destination. */
pbrook9ee6e8b2007-11-11 00:04:49 +00002947 rd = VFP_SREG_D(insn);
bellardb7bcbe92005-02-22 19:27:29 +00002948 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +00002949 VFP_DREG_D(rd, insn);
bellardb7bcbe92005-02-22 19:27:29 +00002950 }
2951
2952 if (op == 15 && (rn == 16 || rn == 17)) {
2953 /* Integer source. */
2954 rm = ((insn << 1) & 0x1e) | ((insn >> 5) & 1);
2955 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +00002956 VFP_DREG_M(rm, insn);
bellardb7bcbe92005-02-22 19:27:29 +00002957 }
2958 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +00002959 rn = VFP_SREG_N(insn);
bellardb7bcbe92005-02-22 19:27:29 +00002960 if (op == 15 && rn == 15) {
2961 /* Double precision destination. */
pbrook9ee6e8b2007-11-11 00:04:49 +00002962 VFP_DREG_D(rd, insn);
2963 } else {
2964 rd = VFP_SREG_D(insn);
2965 }
2966 rm = VFP_SREG_M(insn);
bellardb7bcbe92005-02-22 19:27:29 +00002967 }
2968
2969 veclen = env->vfp.vec_len;
2970 if (op == 15 && rn > 3)
2971 veclen = 0;
2972
2973 /* Shut up compiler warnings. */
2974 delta_m = 0;
2975 delta_d = 0;
2976 bank_mask = 0;
ths3b46e622007-09-17 08:09:54 +00002977
bellardb7bcbe92005-02-22 19:27:29 +00002978 if (veclen > 0) {
2979 if (dp)
2980 bank_mask = 0xc;
2981 else
2982 bank_mask = 0x18;
2983
2984 /* Figure out what type of vector operation this is. */
2985 if ((rd & bank_mask) == 0) {
2986 /* scalar */
2987 veclen = 0;
2988 } else {
2989 if (dp)
2990 delta_d = (env->vfp.vec_stride >> 1) + 1;
2991 else
2992 delta_d = env->vfp.vec_stride + 1;
2993
2994 if ((rm & bank_mask) == 0) {
2995 /* mixed scalar/vector */
2996 delta_m = 0;
2997 } else {
2998 /* vector */
2999 delta_m = delta_d;
3000 }
3001 }
3002 }
3003
3004 /* Load the initial operands. */
3005 if (op == 15) {
3006 switch (rn) {
3007 case 16:
3008 case 17:
3009 /* Integer source */
3010 gen_mov_F0_vreg(0, rm);
3011 break;
3012 case 8:
3013 case 9:
3014 /* Compare */
3015 gen_mov_F0_vreg(dp, rd);
3016 gen_mov_F1_vreg(dp, rm);
3017 break;
3018 case 10:
3019 case 11:
3020 /* Compare with zero */
3021 gen_mov_F0_vreg(dp, rd);
3022 gen_vfp_F1_ld0(dp);
3023 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00003024 case 20:
3025 case 21:
3026 case 22:
3027 case 23:
pbrook644ad802008-12-19 13:02:08 +00003028 case 28:
3029 case 29:
3030 case 30:
3031 case 31:
pbrook9ee6e8b2007-11-11 00:04:49 +00003032 /* Source and destination the same. */
3033 gen_mov_F0_vreg(dp, rd);
3034 break;
bellardb7bcbe92005-02-22 19:27:29 +00003035 default:
3036 /* One source operand. */
3037 gen_mov_F0_vreg(dp, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003038 break;
bellardb7bcbe92005-02-22 19:27:29 +00003039 }
3040 } else {
3041 /* Two source operands. */
3042 gen_mov_F0_vreg(dp, rn);
3043 gen_mov_F1_vreg(dp, rm);
3044 }
3045
3046 for (;;) {
3047 /* Perform the calculation. */
3048 switch (op) {
3049 case 0: /* mac: fd + (fn * fm) */
3050 gen_vfp_mul(dp);
3051 gen_mov_F1_vreg(dp, rd);
3052 gen_vfp_add(dp);
3053 break;
3054 case 1: /* nmac: fd - (fn * fm) */
3055 gen_vfp_mul(dp);
3056 gen_vfp_neg(dp);
3057 gen_mov_F1_vreg(dp, rd);
3058 gen_vfp_add(dp);
3059 break;
3060 case 2: /* msc: -fd + (fn * fm) */
3061 gen_vfp_mul(dp);
3062 gen_mov_F1_vreg(dp, rd);
3063 gen_vfp_sub(dp);
3064 break;
3065 case 3: /* nmsc: -fd - (fn * fm) */
3066 gen_vfp_mul(dp);
bellardb7bcbe92005-02-22 19:27:29 +00003067 gen_vfp_neg(dp);
pbrookc9fb5312008-11-03 19:09:29 +00003068 gen_mov_F1_vreg(dp, rd);
3069 gen_vfp_sub(dp);
bellardb7bcbe92005-02-22 19:27:29 +00003070 break;
3071 case 4: /* mul: fn * fm */
3072 gen_vfp_mul(dp);
3073 break;
3074 case 5: /* nmul: -(fn * fm) */
3075 gen_vfp_mul(dp);
3076 gen_vfp_neg(dp);
3077 break;
3078 case 6: /* add: fn + fm */
3079 gen_vfp_add(dp);
3080 break;
3081 case 7: /* sub: fn - fm */
3082 gen_vfp_sub(dp);
3083 break;
3084 case 8: /* div: fn / fm */
3085 gen_vfp_div(dp);
3086 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00003087 case 14: /* fconst */
3088 if (!arm_feature(env, ARM_FEATURE_VFP3))
3089 return 1;
3090
3091 n = (insn << 12) & 0x80000000;
3092 i = ((insn >> 12) & 0x70) | (insn & 0xf);
3093 if (dp) {
3094 if (i & 0x40)
3095 i |= 0x3f80;
3096 else
3097 i |= 0x4000;
3098 n |= i << 16;
pbrook4373f3c2008-03-31 03:47:19 +00003099 tcg_gen_movi_i64(cpu_F0d, ((uint64_t)n) << 32);
pbrook9ee6e8b2007-11-11 00:04:49 +00003100 } else {
3101 if (i & 0x40)
3102 i |= 0x780;
3103 else
3104 i |= 0x800;
3105 n |= i << 19;
balrog5b340b52008-04-14 02:19:57 +00003106 tcg_gen_movi_i32(cpu_F0s, n);
pbrook9ee6e8b2007-11-11 00:04:49 +00003107 }
pbrook9ee6e8b2007-11-11 00:04:49 +00003108 break;
bellardb7bcbe92005-02-22 19:27:29 +00003109 case 15: /* extension space */
3110 switch (rn) {
3111 case 0: /* cpy */
3112 /* no-op */
3113 break;
3114 case 1: /* abs */
3115 gen_vfp_abs(dp);
3116 break;
3117 case 2: /* neg */
3118 gen_vfp_neg(dp);
3119 break;
3120 case 3: /* sqrt */
3121 gen_vfp_sqrt(dp);
3122 break;
3123 case 8: /* cmp */
3124 gen_vfp_cmp(dp);
3125 break;
3126 case 9: /* cmpe */
3127 gen_vfp_cmpe(dp);
3128 break;
3129 case 10: /* cmpz */
3130 gen_vfp_cmp(dp);
3131 break;
3132 case 11: /* cmpez */
3133 gen_vfp_F1_ld0(dp);
3134 gen_vfp_cmpe(dp);
3135 break;
3136 case 15: /* single<->double conversion */
3137 if (dp)
pbrook4373f3c2008-03-31 03:47:19 +00003138 gen_helper_vfp_fcvtsd(cpu_F0s, cpu_F0d, cpu_env);
bellardb7bcbe92005-02-22 19:27:29 +00003139 else
pbrook4373f3c2008-03-31 03:47:19 +00003140 gen_helper_vfp_fcvtds(cpu_F0d, cpu_F0s, cpu_env);
bellardb7bcbe92005-02-22 19:27:29 +00003141 break;
3142 case 16: /* fuito */
3143 gen_vfp_uito(dp);
3144 break;
3145 case 17: /* fsito */
3146 gen_vfp_sito(dp);
3147 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00003148 case 20: /* fshto */
3149 if (!arm_feature(env, ARM_FEATURE_VFP3))
3150 return 1;
pbrook644ad802008-12-19 13:02:08 +00003151 gen_vfp_shto(dp, 16 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003152 break;
3153 case 21: /* fslto */
3154 if (!arm_feature(env, ARM_FEATURE_VFP3))
3155 return 1;
pbrook644ad802008-12-19 13:02:08 +00003156 gen_vfp_slto(dp, 32 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003157 break;
3158 case 22: /* fuhto */
3159 if (!arm_feature(env, ARM_FEATURE_VFP3))
3160 return 1;
pbrook644ad802008-12-19 13:02:08 +00003161 gen_vfp_uhto(dp, 16 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003162 break;
3163 case 23: /* fulto */
3164 if (!arm_feature(env, ARM_FEATURE_VFP3))
3165 return 1;
pbrook644ad802008-12-19 13:02:08 +00003166 gen_vfp_ulto(dp, 32 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003167 break;
bellardb7bcbe92005-02-22 19:27:29 +00003168 case 24: /* ftoui */
3169 gen_vfp_toui(dp);
3170 break;
3171 case 25: /* ftouiz */
3172 gen_vfp_touiz(dp);
3173 break;
3174 case 26: /* ftosi */
3175 gen_vfp_tosi(dp);
3176 break;
3177 case 27: /* ftosiz */
3178 gen_vfp_tosiz(dp);
3179 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00003180 case 28: /* ftosh */
3181 if (!arm_feature(env, ARM_FEATURE_VFP3))
3182 return 1;
pbrook644ad802008-12-19 13:02:08 +00003183 gen_vfp_tosh(dp, 16 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003184 break;
3185 case 29: /* ftosl */
3186 if (!arm_feature(env, ARM_FEATURE_VFP3))
3187 return 1;
pbrook644ad802008-12-19 13:02:08 +00003188 gen_vfp_tosl(dp, 32 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003189 break;
3190 case 30: /* ftouh */
3191 if (!arm_feature(env, ARM_FEATURE_VFP3))
3192 return 1;
pbrook644ad802008-12-19 13:02:08 +00003193 gen_vfp_touh(dp, 16 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003194 break;
3195 case 31: /* ftoul */
3196 if (!arm_feature(env, ARM_FEATURE_VFP3))
3197 return 1;
pbrook644ad802008-12-19 13:02:08 +00003198 gen_vfp_toul(dp, 32 - rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00003199 break;
bellardb7bcbe92005-02-22 19:27:29 +00003200 default: /* undefined */
3201 printf ("rn:%d\n", rn);
3202 return 1;
3203 }
3204 break;
3205 default: /* undefined */
3206 printf ("op:%d\n", op);
3207 return 1;
3208 }
3209
3210 /* Write back the result. */
3211 if (op == 15 && (rn >= 8 && rn <= 11))
3212 ; /* Comparison, do nothing. */
3213 else if (op == 15 && rn > 17)
3214 /* Integer result. */
3215 gen_mov_vreg_F0(0, rd);
3216 else if (op == 15 && rn == 15)
3217 /* conversion */
3218 gen_mov_vreg_F0(!dp, rd);
3219 else
3220 gen_mov_vreg_F0(dp, rd);
3221
3222 /* break out of the loop if we have finished */
3223 if (veclen == 0)
3224 break;
3225
3226 if (op == 15 && delta_m == 0) {
3227 /* single source one-many */
3228 while (veclen--) {
3229 rd = ((rd + delta_d) & (bank_mask - 1))
3230 | (rd & bank_mask);
3231 gen_mov_vreg_F0(dp, rd);
3232 }
3233 break;
3234 }
3235 /* Setup the next operands. */
3236 veclen--;
3237 rd = ((rd + delta_d) & (bank_mask - 1))
3238 | (rd & bank_mask);
3239
3240 if (op == 15) {
3241 /* One source operand. */
3242 rm = ((rm + delta_m) & (bank_mask - 1))
3243 | (rm & bank_mask);
3244 gen_mov_F0_vreg(dp, rm);
3245 } else {
3246 /* Two source operands. */
3247 rn = ((rn + delta_d) & (bank_mask - 1))
3248 | (rn & bank_mask);
3249 gen_mov_F0_vreg(dp, rn);
3250 if (delta_m) {
3251 rm = ((rm + delta_m) & (bank_mask - 1))
3252 | (rm & bank_mask);
3253 gen_mov_F1_vreg(dp, rm);
3254 }
3255 }
3256 }
3257 }
3258 break;
3259 case 0xc:
3260 case 0xd:
pbrook9ee6e8b2007-11-11 00:04:49 +00003261 if (dp && (insn & 0x03e00000) == 0x00400000) {
bellardb7bcbe92005-02-22 19:27:29 +00003262 /* two-register transfer */
3263 rn = (insn >> 16) & 0xf;
3264 rd = (insn >> 12) & 0xf;
3265 if (dp) {
pbrook9ee6e8b2007-11-11 00:04:49 +00003266 VFP_DREG_M(rm, insn);
3267 } else {
3268 rm = VFP_SREG_M(insn);
3269 }
bellardb7bcbe92005-02-22 19:27:29 +00003270
balrog18c9b562007-04-30 02:02:17 +00003271 if (insn & ARM_CP_RW_BIT) {
bellardb7bcbe92005-02-22 19:27:29 +00003272 /* vfp->arm */
3273 if (dp) {
pbrook4373f3c2008-03-31 03:47:19 +00003274 gen_mov_F0_vreg(0, rm * 2);
3275 tmp = gen_vfp_mrs();
3276 store_reg(s, rd, tmp);
3277 gen_mov_F0_vreg(0, rm * 2 + 1);
3278 tmp = gen_vfp_mrs();
3279 store_reg(s, rn, tmp);
bellardb7bcbe92005-02-22 19:27:29 +00003280 } else {
3281 gen_mov_F0_vreg(0, rm);
pbrook4373f3c2008-03-31 03:47:19 +00003282 tmp = gen_vfp_mrs();
3283 store_reg(s, rn, tmp);
bellardb7bcbe92005-02-22 19:27:29 +00003284 gen_mov_F0_vreg(0, rm + 1);
pbrook4373f3c2008-03-31 03:47:19 +00003285 tmp = gen_vfp_mrs();
3286 store_reg(s, rd, tmp);
bellardb7bcbe92005-02-22 19:27:29 +00003287 }
3288 } else {
3289 /* arm->vfp */
3290 if (dp) {
pbrook4373f3c2008-03-31 03:47:19 +00003291 tmp = load_reg(s, rd);
3292 gen_vfp_msr(tmp);
3293 gen_mov_vreg_F0(0, rm * 2);
3294 tmp = load_reg(s, rn);
3295 gen_vfp_msr(tmp);
3296 gen_mov_vreg_F0(0, rm * 2 + 1);
bellardb7bcbe92005-02-22 19:27:29 +00003297 } else {
pbrook4373f3c2008-03-31 03:47:19 +00003298 tmp = load_reg(s, rn);
3299 gen_vfp_msr(tmp);
bellardb7bcbe92005-02-22 19:27:29 +00003300 gen_mov_vreg_F0(0, rm);
pbrook4373f3c2008-03-31 03:47:19 +00003301 tmp = load_reg(s, rd);
3302 gen_vfp_msr(tmp);
bellardb7bcbe92005-02-22 19:27:29 +00003303 gen_mov_vreg_F0(0, rm + 1);
3304 }
3305 }
3306 } else {
3307 /* Load/store */
3308 rn = (insn >> 16) & 0xf;
3309 if (dp)
pbrook9ee6e8b2007-11-11 00:04:49 +00003310 VFP_DREG_D(rd, insn);
bellardb7bcbe92005-02-22 19:27:29 +00003311 else
pbrook9ee6e8b2007-11-11 00:04:49 +00003312 rd = VFP_SREG_D(insn);
3313 if (s->thumb && rn == 15) {
3314 gen_op_movl_T1_im(s->pc & ~2);
3315 } else {
3316 gen_movl_T1_reg(s, rn);
3317 }
bellardb7bcbe92005-02-22 19:27:29 +00003318 if ((insn & 0x01200000) == 0x01000000) {
3319 /* Single load/store */
3320 offset = (insn & 0xff) << 2;
3321 if ((insn & (1 << 23)) == 0)
3322 offset = -offset;
3323 gen_op_addl_T1_im(offset);
3324 if (insn & (1 << 20)) {
bellardb5ff1b32005-11-26 10:38:39 +00003325 gen_vfp_ld(s, dp);
bellardb7bcbe92005-02-22 19:27:29 +00003326 gen_mov_vreg_F0(dp, rd);
3327 } else {
3328 gen_mov_F0_vreg(dp, rd);
bellardb5ff1b32005-11-26 10:38:39 +00003329 gen_vfp_st(s, dp);
bellardb7bcbe92005-02-22 19:27:29 +00003330 }
3331 } else {
3332 /* load/store multiple */
3333 if (dp)
3334 n = (insn >> 1) & 0x7f;
3335 else
3336 n = insn & 0xff;
3337
3338 if (insn & (1 << 24)) /* pre-decrement */
3339 gen_op_addl_T1_im(-((insn & 0xff) << 2));
3340
3341 if (dp)
3342 offset = 8;
3343 else
3344 offset = 4;
3345 for (i = 0; i < n; i++) {
balrog18c9b562007-04-30 02:02:17 +00003346 if (insn & ARM_CP_RW_BIT) {
bellardb7bcbe92005-02-22 19:27:29 +00003347 /* load */
bellardb5ff1b32005-11-26 10:38:39 +00003348 gen_vfp_ld(s, dp);
bellardb7bcbe92005-02-22 19:27:29 +00003349 gen_mov_vreg_F0(dp, rd + i);
3350 } else {
3351 /* store */
3352 gen_mov_F0_vreg(dp, rd + i);
bellardb5ff1b32005-11-26 10:38:39 +00003353 gen_vfp_st(s, dp);
bellardb7bcbe92005-02-22 19:27:29 +00003354 }
3355 gen_op_addl_T1_im(offset);
3356 }
3357 if (insn & (1 << 21)) {
3358 /* writeback */
3359 if (insn & (1 << 24))
3360 offset = -offset * n;
3361 else if (dp && (insn & 1))
3362 offset = 4;
3363 else
3364 offset = 0;
3365
3366 if (offset != 0)
3367 gen_op_addl_T1_im(offset);
3368 gen_movl_reg_T1(s, rn);
3369 }
3370 }
3371 }
3372 break;
3373 default:
3374 /* Should never happen. */
3375 return 1;
3376 }
3377 return 0;
3378}
3379
bellard6e256c92005-11-20 10:32:05 +00003380static inline void gen_goto_tb(DisasContext *s, int n, uint32_t dest)
bellardc53be332005-10-30 21:39:19 +00003381{
bellard6e256c92005-11-20 10:32:05 +00003382 TranslationBlock *tb;
3383
3384 tb = s->tb;
3385 if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
bellard57fec1f2008-02-01 10:50:11 +00003386 tcg_gen_goto_tb(n);
pbrook8984bd22008-03-31 03:47:48 +00003387 gen_set_pc_im(dest);
bellard57fec1f2008-02-01 10:50:11 +00003388 tcg_gen_exit_tb((long)tb + n);
bellard6e256c92005-11-20 10:32:05 +00003389 } else {
pbrook8984bd22008-03-31 03:47:48 +00003390 gen_set_pc_im(dest);
bellard57fec1f2008-02-01 10:50:11 +00003391 tcg_gen_exit_tb(0);
bellard6e256c92005-11-20 10:32:05 +00003392 }
bellardc53be332005-10-30 21:39:19 +00003393}
3394
bellard8aaca4c2005-04-23 18:27:52 +00003395static inline void gen_jmp (DisasContext *s, uint32_t dest)
3396{
ths551bd272008-07-03 17:57:36 +00003397 if (unlikely(s->singlestep_enabled)) {
bellard8aaca4c2005-04-23 18:27:52 +00003398 /* An indirect jump so that we still trigger the debug exception. */
bellard5899f382005-04-27 20:25:20 +00003399 if (s->thumb)
pbrookd9ba4832008-03-31 03:46:50 +00003400 dest |= 1;
3401 gen_bx_im(s, dest);
bellard8aaca4c2005-04-23 18:27:52 +00003402 } else {
bellard6e256c92005-11-20 10:32:05 +00003403 gen_goto_tb(s, 0, dest);
bellard8aaca4c2005-04-23 18:27:52 +00003404 s->is_jmp = DISAS_TB_JUMP;
3405 }
3406}
3407
pbrookd9ba4832008-03-31 03:46:50 +00003408static inline void gen_mulxy(TCGv t0, TCGv t1, int x, int y)
bellardb5ff1b32005-11-26 10:38:39 +00003409{
bellardee097182005-12-04 18:56:28 +00003410 if (x)
pbrookd9ba4832008-03-31 03:46:50 +00003411 tcg_gen_sari_i32(t0, t0, 16);
bellardb5ff1b32005-11-26 10:38:39 +00003412 else
pbrookd9ba4832008-03-31 03:46:50 +00003413 gen_sxth(t0);
bellardee097182005-12-04 18:56:28 +00003414 if (y)
pbrookd9ba4832008-03-31 03:46:50 +00003415 tcg_gen_sari_i32(t1, t1, 16);
bellardb5ff1b32005-11-26 10:38:39 +00003416 else
pbrookd9ba4832008-03-31 03:46:50 +00003417 gen_sxth(t1);
3418 tcg_gen_mul_i32(t0, t0, t1);
bellardb5ff1b32005-11-26 10:38:39 +00003419}
3420
3421/* Return the mask of PSR bits set by a MSR instruction. */
pbrook9ee6e8b2007-11-11 00:04:49 +00003422static uint32_t msr_mask(CPUState *env, DisasContext *s, int flags, int spsr) {
bellardb5ff1b32005-11-26 10:38:39 +00003423 uint32_t mask;
3424
3425 mask = 0;
3426 if (flags & (1 << 0))
3427 mask |= 0xff;
3428 if (flags & (1 << 1))
3429 mask |= 0xff00;
3430 if (flags & (1 << 2))
3431 mask |= 0xff0000;
3432 if (flags & (1 << 3))
3433 mask |= 0xff000000;
pbrook9ee6e8b2007-11-11 00:04:49 +00003434
pbrook2ae23e72006-02-11 16:20:39 +00003435 /* Mask out undefined bits. */
pbrook9ee6e8b2007-11-11 00:04:49 +00003436 mask &= ~CPSR_RESERVED;
3437 if (!arm_feature(env, ARM_FEATURE_V6))
pbrooke160c512007-11-11 14:36:36 +00003438 mask &= ~(CPSR_E | CPSR_GE);
pbrook9ee6e8b2007-11-11 00:04:49 +00003439 if (!arm_feature(env, ARM_FEATURE_THUMB2))
pbrooke160c512007-11-11 14:36:36 +00003440 mask &= ~CPSR_IT;
pbrook9ee6e8b2007-11-11 00:04:49 +00003441 /* Mask out execution state bits. */
pbrook2ae23e72006-02-11 16:20:39 +00003442 if (!spsr)
pbrooke160c512007-11-11 14:36:36 +00003443 mask &= ~CPSR_EXEC;
bellardb5ff1b32005-11-26 10:38:39 +00003444 /* Mask out privileged bits. */
3445 if (IS_USER(s))
pbrook9ee6e8b2007-11-11 00:04:49 +00003446 mask &= CPSR_USER;
bellardb5ff1b32005-11-26 10:38:39 +00003447 return mask;
3448}
3449
3450/* Returns nonzero if access to the PSR is not permitted. */
3451static int gen_set_psr_T0(DisasContext *s, uint32_t mask, int spsr)
3452{
pbrookd9ba4832008-03-31 03:46:50 +00003453 TCGv tmp;
bellardb5ff1b32005-11-26 10:38:39 +00003454 if (spsr) {
3455 /* ??? This is also undefined in system mode. */
3456 if (IS_USER(s))
3457 return 1;
pbrookd9ba4832008-03-31 03:46:50 +00003458
3459 tmp = load_cpu_field(spsr);
3460 tcg_gen_andi_i32(tmp, tmp, ~mask);
3461 tcg_gen_andi_i32(cpu_T[0], cpu_T[0], mask);
3462 tcg_gen_or_i32(tmp, tmp, cpu_T[0]);
3463 store_cpu_field(tmp, spsr);
bellardb5ff1b32005-11-26 10:38:39 +00003464 } else {
pbrookd9ba4832008-03-31 03:46:50 +00003465 gen_set_cpsr(cpu_T[0], mask);
bellardb5ff1b32005-11-26 10:38:39 +00003466 }
3467 gen_lookup_tb(s);
3468 return 0;
3469}
3470
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03003471/* Generate an old-style exception return. Marks pc as dead. */
3472static void gen_exception_return(DisasContext *s, TCGv pc)
bellardb5ff1b32005-11-26 10:38:39 +00003473{
pbrookd9ba4832008-03-31 03:46:50 +00003474 TCGv tmp;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03003475 store_reg(s, 15, pc);
pbrookd9ba4832008-03-31 03:46:50 +00003476 tmp = load_cpu_field(spsr);
3477 gen_set_cpsr(tmp, 0xffffffff);
3478 dead_tmp(tmp);
bellardb5ff1b32005-11-26 10:38:39 +00003479 s->is_jmp = DISAS_UPDATE;
3480}
3481
pbrookb0109802008-03-31 03:47:03 +00003482/* Generate a v6 exception return. Marks both values as dead. */
3483static void gen_rfe(DisasContext *s, TCGv pc, TCGv cpsr)
pbrook9ee6e8b2007-11-11 00:04:49 +00003484{
pbrookb0109802008-03-31 03:47:03 +00003485 gen_set_cpsr(cpsr, 0xffffffff);
3486 dead_tmp(cpsr);
3487 store_reg(s, 15, pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00003488 s->is_jmp = DISAS_UPDATE;
3489}
3490
3491static inline void
3492gen_set_condexec (DisasContext *s)
3493{
3494 if (s->condexec_mask) {
pbrook8f012452008-03-31 03:46:03 +00003495 uint32_t val = (s->condexec_cond << 4) | (s->condexec_mask >> 1);
3496 TCGv tmp = new_tmp();
3497 tcg_gen_movi_i32(tmp, val);
pbrookd9ba4832008-03-31 03:46:50 +00003498 store_cpu_field(tmp, condexec_bits);
pbrook9ee6e8b2007-11-11 00:04:49 +00003499 }
3500}
3501
3502static void gen_nop_hint(DisasContext *s, int val)
3503{
3504 switch (val) {
3505 case 3: /* wfi */
pbrook8984bd22008-03-31 03:47:48 +00003506 gen_set_pc_im(s->pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00003507 s->is_jmp = DISAS_WFI;
3508 break;
3509 case 2: /* wfe */
3510 case 4: /* sev */
3511 /* TODO: Implement SEV and WFE. May help SMP performance. */
3512 default: /* nop */
3513 break;
3514 }
3515}
3516
pbrookad694712008-03-31 03:48:30 +00003517/* These macros help make the code more readable when migrating from the
3518 old dyngen helpers. They should probably be removed when
3519 T0/T1 are removed. */
3520#define CPU_T001 cpu_T[0], cpu_T[0], cpu_T[1]
3521#define CPU_T0E01 cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]
pbrook9ee6e8b2007-11-11 00:04:49 +00003522
pbrookad694712008-03-31 03:48:30 +00003523#define CPU_V001 cpu_V0, cpu_V0, cpu_V1
pbrook9ee6e8b2007-11-11 00:04:49 +00003524
3525static inline int gen_neon_add(int size)
3526{
3527 switch (size) {
pbrookad694712008-03-31 03:48:30 +00003528 case 0: gen_helper_neon_add_u8(CPU_T001); break;
3529 case 1: gen_helper_neon_add_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00003530 case 2: gen_op_addl_T0_T1(); break;
3531 default: return 1;
3532 }
3533 return 0;
3534}
3535
pbrookad694712008-03-31 03:48:30 +00003536static inline void gen_neon_rsb(int size)
3537{
3538 switch (size) {
3539 case 0: gen_helper_neon_sub_u8(cpu_T[0], cpu_T[1], cpu_T[0]); break;
3540 case 1: gen_helper_neon_sub_u16(cpu_T[0], cpu_T[1], cpu_T[0]); break;
3541 case 2: gen_op_rsbl_T0_T1(); break;
3542 default: return;
3543 }
3544}
3545
3546/* 32-bit pairwise ops end up the same as the elementwise versions. */
3547#define gen_helper_neon_pmax_s32 gen_helper_neon_max_s32
3548#define gen_helper_neon_pmax_u32 gen_helper_neon_max_u32
3549#define gen_helper_neon_pmin_s32 gen_helper_neon_min_s32
3550#define gen_helper_neon_pmin_u32 gen_helper_neon_min_u32
3551
3552/* FIXME: This is wrong. They set the wrong overflow bit. */
3553#define gen_helper_neon_qadd_s32(a, e, b, c) gen_helper_add_saturate(a, b, c)
3554#define gen_helper_neon_qadd_u32(a, e, b, c) gen_helper_add_usaturate(a, b, c)
3555#define gen_helper_neon_qsub_s32(a, e, b, c) gen_helper_sub_saturate(a, b, c)
3556#define gen_helper_neon_qsub_u32(a, e, b, c) gen_helper_sub_usaturate(a, b, c)
3557
3558#define GEN_NEON_INTEGER_OP_ENV(name) do { \
3559 switch ((size << 1) | u) { \
3560 case 0: \
3561 gen_helper_neon_##name##_s8(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3562 break; \
3563 case 1: \
3564 gen_helper_neon_##name##_u8(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3565 break; \
3566 case 2: \
3567 gen_helper_neon_##name##_s16(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3568 break; \
3569 case 3: \
3570 gen_helper_neon_##name##_u16(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3571 break; \
3572 case 4: \
3573 gen_helper_neon_##name##_s32(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3574 break; \
3575 case 5: \
3576 gen_helper_neon_##name##_u32(cpu_T[0], cpu_env, cpu_T[0], cpu_T[1]); \
3577 break; \
3578 default: return 1; \
3579 }} while (0)
pbrook9ee6e8b2007-11-11 00:04:49 +00003580
3581#define GEN_NEON_INTEGER_OP(name) do { \
3582 switch ((size << 1) | u) { \
pbrookad694712008-03-31 03:48:30 +00003583 case 0: \
3584 gen_helper_neon_##name##_s8(cpu_T[0], cpu_T[0], cpu_T[1]); \
3585 break; \
3586 case 1: \
3587 gen_helper_neon_##name##_u8(cpu_T[0], cpu_T[0], cpu_T[1]); \
3588 break; \
3589 case 2: \
3590 gen_helper_neon_##name##_s16(cpu_T[0], cpu_T[0], cpu_T[1]); \
3591 break; \
3592 case 3: \
3593 gen_helper_neon_##name##_u16(cpu_T[0], cpu_T[0], cpu_T[1]); \
3594 break; \
3595 case 4: \
3596 gen_helper_neon_##name##_s32(cpu_T[0], cpu_T[0], cpu_T[1]); \
3597 break; \
3598 case 5: \
3599 gen_helper_neon_##name##_u32(cpu_T[0], cpu_T[0], cpu_T[1]); \
3600 break; \
pbrook9ee6e8b2007-11-11 00:04:49 +00003601 default: return 1; \
3602 }} while (0)
3603
3604static inline void
3605gen_neon_movl_scratch_T0(int scratch)
3606{
3607 uint32_t offset;
3608
3609 offset = offsetof(CPUARMState, vfp.scratch[scratch]);
pbrookad694712008-03-31 03:48:30 +00003610 tcg_gen_st_i32(cpu_T[0], cpu_env, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00003611}
3612
3613static inline void
3614gen_neon_movl_scratch_T1(int scratch)
3615{
3616 uint32_t offset;
3617
3618 offset = offsetof(CPUARMState, vfp.scratch[scratch]);
pbrookad694712008-03-31 03:48:30 +00003619 tcg_gen_st_i32(cpu_T[1], cpu_env, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00003620}
3621
3622static inline void
3623gen_neon_movl_T0_scratch(int scratch)
3624{
3625 uint32_t offset;
3626
3627 offset = offsetof(CPUARMState, vfp.scratch[scratch]);
pbrookad694712008-03-31 03:48:30 +00003628 tcg_gen_ld_i32(cpu_T[0], cpu_env, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00003629}
3630
3631static inline void
3632gen_neon_movl_T1_scratch(int scratch)
3633{
3634 uint32_t offset;
3635
3636 offset = offsetof(CPUARMState, vfp.scratch[scratch]);
pbrookad694712008-03-31 03:48:30 +00003637 tcg_gen_ld_i32(cpu_T[1], cpu_env, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00003638}
3639
3640static inline void gen_neon_get_scalar(int size, int reg)
3641{
3642 if (size == 1) {
3643 NEON_GET_REG(T0, reg >> 1, reg & 1);
3644 } else {
3645 NEON_GET_REG(T0, reg >> 2, (reg >> 1) & 1);
3646 if (reg & 1)
pbrookad694712008-03-31 03:48:30 +00003647 gen_neon_dup_low16(cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00003648 else
pbrookad694712008-03-31 03:48:30 +00003649 gen_neon_dup_high16(cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00003650 }
3651}
3652
3653static void gen_neon_unzip(int reg, int q, int tmp, int size)
3654{
3655 int n;
3656
3657 for (n = 0; n < q + 1; n += 2) {
3658 NEON_GET_REG(T0, reg, n);
3659 NEON_GET_REG(T0, reg, n + n);
3660 switch (size) {
pbrookad694712008-03-31 03:48:30 +00003661 case 0: gen_helper_neon_unzip_u8(); break;
3662 case 1: gen_helper_neon_zip_u16(); break; /* zip and unzip are the same. */
pbrook9ee6e8b2007-11-11 00:04:49 +00003663 case 2: /* no-op */; break;
3664 default: abort();
3665 }
3666 gen_neon_movl_scratch_T0(tmp + n);
3667 gen_neon_movl_scratch_T1(tmp + n + 1);
3668 }
3669}
3670
3671static struct {
3672 int nregs;
3673 int interleave;
3674 int spacing;
3675} neon_ls_element_type[11] = {
3676 {4, 4, 1},
3677 {4, 4, 2},
3678 {4, 1, 1},
3679 {4, 2, 1},
3680 {3, 3, 1},
3681 {3, 3, 2},
3682 {3, 1, 1},
3683 {1, 1, 1},
3684 {2, 2, 1},
3685 {2, 2, 2},
3686 {2, 1, 1}
3687};
3688
3689/* Translate a NEON load/store element instruction. Return nonzero if the
3690 instruction is invalid. */
3691static int disas_neon_ls_insn(CPUState * env, DisasContext *s, uint32_t insn)
3692{
3693 int rd, rn, rm;
3694 int op;
3695 int nregs;
3696 int interleave;
3697 int stride;
3698 int size;
3699 int reg;
3700 int pass;
3701 int load;
3702 int shift;
pbrook9ee6e8b2007-11-11 00:04:49 +00003703 int n;
pbrookb0109802008-03-31 03:47:03 +00003704 TCGv tmp;
pbrook8f8e3aa2008-03-31 03:48:01 +00003705 TCGv tmp2;
pbrook9ee6e8b2007-11-11 00:04:49 +00003706
3707 if (!vfp_enabled(env))
3708 return 1;
3709 VFP_DREG_D(rd, insn);
3710 rn = (insn >> 16) & 0xf;
3711 rm = insn & 0xf;
3712 load = (insn & (1 << 21)) != 0;
3713 if ((insn & (1 << 23)) == 0) {
3714 /* Load store all elements. */
3715 op = (insn >> 8) & 0xf;
3716 size = (insn >> 6) & 3;
3717 if (op > 10 || size == 3)
3718 return 1;
3719 nregs = neon_ls_element_type[op].nregs;
3720 interleave = neon_ls_element_type[op].interleave;
3721 gen_movl_T1_reg(s, rn);
3722 stride = (1 << size) * interleave;
3723 for (reg = 0; reg < nregs; reg++) {
3724 if (interleave > 2 || (interleave == 2 && nregs == 2)) {
3725 gen_movl_T1_reg(s, rn);
3726 gen_op_addl_T1_im((1 << size) * reg);
3727 } else if (interleave == 2 && nregs == 4 && reg == 2) {
3728 gen_movl_T1_reg(s, rn);
3729 gen_op_addl_T1_im(1 << size);
3730 }
3731 for (pass = 0; pass < 2; pass++) {
3732 if (size == 2) {
3733 if (load) {
pbrookb0109802008-03-31 03:47:03 +00003734 tmp = gen_ld32(cpu_T[1], IS_USER(s));
pbrookad694712008-03-31 03:48:30 +00003735 neon_store_reg(rd, pass, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003736 } else {
pbrookad694712008-03-31 03:48:30 +00003737 tmp = neon_load_reg(rd, pass);
pbrookb0109802008-03-31 03:47:03 +00003738 gen_st32(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003739 }
3740 gen_op_addl_T1_im(stride);
3741 } else if (size == 1) {
3742 if (load) {
pbrookb0109802008-03-31 03:47:03 +00003743 tmp = gen_ld16u(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003744 gen_op_addl_T1_im(stride);
pbrook8f8e3aa2008-03-31 03:48:01 +00003745 tmp2 = gen_ld16u(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003746 gen_op_addl_T1_im(stride);
pbrook8f8e3aa2008-03-31 03:48:01 +00003747 gen_bfi(tmp, tmp, tmp2, 16, 0xffff);
3748 dead_tmp(tmp2);
3749 neon_store_reg(rd, pass, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003750 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00003751 tmp = neon_load_reg(rd, pass);
3752 tmp2 = new_tmp();
3753 tcg_gen_shri_i32(tmp2, tmp, 16);
pbrookb0109802008-03-31 03:47:03 +00003754 gen_st16(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003755 gen_op_addl_T1_im(stride);
pbrook8f8e3aa2008-03-31 03:48:01 +00003756 gen_st16(tmp2, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003757 gen_op_addl_T1_im(stride);
3758 }
3759 } else /* size == 0 */ {
3760 if (load) {
pbrooka50f5b92008-06-29 15:25:29 +00003761 TCGV_UNUSED(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00003762 for (n = 0; n < 4; n++) {
pbrookb0109802008-03-31 03:47:03 +00003763 tmp = gen_ld8u(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003764 gen_op_addl_T1_im(stride);
3765 if (n == 0) {
pbrook8f8e3aa2008-03-31 03:48:01 +00003766 tmp2 = tmp;
pbrook9ee6e8b2007-11-11 00:04:49 +00003767 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00003768 gen_bfi(tmp2, tmp2, tmp, n * 8, 0xff);
3769 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003770 }
pbrook9ee6e8b2007-11-11 00:04:49 +00003771 }
pbrook8f8e3aa2008-03-31 03:48:01 +00003772 neon_store_reg(rd, pass, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00003773 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00003774 tmp2 = neon_load_reg(rd, pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00003775 for (n = 0; n < 4; n++) {
pbrookb0109802008-03-31 03:47:03 +00003776 tmp = new_tmp();
pbrook8f8e3aa2008-03-31 03:48:01 +00003777 if (n == 0) {
3778 tcg_gen_mov_i32(tmp, tmp2);
3779 } else {
3780 tcg_gen_shri_i32(tmp, tmp2, n * 8);
3781 }
pbrookb0109802008-03-31 03:47:03 +00003782 gen_st8(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003783 gen_op_addl_T1_im(stride);
pbrook9ee6e8b2007-11-11 00:04:49 +00003784 }
pbrook8f8e3aa2008-03-31 03:48:01 +00003785 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00003786 }
3787 }
3788 }
3789 rd += neon_ls_element_type[op].spacing;
3790 }
3791 stride = nregs * 8;
3792 } else {
3793 size = (insn >> 10) & 3;
3794 if (size == 3) {
3795 /* Load single element to all lanes. */
3796 if (!load)
3797 return 1;
3798 size = (insn >> 6) & 3;
3799 nregs = ((insn >> 8) & 3) + 1;
3800 stride = (insn & (1 << 5)) ? 2 : 1;
3801 gen_movl_T1_reg(s, rn);
3802 for (reg = 0; reg < nregs; reg++) {
3803 switch (size) {
3804 case 0:
pbrookb0109802008-03-31 03:47:03 +00003805 tmp = gen_ld8u(cpu_T[1], IS_USER(s));
pbrookad694712008-03-31 03:48:30 +00003806 gen_neon_dup_u8(tmp, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00003807 break;
3808 case 1:
pbrookb0109802008-03-31 03:47:03 +00003809 tmp = gen_ld16u(cpu_T[1], IS_USER(s));
pbrookad694712008-03-31 03:48:30 +00003810 gen_neon_dup_low16(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003811 break;
3812 case 2:
pbrookb0109802008-03-31 03:47:03 +00003813 tmp = gen_ld32(cpu_T[0], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003814 break;
3815 case 3:
3816 return 1;
pbrooka50f5b92008-06-29 15:25:29 +00003817 default: /* Avoid compiler warnings. */
3818 abort();
pbrook9ee6e8b2007-11-11 00:04:49 +00003819 }
3820 gen_op_addl_T1_im(1 << size);
pbrookad694712008-03-31 03:48:30 +00003821 tmp2 = new_tmp();
3822 tcg_gen_mov_i32(tmp2, tmp);
3823 neon_store_reg(rd, 0, tmp2);
pbrook3018f252008-09-22 00:52:42 +00003824 neon_store_reg(rd, 1, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003825 rd += stride;
3826 }
3827 stride = (1 << size) * nregs;
3828 } else {
3829 /* Single element. */
3830 pass = (insn >> 7) & 1;
3831 switch (size) {
3832 case 0:
3833 shift = ((insn >> 5) & 3) * 8;
pbrook9ee6e8b2007-11-11 00:04:49 +00003834 stride = 1;
3835 break;
3836 case 1:
3837 shift = ((insn >> 6) & 1) * 16;
pbrook9ee6e8b2007-11-11 00:04:49 +00003838 stride = (insn & (1 << 5)) ? 2 : 1;
3839 break;
3840 case 2:
3841 shift = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +00003842 stride = (insn & (1 << 6)) ? 2 : 1;
3843 break;
3844 default:
3845 abort();
3846 }
3847 nregs = ((insn >> 8) & 3) + 1;
3848 gen_movl_T1_reg(s, rn);
3849 for (reg = 0; reg < nregs; reg++) {
3850 if (load) {
pbrook9ee6e8b2007-11-11 00:04:49 +00003851 switch (size) {
3852 case 0:
pbrookb0109802008-03-31 03:47:03 +00003853 tmp = gen_ld8u(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003854 break;
3855 case 1:
pbrookb0109802008-03-31 03:47:03 +00003856 tmp = gen_ld16u(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003857 break;
3858 case 2:
pbrookb0109802008-03-31 03:47:03 +00003859 tmp = gen_ld32(cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003860 break;
pbrooka50f5b92008-06-29 15:25:29 +00003861 default: /* Avoid compiler warnings. */
3862 abort();
pbrook9ee6e8b2007-11-11 00:04:49 +00003863 }
3864 if (size != 2) {
pbrook8f8e3aa2008-03-31 03:48:01 +00003865 tmp2 = neon_load_reg(rd, pass);
3866 gen_bfi(tmp, tmp2, tmp, shift, size ? 0xffff : 0xff);
3867 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00003868 }
pbrook8f8e3aa2008-03-31 03:48:01 +00003869 neon_store_reg(rd, pass, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00003870 } else { /* Store */
pbrook8f8e3aa2008-03-31 03:48:01 +00003871 tmp = neon_load_reg(rd, pass);
3872 if (shift)
3873 tcg_gen_shri_i32(tmp, tmp, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00003874 switch (size) {
3875 case 0:
pbrookb0109802008-03-31 03:47:03 +00003876 gen_st8(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003877 break;
3878 case 1:
pbrookb0109802008-03-31 03:47:03 +00003879 gen_st16(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003880 break;
3881 case 2:
pbrookb0109802008-03-31 03:47:03 +00003882 gen_st32(tmp, cpu_T[1], IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00003883 break;
3884 }
3885 }
3886 rd += stride;
3887 gen_op_addl_T1_im(1 << size);
3888 }
3889 stride = nregs * (1 << size);
3890 }
3891 }
3892 if (rm != 15) {
pbrookb26eefb2008-03-31 03:44:26 +00003893 TCGv base;
3894
3895 base = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00003896 if (rm == 13) {
pbrookb26eefb2008-03-31 03:44:26 +00003897 tcg_gen_addi_i32(base, base, stride);
pbrook9ee6e8b2007-11-11 00:04:49 +00003898 } else {
pbrookb26eefb2008-03-31 03:44:26 +00003899 TCGv index;
3900 index = load_reg(s, rm);
3901 tcg_gen_add_i32(base, base, index);
3902 dead_tmp(index);
pbrook9ee6e8b2007-11-11 00:04:49 +00003903 }
pbrookb26eefb2008-03-31 03:44:26 +00003904 store_reg(s, rn, base);
pbrook9ee6e8b2007-11-11 00:04:49 +00003905 }
3906 return 0;
3907}
3908
pbrook8f8e3aa2008-03-31 03:48:01 +00003909/* Bitwise select. dest = c ? t : f. Clobbers T and F. */
3910static void gen_neon_bsl(TCGv dest, TCGv t, TCGv f, TCGv c)
3911{
3912 tcg_gen_and_i32(t, t, c);
3913 tcg_gen_bic_i32(f, f, c);
3914 tcg_gen_or_i32(dest, t, f);
3915}
3916
pbrooka7812ae2008-11-17 14:43:54 +00003917static inline void gen_neon_narrow(int size, TCGv dest, TCGv_i64 src)
pbrookad694712008-03-31 03:48:30 +00003918{
3919 switch (size) {
3920 case 0: gen_helper_neon_narrow_u8(dest, src); break;
3921 case 1: gen_helper_neon_narrow_u16(dest, src); break;
3922 case 2: tcg_gen_trunc_i64_i32(dest, src); break;
3923 default: abort();
3924 }
3925}
3926
pbrooka7812ae2008-11-17 14:43:54 +00003927static inline void gen_neon_narrow_sats(int size, TCGv dest, TCGv_i64 src)
pbrookad694712008-03-31 03:48:30 +00003928{
3929 switch (size) {
3930 case 0: gen_helper_neon_narrow_sat_s8(dest, cpu_env, src); break;
3931 case 1: gen_helper_neon_narrow_sat_s16(dest, cpu_env, src); break;
3932 case 2: gen_helper_neon_narrow_sat_s32(dest, cpu_env, src); break;
3933 default: abort();
3934 }
3935}
3936
pbrooka7812ae2008-11-17 14:43:54 +00003937static inline void gen_neon_narrow_satu(int size, TCGv dest, TCGv_i64 src)
pbrookad694712008-03-31 03:48:30 +00003938{
3939 switch (size) {
3940 case 0: gen_helper_neon_narrow_sat_u8(dest, cpu_env, src); break;
3941 case 1: gen_helper_neon_narrow_sat_u16(dest, cpu_env, src); break;
3942 case 2: gen_helper_neon_narrow_sat_u32(dest, cpu_env, src); break;
3943 default: abort();
3944 }
3945}
3946
3947static inline void gen_neon_shift_narrow(int size, TCGv var, TCGv shift,
3948 int q, int u)
3949{
3950 if (q) {
3951 if (u) {
3952 switch (size) {
3953 case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
3954 case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
3955 default: abort();
3956 }
3957 } else {
3958 switch (size) {
3959 case 1: gen_helper_neon_rshl_s16(var, var, shift); break;
3960 case 2: gen_helper_neon_rshl_s32(var, var, shift); break;
3961 default: abort();
3962 }
3963 }
3964 } else {
3965 if (u) {
3966 switch (size) {
3967 case 1: gen_helper_neon_rshl_u16(var, var, shift); break;
3968 case 2: gen_helper_neon_rshl_u32(var, var, shift); break;
3969 default: abort();
3970 }
3971 } else {
3972 switch (size) {
3973 case 1: gen_helper_neon_shl_s16(var, var, shift); break;
3974 case 2: gen_helper_neon_shl_s32(var, var, shift); break;
3975 default: abort();
3976 }
3977 }
3978 }
3979}
3980
pbrooka7812ae2008-11-17 14:43:54 +00003981static inline void gen_neon_widen(TCGv_i64 dest, TCGv src, int size, int u)
pbrookad694712008-03-31 03:48:30 +00003982{
3983 if (u) {
3984 switch (size) {
3985 case 0: gen_helper_neon_widen_u8(dest, src); break;
3986 case 1: gen_helper_neon_widen_u16(dest, src); break;
3987 case 2: tcg_gen_extu_i32_i64(dest, src); break;
3988 default: abort();
3989 }
3990 } else {
3991 switch (size) {
3992 case 0: gen_helper_neon_widen_s8(dest, src); break;
3993 case 1: gen_helper_neon_widen_s16(dest, src); break;
3994 case 2: tcg_gen_ext_i32_i64(dest, src); break;
3995 default: abort();
3996 }
3997 }
3998 dead_tmp(src);
3999}
4000
4001static inline void gen_neon_addl(int size)
4002{
4003 switch (size) {
4004 case 0: gen_helper_neon_addl_u16(CPU_V001); break;
4005 case 1: gen_helper_neon_addl_u32(CPU_V001); break;
4006 case 2: tcg_gen_add_i64(CPU_V001); break;
4007 default: abort();
4008 }
4009}
4010
4011static inline void gen_neon_subl(int size)
4012{
4013 switch (size) {
4014 case 0: gen_helper_neon_subl_u16(CPU_V001); break;
4015 case 1: gen_helper_neon_subl_u32(CPU_V001); break;
4016 case 2: tcg_gen_sub_i64(CPU_V001); break;
4017 default: abort();
4018 }
4019}
4020
pbrooka7812ae2008-11-17 14:43:54 +00004021static inline void gen_neon_negl(TCGv_i64 var, int size)
pbrookad694712008-03-31 03:48:30 +00004022{
4023 switch (size) {
4024 case 0: gen_helper_neon_negl_u16(var, var); break;
4025 case 1: gen_helper_neon_negl_u32(var, var); break;
4026 case 2: gen_helper_neon_negl_u64(var, var); break;
4027 default: abort();
4028 }
4029}
4030
pbrooka7812ae2008-11-17 14:43:54 +00004031static inline void gen_neon_addl_saturate(TCGv_i64 op0, TCGv_i64 op1, int size)
pbrookad694712008-03-31 03:48:30 +00004032{
4033 switch (size) {
4034 case 1: gen_helper_neon_addl_saturate_s32(op0, cpu_env, op0, op1); break;
4035 case 2: gen_helper_neon_addl_saturate_s64(op0, cpu_env, op0, op1); break;
4036 default: abort();
4037 }
4038}
4039
pbrooka7812ae2008-11-17 14:43:54 +00004040static inline void gen_neon_mull(TCGv_i64 dest, TCGv a, TCGv b, int size, int u)
pbrookad694712008-03-31 03:48:30 +00004041{
pbrooka7812ae2008-11-17 14:43:54 +00004042 TCGv_i64 tmp;
pbrookad694712008-03-31 03:48:30 +00004043
4044 switch ((size << 1) | u) {
4045 case 0: gen_helper_neon_mull_s8(dest, a, b); break;
4046 case 1: gen_helper_neon_mull_u8(dest, a, b); break;
4047 case 2: gen_helper_neon_mull_s16(dest, a, b); break;
4048 case 3: gen_helper_neon_mull_u16(dest, a, b); break;
4049 case 4:
4050 tmp = gen_muls_i64_i32(a, b);
4051 tcg_gen_mov_i64(dest, tmp);
4052 break;
4053 case 5:
4054 tmp = gen_mulu_i64_i32(a, b);
4055 tcg_gen_mov_i64(dest, tmp);
4056 break;
4057 default: abort();
4058 }
4059 if (size < 2) {
4060 dead_tmp(b);
4061 dead_tmp(a);
4062 }
4063}
4064
pbrook9ee6e8b2007-11-11 00:04:49 +00004065/* Translate a NEON data processing instruction. Return nonzero if the
4066 instruction is invalid.
pbrookad694712008-03-31 03:48:30 +00004067 We process data in a mixture of 32-bit and 64-bit chunks.
4068 Mostly we use 32-bit chunks so we can use normal scalar instructions. */
pbrook9ee6e8b2007-11-11 00:04:49 +00004069
4070static int disas_neon_data_insn(CPUState * env, DisasContext *s, uint32_t insn)
4071{
4072 int op;
4073 int q;
4074 int rd, rn, rm;
4075 int size;
4076 int shift;
4077 int pass;
4078 int count;
4079 int pairwise;
4080 int u;
4081 int n;
4082 uint32_t imm;
pbrook8f8e3aa2008-03-31 03:48:01 +00004083 TCGv tmp;
4084 TCGv tmp2;
4085 TCGv tmp3;
pbrooka7812ae2008-11-17 14:43:54 +00004086 TCGv_i64 tmp64;
pbrook9ee6e8b2007-11-11 00:04:49 +00004087
4088 if (!vfp_enabled(env))
4089 return 1;
4090 q = (insn & (1 << 6)) != 0;
4091 u = (insn >> 24) & 1;
4092 VFP_DREG_D(rd, insn);
4093 VFP_DREG_N(rn, insn);
4094 VFP_DREG_M(rm, insn);
4095 size = (insn >> 20) & 3;
4096 if ((insn & (1 << 23)) == 0) {
4097 /* Three register same length. */
4098 op = ((insn >> 7) & 0x1e) | ((insn >> 4) & 1);
pbrookad694712008-03-31 03:48:30 +00004099 if (size == 3 && (op == 1 || op == 5 || op == 8 || op == 9
4100 || op == 10 || op == 11 || op == 16)) {
4101 /* 64-bit element instructions. */
pbrook9ee6e8b2007-11-11 00:04:49 +00004102 for (pass = 0; pass < (q ? 2 : 1); pass++) {
pbrookad694712008-03-31 03:48:30 +00004103 neon_load_reg64(cpu_V0, rn + pass);
4104 neon_load_reg64(cpu_V1, rm + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004105 switch (op) {
4106 case 1: /* VQADD */
4107 if (u) {
pbrookad694712008-03-31 03:48:30 +00004108 gen_helper_neon_add_saturate_u64(CPU_V001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004109 } else {
pbrookad694712008-03-31 03:48:30 +00004110 gen_helper_neon_add_saturate_s64(CPU_V001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004111 }
4112 break;
4113 case 5: /* VQSUB */
4114 if (u) {
pbrookad694712008-03-31 03:48:30 +00004115 gen_helper_neon_sub_saturate_u64(CPU_V001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004116 } else {
pbrookad694712008-03-31 03:48:30 +00004117 gen_helper_neon_sub_saturate_s64(CPU_V001);
4118 }
4119 break;
4120 case 8: /* VSHL */
4121 if (u) {
4122 gen_helper_neon_shl_u64(cpu_V0, cpu_V1, cpu_V0);
4123 } else {
4124 gen_helper_neon_shl_s64(cpu_V0, cpu_V1, cpu_V0);
4125 }
4126 break;
4127 case 9: /* VQSHL */
4128 if (u) {
4129 gen_helper_neon_qshl_u64(cpu_V0, cpu_env,
4130 cpu_V0, cpu_V0);
4131 } else {
4132 gen_helper_neon_qshl_s64(cpu_V1, cpu_env,
4133 cpu_V1, cpu_V0);
4134 }
4135 break;
4136 case 10: /* VRSHL */
4137 if (u) {
4138 gen_helper_neon_rshl_u64(cpu_V0, cpu_V1, cpu_V0);
4139 } else {
4140 gen_helper_neon_rshl_s64(cpu_V0, cpu_V1, cpu_V0);
4141 }
4142 break;
4143 case 11: /* VQRSHL */
4144 if (u) {
4145 gen_helper_neon_qrshl_u64(cpu_V0, cpu_env,
4146 cpu_V1, cpu_V0);
4147 } else {
4148 gen_helper_neon_qrshl_s64(cpu_V0, cpu_env,
4149 cpu_V1, cpu_V0);
pbrook9ee6e8b2007-11-11 00:04:49 +00004150 }
4151 break;
4152 case 16:
4153 if (u) {
pbrookad694712008-03-31 03:48:30 +00004154 tcg_gen_sub_i64(CPU_V001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004155 } else {
pbrookad694712008-03-31 03:48:30 +00004156 tcg_gen_add_i64(CPU_V001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004157 }
4158 break;
4159 default:
4160 abort();
4161 }
pbrookad694712008-03-31 03:48:30 +00004162 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004163 }
4164 return 0;
4165 }
4166 switch (op) {
4167 case 8: /* VSHL */
4168 case 9: /* VQSHL */
4169 case 10: /* VRSHL */
pbrookad694712008-03-31 03:48:30 +00004170 case 11: /* VQRSHL */
pbrook9ee6e8b2007-11-11 00:04:49 +00004171 {
pbrookad694712008-03-31 03:48:30 +00004172 int rtmp;
4173 /* Shift instruction operands are reversed. */
4174 rtmp = rn;
pbrook9ee6e8b2007-11-11 00:04:49 +00004175 rn = rm;
pbrookad694712008-03-31 03:48:30 +00004176 rm = rtmp;
pbrook9ee6e8b2007-11-11 00:04:49 +00004177 pairwise = 0;
4178 }
4179 break;
4180 case 20: /* VPMAX */
4181 case 21: /* VPMIN */
4182 case 23: /* VPADD */
4183 pairwise = 1;
4184 break;
4185 case 26: /* VPADD (float) */
4186 pairwise = (u && size < 2);
4187 break;
4188 case 30: /* VPMIN/VPMAX (float) */
4189 pairwise = u;
4190 break;
4191 default:
4192 pairwise = 0;
4193 break;
4194 }
4195 for (pass = 0; pass < (q ? 4 : 2); pass++) {
4196
4197 if (pairwise) {
4198 /* Pairwise. */
4199 if (q)
4200 n = (pass & 1) * 2;
4201 else
4202 n = 0;
4203 if (pass < q + 1) {
4204 NEON_GET_REG(T0, rn, n);
4205 NEON_GET_REG(T1, rn, n + 1);
4206 } else {
4207 NEON_GET_REG(T0, rm, n);
4208 NEON_GET_REG(T1, rm, n + 1);
4209 }
4210 } else {
4211 /* Elementwise. */
4212 NEON_GET_REG(T0, rn, pass);
4213 NEON_GET_REG(T1, rm, pass);
4214 }
4215 switch (op) {
4216 case 0: /* VHADD */
4217 GEN_NEON_INTEGER_OP(hadd);
4218 break;
4219 case 1: /* VQADD */
pbrookad694712008-03-31 03:48:30 +00004220 GEN_NEON_INTEGER_OP_ENV(qadd);
pbrook9ee6e8b2007-11-11 00:04:49 +00004221 break;
4222 case 2: /* VRHADD */
4223 GEN_NEON_INTEGER_OP(rhadd);
4224 break;
4225 case 3: /* Logic ops. */
4226 switch ((u << 2) | size) {
4227 case 0: /* VAND */
4228 gen_op_andl_T0_T1();
4229 break;
4230 case 1: /* BIC */
4231 gen_op_bicl_T0_T1();
4232 break;
4233 case 2: /* VORR */
4234 gen_op_orl_T0_T1();
4235 break;
4236 case 3: /* VORN */
4237 gen_op_notl_T1();
4238 gen_op_orl_T0_T1();
4239 break;
4240 case 4: /* VEOR */
4241 gen_op_xorl_T0_T1();
4242 break;
4243 case 5: /* VBSL */
pbrook8f8e3aa2008-03-31 03:48:01 +00004244 tmp = neon_load_reg(rd, pass);
4245 gen_neon_bsl(cpu_T[0], cpu_T[0], cpu_T[1], tmp);
4246 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004247 break;
4248 case 6: /* VBIT */
pbrook8f8e3aa2008-03-31 03:48:01 +00004249 tmp = neon_load_reg(rd, pass);
4250 gen_neon_bsl(cpu_T[0], cpu_T[0], tmp, cpu_T[1]);
4251 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004252 break;
4253 case 7: /* VBIF */
pbrook8f8e3aa2008-03-31 03:48:01 +00004254 tmp = neon_load_reg(rd, pass);
4255 gen_neon_bsl(cpu_T[0], tmp, cpu_T[0], cpu_T[1]);
4256 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004257 break;
4258 }
4259 break;
4260 case 4: /* VHSUB */
4261 GEN_NEON_INTEGER_OP(hsub);
4262 break;
4263 case 5: /* VQSUB */
pbrookad694712008-03-31 03:48:30 +00004264 GEN_NEON_INTEGER_OP_ENV(qsub);
pbrook9ee6e8b2007-11-11 00:04:49 +00004265 break;
4266 case 6: /* VCGT */
4267 GEN_NEON_INTEGER_OP(cgt);
4268 break;
4269 case 7: /* VCGE */
4270 GEN_NEON_INTEGER_OP(cge);
4271 break;
4272 case 8: /* VSHL */
pbrookad694712008-03-31 03:48:30 +00004273 GEN_NEON_INTEGER_OP(shl);
pbrook9ee6e8b2007-11-11 00:04:49 +00004274 break;
4275 case 9: /* VQSHL */
pbrookad694712008-03-31 03:48:30 +00004276 GEN_NEON_INTEGER_OP_ENV(qshl);
pbrook9ee6e8b2007-11-11 00:04:49 +00004277 break;
4278 case 10: /* VRSHL */
pbrookad694712008-03-31 03:48:30 +00004279 GEN_NEON_INTEGER_OP(rshl);
pbrook9ee6e8b2007-11-11 00:04:49 +00004280 break;
4281 case 11: /* VQRSHL */
pbrookad694712008-03-31 03:48:30 +00004282 GEN_NEON_INTEGER_OP_ENV(qrshl);
pbrook9ee6e8b2007-11-11 00:04:49 +00004283 break;
4284 case 12: /* VMAX */
4285 GEN_NEON_INTEGER_OP(max);
4286 break;
4287 case 13: /* VMIN */
4288 GEN_NEON_INTEGER_OP(min);
4289 break;
4290 case 14: /* VABD */
4291 GEN_NEON_INTEGER_OP(abd);
4292 break;
4293 case 15: /* VABA */
4294 GEN_NEON_INTEGER_OP(abd);
4295 NEON_GET_REG(T1, rd, pass);
4296 gen_neon_add(size);
4297 break;
4298 case 16:
4299 if (!u) { /* VADD */
4300 if (gen_neon_add(size))
4301 return 1;
4302 } else { /* VSUB */
4303 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004304 case 0: gen_helper_neon_sub_u8(CPU_T001); break;
4305 case 1: gen_helper_neon_sub_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004306 case 2: gen_op_subl_T0_T1(); break;
4307 default: return 1;
4308 }
4309 }
4310 break;
4311 case 17:
4312 if (!u) { /* VTST */
4313 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004314 case 0: gen_helper_neon_tst_u8(CPU_T001); break;
4315 case 1: gen_helper_neon_tst_u16(CPU_T001); break;
4316 case 2: gen_helper_neon_tst_u32(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004317 default: return 1;
4318 }
4319 } else { /* VCEQ */
4320 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004321 case 0: gen_helper_neon_ceq_u8(CPU_T001); break;
4322 case 1: gen_helper_neon_ceq_u16(CPU_T001); break;
4323 case 2: gen_helper_neon_ceq_u32(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004324 default: return 1;
4325 }
4326 }
4327 break;
4328 case 18: /* Multiply. */
4329 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004330 case 0: gen_helper_neon_mul_u8(CPU_T001); break;
4331 case 1: gen_helper_neon_mul_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004332 case 2: gen_op_mul_T0_T1(); break;
4333 default: return 1;
4334 }
4335 NEON_GET_REG(T1, rd, pass);
4336 if (u) { /* VMLS */
pbrookad694712008-03-31 03:48:30 +00004337 gen_neon_rsb(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004338 } else { /* VMLA */
4339 gen_neon_add(size);
4340 }
4341 break;
4342 case 19: /* VMUL */
4343 if (u) { /* polynomial */
pbrookad694712008-03-31 03:48:30 +00004344 gen_helper_neon_mul_p8(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004345 } else { /* Integer */
4346 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004347 case 0: gen_helper_neon_mul_u8(CPU_T001); break;
4348 case 1: gen_helper_neon_mul_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004349 case 2: gen_op_mul_T0_T1(); break;
4350 default: return 1;
4351 }
4352 }
4353 break;
4354 case 20: /* VPMAX */
4355 GEN_NEON_INTEGER_OP(pmax);
4356 break;
4357 case 21: /* VPMIN */
4358 GEN_NEON_INTEGER_OP(pmin);
4359 break;
4360 case 22: /* Hultiply high. */
4361 if (!u) { /* VQDMULH */
4362 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004363 case 1: gen_helper_neon_qdmulh_s16(CPU_T0E01); break;
4364 case 2: gen_helper_neon_qdmulh_s32(CPU_T0E01); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004365 default: return 1;
4366 }
4367 } else { /* VQRDHMUL */
4368 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004369 case 1: gen_helper_neon_qrdmulh_s16(CPU_T0E01); break;
4370 case 2: gen_helper_neon_qrdmulh_s32(CPU_T0E01); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004371 default: return 1;
4372 }
4373 }
4374 break;
4375 case 23: /* VPADD */
4376 if (u)
4377 return 1;
4378 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004379 case 0: gen_helper_neon_padd_u8(CPU_T001); break;
4380 case 1: gen_helper_neon_padd_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004381 case 2: gen_op_addl_T0_T1(); break;
4382 default: return 1;
4383 }
4384 break;
4385 case 26: /* Floating point arithnetic. */
4386 switch ((u << 2) | size) {
4387 case 0: /* VADD */
pbrookad694712008-03-31 03:48:30 +00004388 gen_helper_neon_add_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004389 break;
4390 case 2: /* VSUB */
pbrookad694712008-03-31 03:48:30 +00004391 gen_helper_neon_sub_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004392 break;
4393 case 4: /* VPADD */
pbrookad694712008-03-31 03:48:30 +00004394 gen_helper_neon_add_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004395 break;
4396 case 6: /* VABD */
pbrookad694712008-03-31 03:48:30 +00004397 gen_helper_neon_abd_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004398 break;
4399 default:
4400 return 1;
4401 }
4402 break;
4403 case 27: /* Float multiply. */
pbrookad694712008-03-31 03:48:30 +00004404 gen_helper_neon_mul_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004405 if (!u) {
4406 NEON_GET_REG(T1, rd, pass);
4407 if (size == 0) {
pbrookad694712008-03-31 03:48:30 +00004408 gen_helper_neon_add_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004409 } else {
pbrookad694712008-03-31 03:48:30 +00004410 gen_helper_neon_sub_f32(cpu_T[0], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00004411 }
4412 }
4413 break;
4414 case 28: /* Float compare. */
4415 if (!u) {
pbrookad694712008-03-31 03:48:30 +00004416 gen_helper_neon_ceq_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004417 } else {
4418 if (size == 0)
pbrookad694712008-03-31 03:48:30 +00004419 gen_helper_neon_cge_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004420 else
pbrookad694712008-03-31 03:48:30 +00004421 gen_helper_neon_cgt_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004422 }
4423 break;
4424 case 29: /* Float compare absolute. */
4425 if (!u)
4426 return 1;
4427 if (size == 0)
pbrookad694712008-03-31 03:48:30 +00004428 gen_helper_neon_acge_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004429 else
pbrookad694712008-03-31 03:48:30 +00004430 gen_helper_neon_acgt_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004431 break;
4432 case 30: /* Float min/max. */
4433 if (size == 0)
pbrookad694712008-03-31 03:48:30 +00004434 gen_helper_neon_max_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004435 else
pbrookad694712008-03-31 03:48:30 +00004436 gen_helper_neon_min_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00004437 break;
4438 case 31:
4439 if (size == 0)
pbrook4373f3c2008-03-31 03:47:19 +00004440 gen_helper_recps_f32(cpu_T[0], cpu_T[0], cpu_T[1], cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00004441 else
pbrook4373f3c2008-03-31 03:47:19 +00004442 gen_helper_rsqrts_f32(cpu_T[0], cpu_T[0], cpu_T[1], cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00004443 break;
4444 default:
4445 abort();
4446 }
4447 /* Save the result. For elementwise operations we can put it
4448 straight into the destination register. For pairwise operations
4449 we have to be careful to avoid clobbering the source operands. */
4450 if (pairwise && rd == rm) {
4451 gen_neon_movl_scratch_T0(pass);
4452 } else {
4453 NEON_SET_REG(T0, rd, pass);
4454 }
4455
4456 } /* for pass */
4457 if (pairwise && rd == rm) {
4458 for (pass = 0; pass < (q ? 4 : 2); pass++) {
4459 gen_neon_movl_T0_scratch(pass);
4460 NEON_SET_REG(T0, rd, pass);
4461 }
4462 }
pbrookad694712008-03-31 03:48:30 +00004463 /* End of 3 register same size operations. */
pbrook9ee6e8b2007-11-11 00:04:49 +00004464 } else if (insn & (1 << 4)) {
4465 if ((insn & 0x00380080) != 0) {
4466 /* Two registers and shift. */
4467 op = (insn >> 8) & 0xf;
4468 if (insn & (1 << 7)) {
4469 /* 64-bit shift. */
4470 size = 3;
4471 } else {
4472 size = 2;
4473 while ((insn & (1 << (size + 19))) == 0)
4474 size--;
4475 }
4476 shift = (insn >> 16) & ((1 << (3 + size)) - 1);
4477 /* To avoid excessive dumplication of ops we implement shift
4478 by immediate using the variable shift operations. */
4479 if (op < 8) {
4480 /* Shift by immediate:
4481 VSHR, VSRA, VRSHR, VRSRA, VSRI, VSHL, VQSHL, VQSHLU. */
4482 /* Right shifts are encoded as N - shift, where N is the
4483 element size in bits. */
4484 if (op <= 4)
4485 shift = shift - (1 << (size + 3));
pbrook9ee6e8b2007-11-11 00:04:49 +00004486 if (size == 3) {
4487 count = q + 1;
4488 } else {
4489 count = q ? 4: 2;
4490 }
4491 switch (size) {
4492 case 0:
4493 imm = (uint8_t) shift;
4494 imm |= imm << 8;
4495 imm |= imm << 16;
4496 break;
4497 case 1:
4498 imm = (uint16_t) shift;
4499 imm |= imm << 16;
4500 break;
4501 case 2:
4502 case 3:
4503 imm = shift;
4504 break;
4505 default:
4506 abort();
4507 }
4508
4509 for (pass = 0; pass < count; pass++) {
pbrookad694712008-03-31 03:48:30 +00004510 if (size == 3) {
4511 neon_load_reg64(cpu_V0, rm + pass);
4512 tcg_gen_movi_i64(cpu_V1, imm);
4513 switch (op) {
4514 case 0: /* VSHR */
4515 case 1: /* VSRA */
4516 if (u)
4517 gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
4518 else
4519 gen_helper_neon_shl_s64(cpu_V0, cpu_V0, cpu_V1);
4520 break;
4521 case 2: /* VRSHR */
4522 case 3: /* VRSRA */
4523 if (u)
4524 gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, cpu_V1);
4525 else
4526 gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, cpu_V1);
4527 break;
4528 case 4: /* VSRI */
4529 if (!u)
4530 return 1;
4531 gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
4532 break;
4533 case 5: /* VSHL, VSLI */
4534 gen_helper_neon_shl_u64(cpu_V0, cpu_V0, cpu_V1);
4535 break;
4536 case 6: /* VQSHL */
4537 if (u)
4538 gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
4539 else
4540 gen_helper_neon_qshl_s64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
4541 break;
4542 case 7: /* VQSHLU */
4543 gen_helper_neon_qshl_u64(cpu_V0, cpu_env, cpu_V0, cpu_V1);
4544 break;
4545 }
4546 if (op == 1 || op == 3) {
4547 /* Accumulate. */
4548 neon_load_reg64(cpu_V0, rd + pass);
4549 tcg_gen_add_i64(cpu_V0, cpu_V0, cpu_V1);
4550 } else if (op == 4 || (op == 5 && u)) {
4551 /* Insert */
4552 cpu_abort(env, "VS[LR]I.64 not implemented");
4553 }
4554 neon_store_reg64(cpu_V0, rd + pass);
4555 } else { /* size < 3 */
pbrook9ee6e8b2007-11-11 00:04:49 +00004556 /* Operands in T0 and T1. */
4557 gen_op_movl_T1_im(imm);
4558 NEON_GET_REG(T0, rm, pass);
pbrookad694712008-03-31 03:48:30 +00004559 switch (op) {
4560 case 0: /* VSHR */
4561 case 1: /* VSRA */
4562 GEN_NEON_INTEGER_OP(shl);
4563 break;
4564 case 2: /* VRSHR */
4565 case 3: /* VRSRA */
4566 GEN_NEON_INTEGER_OP(rshl);
4567 break;
4568 case 4: /* VSRI */
4569 if (!u)
4570 return 1;
4571 GEN_NEON_INTEGER_OP(shl);
4572 break;
4573 case 5: /* VSHL, VSLI */
4574 switch (size) {
4575 case 0: gen_helper_neon_shl_u8(CPU_T001); break;
4576 case 1: gen_helper_neon_shl_u16(CPU_T001); break;
4577 case 2: gen_helper_neon_shl_u32(CPU_T001); break;
4578 default: return 1;
4579 }
4580 break;
4581 case 6: /* VQSHL */
4582 GEN_NEON_INTEGER_OP_ENV(qshl);
4583 break;
4584 case 7: /* VQSHLU */
4585 switch (size) {
4586 case 0: gen_helper_neon_qshl_u8(CPU_T0E01); break;
4587 case 1: gen_helper_neon_qshl_u16(CPU_T0E01); break;
4588 case 2: gen_helper_neon_qshl_u32(CPU_T0E01); break;
4589 default: return 1;
4590 }
4591 break;
4592 }
pbrook9ee6e8b2007-11-11 00:04:49 +00004593
pbrookad694712008-03-31 03:48:30 +00004594 if (op == 1 || op == 3) {
4595 /* Accumulate. */
pbrook9ee6e8b2007-11-11 00:04:49 +00004596 NEON_GET_REG(T1, rd, pass);
4597 gen_neon_add(size);
pbrookad694712008-03-31 03:48:30 +00004598 } else if (op == 4 || (op == 5 && u)) {
4599 /* Insert */
4600 switch (size) {
4601 case 0:
4602 if (op == 4)
4603 imm = 0xff >> -shift;
4604 else
4605 imm = (uint8_t)(0xff << shift);
4606 imm |= imm << 8;
4607 imm |= imm << 16;
4608 break;
4609 case 1:
4610 if (op == 4)
4611 imm = 0xffff >> -shift;
4612 else
4613 imm = (uint16_t)(0xffff << shift);
4614 imm |= imm << 16;
4615 break;
4616 case 2:
4617 if (op == 4)
4618 imm = 0xffffffffu >> -shift;
4619 else
4620 imm = 0xffffffffu << shift;
4621 break;
4622 default:
4623 abort();
4624 }
4625 tmp = neon_load_reg(rd, pass);
4626 tcg_gen_andi_i32(cpu_T[0], cpu_T[0], imm);
4627 tcg_gen_andi_i32(tmp, tmp, ~imm);
4628 tcg_gen_or_i32(cpu_T[0], cpu_T[0], tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004629 }
pbrook9ee6e8b2007-11-11 00:04:49 +00004630 NEON_SET_REG(T0, rd, pass);
4631 }
4632 } /* for pass */
4633 } else if (op < 10) {
pbrookad694712008-03-31 03:48:30 +00004634 /* Shift by immediate and narrow:
pbrook9ee6e8b2007-11-11 00:04:49 +00004635 VSHRN, VRSHRN, VQSHRN, VQRSHRN. */
4636 shift = shift - (1 << (size + 3));
4637 size++;
pbrook9ee6e8b2007-11-11 00:04:49 +00004638 switch (size) {
4639 case 1:
pbrookad694712008-03-31 03:48:30 +00004640 imm = (uint16_t)shift;
pbrook9ee6e8b2007-11-11 00:04:49 +00004641 imm |= imm << 16;
pbrookad694712008-03-31 03:48:30 +00004642 tmp2 = tcg_const_i32(imm);
pbrooka7812ae2008-11-17 14:43:54 +00004643 TCGV_UNUSED_I64(tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00004644 break;
4645 case 2:
pbrookad694712008-03-31 03:48:30 +00004646 imm = (uint32_t)shift;
4647 tmp2 = tcg_const_i32(imm);
pbrooka7812ae2008-11-17 14:43:54 +00004648 TCGV_UNUSED_I64(tmp64);
balrog4cc633c2008-12-07 13:32:09 +00004649 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004650 case 3:
pbrooka7812ae2008-11-17 14:43:54 +00004651 tmp64 = tcg_const_i64(shift);
4652 TCGV_UNUSED(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00004653 break;
4654 default:
4655 abort();
4656 }
4657
pbrookad694712008-03-31 03:48:30 +00004658 for (pass = 0; pass < 2; pass++) {
4659 if (size == 3) {
4660 neon_load_reg64(cpu_V0, rm + pass);
4661 if (q) {
4662 if (u)
pbrooka7812ae2008-11-17 14:43:54 +00004663 gen_helper_neon_rshl_u64(cpu_V0, cpu_V0, tmp64);
pbrookad694712008-03-31 03:48:30 +00004664 else
pbrooka7812ae2008-11-17 14:43:54 +00004665 gen_helper_neon_rshl_s64(cpu_V0, cpu_V0, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00004666 } else {
pbrookad694712008-03-31 03:48:30 +00004667 if (u)
pbrooka7812ae2008-11-17 14:43:54 +00004668 gen_helper_neon_shl_u64(cpu_V0, cpu_V0, tmp64);
pbrookad694712008-03-31 03:48:30 +00004669 else
pbrooka7812ae2008-11-17 14:43:54 +00004670 gen_helper_neon_shl_s64(cpu_V0, cpu_V0, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00004671 }
pbrookad694712008-03-31 03:48:30 +00004672 } else {
4673 tmp = neon_load_reg(rm + pass, 0);
4674 gen_neon_shift_narrow(size, tmp, tmp2, q, u);
pbrook36aa55d2008-09-21 13:48:32 +00004675 tmp3 = neon_load_reg(rm + pass, 1);
4676 gen_neon_shift_narrow(size, tmp3, tmp2, q, u);
4677 tcg_gen_concat_i32_i64(cpu_V0, tmp, tmp3);
pbrookad694712008-03-31 03:48:30 +00004678 dead_tmp(tmp);
pbrook36aa55d2008-09-21 13:48:32 +00004679 dead_tmp(tmp3);
pbrookad694712008-03-31 03:48:30 +00004680 }
4681 tmp = new_tmp();
4682 if (op == 8 && !u) {
4683 gen_neon_narrow(size - 1, tmp, cpu_V0);
4684 } else {
4685 if (op == 8)
4686 gen_neon_narrow_sats(size - 1, tmp, cpu_V0);
pbrook9ee6e8b2007-11-11 00:04:49 +00004687 else
pbrookad694712008-03-31 03:48:30 +00004688 gen_neon_narrow_satu(size - 1, tmp, cpu_V0);
4689 }
4690 if (pass == 0) {
4691 tmp2 = tmp;
4692 } else {
4693 neon_store_reg(rd, 0, tmp2);
4694 neon_store_reg(rd, 1, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004695 }
4696 } /* for pass */
4697 } else if (op == 10) {
4698 /* VSHLL */
pbrookad694712008-03-31 03:48:30 +00004699 if (q || size == 3)
pbrook9ee6e8b2007-11-11 00:04:49 +00004700 return 1;
pbrookad694712008-03-31 03:48:30 +00004701 tmp = neon_load_reg(rm, 0);
4702 tmp2 = neon_load_reg(rm, 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00004703 for (pass = 0; pass < 2; pass++) {
pbrookad694712008-03-31 03:48:30 +00004704 if (pass == 1)
4705 tmp = tmp2;
pbrook9ee6e8b2007-11-11 00:04:49 +00004706
pbrookad694712008-03-31 03:48:30 +00004707 gen_neon_widen(cpu_V0, tmp, size, u);
4708
pbrook9ee6e8b2007-11-11 00:04:49 +00004709 if (shift != 0) {
4710 /* The shift is less than the width of the source
pbrookad694712008-03-31 03:48:30 +00004711 type, so we can just shift the whole register. */
4712 tcg_gen_shli_i64(cpu_V0, cpu_V0, shift);
4713 if (size < 2 || !u) {
4714 uint64_t imm64;
4715 if (size == 0) {
4716 imm = (0xffu >> (8 - shift));
4717 imm |= imm << 16;
4718 } else {
4719 imm = 0xffff >> (16 - shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00004720 }
pbrookad694712008-03-31 03:48:30 +00004721 imm64 = imm | (((uint64_t)imm) << 32);
4722 tcg_gen_andi_i64(cpu_V0, cpu_V0, imm64);
pbrook9ee6e8b2007-11-11 00:04:49 +00004723 }
4724 }
pbrookad694712008-03-31 03:48:30 +00004725 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004726 }
4727 } else if (op == 15 || op == 16) {
4728 /* VCVT fixed-point. */
4729 for (pass = 0; pass < (q ? 4 : 2); pass++) {
pbrook4373f3c2008-03-31 03:47:19 +00004730 tcg_gen_ld_f32(cpu_F0s, cpu_env, neon_reg_offset(rm, pass));
pbrook9ee6e8b2007-11-11 00:04:49 +00004731 if (op & 1) {
4732 if (u)
pbrook4373f3c2008-03-31 03:47:19 +00004733 gen_vfp_ulto(0, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00004734 else
pbrook4373f3c2008-03-31 03:47:19 +00004735 gen_vfp_slto(0, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00004736 } else {
4737 if (u)
pbrook4373f3c2008-03-31 03:47:19 +00004738 gen_vfp_toul(0, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00004739 else
pbrook4373f3c2008-03-31 03:47:19 +00004740 gen_vfp_tosl(0, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00004741 }
pbrook4373f3c2008-03-31 03:47:19 +00004742 tcg_gen_st_f32(cpu_F0s, cpu_env, neon_reg_offset(rd, pass));
pbrook9ee6e8b2007-11-11 00:04:49 +00004743 }
4744 } else {
4745 return 1;
4746 }
4747 } else { /* (insn & 0x00380080) == 0 */
4748 int invert;
4749
4750 op = (insn >> 8) & 0xf;
4751 /* One register and immediate. */
4752 imm = (u << 7) | ((insn >> 12) & 0x70) | (insn & 0xf);
4753 invert = (insn & (1 << 5)) != 0;
4754 switch (op) {
4755 case 0: case 1:
4756 /* no-op */
4757 break;
4758 case 2: case 3:
4759 imm <<= 8;
4760 break;
4761 case 4: case 5:
4762 imm <<= 16;
4763 break;
4764 case 6: case 7:
4765 imm <<= 24;
4766 break;
4767 case 8: case 9:
4768 imm |= imm << 16;
4769 break;
4770 case 10: case 11:
4771 imm = (imm << 8) | (imm << 24);
4772 break;
4773 case 12:
4774 imm = (imm < 8) | 0xff;
4775 break;
4776 case 13:
4777 imm = (imm << 16) | 0xffff;
4778 break;
4779 case 14:
4780 imm |= (imm << 8) | (imm << 16) | (imm << 24);
4781 if (invert)
4782 imm = ~imm;
4783 break;
4784 case 15:
4785 imm = ((imm & 0x80) << 24) | ((imm & 0x3f) << 19)
4786 | ((imm & 0x40) ? (0x1f << 25) : (1 << 30));
4787 break;
4788 }
4789 if (invert)
4790 imm = ~imm;
4791
4792 if (op != 14 || !invert)
4793 gen_op_movl_T1_im(imm);
4794
4795 for (pass = 0; pass < (q ? 4 : 2); pass++) {
4796 if (op & 1 && op < 12) {
pbrookad694712008-03-31 03:48:30 +00004797 tmp = neon_load_reg(rd, pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004798 if (invert) {
4799 /* The immediate value has already been inverted, so
4800 BIC becomes AND. */
pbrookad694712008-03-31 03:48:30 +00004801 tcg_gen_andi_i32(tmp, tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00004802 } else {
pbrookad694712008-03-31 03:48:30 +00004803 tcg_gen_ori_i32(tmp, tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00004804 }
pbrook9ee6e8b2007-11-11 00:04:49 +00004805 } else {
pbrookad694712008-03-31 03:48:30 +00004806 /* VMOV, VMVN. */
4807 tmp = new_tmp();
pbrook9ee6e8b2007-11-11 00:04:49 +00004808 if (op == 14 && invert) {
pbrookad694712008-03-31 03:48:30 +00004809 uint32_t val;
4810 val = 0;
pbrook9ee6e8b2007-11-11 00:04:49 +00004811 for (n = 0; n < 4; n++) {
4812 if (imm & (1 << (n + (pass & 1) * 4)))
pbrookad694712008-03-31 03:48:30 +00004813 val |= 0xff << (n * 8);
pbrook9ee6e8b2007-11-11 00:04:49 +00004814 }
pbrookad694712008-03-31 03:48:30 +00004815 tcg_gen_movi_i32(tmp, val);
4816 } else {
4817 tcg_gen_movi_i32(tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00004818 }
pbrook9ee6e8b2007-11-11 00:04:49 +00004819 }
pbrookad694712008-03-31 03:48:30 +00004820 neon_store_reg(rd, pass, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004821 }
4822 }
pbrooke4b38612008-09-21 23:15:38 +00004823 } else { /* (insn & 0x00800010 == 0x00800000) */
pbrook9ee6e8b2007-11-11 00:04:49 +00004824 if (size != 3) {
4825 op = (insn >> 8) & 0xf;
4826 if ((insn & (1 << 6)) == 0) {
4827 /* Three registers of different lengths. */
4828 int src1_wide;
4829 int src2_wide;
4830 int prewiden;
4831 /* prewiden, src1_wide, src2_wide */
4832 static const int neon_3reg_wide[16][3] = {
4833 {1, 0, 0}, /* VADDL */
4834 {1, 1, 0}, /* VADDW */
4835 {1, 0, 0}, /* VSUBL */
4836 {1, 1, 0}, /* VSUBW */
4837 {0, 1, 1}, /* VADDHN */
4838 {0, 0, 0}, /* VABAL */
4839 {0, 1, 1}, /* VSUBHN */
4840 {0, 0, 0}, /* VABDL */
4841 {0, 0, 0}, /* VMLAL */
4842 {0, 0, 0}, /* VQDMLAL */
4843 {0, 0, 0}, /* VMLSL */
4844 {0, 0, 0}, /* VQDMLSL */
4845 {0, 0, 0}, /* Integer VMULL */
4846 {0, 0, 0}, /* VQDMULL */
4847 {0, 0, 0} /* Polynomial VMULL */
4848 };
4849
4850 prewiden = neon_3reg_wide[op][0];
4851 src1_wide = neon_3reg_wide[op][1];
4852 src2_wide = neon_3reg_wide[op][2];
4853
pbrookad694712008-03-31 03:48:30 +00004854 if (size == 0 && (op == 9 || op == 11 || op == 13))
4855 return 1;
4856
pbrook9ee6e8b2007-11-11 00:04:49 +00004857 /* Avoid overlapping operands. Wide source operands are
4858 always aligned so will never overlap with wide
4859 destinations in problematic ways. */
pbrook8f8e3aa2008-03-31 03:48:01 +00004860 if (rd == rm && !src2_wide) {
4861 NEON_GET_REG(T0, rm, 1);
4862 gen_neon_movl_scratch_T0(2);
4863 } else if (rd == rn && !src1_wide) {
4864 NEON_GET_REG(T0, rn, 1);
4865 gen_neon_movl_scratch_T0(2);
pbrook9ee6e8b2007-11-11 00:04:49 +00004866 }
pbrooka50f5b92008-06-29 15:25:29 +00004867 TCGV_UNUSED(tmp3);
pbrook9ee6e8b2007-11-11 00:04:49 +00004868 for (pass = 0; pass < 2; pass++) {
pbrook8f8e3aa2008-03-31 03:48:01 +00004869 if (src1_wide) {
pbrookad694712008-03-31 03:48:30 +00004870 neon_load_reg64(cpu_V0, rn + pass);
pbrooka50f5b92008-06-29 15:25:29 +00004871 TCGV_UNUSED(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004872 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00004873 if (pass == 1 && rd == rn) {
4874 gen_neon_movl_T0_scratch(2);
pbrookad694712008-03-31 03:48:30 +00004875 tmp = new_tmp();
4876 tcg_gen_mov_i32(tmp, cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00004877 } else {
pbrookad694712008-03-31 03:48:30 +00004878 tmp = neon_load_reg(rn, pass);
4879 }
4880 if (prewiden) {
4881 gen_neon_widen(cpu_V0, tmp, size, u);
pbrook9ee6e8b2007-11-11 00:04:49 +00004882 }
4883 }
pbrookad694712008-03-31 03:48:30 +00004884 if (src2_wide) {
4885 neon_load_reg64(cpu_V1, rm + pass);
pbrooka50f5b92008-06-29 15:25:29 +00004886 TCGV_UNUSED(tmp2);
pbrookad694712008-03-31 03:48:30 +00004887 } else {
4888 if (pass == 1 && rd == rm) {
4889 gen_neon_movl_T0_scratch(2);
4890 tmp2 = new_tmp();
4891 tcg_gen_mov_i32(tmp2, cpu_T[0]);
4892 } else {
4893 tmp2 = neon_load_reg(rm, pass);
4894 }
4895 if (prewiden) {
4896 gen_neon_widen(cpu_V1, tmp2, size, u);
4897 }
pbrook9ee6e8b2007-11-11 00:04:49 +00004898 }
4899 switch (op) {
4900 case 0: case 1: case 4: /* VADDL, VADDW, VADDHN, VRADDHN */
pbrookad694712008-03-31 03:48:30 +00004901 gen_neon_addl(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004902 break;
4903 case 2: case 3: case 6: /* VSUBL, VSUBW, VSUBHL, VRSUBHL */
pbrookad694712008-03-31 03:48:30 +00004904 gen_neon_subl(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004905 break;
4906 case 5: case 7: /* VABAL, VABDL */
4907 switch ((size << 1) | u) {
pbrookad694712008-03-31 03:48:30 +00004908 case 0:
4909 gen_helper_neon_abdl_s16(cpu_V0, tmp, tmp2);
4910 break;
4911 case 1:
4912 gen_helper_neon_abdl_u16(cpu_V0, tmp, tmp2);
4913 break;
4914 case 2:
4915 gen_helper_neon_abdl_s32(cpu_V0, tmp, tmp2);
4916 break;
4917 case 3:
4918 gen_helper_neon_abdl_u32(cpu_V0, tmp, tmp2);
4919 break;
4920 case 4:
4921 gen_helper_neon_abdl_s64(cpu_V0, tmp, tmp2);
4922 break;
4923 case 5:
4924 gen_helper_neon_abdl_u64(cpu_V0, tmp, tmp2);
4925 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004926 default: abort();
4927 }
pbrookad694712008-03-31 03:48:30 +00004928 dead_tmp(tmp2);
4929 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00004930 break;
4931 case 8: case 9: case 10: case 11: case 12: case 13:
4932 /* VMLAL, VQDMLAL, VMLSL, VQDMLSL, VMULL, VQDMULL */
pbrookad694712008-03-31 03:48:30 +00004933 gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
pbrook9ee6e8b2007-11-11 00:04:49 +00004934 break;
4935 case 14: /* Polynomial VMULL */
4936 cpu_abort(env, "Polynomial VMULL not implemented");
4937
4938 default: /* 15 is RESERVED. */
4939 return 1;
4940 }
4941 if (op == 5 || op == 13 || (op >= 8 && op <= 11)) {
4942 /* Accumulate. */
4943 if (op == 10 || op == 11) {
pbrookad694712008-03-31 03:48:30 +00004944 gen_neon_negl(cpu_V0, size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004945 }
4946
pbrook9ee6e8b2007-11-11 00:04:49 +00004947 if (op != 13) {
pbrookad694712008-03-31 03:48:30 +00004948 neon_load_reg64(cpu_V1, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004949 }
4950
4951 switch (op) {
4952 case 5: case 8: case 10: /* VABAL, VMLAL, VMLSL */
pbrookad694712008-03-31 03:48:30 +00004953 gen_neon_addl(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004954 break;
4955 case 9: case 11: /* VQDMLAL, VQDMLSL */
pbrookad694712008-03-31 03:48:30 +00004956 gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
4957 gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
4958 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004959 /* Fall through. */
4960 case 13: /* VQDMULL */
pbrookad694712008-03-31 03:48:30 +00004961 gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
pbrook9ee6e8b2007-11-11 00:04:49 +00004962 break;
4963 default:
4964 abort();
4965 }
pbrookad694712008-03-31 03:48:30 +00004966 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00004967 } else if (op == 4 || op == 6) {
4968 /* Narrowing operation. */
pbrookad694712008-03-31 03:48:30 +00004969 tmp = new_tmp();
pbrook9ee6e8b2007-11-11 00:04:49 +00004970 if (u) {
4971 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004972 case 0:
4973 gen_helper_neon_narrow_high_u8(tmp, cpu_V0);
4974 break;
4975 case 1:
4976 gen_helper_neon_narrow_high_u16(tmp, cpu_V0);
4977 break;
4978 case 2:
4979 tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
4980 tcg_gen_trunc_i64_i32(tmp, cpu_V0);
4981 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004982 default: abort();
4983 }
4984 } else {
4985 switch (size) {
pbrookad694712008-03-31 03:48:30 +00004986 case 0:
4987 gen_helper_neon_narrow_round_high_u8(tmp, cpu_V0);
4988 break;
4989 case 1:
4990 gen_helper_neon_narrow_round_high_u16(tmp, cpu_V0);
4991 break;
4992 case 2:
4993 tcg_gen_addi_i64(cpu_V0, cpu_V0, 1u << 31);
4994 tcg_gen_shri_i64(cpu_V0, cpu_V0, 32);
4995 tcg_gen_trunc_i64_i32(tmp, cpu_V0);
4996 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00004997 default: abort();
4998 }
4999 }
pbrookad694712008-03-31 03:48:30 +00005000 if (pass == 0) {
5001 tmp3 = tmp;
5002 } else {
5003 neon_store_reg(rd, 0, tmp3);
5004 neon_store_reg(rd, 1, tmp);
5005 }
pbrook9ee6e8b2007-11-11 00:04:49 +00005006 } else {
5007 /* Write back the result. */
pbrookad694712008-03-31 03:48:30 +00005008 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00005009 }
5010 }
5011 } else {
5012 /* Two registers and a scalar. */
5013 switch (op) {
5014 case 0: /* Integer VMLA scalar */
5015 case 1: /* Float VMLA scalar */
5016 case 4: /* Integer VMLS scalar */
5017 case 5: /* Floating point VMLS scalar */
5018 case 8: /* Integer VMUL scalar */
5019 case 9: /* Floating point VMUL scalar */
5020 case 12: /* VQDMULH scalar */
5021 case 13: /* VQRDMULH scalar */
5022 gen_neon_get_scalar(size, rm);
pbrook8f8e3aa2008-03-31 03:48:01 +00005023 gen_neon_movl_scratch_T0(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005024 for (pass = 0; pass < (u ? 4 : 2); pass++) {
5025 if (pass != 0)
pbrook8f8e3aa2008-03-31 03:48:01 +00005026 gen_neon_movl_T0_scratch(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005027 NEON_GET_REG(T1, rn, pass);
5028 if (op == 12) {
5029 if (size == 1) {
pbrookad694712008-03-31 03:48:30 +00005030 gen_helper_neon_qdmulh_s16(CPU_T0E01);
pbrook9ee6e8b2007-11-11 00:04:49 +00005031 } else {
pbrookad694712008-03-31 03:48:30 +00005032 gen_helper_neon_qdmulh_s32(CPU_T0E01);
pbrook9ee6e8b2007-11-11 00:04:49 +00005033 }
5034 } else if (op == 13) {
5035 if (size == 1) {
pbrookad694712008-03-31 03:48:30 +00005036 gen_helper_neon_qrdmulh_s16(CPU_T0E01);
pbrook9ee6e8b2007-11-11 00:04:49 +00005037 } else {
pbrookad694712008-03-31 03:48:30 +00005038 gen_helper_neon_qrdmulh_s32(CPU_T0E01);
pbrook9ee6e8b2007-11-11 00:04:49 +00005039 }
5040 } else if (op & 1) {
pbrookad694712008-03-31 03:48:30 +00005041 gen_helper_neon_mul_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00005042 } else {
5043 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005044 case 0: gen_helper_neon_mul_u8(CPU_T001); break;
5045 case 1: gen_helper_neon_mul_u16(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005046 case 2: gen_op_mul_T0_T1(); break;
5047 default: return 1;
5048 }
5049 }
5050 if (op < 8) {
5051 /* Accumulate. */
5052 NEON_GET_REG(T1, rd, pass);
5053 switch (op) {
5054 case 0:
5055 gen_neon_add(size);
5056 break;
5057 case 1:
pbrookad694712008-03-31 03:48:30 +00005058 gen_helper_neon_add_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00005059 break;
5060 case 4:
pbrookad694712008-03-31 03:48:30 +00005061 gen_neon_rsb(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005062 break;
5063 case 5:
pbrookad694712008-03-31 03:48:30 +00005064 gen_helper_neon_sub_f32(cpu_T[0], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005065 break;
5066 default:
5067 abort();
5068 }
5069 }
5070 NEON_SET_REG(T0, rd, pass);
5071 }
5072 break;
5073 case 2: /* VMLAL sclar */
5074 case 3: /* VQDMLAL scalar */
5075 case 6: /* VMLSL scalar */
5076 case 7: /* VQDMLSL scalar */
5077 case 10: /* VMULL scalar */
5078 case 11: /* VQDMULL scalar */
pbrookad694712008-03-31 03:48:30 +00005079 if (size == 0 && (op == 3 || op == 7 || op == 11))
5080 return 1;
5081
pbrook9ee6e8b2007-11-11 00:04:49 +00005082 gen_neon_get_scalar(size, rm);
pbrookad694712008-03-31 03:48:30 +00005083 NEON_GET_REG(T1, rn, 1);
5084
pbrook9ee6e8b2007-11-11 00:04:49 +00005085 for (pass = 0; pass < 2; pass++) {
pbrookad694712008-03-31 03:48:30 +00005086 if (pass == 0) {
5087 tmp = neon_load_reg(rn, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005088 } else {
pbrookad694712008-03-31 03:48:30 +00005089 tmp = new_tmp();
5090 tcg_gen_mov_i32(tmp, cpu_T[1]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005091 }
pbrookad694712008-03-31 03:48:30 +00005092 tmp2 = new_tmp();
5093 tcg_gen_mov_i32(tmp2, cpu_T[0]);
5094 gen_neon_mull(cpu_V0, tmp, tmp2, size, u);
pbrook9ee6e8b2007-11-11 00:04:49 +00005095 if (op == 6 || op == 7) {
pbrookad694712008-03-31 03:48:30 +00005096 gen_neon_negl(cpu_V0, size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005097 }
pbrookad694712008-03-31 03:48:30 +00005098 if (op != 11) {
5099 neon_load_reg64(cpu_V1, rd + pass);
5100 }
pbrook9ee6e8b2007-11-11 00:04:49 +00005101 switch (op) {
5102 case 2: case 6:
pbrookad694712008-03-31 03:48:30 +00005103 gen_neon_addl(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005104 break;
5105 case 3: case 7:
pbrookad694712008-03-31 03:48:30 +00005106 gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
5107 gen_neon_addl_saturate(cpu_V0, cpu_V1, size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005108 break;
5109 case 10:
5110 /* no-op */
5111 break;
5112 case 11:
pbrookad694712008-03-31 03:48:30 +00005113 gen_neon_addl_saturate(cpu_V0, cpu_V0, size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005114 break;
5115 default:
5116 abort();
5117 }
pbrookad694712008-03-31 03:48:30 +00005118 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00005119 }
5120 break;
5121 default: /* 14 and 15 are RESERVED */
5122 return 1;
5123 }
5124 }
5125 } else { /* size == 3 */
5126 if (!u) {
5127 /* Extract. */
pbrook9ee6e8b2007-11-11 00:04:49 +00005128 imm = (insn >> 8) & 0xf;
pbrookad694712008-03-31 03:48:30 +00005129 count = q + 1;
5130
5131 if (imm > 7 && !q)
5132 return 1;
5133
5134 if (imm == 0) {
5135 neon_load_reg64(cpu_V0, rn);
5136 if (q) {
5137 neon_load_reg64(cpu_V1, rn + 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00005138 }
pbrookad694712008-03-31 03:48:30 +00005139 } else if (imm == 8) {
5140 neon_load_reg64(cpu_V0, rn + 1);
5141 if (q) {
5142 neon_load_reg64(cpu_V1, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00005143 }
pbrookad694712008-03-31 03:48:30 +00005144 } else if (q) {
pbrooka7812ae2008-11-17 14:43:54 +00005145 tmp64 = tcg_temp_new_i64();
pbrookad694712008-03-31 03:48:30 +00005146 if (imm < 8) {
5147 neon_load_reg64(cpu_V0, rn);
pbrooka7812ae2008-11-17 14:43:54 +00005148 neon_load_reg64(tmp64, rn + 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00005149 } else {
pbrookad694712008-03-31 03:48:30 +00005150 neon_load_reg64(cpu_V0, rn + 1);
pbrooka7812ae2008-11-17 14:43:54 +00005151 neon_load_reg64(tmp64, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00005152 }
pbrookad694712008-03-31 03:48:30 +00005153 tcg_gen_shri_i64(cpu_V0, cpu_V0, (imm & 7) * 8);
pbrooka7812ae2008-11-17 14:43:54 +00005154 tcg_gen_shli_i64(cpu_V1, tmp64, 64 - ((imm & 7) * 8));
pbrookad694712008-03-31 03:48:30 +00005155 tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
5156 if (imm < 8) {
5157 neon_load_reg64(cpu_V1, rm);
5158 } else {
5159 neon_load_reg64(cpu_V1, rm + 1);
5160 imm -= 8;
5161 }
5162 tcg_gen_shli_i64(cpu_V1, cpu_V1, 64 - (imm * 8));
pbrooka7812ae2008-11-17 14:43:54 +00005163 tcg_gen_shri_i64(tmp64, tmp64, imm * 8);
5164 tcg_gen_or_i64(cpu_V1, cpu_V1, tmp64);
pbrookad694712008-03-31 03:48:30 +00005165 } else {
pbrooka7812ae2008-11-17 14:43:54 +00005166 /* BUGFIX */
pbrookad694712008-03-31 03:48:30 +00005167 neon_load_reg64(cpu_V0, rn);
pbrooka7812ae2008-11-17 14:43:54 +00005168 tcg_gen_shri_i64(cpu_V0, cpu_V0, imm * 8);
pbrookad694712008-03-31 03:48:30 +00005169 neon_load_reg64(cpu_V1, rm);
pbrooka7812ae2008-11-17 14:43:54 +00005170 tcg_gen_shli_i64(cpu_V1, cpu_V1, 64 - (imm * 8));
pbrookad694712008-03-31 03:48:30 +00005171 tcg_gen_or_i64(cpu_V0, cpu_V0, cpu_V1);
5172 }
5173 neon_store_reg64(cpu_V0, rd);
5174 if (q) {
5175 neon_store_reg64(cpu_V1, rd + 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00005176 }
5177 } else if ((insn & (1 << 11)) == 0) {
5178 /* Two register misc. */
5179 op = ((insn >> 12) & 0x30) | ((insn >> 7) & 0xf);
5180 size = (insn >> 18) & 3;
5181 switch (op) {
5182 case 0: /* VREV64 */
5183 if (size == 3)
5184 return 1;
5185 for (pass = 0; pass < (q ? 2 : 1); pass++) {
5186 NEON_GET_REG(T0, rm, pass * 2);
5187 NEON_GET_REG(T1, rm, pass * 2 + 1);
5188 switch (size) {
aurel3266896cb2009-03-13 09:34:48 +00005189 case 0: tcg_gen_bswap32_i32(cpu_T[0], cpu_T[0]); break;
pbrook8f012452008-03-31 03:46:03 +00005190 case 1: gen_swap_half(cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005191 case 2: /* no-op */ break;
5192 default: abort();
5193 }
5194 NEON_SET_REG(T0, rd, pass * 2 + 1);
5195 if (size == 2) {
5196 NEON_SET_REG(T1, rd, pass * 2);
5197 } else {
5198 gen_op_movl_T0_T1();
5199 switch (size) {
aurel3266896cb2009-03-13 09:34:48 +00005200 case 0: tcg_gen_bswap32_i32(cpu_T[0], cpu_T[0]); break;
pbrook8f012452008-03-31 03:46:03 +00005201 case 1: gen_swap_half(cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005202 default: abort();
5203 }
5204 NEON_SET_REG(T0, rd, pass * 2);
5205 }
5206 }
5207 break;
5208 case 4: case 5: /* VPADDL */
5209 case 12: case 13: /* VPADAL */
pbrook9ee6e8b2007-11-11 00:04:49 +00005210 if (size == 3)
5211 return 1;
pbrookad694712008-03-31 03:48:30 +00005212 for (pass = 0; pass < q + 1; pass++) {
5213 tmp = neon_load_reg(rm, pass * 2);
5214 gen_neon_widen(cpu_V0, tmp, size, op & 1);
5215 tmp = neon_load_reg(rm, pass * 2 + 1);
5216 gen_neon_widen(cpu_V1, tmp, size, op & 1);
5217 switch (size) {
5218 case 0: gen_helper_neon_paddl_u16(CPU_V001); break;
5219 case 1: gen_helper_neon_paddl_u32(CPU_V001); break;
5220 case 2: tcg_gen_add_i64(CPU_V001); break;
5221 default: abort();
5222 }
pbrook9ee6e8b2007-11-11 00:04:49 +00005223 if (op >= 12) {
5224 /* Accumulate. */
pbrookad694712008-03-31 03:48:30 +00005225 neon_load_reg64(cpu_V1, rd + pass);
5226 gen_neon_addl(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005227 }
pbrookad694712008-03-31 03:48:30 +00005228 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00005229 }
5230 break;
5231 case 33: /* VTRN */
5232 if (size == 2) {
5233 for (n = 0; n < (q ? 4 : 2); n += 2) {
5234 NEON_GET_REG(T0, rm, n);
5235 NEON_GET_REG(T1, rd, n + 1);
5236 NEON_SET_REG(T1, rm, n);
5237 NEON_SET_REG(T0, rd, n + 1);
5238 }
5239 } else {
5240 goto elementwise;
5241 }
5242 break;
5243 case 34: /* VUZP */
5244 /* Reg Before After
5245 Rd A3 A2 A1 A0 B2 B0 A2 A0
5246 Rm B3 B2 B1 B0 B3 B1 A3 A1
5247 */
5248 if (size == 3)
5249 return 1;
5250 gen_neon_unzip(rd, q, 0, size);
5251 gen_neon_unzip(rm, q, 4, size);
5252 if (q) {
5253 static int unzip_order_q[8] =
5254 {0, 2, 4, 6, 1, 3, 5, 7};
5255 for (n = 0; n < 8; n++) {
5256 int reg = (n < 4) ? rd : rm;
5257 gen_neon_movl_T0_scratch(unzip_order_q[n]);
5258 NEON_SET_REG(T0, reg, n % 4);
5259 }
5260 } else {
5261 static int unzip_order[4] =
5262 {0, 4, 1, 5};
5263 for (n = 0; n < 4; n++) {
5264 int reg = (n < 2) ? rd : rm;
5265 gen_neon_movl_T0_scratch(unzip_order[n]);
5266 NEON_SET_REG(T0, reg, n % 2);
5267 }
5268 }
5269 break;
5270 case 35: /* VZIP */
5271 /* Reg Before After
5272 Rd A3 A2 A1 A0 B1 A1 B0 A0
5273 Rm B3 B2 B1 B0 B3 A3 B2 A2
5274 */
5275 if (size == 3)
5276 return 1;
5277 count = (q ? 4 : 2);
5278 for (n = 0; n < count; n++) {
5279 NEON_GET_REG(T0, rd, n);
5280 NEON_GET_REG(T1, rd, n);
5281 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005282 case 0: gen_helper_neon_zip_u8(); break;
5283 case 1: gen_helper_neon_zip_u16(); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005284 case 2: /* no-op */; break;
5285 default: abort();
5286 }
5287 gen_neon_movl_scratch_T0(n * 2);
5288 gen_neon_movl_scratch_T1(n * 2 + 1);
5289 }
5290 for (n = 0; n < count * 2; n++) {
5291 int reg = (n < count) ? rd : rm;
5292 gen_neon_movl_T0_scratch(n);
5293 NEON_SET_REG(T0, reg, n % count);
5294 }
5295 break;
5296 case 36: case 37: /* VMOVN, VQMOVUN, VQMOVN */
pbrookad694712008-03-31 03:48:30 +00005297 if (size == 3)
5298 return 1;
pbrooka50f5b92008-06-29 15:25:29 +00005299 TCGV_UNUSED(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00005300 for (pass = 0; pass < 2; pass++) {
pbrookad694712008-03-31 03:48:30 +00005301 neon_load_reg64(cpu_V0, rm + pass);
5302 tmp = new_tmp();
pbrook9ee6e8b2007-11-11 00:04:49 +00005303 if (op == 36 && q == 0) {
pbrookad694712008-03-31 03:48:30 +00005304 gen_neon_narrow(size, tmp, cpu_V0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005305 } else if (q) {
pbrookad694712008-03-31 03:48:30 +00005306 gen_neon_narrow_satu(size, tmp, cpu_V0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005307 } else {
pbrookad694712008-03-31 03:48:30 +00005308 gen_neon_narrow_sats(size, tmp, cpu_V0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005309 }
pbrookad694712008-03-31 03:48:30 +00005310 if (pass == 0) {
5311 tmp2 = tmp;
5312 } else {
5313 neon_store_reg(rd, 0, tmp2);
5314 neon_store_reg(rd, 1, tmp);
5315 }
pbrook9ee6e8b2007-11-11 00:04:49 +00005316 }
5317 break;
5318 case 38: /* VSHLL */
pbrookad694712008-03-31 03:48:30 +00005319 if (q || size == 3)
pbrook9ee6e8b2007-11-11 00:04:49 +00005320 return 1;
pbrookad694712008-03-31 03:48:30 +00005321 tmp = neon_load_reg(rm, 0);
5322 tmp2 = neon_load_reg(rm, 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00005323 for (pass = 0; pass < 2; pass++) {
pbrookad694712008-03-31 03:48:30 +00005324 if (pass == 1)
5325 tmp = tmp2;
5326 gen_neon_widen(cpu_V0, tmp, size, 1);
5327 neon_store_reg64(cpu_V0, rd + pass);
pbrook9ee6e8b2007-11-11 00:04:49 +00005328 }
5329 break;
5330 default:
5331 elementwise:
5332 for (pass = 0; pass < (q ? 4 : 2); pass++) {
5333 if (op == 30 || op == 31 || op >= 58) {
pbrook4373f3c2008-03-31 03:47:19 +00005334 tcg_gen_ld_f32(cpu_F0s, cpu_env,
5335 neon_reg_offset(rm, pass));
pbrook9ee6e8b2007-11-11 00:04:49 +00005336 } else {
5337 NEON_GET_REG(T0, rm, pass);
5338 }
5339 switch (op) {
5340 case 1: /* VREV32 */
5341 switch (size) {
aurel3266896cb2009-03-13 09:34:48 +00005342 case 0: tcg_gen_bswap32_i32(cpu_T[0], cpu_T[0]); break;
pbrook8f012452008-03-31 03:46:03 +00005343 case 1: gen_swap_half(cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005344 default: return 1;
5345 }
5346 break;
5347 case 2: /* VREV16 */
5348 if (size != 0)
5349 return 1;
pbrook36706692008-03-31 03:46:19 +00005350 gen_rev16(cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005351 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005352 case 8: /* CLS */
5353 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005354 case 0: gen_helper_neon_cls_s8(cpu_T[0], cpu_T[0]); break;
5355 case 1: gen_helper_neon_cls_s16(cpu_T[0], cpu_T[0]); break;
5356 case 2: gen_helper_neon_cls_s32(cpu_T[0], cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005357 default: return 1;
5358 }
5359 break;
5360 case 9: /* CLZ */
5361 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005362 case 0: gen_helper_neon_clz_u8(cpu_T[0], cpu_T[0]); break;
5363 case 1: gen_helper_neon_clz_u16(cpu_T[0], cpu_T[0]); break;
pbrook1497c962008-03-31 03:45:50 +00005364 case 2: gen_helper_clz(cpu_T[0], cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005365 default: return 1;
5366 }
5367 break;
5368 case 10: /* CNT */
5369 if (size != 0)
5370 return 1;
pbrookad694712008-03-31 03:48:30 +00005371 gen_helper_neon_cnt_u8(cpu_T[0], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005372 break;
5373 case 11: /* VNOT */
5374 if (size != 0)
5375 return 1;
5376 gen_op_notl_T0();
5377 break;
5378 case 14: /* VQABS */
5379 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005380 case 0: gen_helper_neon_qabs_s8(cpu_T[0], cpu_env, cpu_T[0]); break;
5381 case 1: gen_helper_neon_qabs_s16(cpu_T[0], cpu_env, cpu_T[0]); break;
5382 case 2: gen_helper_neon_qabs_s32(cpu_T[0], cpu_env, cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005383 default: return 1;
5384 }
5385 break;
5386 case 15: /* VQNEG */
5387 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005388 case 0: gen_helper_neon_qneg_s8(cpu_T[0], cpu_env, cpu_T[0]); break;
5389 case 1: gen_helper_neon_qneg_s16(cpu_T[0], cpu_env, cpu_T[0]); break;
5390 case 2: gen_helper_neon_qneg_s32(cpu_T[0], cpu_env, cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005391 default: return 1;
5392 }
5393 break;
5394 case 16: case 19: /* VCGT #0, VCLE #0 */
5395 gen_op_movl_T1_im(0);
5396 switch(size) {
pbrookad694712008-03-31 03:48:30 +00005397 case 0: gen_helper_neon_cgt_s8(CPU_T001); break;
5398 case 1: gen_helper_neon_cgt_s16(CPU_T001); break;
5399 case 2: gen_helper_neon_cgt_s32(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005400 default: return 1;
5401 }
5402 if (op == 19)
5403 gen_op_notl_T0();
5404 break;
5405 case 17: case 20: /* VCGE #0, VCLT #0 */
5406 gen_op_movl_T1_im(0);
5407 switch(size) {
pbrookad694712008-03-31 03:48:30 +00005408 case 0: gen_helper_neon_cge_s8(CPU_T001); break;
5409 case 1: gen_helper_neon_cge_s16(CPU_T001); break;
5410 case 2: gen_helper_neon_cge_s32(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005411 default: return 1;
5412 }
5413 if (op == 20)
5414 gen_op_notl_T0();
5415 break;
5416 case 18: /* VCEQ #0 */
5417 gen_op_movl_T1_im(0);
5418 switch(size) {
pbrookad694712008-03-31 03:48:30 +00005419 case 0: gen_helper_neon_ceq_u8(CPU_T001); break;
5420 case 1: gen_helper_neon_ceq_u16(CPU_T001); break;
5421 case 2: gen_helper_neon_ceq_u32(CPU_T001); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005422 default: return 1;
5423 }
5424 break;
5425 case 22: /* VABS */
5426 switch(size) {
pbrookad694712008-03-31 03:48:30 +00005427 case 0: gen_helper_neon_abs_s8(cpu_T[0], cpu_T[0]); break;
5428 case 1: gen_helper_neon_abs_s16(cpu_T[0], cpu_T[0]); break;
5429 case 2: tcg_gen_abs_i32(cpu_T[0], cpu_T[0]); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005430 default: return 1;
5431 }
5432 break;
5433 case 23: /* VNEG */
5434 gen_op_movl_T1_im(0);
pbrookad694712008-03-31 03:48:30 +00005435 if (size == 3)
5436 return 1;
5437 gen_neon_rsb(size);
pbrook9ee6e8b2007-11-11 00:04:49 +00005438 break;
5439 case 24: case 27: /* Float VCGT #0, Float VCLE #0 */
5440 gen_op_movl_T1_im(0);
pbrookad694712008-03-31 03:48:30 +00005441 gen_helper_neon_cgt_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00005442 if (op == 27)
5443 gen_op_notl_T0();
5444 break;
5445 case 25: case 28: /* Float VCGE #0, Float VCLT #0 */
5446 gen_op_movl_T1_im(0);
pbrookad694712008-03-31 03:48:30 +00005447 gen_helper_neon_cge_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00005448 if (op == 28)
5449 gen_op_notl_T0();
5450 break;
5451 case 26: /* Float VCEQ #0 */
5452 gen_op_movl_T1_im(0);
pbrookad694712008-03-31 03:48:30 +00005453 gen_helper_neon_ceq_f32(CPU_T001);
pbrook9ee6e8b2007-11-11 00:04:49 +00005454 break;
5455 case 30: /* Float VABS */
pbrook4373f3c2008-03-31 03:47:19 +00005456 gen_vfp_abs(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005457 break;
5458 case 31: /* Float VNEG */
pbrook4373f3c2008-03-31 03:47:19 +00005459 gen_vfp_neg(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005460 break;
5461 case 32: /* VSWP */
5462 NEON_GET_REG(T1, rd, pass);
5463 NEON_SET_REG(T1, rm, pass);
5464 break;
5465 case 33: /* VTRN */
5466 NEON_GET_REG(T1, rd, pass);
5467 switch (size) {
pbrookad694712008-03-31 03:48:30 +00005468 case 0: gen_helper_neon_trn_u8(); break;
5469 case 1: gen_helper_neon_trn_u16(); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005470 case 2: abort();
5471 default: return 1;
5472 }
5473 NEON_SET_REG(T1, rm, pass);
5474 break;
5475 case 56: /* Integer VRECPE */
pbrook4373f3c2008-03-31 03:47:19 +00005476 gen_helper_recpe_u32(cpu_T[0], cpu_T[0], cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00005477 break;
5478 case 57: /* Integer VRSQRTE */
pbrook4373f3c2008-03-31 03:47:19 +00005479 gen_helper_rsqrte_u32(cpu_T[0], cpu_T[0], cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00005480 break;
5481 case 58: /* Float VRECPE */
pbrook4373f3c2008-03-31 03:47:19 +00005482 gen_helper_recpe_f32(cpu_F0s, cpu_F0s, cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00005483 break;
5484 case 59: /* Float VRSQRTE */
pbrook4373f3c2008-03-31 03:47:19 +00005485 gen_helper_rsqrte_f32(cpu_F0s, cpu_F0s, cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00005486 break;
5487 case 60: /* VCVT.F32.S32 */
pbrook4373f3c2008-03-31 03:47:19 +00005488 gen_vfp_tosiz(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005489 break;
5490 case 61: /* VCVT.F32.U32 */
pbrook4373f3c2008-03-31 03:47:19 +00005491 gen_vfp_touiz(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005492 break;
5493 case 62: /* VCVT.S32.F32 */
pbrook4373f3c2008-03-31 03:47:19 +00005494 gen_vfp_sito(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005495 break;
5496 case 63: /* VCVT.U32.F32 */
pbrook4373f3c2008-03-31 03:47:19 +00005497 gen_vfp_uito(0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005498 break;
5499 default:
5500 /* Reserved: 21, 29, 39-56 */
5501 return 1;
5502 }
5503 if (op == 30 || op == 31 || op >= 58) {
pbrook4373f3c2008-03-31 03:47:19 +00005504 tcg_gen_st_f32(cpu_F0s, cpu_env,
5505 neon_reg_offset(rd, pass));
pbrook9ee6e8b2007-11-11 00:04:49 +00005506 } else {
5507 NEON_SET_REG(T0, rd, pass);
5508 }
5509 }
5510 break;
5511 }
5512 } else if ((insn & (1 << 10)) == 0) {
5513 /* VTBL, VTBX. */
pbrook3018f252008-09-22 00:52:42 +00005514 n = ((insn >> 5) & 0x18) + 8;
pbrook9ee6e8b2007-11-11 00:04:49 +00005515 if (insn & (1 << 6)) {
pbrook8f8e3aa2008-03-31 03:48:01 +00005516 tmp = neon_load_reg(rd, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005517 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00005518 tmp = new_tmp();
5519 tcg_gen_movi_i32(tmp, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005520 }
pbrook8f8e3aa2008-03-31 03:48:01 +00005521 tmp2 = neon_load_reg(rm, 0);
5522 gen_helper_neon_tbl(tmp2, tmp2, tmp, tcg_const_i32(rn),
5523 tcg_const_i32(n));
pbrook3018f252008-09-22 00:52:42 +00005524 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00005525 if (insn & (1 << 6)) {
pbrook8f8e3aa2008-03-31 03:48:01 +00005526 tmp = neon_load_reg(rd, 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00005527 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00005528 tmp = new_tmp();
5529 tcg_gen_movi_i32(tmp, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005530 }
pbrook8f8e3aa2008-03-31 03:48:01 +00005531 tmp3 = neon_load_reg(rm, 1);
5532 gen_helper_neon_tbl(tmp3, tmp3, tmp, tcg_const_i32(rn),
5533 tcg_const_i32(n));
5534 neon_store_reg(rd, 0, tmp2);
pbrook3018f252008-09-22 00:52:42 +00005535 neon_store_reg(rd, 1, tmp3);
5536 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00005537 } else if ((insn & 0x380) == 0) {
5538 /* VDUP */
5539 if (insn & (1 << 19)) {
5540 NEON_SET_REG(T0, rm, 1);
5541 } else {
5542 NEON_SET_REG(T0, rm, 0);
5543 }
5544 if (insn & (1 << 16)) {
pbrookad694712008-03-31 03:48:30 +00005545 gen_neon_dup_u8(cpu_T[0], ((insn >> 17) & 3) * 8);
pbrook9ee6e8b2007-11-11 00:04:49 +00005546 } else if (insn & (1 << 17)) {
5547 if ((insn >> 18) & 1)
pbrookad694712008-03-31 03:48:30 +00005548 gen_neon_dup_high16(cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005549 else
pbrookad694712008-03-31 03:48:30 +00005550 gen_neon_dup_low16(cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005551 }
5552 for (pass = 0; pass < (q ? 4 : 2); pass++) {
5553 NEON_SET_REG(T0, rd, pass);
5554 }
5555 } else {
5556 return 1;
5557 }
5558 }
5559 }
5560 return 0;
5561}
5562
pbrookfe1479c2008-12-19 13:18:36 +00005563static int disas_cp14_read(CPUState * env, DisasContext *s, uint32_t insn)
5564{
5565 int crn = (insn >> 16) & 0xf;
5566 int crm = insn & 0xf;
5567 int op1 = (insn >> 21) & 7;
5568 int op2 = (insn >> 5) & 7;
5569 int rt = (insn >> 12) & 0xf;
5570 TCGv tmp;
5571
5572 if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
5573 if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) {
5574 /* TEECR */
5575 if (IS_USER(s))
5576 return 1;
5577 tmp = load_cpu_field(teecr);
5578 store_reg(s, rt, tmp);
5579 return 0;
5580 }
5581 if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) {
5582 /* TEEHBR */
5583 if (IS_USER(s) && (env->teecr & 1))
5584 return 1;
5585 tmp = load_cpu_field(teehbr);
5586 store_reg(s, rt, tmp);
5587 return 0;
5588 }
5589 }
5590 fprintf(stderr, "Unknown cp14 read op1:%d crn:%d crm:%d op2:%d\n",
5591 op1, crn, crm, op2);
5592 return 1;
5593}
5594
5595static int disas_cp14_write(CPUState * env, DisasContext *s, uint32_t insn)
5596{
5597 int crn = (insn >> 16) & 0xf;
5598 int crm = insn & 0xf;
5599 int op1 = (insn >> 21) & 7;
5600 int op2 = (insn >> 5) & 7;
5601 int rt = (insn >> 12) & 0xf;
5602 TCGv tmp;
5603
5604 if (arm_feature(env, ARM_FEATURE_THUMB2EE)) {
5605 if (op1 == 6 && crn == 0 && crm == 0 && op2 == 0) {
5606 /* TEECR */
5607 if (IS_USER(s))
5608 return 1;
5609 tmp = load_reg(s, rt);
5610 gen_helper_set_teecr(cpu_env, tmp);
5611 dead_tmp(tmp);
5612 return 0;
5613 }
5614 if (op1 == 6 && crn == 1 && crm == 0 && op2 == 0) {
5615 /* TEEHBR */
5616 if (IS_USER(s) && (env->teecr & 1))
5617 return 1;
5618 tmp = load_reg(s, rt);
5619 store_cpu_field(tmp, teehbr);
5620 return 0;
5621 }
5622 }
5623 fprintf(stderr, "Unknown cp14 write op1:%d crn:%d crm:%d op2:%d\n",
5624 op1, crn, crm, op2);
5625 return 1;
5626}
5627
pbrook9ee6e8b2007-11-11 00:04:49 +00005628static int disas_coproc_insn(CPUState * env, DisasContext *s, uint32_t insn)
5629{
5630 int cpnum;
5631
5632 cpnum = (insn >> 8) & 0xf;
5633 if (arm_feature(env, ARM_FEATURE_XSCALE)
5634 && ((env->cp15.c15_cpar ^ 0x3fff) & (1 << cpnum)))
5635 return 1;
5636
5637 switch (cpnum) {
5638 case 0:
5639 case 1:
5640 if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
5641 return disas_iwmmxt_insn(env, s, insn);
5642 } else if (arm_feature(env, ARM_FEATURE_XSCALE)) {
5643 return disas_dsp_insn(env, s, insn);
5644 }
5645 return 1;
5646 case 10:
5647 case 11:
5648 return disas_vfp_insn (env, s, insn);
pbrookfe1479c2008-12-19 13:18:36 +00005649 case 14:
5650 /* Coprocessors 7-15 are architecturally reserved by ARM.
5651 Unfortunately Intel decided to ignore this. */
5652 if (arm_feature(env, ARM_FEATURE_XSCALE))
5653 goto board;
5654 if (insn & (1 << 20))
5655 return disas_cp14_read(env, s, insn);
5656 else
5657 return disas_cp14_write(env, s, insn);
pbrook9ee6e8b2007-11-11 00:04:49 +00005658 case 15:
5659 return disas_cp15_insn (env, s, insn);
5660 default:
pbrookfe1479c2008-12-19 13:18:36 +00005661 board:
pbrook9ee6e8b2007-11-11 00:04:49 +00005662 /* Unknown coprocessor. See if the board has hooked it. */
5663 return disas_cp_insn (env, s, insn);
5664 }
5665}
5666
pbrook5e3f8782008-03-31 03:47:34 +00005667
5668/* Store a 64-bit value to a register pair. Clobbers val. */
pbrooka7812ae2008-11-17 14:43:54 +00005669static void gen_storeq_reg(DisasContext *s, int rlow, int rhigh, TCGv_i64 val)
pbrook5e3f8782008-03-31 03:47:34 +00005670{
5671 TCGv tmp;
5672 tmp = new_tmp();
5673 tcg_gen_trunc_i64_i32(tmp, val);
5674 store_reg(s, rlow, tmp);
5675 tmp = new_tmp();
5676 tcg_gen_shri_i64(val, val, 32);
5677 tcg_gen_trunc_i64_i32(tmp, val);
5678 store_reg(s, rhigh, tmp);
5679}
5680
5681/* load a 32-bit value from a register and perform a 64-bit accumulate. */
pbrooka7812ae2008-11-17 14:43:54 +00005682static void gen_addq_lo(DisasContext *s, TCGv_i64 val, int rlow)
pbrook5e3f8782008-03-31 03:47:34 +00005683{
pbrooka7812ae2008-11-17 14:43:54 +00005684 TCGv_i64 tmp;
pbrook5e3f8782008-03-31 03:47:34 +00005685 TCGv tmp2;
5686
pbrook36aa55d2008-09-21 13:48:32 +00005687 /* Load value and extend to 64 bits. */
pbrooka7812ae2008-11-17 14:43:54 +00005688 tmp = tcg_temp_new_i64();
pbrook5e3f8782008-03-31 03:47:34 +00005689 tmp2 = load_reg(s, rlow);
5690 tcg_gen_extu_i32_i64(tmp, tmp2);
5691 dead_tmp(tmp2);
5692 tcg_gen_add_i64(val, val, tmp);
5693}
5694
5695/* load and add a 64-bit value from a register pair. */
pbrooka7812ae2008-11-17 14:43:54 +00005696static void gen_addq(DisasContext *s, TCGv_i64 val, int rlow, int rhigh)
pbrook5e3f8782008-03-31 03:47:34 +00005697{
pbrooka7812ae2008-11-17 14:43:54 +00005698 TCGv_i64 tmp;
pbrook36aa55d2008-09-21 13:48:32 +00005699 TCGv tmpl;
5700 TCGv tmph;
pbrook5e3f8782008-03-31 03:47:34 +00005701
5702 /* Load 64-bit value rd:rn. */
pbrook36aa55d2008-09-21 13:48:32 +00005703 tmpl = load_reg(s, rlow);
5704 tmph = load_reg(s, rhigh);
pbrooka7812ae2008-11-17 14:43:54 +00005705 tmp = tcg_temp_new_i64();
pbrook36aa55d2008-09-21 13:48:32 +00005706 tcg_gen_concat_i32_i64(tmp, tmpl, tmph);
5707 dead_tmp(tmpl);
5708 dead_tmp(tmph);
pbrook5e3f8782008-03-31 03:47:34 +00005709 tcg_gen_add_i64(val, val, tmp);
5710}
5711
5712/* Set N and Z flags from a 64-bit value. */
pbrooka7812ae2008-11-17 14:43:54 +00005713static void gen_logicq_cc(TCGv_i64 val)
pbrook5e3f8782008-03-31 03:47:34 +00005714{
5715 TCGv tmp = new_tmp();
5716 gen_helper_logicq_cc(tmp, val);
pbrook6fbe23d2008-04-01 17:19:11 +00005717 gen_logic_CC(tmp);
5718 dead_tmp(tmp);
pbrook5e3f8782008-03-31 03:47:34 +00005719}
5720
bellardb7bcbe92005-02-22 19:27:29 +00005721static void disas_arm_insn(CPUState * env, DisasContext *s)
bellard2c0262a2003-09-30 20:34:21 +00005722{
5723 unsigned int cond, insn, val, op1, i, shift, rm, rs, rn, rd, sh;
pbrookb26eefb2008-03-31 03:44:26 +00005724 TCGv tmp;
pbrook36706692008-03-31 03:46:19 +00005725 TCGv tmp2;
pbrook6ddbc6e2008-03-31 03:46:33 +00005726 TCGv tmp3;
pbrookb0109802008-03-31 03:47:03 +00005727 TCGv addr;
pbrooka7812ae2008-11-17 14:43:54 +00005728 TCGv_i64 tmp64;
ths3b46e622007-09-17 08:09:54 +00005729
bellardb5ff1b32005-11-26 10:38:39 +00005730 insn = ldl_code(s->pc);
bellard2c0262a2003-09-30 20:34:21 +00005731 s->pc += 4;
ths3b46e622007-09-17 08:09:54 +00005732
pbrook9ee6e8b2007-11-11 00:04:49 +00005733 /* M variants do not implement ARM mode. */
5734 if (IS_M(env))
5735 goto illegal_op;
bellard2c0262a2003-09-30 20:34:21 +00005736 cond = insn >> 28;
bellard99c475a2005-01-31 20:45:13 +00005737 if (cond == 0xf){
bellardb7bcbe92005-02-22 19:27:29 +00005738 /* Unconditional instructions. */
pbrook9ee6e8b2007-11-11 00:04:49 +00005739 if (((insn >> 25) & 7) == 1) {
5740 /* NEON Data processing. */
5741 if (!arm_feature(env, ARM_FEATURE_NEON))
5742 goto illegal_op;
5743
5744 if (disas_neon_data_insn(env, s, insn))
5745 goto illegal_op;
5746 return;
5747 }
5748 if ((insn & 0x0f100000) == 0x04000000) {
5749 /* NEON load/store. */
5750 if (!arm_feature(env, ARM_FEATURE_NEON))
5751 goto illegal_op;
5752
5753 if (disas_neon_ls_insn(env, s, insn))
5754 goto illegal_op;
5755 return;
5756 }
bellard99c475a2005-01-31 20:45:13 +00005757 if ((insn & 0x0d70f000) == 0x0550f000)
5758 return; /* PLD */
pbrook9ee6e8b2007-11-11 00:04:49 +00005759 else if ((insn & 0x0ffffdff) == 0x01010000) {
5760 ARCH(6);
5761 /* setend */
5762 if (insn & (1 << 9)) {
5763 /* BE8 mode not implemented. */
5764 goto illegal_op;
5765 }
5766 return;
5767 } else if ((insn & 0x0fffff00) == 0x057ff000) {
5768 switch ((insn >> 4) & 0xf) {
5769 case 1: /* clrex */
5770 ARCH(6K);
pbrook8f8e3aa2008-03-31 03:48:01 +00005771 gen_helper_clrex(cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00005772 return;
5773 case 4: /* dsb */
5774 case 5: /* dmb */
5775 case 6: /* isb */
5776 ARCH(7);
5777 /* We don't emulate caches so these are a no-op. */
5778 return;
5779 default:
5780 goto illegal_op;
5781 }
5782 } else if ((insn & 0x0e5fffe0) == 0x084d0500) {
5783 /* srs */
5784 uint32_t offset;
5785 if (IS_USER(s))
5786 goto illegal_op;
5787 ARCH(6);
5788 op1 = (insn & 0x1f);
5789 if (op1 == (env->uncached_cpsr & CPSR_M)) {
pbrookb0109802008-03-31 03:47:03 +00005790 addr = load_reg(s, 13);
pbrook9ee6e8b2007-11-11 00:04:49 +00005791 } else {
pbrookb0109802008-03-31 03:47:03 +00005792 addr = new_tmp();
5793 gen_helper_get_r13_banked(addr, cpu_env, tcg_const_i32(op1));
pbrook9ee6e8b2007-11-11 00:04:49 +00005794 }
5795 i = (insn >> 23) & 3;
5796 switch (i) {
5797 case 0: offset = -4; break; /* DA */
5798 case 1: offset = -8; break; /* DB */
5799 case 2: offset = 0; break; /* IA */
5800 case 3: offset = 4; break; /* IB */
5801 default: abort();
5802 }
5803 if (offset)
pbrookb0109802008-03-31 03:47:03 +00005804 tcg_gen_addi_i32(addr, addr, offset);
5805 tmp = load_reg(s, 14);
5806 gen_st32(tmp, addr, 0);
5807 tmp = new_tmp();
5808 gen_helper_cpsr_read(tmp);
5809 tcg_gen_addi_i32(addr, addr, 4);
5810 gen_st32(tmp, addr, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005811 if (insn & (1 << 21)) {
5812 /* Base writeback. */
5813 switch (i) {
5814 case 0: offset = -8; break;
5815 case 1: offset = -4; break;
5816 case 2: offset = 4; break;
5817 case 3: offset = 0; break;
5818 default: abort();
5819 }
5820 if (offset)
pbrookb0109802008-03-31 03:47:03 +00005821 tcg_gen_addi_i32(addr, tmp, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00005822 if (op1 == (env->uncached_cpsr & CPSR_M)) {
5823 gen_movl_reg_T1(s, 13);
5824 } else {
pbrookb0109802008-03-31 03:47:03 +00005825 gen_helper_set_r13_banked(cpu_env, tcg_const_i32(op1), cpu_T[1]);
pbrook9ee6e8b2007-11-11 00:04:49 +00005826 }
pbrookb0109802008-03-31 03:47:03 +00005827 } else {
5828 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00005829 }
5830 } else if ((insn & 0x0e5fffe0) == 0x081d0a00) {
5831 /* rfe */
5832 uint32_t offset;
5833 if (IS_USER(s))
5834 goto illegal_op;
5835 ARCH(6);
5836 rn = (insn >> 16) & 0xf;
pbrookb0109802008-03-31 03:47:03 +00005837 addr = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00005838 i = (insn >> 23) & 3;
5839 switch (i) {
pbrookb0109802008-03-31 03:47:03 +00005840 case 0: offset = -4; break; /* DA */
5841 case 1: offset = -8; break; /* DB */
5842 case 2: offset = 0; break; /* IA */
5843 case 3: offset = 4; break; /* IB */
pbrook9ee6e8b2007-11-11 00:04:49 +00005844 default: abort();
5845 }
5846 if (offset)
pbrookb0109802008-03-31 03:47:03 +00005847 tcg_gen_addi_i32(addr, addr, offset);
5848 /* Load PC into tmp and CPSR into tmp2. */
5849 tmp = gen_ld32(addr, 0);
5850 tcg_gen_addi_i32(addr, addr, 4);
5851 tmp2 = gen_ld32(addr, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005852 if (insn & (1 << 21)) {
5853 /* Base writeback. */
5854 switch (i) {
pbrookb0109802008-03-31 03:47:03 +00005855 case 0: offset = -8; break;
5856 case 1: offset = -4; break;
5857 case 2: offset = 4; break;
5858 case 3: offset = 0; break;
pbrook9ee6e8b2007-11-11 00:04:49 +00005859 default: abort();
5860 }
5861 if (offset)
pbrookb0109802008-03-31 03:47:03 +00005862 tcg_gen_addi_i32(addr, addr, offset);
5863 store_reg(s, rn, addr);
5864 } else {
5865 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00005866 }
pbrookb0109802008-03-31 03:47:03 +00005867 gen_rfe(s, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00005868 } else if ((insn & 0x0e000000) == 0x0a000000) {
bellard99c475a2005-01-31 20:45:13 +00005869 /* branch link and change to thumb (blx <offset>) */
5870 int32_t offset;
5871
5872 val = (uint32_t)s->pc;
pbrookd9ba4832008-03-31 03:46:50 +00005873 tmp = new_tmp();
5874 tcg_gen_movi_i32(tmp, val);
5875 store_reg(s, 14, tmp);
bellard99c475a2005-01-31 20:45:13 +00005876 /* Sign-extend the 24-bit offset */
5877 offset = (((int32_t)insn) << 8) >> 8;
5878 /* offset * 4 + bit24 * 2 + (thumb bit) */
5879 val += (offset << 2) | ((insn >> 23) & 2) | 1;
5880 /* pipeline offset */
5881 val += 4;
pbrookd9ba4832008-03-31 03:46:50 +00005882 gen_bx_im(s, val);
bellard99c475a2005-01-31 20:45:13 +00005883 return;
balrog2e232132007-08-01 02:31:54 +00005884 } else if ((insn & 0x0e000f00) == 0x0c000100) {
5885 if (arm_feature(env, ARM_FEATURE_IWMMXT)) {
5886 /* iWMMXt register transfer. */
5887 if (env->cp15.c15_cpar & (1 << 1))
5888 if (!disas_iwmmxt_insn(env, s, insn))
5889 return;
5890 }
bellardb7bcbe92005-02-22 19:27:29 +00005891 } else if ((insn & 0x0fe00000) == 0x0c400000) {
5892 /* Coprocessor double register transfer. */
5893 } else if ((insn & 0x0f000010) == 0x0e000010) {
5894 /* Additional coprocessor register transfer. */
balrog7997d922008-07-19 10:34:35 +00005895 } else if ((insn & 0x0ff10020) == 0x01000000) {
pbrook9ee6e8b2007-11-11 00:04:49 +00005896 uint32_t mask;
5897 uint32_t val;
bellardb5ff1b32005-11-26 10:38:39 +00005898 /* cps (privileged) */
pbrook9ee6e8b2007-11-11 00:04:49 +00005899 if (IS_USER(s))
5900 return;
5901 mask = val = 0;
5902 if (insn & (1 << 19)) {
5903 if (insn & (1 << 8))
5904 mask |= CPSR_A;
5905 if (insn & (1 << 7))
5906 mask |= CPSR_I;
5907 if (insn & (1 << 6))
5908 mask |= CPSR_F;
5909 if (insn & (1 << 18))
5910 val |= mask;
5911 }
balrog7997d922008-07-19 10:34:35 +00005912 if (insn & (1 << 17)) {
pbrook9ee6e8b2007-11-11 00:04:49 +00005913 mask |= CPSR_M;
5914 val |= (insn & 0x1f);
5915 }
5916 if (mask) {
5917 gen_op_movl_T0_im(val);
5918 gen_set_psr_T0(s, mask, 0);
bellardb5ff1b32005-11-26 10:38:39 +00005919 }
5920 return;
bellard99c475a2005-01-31 20:45:13 +00005921 }
bellard2c0262a2003-09-30 20:34:21 +00005922 goto illegal_op;
bellard99c475a2005-01-31 20:45:13 +00005923 }
bellard2c0262a2003-09-30 20:34:21 +00005924 if (cond != 0xe) {
5925 /* if not always execute, we generate a conditional jump to
5926 next instruction */
bellarde50e6a22005-04-26 20:36:11 +00005927 s->condlabel = gen_new_label();
pbrookd9ba4832008-03-31 03:46:50 +00005928 gen_test_cc(cond ^ 1, s->condlabel);
bellarde50e6a22005-04-26 20:36:11 +00005929 s->condjmp = 1;
bellard2c0262a2003-09-30 20:34:21 +00005930 }
bellard99c475a2005-01-31 20:45:13 +00005931 if ((insn & 0x0f900000) == 0x03000000) {
pbrook9ee6e8b2007-11-11 00:04:49 +00005932 if ((insn & (1 << 21)) == 0) {
5933 ARCH(6T2);
5934 rd = (insn >> 12) & 0xf;
5935 val = ((insn >> 4) & 0xf000) | (insn & 0xfff);
5936 if ((insn & (1 << 22)) == 0) {
5937 /* MOVW */
pbrook5e3f8782008-03-31 03:47:34 +00005938 tmp = new_tmp();
5939 tcg_gen_movi_i32(tmp, val);
pbrook9ee6e8b2007-11-11 00:04:49 +00005940 } else {
5941 /* MOVT */
pbrook5e3f8782008-03-31 03:47:34 +00005942 tmp = load_reg(s, rd);
pbrook86831432008-05-11 12:22:01 +00005943 tcg_gen_ext16u_i32(tmp, tmp);
pbrook5e3f8782008-03-31 03:47:34 +00005944 tcg_gen_ori_i32(tmp, tmp, val << 16);
pbrook9ee6e8b2007-11-11 00:04:49 +00005945 }
pbrook5e3f8782008-03-31 03:47:34 +00005946 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00005947 } else {
5948 if (((insn >> 12) & 0xf) != 0xf)
5949 goto illegal_op;
5950 if (((insn >> 16) & 0xf) == 0) {
5951 gen_nop_hint(s, insn & 0xff);
5952 } else {
5953 /* CPSR = immediate */
5954 val = insn & 0xff;
5955 shift = ((insn >> 8) & 0xf) * 2;
5956 if (shift)
5957 val = (val >> shift) | (val << (32 - shift));
5958 gen_op_movl_T0_im(val);
5959 i = ((insn & (1 << 22)) != 0);
5960 if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
5961 goto illegal_op;
5962 }
5963 }
bellard99c475a2005-01-31 20:45:13 +00005964 } else if ((insn & 0x0f900000) == 0x01000000
5965 && (insn & 0x00000090) != 0x00000090) {
5966 /* miscellaneous instructions */
5967 op1 = (insn >> 21) & 3;
5968 sh = (insn >> 4) & 0xf;
5969 rm = insn & 0xf;
5970 switch (sh) {
5971 case 0x0: /* move program status register */
bellard99c475a2005-01-31 20:45:13 +00005972 if (op1 & 1) {
bellardb5ff1b32005-11-26 10:38:39 +00005973 /* PSR = reg */
bellard99c475a2005-01-31 20:45:13 +00005974 gen_movl_T0_reg(s, rm);
pbrook2ae23e72006-02-11 16:20:39 +00005975 i = ((op1 & 2) != 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00005976 if (gen_set_psr_T0(s, msr_mask(env, s, (insn >> 16) & 0xf, i), i))
bellardb5ff1b32005-11-26 10:38:39 +00005977 goto illegal_op;
bellard99c475a2005-01-31 20:45:13 +00005978 } else {
pbrook2ae23e72006-02-11 16:20:39 +00005979 /* reg = PSR */
bellard99c475a2005-01-31 20:45:13 +00005980 rd = (insn >> 12) & 0xf;
bellardb5ff1b32005-11-26 10:38:39 +00005981 if (op1 & 2) {
5982 if (IS_USER(s))
5983 goto illegal_op;
pbrookd9ba4832008-03-31 03:46:50 +00005984 tmp = load_cpu_field(spsr);
bellardb5ff1b32005-11-26 10:38:39 +00005985 } else {
pbrookd9ba4832008-03-31 03:46:50 +00005986 tmp = new_tmp();
5987 gen_helper_cpsr_read(tmp);
bellardb5ff1b32005-11-26 10:38:39 +00005988 }
pbrookd9ba4832008-03-31 03:46:50 +00005989 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00005990 }
bellardb8a9e8f2005-02-07 23:10:07 +00005991 break;
bellard99c475a2005-01-31 20:45:13 +00005992 case 0x1:
5993 if (op1 == 1) {
5994 /* branch/exchange thumb (bx). */
pbrookd9ba4832008-03-31 03:46:50 +00005995 tmp = load_reg(s, rm);
5996 gen_bx(s, tmp);
bellard99c475a2005-01-31 20:45:13 +00005997 } else if (op1 == 3) {
5998 /* clz */
5999 rd = (insn >> 12) & 0xf;
pbrook1497c962008-03-31 03:45:50 +00006000 tmp = load_reg(s, rm);
6001 gen_helper_clz(tmp, tmp);
6002 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00006003 } else {
6004 goto illegal_op;
6005 }
6006 break;
bellardb5ff1b32005-11-26 10:38:39 +00006007 case 0x2:
6008 if (op1 == 1) {
6009 ARCH(5J); /* bxj */
6010 /* Trivial implementation equivalent to bx. */
pbrookd9ba4832008-03-31 03:46:50 +00006011 tmp = load_reg(s, rm);
6012 gen_bx(s, tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006013 } else {
6014 goto illegal_op;
6015 }
6016 break;
bellard99c475a2005-01-31 20:45:13 +00006017 case 0x3:
6018 if (op1 != 1)
6019 goto illegal_op;
6020
6021 /* branch link/exchange thumb (blx) */
pbrookd9ba4832008-03-31 03:46:50 +00006022 tmp = load_reg(s, rm);
6023 tmp2 = new_tmp();
6024 tcg_gen_movi_i32(tmp2, s->pc);
6025 store_reg(s, 14, tmp2);
6026 gen_bx(s, tmp);
bellard99c475a2005-01-31 20:45:13 +00006027 break;
6028 case 0x5: /* saturating add/subtract */
6029 rd = (insn >> 12) & 0xf;
6030 rn = (insn >> 16) & 0xf;
balrogb40d0352008-09-20 03:18:07 +00006031 tmp = load_reg(s, rm);
pbrook5e3f8782008-03-31 03:47:34 +00006032 tmp2 = load_reg(s, rn);
bellardff8263a2005-05-13 22:45:23 +00006033 if (op1 & 2)
pbrook5e3f8782008-03-31 03:47:34 +00006034 gen_helper_double_saturate(tmp2, tmp2);
bellard99c475a2005-01-31 20:45:13 +00006035 if (op1 & 1)
pbrook5e3f8782008-03-31 03:47:34 +00006036 gen_helper_sub_saturate(tmp, tmp, tmp2);
bellard99c475a2005-01-31 20:45:13 +00006037 else
pbrook5e3f8782008-03-31 03:47:34 +00006038 gen_helper_add_saturate(tmp, tmp, tmp2);
6039 dead_tmp(tmp2);
6040 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00006041 break;
pbrook06c949e2006-02-04 19:35:26 +00006042 case 7: /* bkpt */
pbrook9ee6e8b2007-11-11 00:04:49 +00006043 gen_set_condexec(s);
pbrook5e3f8782008-03-31 03:47:34 +00006044 gen_set_pc_im(s->pc - 4);
pbrookd9ba4832008-03-31 03:46:50 +00006045 gen_exception(EXCP_BKPT);
pbrook06c949e2006-02-04 19:35:26 +00006046 s->is_jmp = DISAS_JUMP;
6047 break;
bellard99c475a2005-01-31 20:45:13 +00006048 case 0x8: /* signed multiply */
6049 case 0xa:
6050 case 0xc:
6051 case 0xe:
6052 rs = (insn >> 8) & 0xf;
6053 rn = (insn >> 12) & 0xf;
6054 rd = (insn >> 16) & 0xf;
6055 if (op1 == 1) {
6056 /* (32 * 16) >> 16 */
pbrook5e3f8782008-03-31 03:47:34 +00006057 tmp = load_reg(s, rm);
6058 tmp2 = load_reg(s, rs);
bellard99c475a2005-01-31 20:45:13 +00006059 if (sh & 4)
pbrook5e3f8782008-03-31 03:47:34 +00006060 tcg_gen_sari_i32(tmp2, tmp2, 16);
bellard99c475a2005-01-31 20:45:13 +00006061 else
pbrook5e3f8782008-03-31 03:47:34 +00006062 gen_sxth(tmp2);
pbrooka7812ae2008-11-17 14:43:54 +00006063 tmp64 = gen_muls_i64_i32(tmp, tmp2);
6064 tcg_gen_shri_i64(tmp64, tmp64, 16);
pbrook5e3f8782008-03-31 03:47:34 +00006065 tmp = new_tmp();
pbrooka7812ae2008-11-17 14:43:54 +00006066 tcg_gen_trunc_i64_i32(tmp, tmp64);
bellard99c475a2005-01-31 20:45:13 +00006067 if ((sh & 2) == 0) {
pbrook5e3f8782008-03-31 03:47:34 +00006068 tmp2 = load_reg(s, rn);
6069 gen_helper_add_setq(tmp, tmp, tmp2);
6070 dead_tmp(tmp2);
bellard99c475a2005-01-31 20:45:13 +00006071 }
pbrook5e3f8782008-03-31 03:47:34 +00006072 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00006073 } else {
6074 /* 16 * 16 */
pbrook5e3f8782008-03-31 03:47:34 +00006075 tmp = load_reg(s, rm);
6076 tmp2 = load_reg(s, rs);
6077 gen_mulxy(tmp, tmp2, sh & 2, sh & 4);
6078 dead_tmp(tmp2);
bellard99c475a2005-01-31 20:45:13 +00006079 if (op1 == 2) {
pbrooka7812ae2008-11-17 14:43:54 +00006080 tmp64 = tcg_temp_new_i64();
6081 tcg_gen_ext_i32_i64(tmp64, tmp);
balrog22478e72008-07-19 10:12:22 +00006082 dead_tmp(tmp);
pbrooka7812ae2008-11-17 14:43:54 +00006083 gen_addq(s, tmp64, rn, rd);
6084 gen_storeq_reg(s, rn, rd, tmp64);
bellard99c475a2005-01-31 20:45:13 +00006085 } else {
bellard99c475a2005-01-31 20:45:13 +00006086 if (op1 == 0) {
pbrook5e3f8782008-03-31 03:47:34 +00006087 tmp2 = load_reg(s, rn);
6088 gen_helper_add_setq(tmp, tmp, tmp2);
6089 dead_tmp(tmp2);
bellard99c475a2005-01-31 20:45:13 +00006090 }
pbrook5e3f8782008-03-31 03:47:34 +00006091 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00006092 }
6093 }
6094 break;
6095 default:
6096 goto illegal_op;
6097 }
6098 } else if (((insn & 0x0e000000) == 0 &&
6099 (insn & 0x00000090) != 0x90) ||
6100 ((insn & 0x0e000000) == (1 << 25))) {
bellard2c0262a2003-09-30 20:34:21 +00006101 int set_cc, logic_cc, shiftop;
ths3b46e622007-09-17 08:09:54 +00006102
bellard2c0262a2003-09-30 20:34:21 +00006103 op1 = (insn >> 21) & 0xf;
6104 set_cc = (insn >> 20) & 1;
6105 logic_cc = table_logic_cc[op1] & set_cc;
6106
6107 /* data processing instruction */
6108 if (insn & (1 << 25)) {
6109 /* immediate operand */
6110 val = insn & 0xff;
6111 shift = ((insn >> 8) & 0xf) * 2;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006112 if (shift) {
bellard2c0262a2003-09-30 20:34:21 +00006113 val = (val >> shift) | (val << (32 - shift));
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006114 }
6115 tmp2 = new_tmp();
6116 tcg_gen_movi_i32(tmp2, val);
6117 if (logic_cc && shift) {
6118 gen_set_CF_bit31(tmp2);
6119 }
bellard2c0262a2003-09-30 20:34:21 +00006120 } else {
6121 /* register */
6122 rm = (insn) & 0xf;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006123 tmp2 = load_reg(s, rm);
bellard2c0262a2003-09-30 20:34:21 +00006124 shiftop = (insn >> 5) & 3;
6125 if (!(insn & (1 << 4))) {
6126 shift = (insn >> 7) & 0x1f;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006127 gen_arm_shift_im(tmp2, shiftop, shift, logic_cc);
bellard2c0262a2003-09-30 20:34:21 +00006128 } else {
6129 rs = (insn >> 8) & 0xf;
pbrook8984bd22008-03-31 03:47:48 +00006130 tmp = load_reg(s, rs);
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006131 gen_arm_shift_reg(tmp2, shiftop, tmp, logic_cc);
bellard2c0262a2003-09-30 20:34:21 +00006132 }
6133 }
6134 if (op1 != 0x0f && op1 != 0x0d) {
6135 rn = (insn >> 16) & 0xf;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006136 tmp = load_reg(s, rn);
6137 } else {
6138 TCGV_UNUSED(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006139 }
6140 rd = (insn >> 12) & 0xf;
6141 switch(op1) {
6142 case 0x00:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006143 tcg_gen_and_i32(tmp, tmp, tmp2);
6144 if (logic_cc) {
6145 gen_logic_CC(tmp);
6146 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006147 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006148 break;
6149 case 0x01:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006150 tcg_gen_xor_i32(tmp, tmp, tmp2);
6151 if (logic_cc) {
6152 gen_logic_CC(tmp);
6153 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006154 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006155 break;
6156 case 0x02:
bellardb5ff1b32005-11-26 10:38:39 +00006157 if (set_cc && rd == 15) {
6158 /* SUBS r15, ... is used for exception return. */
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006159 if (IS_USER(s)) {
bellardb5ff1b32005-11-26 10:38:39 +00006160 goto illegal_op;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006161 }
6162 gen_helper_sub_cc(tmp, tmp, tmp2);
6163 gen_exception_return(s, tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006164 } else {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006165 if (set_cc) {
6166 gen_helper_sub_cc(tmp, tmp, tmp2);
6167 } else {
6168 tcg_gen_sub_i32(tmp, tmp, tmp2);
6169 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006170 store_reg_bx(env, s, rd, tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006171 }
bellard2c0262a2003-09-30 20:34:21 +00006172 break;
6173 case 0x03:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006174 if (set_cc) {
6175 gen_helper_sub_cc(tmp, tmp2, tmp);
6176 } else {
6177 tcg_gen_sub_i32(tmp, tmp2, tmp);
6178 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006179 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006180 break;
6181 case 0x04:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006182 if (set_cc) {
6183 gen_helper_add_cc(tmp, tmp, tmp2);
6184 } else {
6185 tcg_gen_add_i32(tmp, tmp, tmp2);
6186 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006187 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006188 break;
6189 case 0x05:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006190 if (set_cc) {
6191 gen_helper_adc_cc(tmp, tmp, tmp2);
6192 } else {
6193 gen_add_carry(tmp, tmp, tmp2);
6194 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006195 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006196 break;
6197 case 0x06:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006198 if (set_cc) {
6199 gen_helper_sbc_cc(tmp, tmp, tmp2);
6200 } else {
6201 gen_sub_carry(tmp, tmp, tmp2);
6202 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006203 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006204 break;
6205 case 0x07:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006206 if (set_cc) {
6207 gen_helper_sbc_cc(tmp, tmp2, tmp);
6208 } else {
6209 gen_sub_carry(tmp, tmp2, tmp);
6210 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006211 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006212 break;
6213 case 0x08:
6214 if (set_cc) {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006215 tcg_gen_and_i32(tmp, tmp, tmp2);
6216 gen_logic_CC(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006217 }
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006218 dead_tmp(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006219 break;
6220 case 0x09:
6221 if (set_cc) {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006222 tcg_gen_xor_i32(tmp, tmp, tmp2);
6223 gen_logic_CC(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006224 }
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006225 dead_tmp(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006226 break;
6227 case 0x0a:
6228 if (set_cc) {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006229 gen_helper_sub_cc(tmp, tmp, tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006230 }
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006231 dead_tmp(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006232 break;
6233 case 0x0b:
6234 if (set_cc) {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006235 gen_helper_add_cc(tmp, tmp, tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006236 }
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006237 dead_tmp(tmp);
bellard2c0262a2003-09-30 20:34:21 +00006238 break;
6239 case 0x0c:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006240 tcg_gen_or_i32(tmp, tmp, tmp2);
6241 if (logic_cc) {
6242 gen_logic_CC(tmp);
6243 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006244 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006245 break;
6246 case 0x0d:
bellardb5ff1b32005-11-26 10:38:39 +00006247 if (logic_cc && rd == 15) {
6248 /* MOVS r15, ... is used for exception return. */
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006249 if (IS_USER(s)) {
bellardb5ff1b32005-11-26 10:38:39 +00006250 goto illegal_op;
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006251 }
6252 gen_exception_return(s, tmp2);
bellardb5ff1b32005-11-26 10:38:39 +00006253 } else {
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006254 if (logic_cc) {
6255 gen_logic_CC(tmp2);
6256 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006257 store_reg_bx(env, s, rd, tmp2);
bellardb5ff1b32005-11-26 10:38:39 +00006258 }
bellard2c0262a2003-09-30 20:34:21 +00006259 break;
6260 case 0x0e:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006261 tcg_gen_bic_i32(tmp, tmp, tmp2);
6262 if (logic_cc) {
6263 gen_logic_CC(tmp);
6264 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006265 store_reg_bx(env, s, rd, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006266 break;
6267 default:
6268 case 0x0f:
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006269 tcg_gen_not_i32(tmp2, tmp2);
6270 if (logic_cc) {
6271 gen_logic_CC(tmp2);
6272 }
Juha Riihimäki21aeb342009-05-06 09:16:12 +03006273 store_reg_bx(env, s, rd, tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006274 break;
6275 }
Juha Riihimäkie9bb4aa2009-05-06 09:15:38 +03006276 if (op1 != 0x0f && op1 != 0x0d) {
6277 dead_tmp(tmp2);
6278 }
bellard2c0262a2003-09-30 20:34:21 +00006279 } else {
6280 /* other instructions */
6281 op1 = (insn >> 24) & 0xf;
6282 switch(op1) {
6283 case 0x0:
6284 case 0x1:
bellard99c475a2005-01-31 20:45:13 +00006285 /* multiplies, extra load/stores */
bellard2c0262a2003-09-30 20:34:21 +00006286 sh = (insn >> 5) & 3;
6287 if (sh == 0) {
6288 if (op1 == 0x0) {
6289 rd = (insn >> 16) & 0xf;
6290 rn = (insn >> 12) & 0xf;
6291 rs = (insn >> 8) & 0xf;
6292 rm = (insn) & 0xf;
pbrook9ee6e8b2007-11-11 00:04:49 +00006293 op1 = (insn >> 20) & 0xf;
6294 switch (op1) {
6295 case 0: case 1: case 2: case 3: case 6:
bellard2c0262a2003-09-30 20:34:21 +00006296 /* 32 bit mul */
pbrook5e3f8782008-03-31 03:47:34 +00006297 tmp = load_reg(s, rs);
6298 tmp2 = load_reg(s, rm);
6299 tcg_gen_mul_i32(tmp, tmp, tmp2);
6300 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006301 if (insn & (1 << 22)) {
6302 /* Subtract (mls) */
6303 ARCH(6T2);
pbrook5e3f8782008-03-31 03:47:34 +00006304 tmp2 = load_reg(s, rn);
6305 tcg_gen_sub_i32(tmp, tmp2, tmp);
6306 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006307 } else if (insn & (1 << 21)) {
6308 /* Add */
pbrook5e3f8782008-03-31 03:47:34 +00006309 tmp2 = load_reg(s, rn);
6310 tcg_gen_add_i32(tmp, tmp, tmp2);
6311 dead_tmp(tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006312 }
ths5fafdf22007-09-16 21:08:06 +00006313 if (insn & (1 << 20))
pbrook5e3f8782008-03-31 03:47:34 +00006314 gen_logic_CC(tmp);
6315 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006316 break;
6317 default:
bellard2c0262a2003-09-30 20:34:21 +00006318 /* 64 bit mul */
pbrook5e3f8782008-03-31 03:47:34 +00006319 tmp = load_reg(s, rs);
6320 tmp2 = load_reg(s, rm);
ths5fafdf22007-09-16 21:08:06 +00006321 if (insn & (1 << 22))
pbrooka7812ae2008-11-17 14:43:54 +00006322 tmp64 = gen_muls_i64_i32(tmp, tmp2);
bellard2e134c92003-11-11 13:55:33 +00006323 else
pbrooka7812ae2008-11-17 14:43:54 +00006324 tmp64 = gen_mulu_i64_i32(tmp, tmp2);
bellard99c475a2005-01-31 20:45:13 +00006325 if (insn & (1 << 21)) /* mult accumulate */
pbrooka7812ae2008-11-17 14:43:54 +00006326 gen_addq(s, tmp64, rn, rd);
bellard99c475a2005-01-31 20:45:13 +00006327 if (!(insn & (1 << 23))) { /* double accumulate */
bellardb5ff1b32005-11-26 10:38:39 +00006328 ARCH(6);
pbrooka7812ae2008-11-17 14:43:54 +00006329 gen_addq_lo(s, tmp64, rn);
6330 gen_addq_lo(s, tmp64, rd);
bellard99c475a2005-01-31 20:45:13 +00006331 }
ths5fafdf22007-09-16 21:08:06 +00006332 if (insn & (1 << 20))
pbrooka7812ae2008-11-17 14:43:54 +00006333 gen_logicq_cc(tmp64);
6334 gen_storeq_reg(s, rn, rd, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00006335 break;
bellard2c0262a2003-09-30 20:34:21 +00006336 }
6337 } else {
bellard2c0262a2003-09-30 20:34:21 +00006338 rn = (insn >> 16) & 0xf;
6339 rd = (insn >> 12) & 0xf;
bellard99c475a2005-01-31 20:45:13 +00006340 if (insn & (1 << 23)) {
6341 /* load/store exclusive */
pbrook86753402008-10-22 20:35:54 +00006342 op1 = (insn >> 21) & 0x3;
6343 if (op1)
pbrooka47f43d2008-10-22 21:42:54 +00006344 ARCH(6K);
pbrook86753402008-10-22 20:35:54 +00006345 else
6346 ARCH(6);
pbrook9ee6e8b2007-11-11 00:04:49 +00006347 gen_movl_T1_reg(s, rn);
balrog72f1c622008-04-13 01:53:27 +00006348 addr = cpu_T[1];
pbrook9ee6e8b2007-11-11 00:04:49 +00006349 if (insn & (1 << 20)) {
pbrook8f8e3aa2008-03-31 03:48:01 +00006350 gen_helper_mark_exclusive(cpu_env, cpu_T[1]);
pbrook86753402008-10-22 20:35:54 +00006351 switch (op1) {
6352 case 0: /* ldrex */
6353 tmp = gen_ld32(addr, IS_USER(s));
6354 break;
6355 case 1: /* ldrexd */
6356 tmp = gen_ld32(addr, IS_USER(s));
6357 store_reg(s, rd, tmp);
6358 tcg_gen_addi_i32(addr, addr, 4);
6359 tmp = gen_ld32(addr, IS_USER(s));
6360 rd++;
6361 break;
6362 case 2: /* ldrexb */
6363 tmp = gen_ld8u(addr, IS_USER(s));
6364 break;
6365 case 3: /* ldrexh */
6366 tmp = gen_ld16u(addr, IS_USER(s));
6367 break;
6368 default:
6369 abort();
6370 }
pbrook8f8e3aa2008-03-31 03:48:01 +00006371 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006372 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00006373 int label = gen_new_label();
pbrook9ee6e8b2007-11-11 00:04:49 +00006374 rm = insn & 0xf;
pbrook8f8e3aa2008-03-31 03:48:01 +00006375 gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
pbrookcb636692008-05-24 02:22:00 +00006376 tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0],
6377 0, label);
pbrook8f8e3aa2008-03-31 03:48:01 +00006378 tmp = load_reg(s,rm);
pbrook86753402008-10-22 20:35:54 +00006379 switch (op1) {
6380 case 0: /* strex */
6381 gen_st32(tmp, addr, IS_USER(s));
6382 break;
6383 case 1: /* strexd */
6384 gen_st32(tmp, addr, IS_USER(s));
6385 tcg_gen_addi_i32(addr, addr, 4);
6386 tmp = load_reg(s, rm + 1);
6387 gen_st32(tmp, addr, IS_USER(s));
6388 break;
6389 case 2: /* strexb */
6390 gen_st8(tmp, addr, IS_USER(s));
6391 break;
6392 case 3: /* strexh */
6393 gen_st16(tmp, addr, IS_USER(s));
6394 break;
6395 default:
6396 abort();
6397 }
balrog2637a3b2008-04-13 03:15:14 +00006398 gen_set_label(label);
pbrook8f8e3aa2008-03-31 03:48:01 +00006399 gen_movl_reg_T0(s, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00006400 }
bellard2c0262a2003-09-30 20:34:21 +00006401 } else {
bellard99c475a2005-01-31 20:45:13 +00006402 /* SWP instruction */
6403 rm = (insn) & 0xf;
ths3b46e622007-09-17 08:09:54 +00006404
pbrook8984bd22008-03-31 03:47:48 +00006405 /* ??? This is not really atomic. However we know
6406 we never have multiple CPUs running in parallel,
6407 so it is good enough. */
6408 addr = load_reg(s, rn);
6409 tmp = load_reg(s, rm);
bellard99c475a2005-01-31 20:45:13 +00006410 if (insn & (1 << 22)) {
pbrook8984bd22008-03-31 03:47:48 +00006411 tmp2 = gen_ld8u(addr, IS_USER(s));
6412 gen_st8(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00006413 } else {
pbrook8984bd22008-03-31 03:47:48 +00006414 tmp2 = gen_ld32(addr, IS_USER(s));
6415 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00006416 }
pbrook8984bd22008-03-31 03:47:48 +00006417 dead_tmp(addr);
6418 store_reg(s, rd, tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006419 }
bellard2c0262a2003-09-30 20:34:21 +00006420 }
6421 } else {
pbrook191f9a92006-06-14 14:36:07 +00006422 int address_offset;
pbrook5fd46862007-03-17 01:43:01 +00006423 int load;
bellard99c475a2005-01-31 20:45:13 +00006424 /* Misc load/store */
bellard2c0262a2003-09-30 20:34:21 +00006425 rn = (insn >> 16) & 0xf;
6426 rd = (insn >> 12) & 0xf;
pbrookb0109802008-03-31 03:47:03 +00006427 addr = load_reg(s, rn);
bellardbeddab72004-05-05 18:36:10 +00006428 if (insn & (1 << 24))
pbrookb0109802008-03-31 03:47:03 +00006429 gen_add_datah_offset(s, insn, 0, addr);
pbrook191f9a92006-06-14 14:36:07 +00006430 address_offset = 0;
bellard2c0262a2003-09-30 20:34:21 +00006431 if (insn & (1 << 20)) {
6432 /* load */
6433 switch(sh) {
6434 case 1:
pbrookb0109802008-03-31 03:47:03 +00006435 tmp = gen_ld16u(addr, IS_USER(s));
bellard2c0262a2003-09-30 20:34:21 +00006436 break;
6437 case 2:
pbrookb0109802008-03-31 03:47:03 +00006438 tmp = gen_ld8s(addr, IS_USER(s));
bellard2c0262a2003-09-30 20:34:21 +00006439 break;
6440 default:
6441 case 3:
pbrookb0109802008-03-31 03:47:03 +00006442 tmp = gen_ld16s(addr, IS_USER(s));
bellard2c0262a2003-09-30 20:34:21 +00006443 break;
6444 }
pbrook5fd46862007-03-17 01:43:01 +00006445 load = 1;
bellard99c475a2005-01-31 20:45:13 +00006446 } else if (sh & 2) {
6447 /* doubleword */
6448 if (sh & 1) {
6449 /* store */
pbrookb0109802008-03-31 03:47:03 +00006450 tmp = load_reg(s, rd);
6451 gen_st32(tmp, addr, IS_USER(s));
6452 tcg_gen_addi_i32(addr, addr, 4);
6453 tmp = load_reg(s, rd + 1);
6454 gen_st32(tmp, addr, IS_USER(s));
pbrook5fd46862007-03-17 01:43:01 +00006455 load = 0;
bellard99c475a2005-01-31 20:45:13 +00006456 } else {
6457 /* load */
pbrookb0109802008-03-31 03:47:03 +00006458 tmp = gen_ld32(addr, IS_USER(s));
6459 store_reg(s, rd, tmp);
6460 tcg_gen_addi_i32(addr, addr, 4);
6461 tmp = gen_ld32(addr, IS_USER(s));
pbrook5fd46862007-03-17 01:43:01 +00006462 rd++;
6463 load = 1;
bellard99c475a2005-01-31 20:45:13 +00006464 }
pbrook191f9a92006-06-14 14:36:07 +00006465 address_offset = -4;
bellard2c0262a2003-09-30 20:34:21 +00006466 } else {
6467 /* store */
pbrookb0109802008-03-31 03:47:03 +00006468 tmp = load_reg(s, rd);
6469 gen_st16(tmp, addr, IS_USER(s));
pbrook5fd46862007-03-17 01:43:01 +00006470 load = 0;
bellard2c0262a2003-09-30 20:34:21 +00006471 }
pbrook5fd46862007-03-17 01:43:01 +00006472 /* Perform base writeback before the loaded value to
6473 ensure correct behavior with overlapping index registers.
6474 ldrd with base writeback is is undefined if the
6475 destination and index registers overlap. */
bellard2c0262a2003-09-30 20:34:21 +00006476 if (!(insn & (1 << 24))) {
pbrookb0109802008-03-31 03:47:03 +00006477 gen_add_datah_offset(s, insn, address_offset, addr);
6478 store_reg(s, rn, addr);
bellard2c0262a2003-09-30 20:34:21 +00006479 } else if (insn & (1 << 21)) {
pbrook191f9a92006-06-14 14:36:07 +00006480 if (address_offset)
pbrookb0109802008-03-31 03:47:03 +00006481 tcg_gen_addi_i32(addr, addr, address_offset);
6482 store_reg(s, rn, addr);
6483 } else {
6484 dead_tmp(addr);
bellard2c0262a2003-09-30 20:34:21 +00006485 }
pbrook5fd46862007-03-17 01:43:01 +00006486 if (load) {
6487 /* Complete the load. */
pbrookb0109802008-03-31 03:47:03 +00006488 store_reg(s, rd, tmp);
pbrook5fd46862007-03-17 01:43:01 +00006489 }
bellard2c0262a2003-09-30 20:34:21 +00006490 }
6491 break;
6492 case 0x4:
6493 case 0x5:
pbrook9ee6e8b2007-11-11 00:04:49 +00006494 goto do_ldst;
bellard2c0262a2003-09-30 20:34:21 +00006495 case 0x6:
6496 case 0x7:
pbrook9ee6e8b2007-11-11 00:04:49 +00006497 if (insn & (1 << 4)) {
6498 ARCH(6);
6499 /* Armv6 Media instructions. */
6500 rm = insn & 0xf;
6501 rn = (insn >> 16) & 0xf;
6502 rd = (insn >> 12) & 0xf;
6503 rs = (insn >> 8) & 0xf;
6504 switch ((insn >> 23) & 3) {
6505 case 0: /* Parallel add/subtract. */
6506 op1 = (insn >> 20) & 7;
pbrook6ddbc6e2008-03-31 03:46:33 +00006507 tmp = load_reg(s, rn);
6508 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006509 sh = (insn >> 5) & 7;
6510 if ((op1 & 3) == 0 || sh == 5 || sh == 6)
6511 goto illegal_op;
pbrook6ddbc6e2008-03-31 03:46:33 +00006512 gen_arm_parallel_addsub(op1, sh, tmp, tmp2);
6513 dead_tmp(tmp2);
6514 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006515 break;
6516 case 1:
6517 if ((insn & 0x00700020) == 0) {
balrog6c956762008-04-13 00:57:49 +00006518 /* Halfword pack. */
pbrook36706692008-03-31 03:46:19 +00006519 tmp = load_reg(s, rn);
6520 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006521 shift = (insn >> 7) & 0x1f;
pbrook36706692008-03-31 03:46:19 +00006522 if (insn & (1 << 6)) {
6523 /* pkhtb */
balrog22478e72008-07-19 10:12:22 +00006524 if (shift == 0)
6525 shift = 31;
6526 tcg_gen_sari_i32(tmp2, tmp2, shift);
pbrook36706692008-03-31 03:46:19 +00006527 tcg_gen_andi_i32(tmp, tmp, 0xffff0000);
pbrook86831432008-05-11 12:22:01 +00006528 tcg_gen_ext16u_i32(tmp2, tmp2);
pbrook36706692008-03-31 03:46:19 +00006529 } else {
6530 /* pkhbt */
balrog22478e72008-07-19 10:12:22 +00006531 if (shift)
6532 tcg_gen_shli_i32(tmp2, tmp2, shift);
pbrook86831432008-05-11 12:22:01 +00006533 tcg_gen_ext16u_i32(tmp, tmp);
pbrook36706692008-03-31 03:46:19 +00006534 tcg_gen_andi_i32(tmp2, tmp2, 0xffff0000);
6535 }
6536 tcg_gen_or_i32(tmp, tmp, tmp2);
balrog22478e72008-07-19 10:12:22 +00006537 dead_tmp(tmp2);
pbrook36706692008-03-31 03:46:19 +00006538 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006539 } else if ((insn & 0x00200020) == 0x00200000) {
6540 /* [us]sat */
pbrook6ddbc6e2008-03-31 03:46:33 +00006541 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006542 shift = (insn >> 7) & 0x1f;
6543 if (insn & (1 << 6)) {
6544 if (shift == 0)
6545 shift = 31;
pbrook6ddbc6e2008-03-31 03:46:33 +00006546 tcg_gen_sari_i32(tmp, tmp, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00006547 } else {
pbrook6ddbc6e2008-03-31 03:46:33 +00006548 tcg_gen_shli_i32(tmp, tmp, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00006549 }
6550 sh = (insn >> 16) & 0x1f;
6551 if (sh != 0) {
6552 if (insn & (1 << 22))
pbrook6ddbc6e2008-03-31 03:46:33 +00006553 gen_helper_usat(tmp, tmp, tcg_const_i32(sh));
pbrook9ee6e8b2007-11-11 00:04:49 +00006554 else
pbrook6ddbc6e2008-03-31 03:46:33 +00006555 gen_helper_ssat(tmp, tmp, tcg_const_i32(sh));
pbrook9ee6e8b2007-11-11 00:04:49 +00006556 }
pbrook6ddbc6e2008-03-31 03:46:33 +00006557 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006558 } else if ((insn & 0x00300fe0) == 0x00200f20) {
6559 /* [us]sat16 */
pbrook6ddbc6e2008-03-31 03:46:33 +00006560 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006561 sh = (insn >> 16) & 0x1f;
6562 if (sh != 0) {
6563 if (insn & (1 << 22))
pbrook6ddbc6e2008-03-31 03:46:33 +00006564 gen_helper_usat16(tmp, tmp, tcg_const_i32(sh));
pbrook9ee6e8b2007-11-11 00:04:49 +00006565 else
pbrook6ddbc6e2008-03-31 03:46:33 +00006566 gen_helper_ssat16(tmp, tmp, tcg_const_i32(sh));
pbrook9ee6e8b2007-11-11 00:04:49 +00006567 }
pbrook6ddbc6e2008-03-31 03:46:33 +00006568 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006569 } else if ((insn & 0x00700fe0) == 0x00000fa0) {
6570 /* Select bytes. */
pbrook6ddbc6e2008-03-31 03:46:33 +00006571 tmp = load_reg(s, rn);
6572 tmp2 = load_reg(s, rm);
6573 tmp3 = new_tmp();
6574 tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
6575 gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
6576 dead_tmp(tmp3);
6577 dead_tmp(tmp2);
6578 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006579 } else if ((insn & 0x000003e0) == 0x00000060) {
pbrook5e3f8782008-03-31 03:47:34 +00006580 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006581 shift = (insn >> 10) & 3;
6582 /* ??? In many cases it's not neccessary to do a
6583 rotate, a shift is sufficient. */
6584 if (shift != 0)
pbrook5e3f8782008-03-31 03:47:34 +00006585 tcg_gen_rori_i32(tmp, tmp, shift * 8);
pbrook9ee6e8b2007-11-11 00:04:49 +00006586 op1 = (insn >> 20) & 7;
6587 switch (op1) {
pbrook5e3f8782008-03-31 03:47:34 +00006588 case 0: gen_sxtb16(tmp); break;
6589 case 2: gen_sxtb(tmp); break;
6590 case 3: gen_sxth(tmp); break;
6591 case 4: gen_uxtb16(tmp); break;
6592 case 6: gen_uxtb(tmp); break;
6593 case 7: gen_uxth(tmp); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00006594 default: goto illegal_op;
6595 }
6596 if (rn != 15) {
pbrook5e3f8782008-03-31 03:47:34 +00006597 tmp2 = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00006598 if ((op1 & 3) == 0) {
pbrook5e3f8782008-03-31 03:47:34 +00006599 gen_add16(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006600 } else {
pbrook5e3f8782008-03-31 03:47:34 +00006601 tcg_gen_add_i32(tmp, tmp, tmp2);
6602 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006603 }
6604 }
balrog6c956762008-04-13 00:57:49 +00006605 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006606 } else if ((insn & 0x003f0f60) == 0x003f0f20) {
6607 /* rev */
pbrookb0109802008-03-31 03:47:03 +00006608 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006609 if (insn & (1 << 22)) {
6610 if (insn & (1 << 7)) {
pbrookb0109802008-03-31 03:47:03 +00006611 gen_revsh(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006612 } else {
6613 ARCH(6T2);
pbrookb0109802008-03-31 03:47:03 +00006614 gen_helper_rbit(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006615 }
6616 } else {
6617 if (insn & (1 << 7))
pbrookb0109802008-03-31 03:47:03 +00006618 gen_rev16(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006619 else
aurel3266896cb2009-03-13 09:34:48 +00006620 tcg_gen_bswap32_i32(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006621 }
pbrookb0109802008-03-31 03:47:03 +00006622 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006623 } else {
6624 goto illegal_op;
6625 }
6626 break;
6627 case 2: /* Multiplies (Type 3). */
pbrook5e3f8782008-03-31 03:47:34 +00006628 tmp = load_reg(s, rm);
6629 tmp2 = load_reg(s, rs);
pbrook9ee6e8b2007-11-11 00:04:49 +00006630 if (insn & (1 << 20)) {
6631 /* Signed multiply most significant [accumulate]. */
pbrooka7812ae2008-11-17 14:43:54 +00006632 tmp64 = gen_muls_i64_i32(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006633 if (insn & (1 << 5))
pbrooka7812ae2008-11-17 14:43:54 +00006634 tcg_gen_addi_i64(tmp64, tmp64, 0x80000000u);
6635 tcg_gen_shri_i64(tmp64, tmp64, 32);
pbrook5e3f8782008-03-31 03:47:34 +00006636 tmp = new_tmp();
pbrooka7812ae2008-11-17 14:43:54 +00006637 tcg_gen_trunc_i64_i32(tmp, tmp64);
balrog955a7dd2008-12-07 14:18:02 +00006638 if (rd != 15) {
6639 tmp2 = load_reg(s, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00006640 if (insn & (1 << 6)) {
pbrook5e3f8782008-03-31 03:47:34 +00006641 tcg_gen_sub_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006642 } else {
pbrook5e3f8782008-03-31 03:47:34 +00006643 tcg_gen_add_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006644 }
pbrook5e3f8782008-03-31 03:47:34 +00006645 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006646 }
balrog955a7dd2008-12-07 14:18:02 +00006647 store_reg(s, rn, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006648 } else {
6649 if (insn & (1 << 5))
pbrook5e3f8782008-03-31 03:47:34 +00006650 gen_swap_half(tmp2);
6651 gen_smul_dual(tmp, tmp2);
6652 /* This addition cannot overflow. */
6653 if (insn & (1 << 6)) {
6654 tcg_gen_sub_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006655 } else {
pbrook5e3f8782008-03-31 03:47:34 +00006656 tcg_gen_add_i32(tmp, tmp, tmp2);
6657 }
6658 dead_tmp(tmp2);
6659 if (insn & (1 << 22)) {
6660 /* smlald, smlsld */
pbrooka7812ae2008-11-17 14:43:54 +00006661 tmp64 = tcg_temp_new_i64();
6662 tcg_gen_ext_i32_i64(tmp64, tmp);
pbrook5e3f8782008-03-31 03:47:34 +00006663 dead_tmp(tmp);
pbrooka7812ae2008-11-17 14:43:54 +00006664 gen_addq(s, tmp64, rd, rn);
6665 gen_storeq_reg(s, rd, rn, tmp64);
pbrook5e3f8782008-03-31 03:47:34 +00006666 } else {
6667 /* smuad, smusd, smlad, smlsd */
balrog22478e72008-07-19 10:12:22 +00006668 if (rd != 15)
pbrook9ee6e8b2007-11-11 00:04:49 +00006669 {
balrog22478e72008-07-19 10:12:22 +00006670 tmp2 = load_reg(s, rd);
pbrook5e3f8782008-03-31 03:47:34 +00006671 gen_helper_add_setq(tmp, tmp, tmp2);
6672 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006673 }
balrog22478e72008-07-19 10:12:22 +00006674 store_reg(s, rn, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006675 }
6676 }
6677 break;
6678 case 3:
6679 op1 = ((insn >> 17) & 0x38) | ((insn >> 5) & 7);
6680 switch (op1) {
6681 case 0: /* Unsigned sum of absolute differences. */
pbrook6ddbc6e2008-03-31 03:46:33 +00006682 ARCH(6);
6683 tmp = load_reg(s, rm);
6684 tmp2 = load_reg(s, rs);
6685 gen_helper_usad8(tmp, tmp, tmp2);
6686 dead_tmp(tmp2);
balrogded9d292008-12-07 14:03:27 +00006687 if (rd != 15) {
6688 tmp2 = load_reg(s, rd);
pbrook6ddbc6e2008-03-31 03:46:33 +00006689 tcg_gen_add_i32(tmp, tmp, tmp2);
6690 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006691 }
balrogded9d292008-12-07 14:03:27 +00006692 store_reg(s, rn, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006693 break;
6694 case 0x20: case 0x24: case 0x28: case 0x2c:
6695 /* Bitfield insert/clear. */
6696 ARCH(6T2);
6697 shift = (insn >> 7) & 0x1f;
6698 i = (insn >> 16) & 0x1f;
6699 i = i + 1 - shift;
6700 if (rm == 15) {
pbrook5e3f8782008-03-31 03:47:34 +00006701 tmp = new_tmp();
6702 tcg_gen_movi_i32(tmp, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00006703 } else {
pbrook5e3f8782008-03-31 03:47:34 +00006704 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006705 }
6706 if (i != 32) {
pbrook5e3f8782008-03-31 03:47:34 +00006707 tmp2 = load_reg(s, rd);
pbrook8f8e3aa2008-03-31 03:48:01 +00006708 gen_bfi(tmp, tmp2, tmp, shift, (1u << i) - 1);
pbrook5e3f8782008-03-31 03:47:34 +00006709 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00006710 }
pbrook5e3f8782008-03-31 03:47:34 +00006711 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006712 break;
6713 case 0x12: case 0x16: case 0x1a: case 0x1e: /* sbfx */
6714 case 0x32: case 0x36: case 0x3a: case 0x3e: /* ubfx */
balrog4cc633c2008-12-07 13:32:09 +00006715 ARCH(6T2);
pbrook5e3f8782008-03-31 03:47:34 +00006716 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00006717 shift = (insn >> 7) & 0x1f;
6718 i = ((insn >> 16) & 0x1f) + 1;
6719 if (shift + i > 32)
6720 goto illegal_op;
6721 if (i < 32) {
6722 if (op1 & 0x20) {
pbrook5e3f8782008-03-31 03:47:34 +00006723 gen_ubfx(tmp, shift, (1u << i) - 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00006724 } else {
pbrook5e3f8782008-03-31 03:47:34 +00006725 gen_sbfx(tmp, shift, i);
pbrook9ee6e8b2007-11-11 00:04:49 +00006726 }
6727 }
pbrook5e3f8782008-03-31 03:47:34 +00006728 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00006729 break;
6730 default:
6731 goto illegal_op;
6732 }
6733 break;
6734 }
6735 break;
6736 }
6737 do_ldst:
bellard159f3662006-05-22 23:06:04 +00006738 /* Check for undefined extension instructions
6739 * per the ARM Bible IE:
6740 * xxxx 0111 1111 xxxx xxxx xxxx 1111 xxxx
6741 */
6742 sh = (0xf << 20) | (0xf << 4);
6743 if (op1 == 0x7 && ((insn & sh) == sh))
6744 {
6745 goto illegal_op;
6746 }
bellard2c0262a2003-09-30 20:34:21 +00006747 /* load/store byte/word */
6748 rn = (insn >> 16) & 0xf;
6749 rd = (insn >> 12) & 0xf;
pbrookb0109802008-03-31 03:47:03 +00006750 tmp2 = load_reg(s, rn);
bellardb5ff1b32005-11-26 10:38:39 +00006751 i = (IS_USER(s) || (insn & 0x01200000) == 0x00200000);
bellard2c0262a2003-09-30 20:34:21 +00006752 if (insn & (1 << 24))
pbrookb0109802008-03-31 03:47:03 +00006753 gen_add_data_offset(s, insn, tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006754 if (insn & (1 << 20)) {
6755 /* load */
bellardb5ff1b32005-11-26 10:38:39 +00006756 if (insn & (1 << 22)) {
pbrookb0109802008-03-31 03:47:03 +00006757 tmp = gen_ld8u(tmp2, i);
bellardb5ff1b32005-11-26 10:38:39 +00006758 } else {
pbrookb0109802008-03-31 03:47:03 +00006759 tmp = gen_ld32(tmp2, i);
bellardb5ff1b32005-11-26 10:38:39 +00006760 }
bellard2c0262a2003-09-30 20:34:21 +00006761 } else {
6762 /* store */
pbrookb0109802008-03-31 03:47:03 +00006763 tmp = load_reg(s, rd);
bellard2c0262a2003-09-30 20:34:21 +00006764 if (insn & (1 << 22))
pbrookb0109802008-03-31 03:47:03 +00006765 gen_st8(tmp, tmp2, i);
bellard2c0262a2003-09-30 20:34:21 +00006766 else
pbrookb0109802008-03-31 03:47:03 +00006767 gen_st32(tmp, tmp2, i);
bellard2c0262a2003-09-30 20:34:21 +00006768 }
6769 if (!(insn & (1 << 24))) {
pbrookb0109802008-03-31 03:47:03 +00006770 gen_add_data_offset(s, insn, tmp2);
6771 store_reg(s, rn, tmp2);
6772 } else if (insn & (1 << 21)) {
6773 store_reg(s, rn, tmp2);
6774 } else {
6775 dead_tmp(tmp2);
bellard2c0262a2003-09-30 20:34:21 +00006776 }
pbrook5fd46862007-03-17 01:43:01 +00006777 if (insn & (1 << 20)) {
6778 /* Complete the load. */
6779 if (rd == 15)
pbrookb0109802008-03-31 03:47:03 +00006780 gen_bx(s, tmp);
pbrook5fd46862007-03-17 01:43:01 +00006781 else
pbrookb0109802008-03-31 03:47:03 +00006782 store_reg(s, rd, tmp);
pbrook5fd46862007-03-17 01:43:01 +00006783 }
bellard2c0262a2003-09-30 20:34:21 +00006784 break;
6785 case 0x08:
6786 case 0x09:
6787 {
pbrook191abaa2006-02-04 21:50:36 +00006788 int j, n, user, loaded_base;
pbrookb0109802008-03-31 03:47:03 +00006789 TCGv loaded_var;
bellard2c0262a2003-09-30 20:34:21 +00006790 /* load/store multiple words */
6791 /* XXX: store correct base if write back */
bellardb5ff1b32005-11-26 10:38:39 +00006792 user = 0;
6793 if (insn & (1 << 22)) {
6794 if (IS_USER(s))
6795 goto illegal_op; /* only usable in supervisor mode */
6796
6797 if ((insn & (1 << 15)) == 0)
6798 user = 1;
6799 }
bellard2c0262a2003-09-30 20:34:21 +00006800 rn = (insn >> 16) & 0xf;
pbrookb0109802008-03-31 03:47:03 +00006801 addr = load_reg(s, rn);
ths3b46e622007-09-17 08:09:54 +00006802
bellard2c0262a2003-09-30 20:34:21 +00006803 /* compute total size */
pbrook191abaa2006-02-04 21:50:36 +00006804 loaded_base = 0;
pbrooka50f5b92008-06-29 15:25:29 +00006805 TCGV_UNUSED(loaded_var);
bellard2c0262a2003-09-30 20:34:21 +00006806 n = 0;
6807 for(i=0;i<16;i++) {
6808 if (insn & (1 << i))
6809 n++;
6810 }
6811 /* XXX: test invalid n == 0 case ? */
6812 if (insn & (1 << 23)) {
6813 if (insn & (1 << 24)) {
6814 /* pre increment */
pbrookb0109802008-03-31 03:47:03 +00006815 tcg_gen_addi_i32(addr, addr, 4);
bellard2c0262a2003-09-30 20:34:21 +00006816 } else {
6817 /* post increment */
6818 }
6819 } else {
6820 if (insn & (1 << 24)) {
6821 /* pre decrement */
pbrookb0109802008-03-31 03:47:03 +00006822 tcg_gen_addi_i32(addr, addr, -(n * 4));
bellard2c0262a2003-09-30 20:34:21 +00006823 } else {
6824 /* post decrement */
6825 if (n != 1)
pbrookb0109802008-03-31 03:47:03 +00006826 tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
bellard2c0262a2003-09-30 20:34:21 +00006827 }
6828 }
6829 j = 0;
6830 for(i=0;i<16;i++) {
6831 if (insn & (1 << i)) {
6832 if (insn & (1 << 20)) {
6833 /* load */
pbrookb0109802008-03-31 03:47:03 +00006834 tmp = gen_ld32(addr, IS_USER(s));
bellardb5ff1b32005-11-26 10:38:39 +00006835 if (i == 15) {
pbrookb0109802008-03-31 03:47:03 +00006836 gen_bx(s, tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006837 } else if (user) {
pbrookb0109802008-03-31 03:47:03 +00006838 gen_helper_set_user_reg(tcg_const_i32(i), tmp);
6839 dead_tmp(tmp);
pbrook191abaa2006-02-04 21:50:36 +00006840 } else if (i == rn) {
pbrookb0109802008-03-31 03:47:03 +00006841 loaded_var = tmp;
pbrook191abaa2006-02-04 21:50:36 +00006842 loaded_base = 1;
bellardb5ff1b32005-11-26 10:38:39 +00006843 } else {
pbrookb0109802008-03-31 03:47:03 +00006844 store_reg(s, i, tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006845 }
bellard2c0262a2003-09-30 20:34:21 +00006846 } else {
6847 /* store */
6848 if (i == 15) {
balrog7a774c82007-06-10 13:53:18 +00006849 /* special case: r15 = PC + 8 */
6850 val = (long)s->pc + 4;
pbrookb0109802008-03-31 03:47:03 +00006851 tmp = new_tmp();
6852 tcg_gen_movi_i32(tmp, val);
bellardb5ff1b32005-11-26 10:38:39 +00006853 } else if (user) {
pbrookb0109802008-03-31 03:47:03 +00006854 tmp = new_tmp();
6855 gen_helper_get_user_reg(tmp, tcg_const_i32(i));
bellard2c0262a2003-09-30 20:34:21 +00006856 } else {
pbrookb0109802008-03-31 03:47:03 +00006857 tmp = load_reg(s, i);
bellard2c0262a2003-09-30 20:34:21 +00006858 }
pbrookb0109802008-03-31 03:47:03 +00006859 gen_st32(tmp, addr, IS_USER(s));
bellard2c0262a2003-09-30 20:34:21 +00006860 }
6861 j++;
6862 /* no need to add after the last transfer */
6863 if (j != n)
pbrookb0109802008-03-31 03:47:03 +00006864 tcg_gen_addi_i32(addr, addr, 4);
bellard2c0262a2003-09-30 20:34:21 +00006865 }
6866 }
6867 if (insn & (1 << 21)) {
6868 /* write back */
6869 if (insn & (1 << 23)) {
6870 if (insn & (1 << 24)) {
6871 /* pre increment */
6872 } else {
6873 /* post increment */
pbrookb0109802008-03-31 03:47:03 +00006874 tcg_gen_addi_i32(addr, addr, 4);
bellard2c0262a2003-09-30 20:34:21 +00006875 }
6876 } else {
6877 if (insn & (1 << 24)) {
6878 /* pre decrement */
6879 if (n != 1)
pbrookb0109802008-03-31 03:47:03 +00006880 tcg_gen_addi_i32(addr, addr, -((n - 1) * 4));
bellard2c0262a2003-09-30 20:34:21 +00006881 } else {
6882 /* post decrement */
pbrookb0109802008-03-31 03:47:03 +00006883 tcg_gen_addi_i32(addr, addr, -(n * 4));
bellard2c0262a2003-09-30 20:34:21 +00006884 }
6885 }
pbrookb0109802008-03-31 03:47:03 +00006886 store_reg(s, rn, addr);
6887 } else {
6888 dead_tmp(addr);
bellard2c0262a2003-09-30 20:34:21 +00006889 }
pbrook191abaa2006-02-04 21:50:36 +00006890 if (loaded_base) {
pbrookb0109802008-03-31 03:47:03 +00006891 store_reg(s, rn, loaded_var);
pbrook191abaa2006-02-04 21:50:36 +00006892 }
bellardb5ff1b32005-11-26 10:38:39 +00006893 if ((insn & (1 << 22)) && !user) {
6894 /* Restore CPSR from SPSR. */
pbrookd9ba4832008-03-31 03:46:50 +00006895 tmp = load_cpu_field(spsr);
6896 gen_set_cpsr(tmp, 0xffffffff);
6897 dead_tmp(tmp);
bellardb5ff1b32005-11-26 10:38:39 +00006898 s->is_jmp = DISAS_UPDATE;
6899 }
bellard2c0262a2003-09-30 20:34:21 +00006900 }
6901 break;
6902 case 0xa:
6903 case 0xb:
6904 {
bellard99c475a2005-01-31 20:45:13 +00006905 int32_t offset;
ths3b46e622007-09-17 08:09:54 +00006906
bellard2c0262a2003-09-30 20:34:21 +00006907 /* branch (and link) */
bellard99c475a2005-01-31 20:45:13 +00006908 val = (int32_t)s->pc;
bellard2c0262a2003-09-30 20:34:21 +00006909 if (insn & (1 << 24)) {
pbrook5e3f8782008-03-31 03:47:34 +00006910 tmp = new_tmp();
6911 tcg_gen_movi_i32(tmp, val);
6912 store_reg(s, 14, tmp);
bellard2c0262a2003-09-30 20:34:21 +00006913 }
bellard99c475a2005-01-31 20:45:13 +00006914 offset = (((int32_t)insn << 8) >> 8);
bellard2c0262a2003-09-30 20:34:21 +00006915 val += (offset << 2) + 4;
bellard8aaca4c2005-04-23 18:27:52 +00006916 gen_jmp(s, val);
bellard2c0262a2003-09-30 20:34:21 +00006917 }
6918 break;
bellardb7bcbe92005-02-22 19:27:29 +00006919 case 0xc:
6920 case 0xd:
6921 case 0xe:
6922 /* Coprocessor. */
pbrook9ee6e8b2007-11-11 00:04:49 +00006923 if (disas_coproc_insn(env, s, insn))
balrogc1713132007-04-30 01:26:42 +00006924 goto illegal_op;
bellardb7bcbe92005-02-22 19:27:29 +00006925 break;
bellard2c0262a2003-09-30 20:34:21 +00006926 case 0xf:
6927 /* swi */
pbrook5e3f8782008-03-31 03:47:34 +00006928 gen_set_pc_im(s->pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00006929 s->is_jmp = DISAS_SWI;
bellard2c0262a2003-09-30 20:34:21 +00006930 break;
bellard2c0262a2003-09-30 20:34:21 +00006931 default:
6932 illegal_op:
pbrook9ee6e8b2007-11-11 00:04:49 +00006933 gen_set_condexec(s);
pbrook5e3f8782008-03-31 03:47:34 +00006934 gen_set_pc_im(s->pc - 4);
pbrookd9ba4832008-03-31 03:46:50 +00006935 gen_exception(EXCP_UDEF);
bellard2c0262a2003-09-30 20:34:21 +00006936 s->is_jmp = DISAS_JUMP;
6937 break;
6938 }
6939 }
6940}
6941
pbrook9ee6e8b2007-11-11 00:04:49 +00006942/* Return true if this is a Thumb-2 logical op. */
6943static int
6944thumb2_logic_op(int op)
6945{
6946 return (op < 8);
6947}
6948
6949/* Generate code for a Thumb-2 data processing operation. If CONDS is nonzero
6950 then set condition code flags based on the result of the operation.
6951 If SHIFTER_OUT is nonzero then set the carry flag for logical operations
6952 to the high bit of T1.
6953 Returns zero if the opcode is valid. */
6954
6955static int
6956gen_thumb2_data_op(DisasContext *s, int op, int conds, uint32_t shifter_out)
6957{
6958 int logic_cc;
6959
6960 logic_cc = 0;
6961 switch (op) {
6962 case 0: /* and */
6963 gen_op_andl_T0_T1();
6964 logic_cc = conds;
6965 break;
6966 case 1: /* bic */
6967 gen_op_bicl_T0_T1();
6968 logic_cc = conds;
6969 break;
6970 case 2: /* orr */
6971 gen_op_orl_T0_T1();
6972 logic_cc = conds;
6973 break;
6974 case 3: /* orn */
6975 gen_op_notl_T1();
6976 gen_op_orl_T0_T1();
6977 logic_cc = conds;
6978 break;
6979 case 4: /* eor */
6980 gen_op_xorl_T0_T1();
6981 logic_cc = conds;
6982 break;
6983 case 8: /* add */
6984 if (conds)
6985 gen_op_addl_T0_T1_cc();
6986 else
6987 gen_op_addl_T0_T1();
6988 break;
6989 case 10: /* adc */
6990 if (conds)
6991 gen_op_adcl_T0_T1_cc();
6992 else
pbrookb26eefb2008-03-31 03:44:26 +00006993 gen_adc_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00006994 break;
6995 case 11: /* sbc */
6996 if (conds)
6997 gen_op_sbcl_T0_T1_cc();
6998 else
pbrook36706692008-03-31 03:46:19 +00006999 gen_sbc_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00007000 break;
7001 case 13: /* sub */
7002 if (conds)
7003 gen_op_subl_T0_T1_cc();
7004 else
7005 gen_op_subl_T0_T1();
7006 break;
7007 case 14: /* rsb */
7008 if (conds)
7009 gen_op_rsbl_T0_T1_cc();
7010 else
7011 gen_op_rsbl_T0_T1();
7012 break;
7013 default: /* 5, 6, 7, 9, 12, 15. */
7014 return 1;
7015 }
7016 if (logic_cc) {
7017 gen_op_logic_T0_cc();
7018 if (shifter_out)
pbrookb26eefb2008-03-31 03:44:26 +00007019 gen_set_CF_bit31(cpu_T[1]);
pbrook9ee6e8b2007-11-11 00:04:49 +00007020 }
7021 return 0;
7022}
7023
7024/* Translate a 32-bit thumb instruction. Returns nonzero if the instruction
7025 is not legal. */
7026static int disas_thumb2_insn(CPUState *env, DisasContext *s, uint16_t insn_hw1)
7027{
pbrookb0109802008-03-31 03:47:03 +00007028 uint32_t insn, imm, shift, offset;
pbrook9ee6e8b2007-11-11 00:04:49 +00007029 uint32_t rd, rn, rm, rs;
pbrookb26eefb2008-03-31 03:44:26 +00007030 TCGv tmp;
pbrook6ddbc6e2008-03-31 03:46:33 +00007031 TCGv tmp2;
7032 TCGv tmp3;
pbrookb0109802008-03-31 03:47:03 +00007033 TCGv addr;
pbrooka7812ae2008-11-17 14:43:54 +00007034 TCGv_i64 tmp64;
pbrook9ee6e8b2007-11-11 00:04:49 +00007035 int op;
7036 int shiftop;
7037 int conds;
7038 int logic_cc;
7039
7040 if (!(arm_feature(env, ARM_FEATURE_THUMB2)
7041 || arm_feature (env, ARM_FEATURE_M))) {
balrog601d70b2008-04-20 01:03:45 +00007042 /* Thumb-1 cores may need to treat bl and blx as a pair of
pbrook9ee6e8b2007-11-11 00:04:49 +00007043 16-bit instructions to get correct prefetch abort behavior. */
7044 insn = insn_hw1;
7045 if ((insn & (1 << 12)) == 0) {
7046 /* Second half of blx. */
7047 offset = ((insn & 0x7ff) << 1);
pbrookd9ba4832008-03-31 03:46:50 +00007048 tmp = load_reg(s, 14);
7049 tcg_gen_addi_i32(tmp, tmp, offset);
7050 tcg_gen_andi_i32(tmp, tmp, 0xfffffffc);
pbrook9ee6e8b2007-11-11 00:04:49 +00007051
pbrookd9ba4832008-03-31 03:46:50 +00007052 tmp2 = new_tmp();
pbrookb0109802008-03-31 03:47:03 +00007053 tcg_gen_movi_i32(tmp2, s->pc | 1);
pbrookd9ba4832008-03-31 03:46:50 +00007054 store_reg(s, 14, tmp2);
7055 gen_bx(s, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007056 return 0;
7057 }
7058 if (insn & (1 << 11)) {
7059 /* Second half of bl. */
7060 offset = ((insn & 0x7ff) << 1) | 1;
pbrookd9ba4832008-03-31 03:46:50 +00007061 tmp = load_reg(s, 14);
balrog6a0d8a12008-04-13 13:25:31 +00007062 tcg_gen_addi_i32(tmp, tmp, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007063
pbrookd9ba4832008-03-31 03:46:50 +00007064 tmp2 = new_tmp();
pbrookb0109802008-03-31 03:47:03 +00007065 tcg_gen_movi_i32(tmp2, s->pc | 1);
pbrookd9ba4832008-03-31 03:46:50 +00007066 store_reg(s, 14, tmp2);
7067 gen_bx(s, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007068 return 0;
7069 }
7070 if ((s->pc & ~TARGET_PAGE_MASK) == 0) {
7071 /* Instruction spans a page boundary. Implement it as two
7072 16-bit instructions in case the second half causes an
7073 prefetch abort. */
7074 offset = ((int32_t)insn << 21) >> 9;
pbrookb0109802008-03-31 03:47:03 +00007075 gen_op_movl_T0_im(s->pc + 2 + offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007076 gen_movl_reg_T0(s, 14);
7077 return 0;
7078 }
7079 /* Fall through to 32-bit decode. */
7080 }
7081
7082 insn = lduw_code(s->pc);
7083 s->pc += 2;
7084 insn |= (uint32_t)insn_hw1 << 16;
7085
7086 if ((insn & 0xf800e800) != 0xf000e800) {
7087 ARCH(6T2);
7088 }
7089
7090 rn = (insn >> 16) & 0xf;
7091 rs = (insn >> 12) & 0xf;
7092 rd = (insn >> 8) & 0xf;
7093 rm = insn & 0xf;
7094 switch ((insn >> 25) & 0xf) {
7095 case 0: case 1: case 2: case 3:
7096 /* 16-bit instructions. Should never happen. */
7097 abort();
7098 case 4:
7099 if (insn & (1 << 22)) {
7100 /* Other load/store, table branch. */
7101 if (insn & 0x01200000) {
7102 /* Load/store doubleword. */
7103 if (rn == 15) {
pbrookb0109802008-03-31 03:47:03 +00007104 addr = new_tmp();
7105 tcg_gen_movi_i32(addr, s->pc & ~3);
pbrook9ee6e8b2007-11-11 00:04:49 +00007106 } else {
pbrookb0109802008-03-31 03:47:03 +00007107 addr = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007108 }
7109 offset = (insn & 0xff) * 4;
7110 if ((insn & (1 << 23)) == 0)
7111 offset = -offset;
7112 if (insn & (1 << 24)) {
pbrookb0109802008-03-31 03:47:03 +00007113 tcg_gen_addi_i32(addr, addr, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007114 offset = 0;
7115 }
7116 if (insn & (1 << 20)) {
7117 /* ldrd */
pbrookb0109802008-03-31 03:47:03 +00007118 tmp = gen_ld32(addr, IS_USER(s));
7119 store_reg(s, rs, tmp);
7120 tcg_gen_addi_i32(addr, addr, 4);
7121 tmp = gen_ld32(addr, IS_USER(s));
7122 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007123 } else {
7124 /* strd */
pbrookb0109802008-03-31 03:47:03 +00007125 tmp = load_reg(s, rs);
7126 gen_st32(tmp, addr, IS_USER(s));
7127 tcg_gen_addi_i32(addr, addr, 4);
7128 tmp = load_reg(s, rd);
7129 gen_st32(tmp, addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007130 }
7131 if (insn & (1 << 21)) {
7132 /* Base writeback. */
7133 if (rn == 15)
7134 goto illegal_op;
pbrookb0109802008-03-31 03:47:03 +00007135 tcg_gen_addi_i32(addr, addr, offset - 4);
7136 store_reg(s, rn, addr);
7137 } else {
7138 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007139 }
7140 } else if ((insn & (1 << 23)) == 0) {
7141 /* Load/store exclusive word. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007142 gen_movl_T1_reg(s, rn);
balrog72f1c622008-04-13 01:53:27 +00007143 addr = cpu_T[1];
pbrook9ee6e8b2007-11-11 00:04:49 +00007144 if (insn & (1 << 20)) {
pbrook8f8e3aa2008-03-31 03:48:01 +00007145 gen_helper_mark_exclusive(cpu_env, cpu_T[1]);
7146 tmp = gen_ld32(addr, IS_USER(s));
7147 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007148 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00007149 int label = gen_new_label();
7150 gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
pbrookcb636692008-05-24 02:22:00 +00007151 tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0],
7152 0, label);
pbrook8f8e3aa2008-03-31 03:48:01 +00007153 tmp = load_reg(s, rs);
7154 gen_st32(tmp, cpu_T[1], IS_USER(s));
7155 gen_set_label(label);
7156 gen_movl_reg_T0(s, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00007157 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007158 } else if ((insn & (1 << 6)) == 0) {
7159 /* Table Branch. */
7160 if (rn == 15) {
pbrookb0109802008-03-31 03:47:03 +00007161 addr = new_tmp();
7162 tcg_gen_movi_i32(addr, s->pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00007163 } else {
pbrookb0109802008-03-31 03:47:03 +00007164 addr = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007165 }
pbrookb26eefb2008-03-31 03:44:26 +00007166 tmp = load_reg(s, rm);
pbrookb0109802008-03-31 03:47:03 +00007167 tcg_gen_add_i32(addr, addr, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007168 if (insn & (1 << 4)) {
7169 /* tbh */
pbrookb0109802008-03-31 03:47:03 +00007170 tcg_gen_add_i32(addr, addr, tmp);
pbrookb26eefb2008-03-31 03:44:26 +00007171 dead_tmp(tmp);
pbrookb0109802008-03-31 03:47:03 +00007172 tmp = gen_ld16u(addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007173 } else { /* tbb */
pbrookb26eefb2008-03-31 03:44:26 +00007174 dead_tmp(tmp);
pbrookb0109802008-03-31 03:47:03 +00007175 tmp = gen_ld8u(addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007176 }
pbrookb0109802008-03-31 03:47:03 +00007177 dead_tmp(addr);
7178 tcg_gen_shli_i32(tmp, tmp, 1);
7179 tcg_gen_addi_i32(tmp, tmp, s->pc);
7180 store_reg(s, 15, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007181 } else {
7182 /* Load/store exclusive byte/halfword/doubleword. */
pbrook8f8e3aa2008-03-31 03:48:01 +00007183 /* ??? These are not really atomic. However we know
7184 we never have multiple CPUs running in parallel,
7185 so it is good enough. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007186 op = (insn >> 4) & 0x3;
pbrook8f8e3aa2008-03-31 03:48:01 +00007187 /* Must use a global reg for the address because we have
7188 a conditional branch in the store instruction. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007189 gen_movl_T1_reg(s, rn);
pbrook8f8e3aa2008-03-31 03:48:01 +00007190 addr = cpu_T[1];
pbrook9ee6e8b2007-11-11 00:04:49 +00007191 if (insn & (1 << 20)) {
pbrook8f8e3aa2008-03-31 03:48:01 +00007192 gen_helper_mark_exclusive(cpu_env, addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007193 switch (op) {
7194 case 0:
pbrook8f8e3aa2008-03-31 03:48:01 +00007195 tmp = gen_ld8u(addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007196 break;
7197 case 1:
pbrook8f8e3aa2008-03-31 03:48:01 +00007198 tmp = gen_ld16u(addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007199 break;
7200 case 3:
pbrook8f8e3aa2008-03-31 03:48:01 +00007201 tmp = gen_ld32(addr, IS_USER(s));
7202 tcg_gen_addi_i32(addr, addr, 4);
7203 tmp2 = gen_ld32(addr, IS_USER(s));
7204 store_reg(s, rd, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007205 break;
7206 default:
7207 goto illegal_op;
7208 }
pbrook8f8e3aa2008-03-31 03:48:01 +00007209 store_reg(s, rs, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007210 } else {
pbrook8f8e3aa2008-03-31 03:48:01 +00007211 int label = gen_new_label();
7212 /* Must use a global that is not killed by the branch. */
7213 gen_helper_test_exclusive(cpu_T[0], cpu_env, addr);
pbrookcb636692008-05-24 02:22:00 +00007214 tcg_gen_brcondi_i32(TCG_COND_NE, cpu_T[0], 0, label);
pbrook8f8e3aa2008-03-31 03:48:01 +00007215 tmp = load_reg(s, rs);
pbrook9ee6e8b2007-11-11 00:04:49 +00007216 switch (op) {
7217 case 0:
pbrook8f8e3aa2008-03-31 03:48:01 +00007218 gen_st8(tmp, addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007219 break;
7220 case 1:
pbrook8f8e3aa2008-03-31 03:48:01 +00007221 gen_st16(tmp, addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007222 break;
7223 case 3:
pbrook8f8e3aa2008-03-31 03:48:01 +00007224 gen_st32(tmp, addr, IS_USER(s));
7225 tcg_gen_addi_i32(addr, addr, 4);
7226 tmp = load_reg(s, rd);
7227 gen_st32(tmp, addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007228 break;
7229 default:
7230 goto illegal_op;
7231 }
pbrook8f8e3aa2008-03-31 03:48:01 +00007232 gen_set_label(label);
pbrook9ee6e8b2007-11-11 00:04:49 +00007233 gen_movl_reg_T0(s, rm);
7234 }
7235 }
7236 } else {
7237 /* Load/store multiple, RFE, SRS. */
7238 if (((insn >> 23) & 1) == ((insn >> 24) & 1)) {
7239 /* Not available in user mode. */
pbrookb0109802008-03-31 03:47:03 +00007240 if (IS_USER(s))
pbrook9ee6e8b2007-11-11 00:04:49 +00007241 goto illegal_op;
7242 if (insn & (1 << 20)) {
7243 /* rfe */
pbrookb0109802008-03-31 03:47:03 +00007244 addr = load_reg(s, rn);
7245 if ((insn & (1 << 24)) == 0)
7246 tcg_gen_addi_i32(addr, addr, -8);
7247 /* Load PC into tmp and CPSR into tmp2. */
7248 tmp = gen_ld32(addr, 0);
7249 tcg_gen_addi_i32(addr, addr, 4);
7250 tmp2 = gen_ld32(addr, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00007251 if (insn & (1 << 21)) {
7252 /* Base writeback. */
pbrookb0109802008-03-31 03:47:03 +00007253 if (insn & (1 << 24)) {
7254 tcg_gen_addi_i32(addr, addr, 4);
7255 } else {
7256 tcg_gen_addi_i32(addr, addr, -4);
7257 }
7258 store_reg(s, rn, addr);
7259 } else {
7260 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007261 }
pbrookb0109802008-03-31 03:47:03 +00007262 gen_rfe(s, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007263 } else {
7264 /* srs */
7265 op = (insn & 0x1f);
7266 if (op == (env->uncached_cpsr & CPSR_M)) {
pbrookb0109802008-03-31 03:47:03 +00007267 addr = load_reg(s, 13);
pbrook9ee6e8b2007-11-11 00:04:49 +00007268 } else {
pbrookb0109802008-03-31 03:47:03 +00007269 addr = new_tmp();
7270 gen_helper_get_r13_banked(addr, cpu_env, tcg_const_i32(op));
pbrook9ee6e8b2007-11-11 00:04:49 +00007271 }
7272 if ((insn & (1 << 24)) == 0) {
pbrookb0109802008-03-31 03:47:03 +00007273 tcg_gen_addi_i32(addr, addr, -8);
pbrook9ee6e8b2007-11-11 00:04:49 +00007274 }
pbrookb0109802008-03-31 03:47:03 +00007275 tmp = load_reg(s, 14);
7276 gen_st32(tmp, addr, 0);
7277 tcg_gen_addi_i32(addr, addr, 4);
7278 tmp = new_tmp();
7279 gen_helper_cpsr_read(tmp);
7280 gen_st32(tmp, addr, 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00007281 if (insn & (1 << 21)) {
7282 if ((insn & (1 << 24)) == 0) {
pbrookb0109802008-03-31 03:47:03 +00007283 tcg_gen_addi_i32(addr, addr, -4);
pbrook9ee6e8b2007-11-11 00:04:49 +00007284 } else {
pbrookb0109802008-03-31 03:47:03 +00007285 tcg_gen_addi_i32(addr, addr, 4);
pbrook9ee6e8b2007-11-11 00:04:49 +00007286 }
7287 if (op == (env->uncached_cpsr & CPSR_M)) {
pbrookb0109802008-03-31 03:47:03 +00007288 store_reg(s, 13, addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007289 } else {
pbrookb0109802008-03-31 03:47:03 +00007290 gen_helper_set_r13_banked(cpu_env,
7291 tcg_const_i32(op), addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007292 }
pbrookb0109802008-03-31 03:47:03 +00007293 } else {
7294 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007295 }
7296 }
7297 } else {
7298 int i;
7299 /* Load/store multiple. */
pbrookb0109802008-03-31 03:47:03 +00007300 addr = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007301 offset = 0;
7302 for (i = 0; i < 16; i++) {
7303 if (insn & (1 << i))
7304 offset += 4;
7305 }
7306 if (insn & (1 << 24)) {
pbrookb0109802008-03-31 03:47:03 +00007307 tcg_gen_addi_i32(addr, addr, -offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007308 }
7309
7310 for (i = 0; i < 16; i++) {
7311 if ((insn & (1 << i)) == 0)
7312 continue;
7313 if (insn & (1 << 20)) {
7314 /* Load. */
pbrookb0109802008-03-31 03:47:03 +00007315 tmp = gen_ld32(addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007316 if (i == 15) {
pbrookb0109802008-03-31 03:47:03 +00007317 gen_bx(s, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007318 } else {
pbrookb0109802008-03-31 03:47:03 +00007319 store_reg(s, i, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007320 }
7321 } else {
7322 /* Store. */
pbrookb0109802008-03-31 03:47:03 +00007323 tmp = load_reg(s, i);
7324 gen_st32(tmp, addr, IS_USER(s));
pbrook9ee6e8b2007-11-11 00:04:49 +00007325 }
pbrookb0109802008-03-31 03:47:03 +00007326 tcg_gen_addi_i32(addr, addr, 4);
pbrook9ee6e8b2007-11-11 00:04:49 +00007327 }
7328 if (insn & (1 << 21)) {
7329 /* Base register writeback. */
7330 if (insn & (1 << 24)) {
pbrookb0109802008-03-31 03:47:03 +00007331 tcg_gen_addi_i32(addr, addr, -offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007332 }
7333 /* Fault if writeback register is in register list. */
7334 if (insn & (1 << rn))
7335 goto illegal_op;
pbrookb0109802008-03-31 03:47:03 +00007336 store_reg(s, rn, addr);
7337 } else {
7338 dead_tmp(addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007339 }
7340 }
7341 }
7342 break;
7343 case 5: /* Data processing register constant shift. */
7344 if (rn == 15)
7345 gen_op_movl_T0_im(0);
7346 else
7347 gen_movl_T0_reg(s, rn);
7348 gen_movl_T1_reg(s, rm);
7349 op = (insn >> 21) & 0xf;
7350 shiftop = (insn >> 4) & 3;
7351 shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
7352 conds = (insn & (1 << 20)) != 0;
7353 logic_cc = (conds && thumb2_logic_op(op));
pbrook9a119ff2008-03-31 03:45:35 +00007354 gen_arm_shift_im(cpu_T[1], shiftop, shift, logic_cc);
pbrook9ee6e8b2007-11-11 00:04:49 +00007355 if (gen_thumb2_data_op(s, op, conds, 0))
7356 goto illegal_op;
7357 if (rd != 15)
7358 gen_movl_reg_T0(s, rd);
7359 break;
7360 case 13: /* Misc data processing. */
7361 op = ((insn >> 22) & 6) | ((insn >> 7) & 1);
7362 if (op < 4 && (insn & 0xf000) != 0xf000)
7363 goto illegal_op;
7364 switch (op) {
7365 case 0: /* Register controlled shift. */
pbrook8984bd22008-03-31 03:47:48 +00007366 tmp = load_reg(s, rn);
7367 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007368 if ((insn & 0x70) != 0)
7369 goto illegal_op;
7370 op = (insn >> 21) & 3;
pbrook8984bd22008-03-31 03:47:48 +00007371 logic_cc = (insn & (1 << 20)) != 0;
7372 gen_arm_shift_reg(tmp, op, tmp2, logic_cc);
7373 if (logic_cc)
7374 gen_logic_CC(tmp);
Juha Riihimäki21aeb342009-05-06 09:16:12 +03007375 store_reg_bx(env, s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007376 break;
7377 case 1: /* Sign/zero extend. */
pbrook5e3f8782008-03-31 03:47:34 +00007378 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007379 shift = (insn >> 4) & 3;
7380 /* ??? In many cases it's not neccessary to do a
7381 rotate, a shift is sufficient. */
7382 if (shift != 0)
pbrook5e3f8782008-03-31 03:47:34 +00007383 tcg_gen_rori_i32(tmp, tmp, shift * 8);
pbrook9ee6e8b2007-11-11 00:04:49 +00007384 op = (insn >> 20) & 7;
7385 switch (op) {
pbrook5e3f8782008-03-31 03:47:34 +00007386 case 0: gen_sxth(tmp); break;
7387 case 1: gen_uxth(tmp); break;
7388 case 2: gen_sxtb16(tmp); break;
7389 case 3: gen_uxtb16(tmp); break;
7390 case 4: gen_sxtb(tmp); break;
7391 case 5: gen_uxtb(tmp); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00007392 default: goto illegal_op;
7393 }
7394 if (rn != 15) {
pbrook5e3f8782008-03-31 03:47:34 +00007395 tmp2 = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007396 if ((op >> 1) == 1) {
pbrook5e3f8782008-03-31 03:47:34 +00007397 gen_add16(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007398 } else {
pbrook5e3f8782008-03-31 03:47:34 +00007399 tcg_gen_add_i32(tmp, tmp, tmp2);
7400 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007401 }
7402 }
pbrook5e3f8782008-03-31 03:47:34 +00007403 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007404 break;
7405 case 2: /* SIMD add/subtract. */
7406 op = (insn >> 20) & 7;
7407 shift = (insn >> 4) & 7;
7408 if ((op & 3) == 3 || (shift & 3) == 3)
7409 goto illegal_op;
pbrook6ddbc6e2008-03-31 03:46:33 +00007410 tmp = load_reg(s, rn);
7411 tmp2 = load_reg(s, rm);
7412 gen_thumb2_parallel_addsub(op, shift, tmp, tmp2);
7413 dead_tmp(tmp2);
7414 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007415 break;
7416 case 3: /* Other data processing. */
7417 op = ((insn >> 17) & 0x38) | ((insn >> 4) & 7);
7418 if (op < 4) {
7419 /* Saturating add/subtract. */
pbrookd9ba4832008-03-31 03:46:50 +00007420 tmp = load_reg(s, rn);
7421 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007422 if (op & 2)
pbrookd9ba4832008-03-31 03:46:50 +00007423 gen_helper_double_saturate(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007424 if (op & 1)
pbrookd9ba4832008-03-31 03:46:50 +00007425 gen_helper_sub_saturate(tmp, tmp2, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007426 else
pbrookd9ba4832008-03-31 03:46:50 +00007427 gen_helper_add_saturate(tmp, tmp, tmp2);
7428 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007429 } else {
pbrookd9ba4832008-03-31 03:46:50 +00007430 tmp = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007431 switch (op) {
7432 case 0x0a: /* rbit */
pbrookd9ba4832008-03-31 03:46:50 +00007433 gen_helper_rbit(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007434 break;
7435 case 0x08: /* rev */
aurel3266896cb2009-03-13 09:34:48 +00007436 tcg_gen_bswap32_i32(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007437 break;
7438 case 0x09: /* rev16 */
pbrookd9ba4832008-03-31 03:46:50 +00007439 gen_rev16(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007440 break;
7441 case 0x0b: /* revsh */
pbrookd9ba4832008-03-31 03:46:50 +00007442 gen_revsh(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007443 break;
7444 case 0x10: /* sel */
pbrookd9ba4832008-03-31 03:46:50 +00007445 tmp2 = load_reg(s, rm);
pbrook6ddbc6e2008-03-31 03:46:33 +00007446 tmp3 = new_tmp();
7447 tcg_gen_ld_i32(tmp3, cpu_env, offsetof(CPUState, GE));
pbrookd9ba4832008-03-31 03:46:50 +00007448 gen_helper_sel_flags(tmp, tmp3, tmp, tmp2);
pbrook6ddbc6e2008-03-31 03:46:33 +00007449 dead_tmp(tmp3);
pbrookd9ba4832008-03-31 03:46:50 +00007450 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007451 break;
7452 case 0x18: /* clz */
pbrookd9ba4832008-03-31 03:46:50 +00007453 gen_helper_clz(tmp, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007454 break;
7455 default:
7456 goto illegal_op;
7457 }
7458 }
pbrookd9ba4832008-03-31 03:46:50 +00007459 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007460 break;
7461 case 4: case 5: /* 32-bit multiply. Sum of absolute differences. */
7462 op = (insn >> 4) & 0xf;
pbrookd9ba4832008-03-31 03:46:50 +00007463 tmp = load_reg(s, rn);
7464 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007465 switch ((insn >> 20) & 7) {
7466 case 0: /* 32 x 32 -> 32 */
pbrookd9ba4832008-03-31 03:46:50 +00007467 tcg_gen_mul_i32(tmp, tmp, tmp2);
7468 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007469 if (rs != 15) {
pbrookd9ba4832008-03-31 03:46:50 +00007470 tmp2 = load_reg(s, rs);
pbrook9ee6e8b2007-11-11 00:04:49 +00007471 if (op)
pbrookd9ba4832008-03-31 03:46:50 +00007472 tcg_gen_sub_i32(tmp, tmp2, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007473 else
pbrookd9ba4832008-03-31 03:46:50 +00007474 tcg_gen_add_i32(tmp, tmp, tmp2);
7475 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007476 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007477 break;
7478 case 1: /* 16 x 16 -> 32 */
pbrookd9ba4832008-03-31 03:46:50 +00007479 gen_mulxy(tmp, tmp2, op & 2, op & 1);
7480 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007481 if (rs != 15) {
pbrookd9ba4832008-03-31 03:46:50 +00007482 tmp2 = load_reg(s, rs);
7483 gen_helper_add_setq(tmp, tmp, tmp2);
7484 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007485 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007486 break;
7487 case 2: /* Dual multiply add. */
7488 case 4: /* Dual multiply subtract. */
7489 if (op)
pbrookd9ba4832008-03-31 03:46:50 +00007490 gen_swap_half(tmp2);
7491 gen_smul_dual(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007492 /* This addition cannot overflow. */
7493 if (insn & (1 << 22)) {
pbrookd9ba4832008-03-31 03:46:50 +00007494 tcg_gen_sub_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007495 } else {
pbrookd9ba4832008-03-31 03:46:50 +00007496 tcg_gen_add_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007497 }
pbrookd9ba4832008-03-31 03:46:50 +00007498 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007499 if (rs != 15)
7500 {
pbrookd9ba4832008-03-31 03:46:50 +00007501 tmp2 = load_reg(s, rs);
7502 gen_helper_add_setq(tmp, tmp, tmp2);
7503 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007504 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007505 break;
7506 case 3: /* 32 * 16 -> 32msb */
7507 if (op)
pbrookd9ba4832008-03-31 03:46:50 +00007508 tcg_gen_sari_i32(tmp2, tmp2, 16);
pbrook9ee6e8b2007-11-11 00:04:49 +00007509 else
pbrookd9ba4832008-03-31 03:46:50 +00007510 gen_sxth(tmp2);
pbrooka7812ae2008-11-17 14:43:54 +00007511 tmp64 = gen_muls_i64_i32(tmp, tmp2);
7512 tcg_gen_shri_i64(tmp64, tmp64, 16);
pbrook5e3f8782008-03-31 03:47:34 +00007513 tmp = new_tmp();
pbrooka7812ae2008-11-17 14:43:54 +00007514 tcg_gen_trunc_i64_i32(tmp, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00007515 if (rs != 15)
7516 {
pbrookd9ba4832008-03-31 03:46:50 +00007517 tmp2 = load_reg(s, rs);
7518 gen_helper_add_setq(tmp, tmp, tmp2);
7519 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007520 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007521 break;
7522 case 5: case 6: /* 32 * 32 -> 32msb */
pbrookd9ba4832008-03-31 03:46:50 +00007523 gen_imull(tmp, tmp2);
7524 if (insn & (1 << 5)) {
7525 gen_roundqd(tmp, tmp2);
7526 dead_tmp(tmp2);
7527 } else {
7528 dead_tmp(tmp);
7529 tmp = tmp2;
pbrook9ee6e8b2007-11-11 00:04:49 +00007530 }
pbrookd9ba4832008-03-31 03:46:50 +00007531 if (rs != 15) {
7532 tmp2 = load_reg(s, rs);
7533 if (insn & (1 << 21)) {
7534 tcg_gen_add_i32(tmp, tmp, tmp2);
7535 } else {
7536 tcg_gen_sub_i32(tmp, tmp2, tmp);
7537 }
7538 dead_tmp(tmp2);
7539 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007540 break;
7541 case 7: /* Unsigned sum of absolute differences. */
pbrookd9ba4832008-03-31 03:46:50 +00007542 gen_helper_usad8(tmp, tmp, tmp2);
7543 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007544 if (rs != 15) {
pbrookd9ba4832008-03-31 03:46:50 +00007545 tmp2 = load_reg(s, rs);
7546 tcg_gen_add_i32(tmp, tmp, tmp2);
7547 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007548 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007549 break;
7550 }
pbrookd9ba4832008-03-31 03:46:50 +00007551 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007552 break;
7553 case 6: case 7: /* 64-bit multiply, Divide. */
7554 op = ((insn >> 4) & 0xf) | ((insn >> 16) & 0x70);
pbrook5e3f8782008-03-31 03:47:34 +00007555 tmp = load_reg(s, rn);
7556 tmp2 = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007557 if ((op & 0x50) == 0x10) {
7558 /* sdiv, udiv */
7559 if (!arm_feature(env, ARM_FEATURE_DIV))
7560 goto illegal_op;
7561 if (op & 0x20)
pbrook5e3f8782008-03-31 03:47:34 +00007562 gen_helper_udiv(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007563 else
pbrook5e3f8782008-03-31 03:47:34 +00007564 gen_helper_sdiv(tmp, tmp, tmp2);
7565 dead_tmp(tmp2);
7566 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007567 } else if ((op & 0xe) == 0xc) {
7568 /* Dual multiply accumulate long. */
7569 if (op & 1)
pbrook5e3f8782008-03-31 03:47:34 +00007570 gen_swap_half(tmp2);
7571 gen_smul_dual(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007572 if (op & 0x10) {
pbrook5e3f8782008-03-31 03:47:34 +00007573 tcg_gen_sub_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007574 } else {
pbrook5e3f8782008-03-31 03:47:34 +00007575 tcg_gen_add_i32(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007576 }
pbrook5e3f8782008-03-31 03:47:34 +00007577 dead_tmp(tmp2);
pbrooka7812ae2008-11-17 14:43:54 +00007578 /* BUGFIX */
7579 tmp64 = tcg_temp_new_i64();
7580 tcg_gen_ext_i32_i64(tmp64, tmp);
7581 dead_tmp(tmp);
7582 gen_addq(s, tmp64, rs, rd);
7583 gen_storeq_reg(s, rs, rd, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00007584 } else {
7585 if (op & 0x20) {
7586 /* Unsigned 64-bit multiply */
pbrooka7812ae2008-11-17 14:43:54 +00007587 tmp64 = gen_mulu_i64_i32(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007588 } else {
7589 if (op & 8) {
7590 /* smlalxy */
pbrook5e3f8782008-03-31 03:47:34 +00007591 gen_mulxy(tmp, tmp2, op & 2, op & 1);
7592 dead_tmp(tmp2);
pbrooka7812ae2008-11-17 14:43:54 +00007593 tmp64 = tcg_temp_new_i64();
7594 tcg_gen_ext_i32_i64(tmp64, tmp);
pbrook5e3f8782008-03-31 03:47:34 +00007595 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007596 } else {
7597 /* Signed 64-bit multiply */
pbrooka7812ae2008-11-17 14:43:54 +00007598 tmp64 = gen_muls_i64_i32(tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007599 }
7600 }
7601 if (op & 4) {
7602 /* umaal */
pbrooka7812ae2008-11-17 14:43:54 +00007603 gen_addq_lo(s, tmp64, rs);
7604 gen_addq_lo(s, tmp64, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00007605 } else if (op & 0x40) {
7606 /* 64-bit accumulate. */
pbrooka7812ae2008-11-17 14:43:54 +00007607 gen_addq(s, tmp64, rs, rd);
pbrook9ee6e8b2007-11-11 00:04:49 +00007608 }
pbrooka7812ae2008-11-17 14:43:54 +00007609 gen_storeq_reg(s, rs, rd, tmp64);
pbrook9ee6e8b2007-11-11 00:04:49 +00007610 }
7611 break;
7612 }
7613 break;
7614 case 6: case 7: case 14: case 15:
7615 /* Coprocessor. */
7616 if (((insn >> 24) & 3) == 3) {
7617 /* Translate into the equivalent ARM encoding. */
7618 insn = (insn & 0xe2ffffff) | ((insn & (1 << 28)) >> 4);
7619 if (disas_neon_data_insn(env, s, insn))
7620 goto illegal_op;
7621 } else {
7622 if (insn & (1 << 28))
7623 goto illegal_op;
7624 if (disas_coproc_insn (env, s, insn))
7625 goto illegal_op;
7626 }
7627 break;
7628 case 8: case 9: case 10: case 11:
7629 if (insn & (1 << 15)) {
7630 /* Branches, misc control. */
7631 if (insn & 0x5000) {
7632 /* Unconditional branch. */
7633 /* signextend(hw1[10:0]) -> offset[:12]. */
7634 offset = ((int32_t)insn << 5) >> 9 & ~(int32_t)0xfff;
7635 /* hw1[10:0] -> offset[11:1]. */
7636 offset |= (insn & 0x7ff) << 1;
7637 /* (~hw2[13, 11] ^ offset[24]) -> offset[23,22]
7638 offset[24:22] already have the same value because of the
7639 sign extension above. */
7640 offset ^= ((~insn) & (1 << 13)) << 10;
7641 offset ^= ((~insn) & (1 << 11)) << 11;
7642
pbrook9ee6e8b2007-11-11 00:04:49 +00007643 if (insn & (1 << 14)) {
7644 /* Branch and link. */
pbrookb0109802008-03-31 03:47:03 +00007645 gen_op_movl_T1_im(s->pc | 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00007646 gen_movl_reg_T1(s, 14);
7647 }
7648
pbrookb0109802008-03-31 03:47:03 +00007649 offset += s->pc;
pbrook9ee6e8b2007-11-11 00:04:49 +00007650 if (insn & (1 << 12)) {
7651 /* b/bl */
pbrookb0109802008-03-31 03:47:03 +00007652 gen_jmp(s, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007653 } else {
7654 /* blx */
pbrookb0109802008-03-31 03:47:03 +00007655 offset &= ~(uint32_t)2;
7656 gen_bx_im(s, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007657 }
7658 } else if (((insn >> 23) & 7) == 7) {
7659 /* Misc control */
7660 if (insn & (1 << 13))
7661 goto illegal_op;
7662
7663 if (insn & (1 << 26)) {
7664 /* Secure monitor call (v6Z) */
7665 goto illegal_op; /* not implemented. */
7666 } else {
7667 op = (insn >> 20) & 7;
7668 switch (op) {
7669 case 0: /* msr cpsr. */
7670 if (IS_M(env)) {
pbrook8984bd22008-03-31 03:47:48 +00007671 tmp = load_reg(s, rn);
7672 addr = tcg_const_i32(insn & 0xff);
7673 gen_helper_v7m_msr(cpu_env, addr, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007674 gen_lookup_tb(s);
7675 break;
7676 }
7677 /* fall through */
7678 case 1: /* msr spsr. */
7679 if (IS_M(env))
7680 goto illegal_op;
7681 gen_movl_T0_reg(s, rn);
7682 if (gen_set_psr_T0(s,
7683 msr_mask(env, s, (insn >> 8) & 0xf, op == 1),
7684 op == 1))
7685 goto illegal_op;
7686 break;
7687 case 2: /* cps, nop-hint. */
7688 if (((insn >> 8) & 7) == 0) {
7689 gen_nop_hint(s, insn & 0xff);
7690 }
7691 /* Implemented as NOP in user mode. */
7692 if (IS_USER(s))
7693 break;
7694 offset = 0;
7695 imm = 0;
7696 if (insn & (1 << 10)) {
7697 if (insn & (1 << 7))
7698 offset |= CPSR_A;
7699 if (insn & (1 << 6))
7700 offset |= CPSR_I;
7701 if (insn & (1 << 5))
7702 offset |= CPSR_F;
7703 if (insn & (1 << 9))
7704 imm = CPSR_A | CPSR_I | CPSR_F;
7705 }
7706 if (insn & (1 << 8)) {
7707 offset |= 0x1f;
7708 imm |= (insn & 0x1f);
7709 }
7710 if (offset) {
7711 gen_op_movl_T0_im(imm);
7712 gen_set_psr_T0(s, offset, 0);
7713 }
7714 break;
7715 case 3: /* Special control operations. */
7716 op = (insn >> 4) & 0xf;
7717 switch (op) {
7718 case 2: /* clrex */
pbrook8f8e3aa2008-03-31 03:48:01 +00007719 gen_helper_clrex(cpu_env);
pbrook9ee6e8b2007-11-11 00:04:49 +00007720 break;
7721 case 4: /* dsb */
7722 case 5: /* dmb */
7723 case 6: /* isb */
7724 /* These execute as NOPs. */
7725 ARCH(7);
7726 break;
7727 default:
7728 goto illegal_op;
7729 }
7730 break;
7731 case 4: /* bxj */
7732 /* Trivial implementation equivalent to bx. */
pbrookd9ba4832008-03-31 03:46:50 +00007733 tmp = load_reg(s, rn);
7734 gen_bx(s, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007735 break;
7736 case 5: /* Exception return. */
7737 /* Unpredictable in user mode. */
7738 goto illegal_op;
7739 case 6: /* mrs cpsr. */
pbrook8984bd22008-03-31 03:47:48 +00007740 tmp = new_tmp();
pbrook9ee6e8b2007-11-11 00:04:49 +00007741 if (IS_M(env)) {
pbrook8984bd22008-03-31 03:47:48 +00007742 addr = tcg_const_i32(insn & 0xff);
7743 gen_helper_v7m_mrs(tmp, cpu_env, addr);
pbrook9ee6e8b2007-11-11 00:04:49 +00007744 } else {
pbrook8984bd22008-03-31 03:47:48 +00007745 gen_helper_cpsr_read(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007746 }
pbrook8984bd22008-03-31 03:47:48 +00007747 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007748 break;
7749 case 7: /* mrs spsr. */
7750 /* Not accessible in user mode. */
7751 if (IS_USER(s) || IS_M(env))
7752 goto illegal_op;
pbrookd9ba4832008-03-31 03:46:50 +00007753 tmp = load_cpu_field(spsr);
7754 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007755 break;
7756 }
7757 }
7758 } else {
7759 /* Conditional branch. */
7760 op = (insn >> 22) & 0xf;
7761 /* Generate a conditional jump to next instruction. */
7762 s->condlabel = gen_new_label();
pbrookd9ba4832008-03-31 03:46:50 +00007763 gen_test_cc(op ^ 1, s->condlabel);
pbrook9ee6e8b2007-11-11 00:04:49 +00007764 s->condjmp = 1;
7765
7766 /* offset[11:1] = insn[10:0] */
7767 offset = (insn & 0x7ff) << 1;
7768 /* offset[17:12] = insn[21:16]. */
7769 offset |= (insn & 0x003f0000) >> 4;
7770 /* offset[31:20] = insn[26]. */
7771 offset |= ((int32_t)((insn << 5) & 0x80000000)) >> 11;
7772 /* offset[18] = insn[13]. */
7773 offset |= (insn & (1 << 13)) << 5;
7774 /* offset[19] = insn[11]. */
7775 offset |= (insn & (1 << 11)) << 8;
7776
7777 /* jump to the offset */
pbrookb0109802008-03-31 03:47:03 +00007778 gen_jmp(s, s->pc + offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007779 }
7780 } else {
7781 /* Data processing immediate. */
7782 if (insn & (1 << 25)) {
7783 if (insn & (1 << 24)) {
7784 if (insn & (1 << 20))
7785 goto illegal_op;
7786 /* Bitfield/Saturate. */
7787 op = (insn >> 21) & 7;
7788 imm = insn & 0x1f;
7789 shift = ((insn >> 6) & 3) | ((insn >> 10) & 0x1c);
pbrook6ddbc6e2008-03-31 03:46:33 +00007790 if (rn == 15) {
7791 tmp = new_tmp();
7792 tcg_gen_movi_i32(tmp, 0);
7793 } else {
7794 tmp = load_reg(s, rn);
7795 }
pbrook9ee6e8b2007-11-11 00:04:49 +00007796 switch (op) {
7797 case 2: /* Signed bitfield extract. */
7798 imm++;
7799 if (shift + imm > 32)
7800 goto illegal_op;
7801 if (imm < 32)
pbrook6ddbc6e2008-03-31 03:46:33 +00007802 gen_sbfx(tmp, shift, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007803 break;
7804 case 6: /* Unsigned bitfield extract. */
7805 imm++;
7806 if (shift + imm > 32)
7807 goto illegal_op;
7808 if (imm < 32)
pbrook6ddbc6e2008-03-31 03:46:33 +00007809 gen_ubfx(tmp, shift, (1u << imm) - 1);
pbrook9ee6e8b2007-11-11 00:04:49 +00007810 break;
7811 case 3: /* Bitfield insert/clear. */
7812 if (imm < shift)
7813 goto illegal_op;
7814 imm = imm + 1 - shift;
7815 if (imm != 32) {
pbrook6ddbc6e2008-03-31 03:46:33 +00007816 tmp2 = load_reg(s, rd);
pbrook8f8e3aa2008-03-31 03:48:01 +00007817 gen_bfi(tmp, tmp2, tmp, shift, (1u << imm) - 1);
pbrook6ddbc6e2008-03-31 03:46:33 +00007818 dead_tmp(tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007819 }
7820 break;
7821 case 7:
7822 goto illegal_op;
7823 default: /* Saturate. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007824 if (shift) {
7825 if (op & 1)
pbrook6ddbc6e2008-03-31 03:46:33 +00007826 tcg_gen_sari_i32(tmp, tmp, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00007827 else
pbrook6ddbc6e2008-03-31 03:46:33 +00007828 tcg_gen_shli_i32(tmp, tmp, shift);
pbrook9ee6e8b2007-11-11 00:04:49 +00007829 }
pbrook6ddbc6e2008-03-31 03:46:33 +00007830 tmp2 = tcg_const_i32(imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007831 if (op & 4) {
7832 /* Unsigned. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007833 if ((op & 1) && shift == 0)
pbrook6ddbc6e2008-03-31 03:46:33 +00007834 gen_helper_usat16(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007835 else
pbrook6ddbc6e2008-03-31 03:46:33 +00007836 gen_helper_usat(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007837 } else {
7838 /* Signed. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007839 if ((op & 1) && shift == 0)
pbrook6ddbc6e2008-03-31 03:46:33 +00007840 gen_helper_ssat16(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007841 else
pbrook6ddbc6e2008-03-31 03:46:33 +00007842 gen_helper_ssat(tmp, tmp, tmp2);
pbrook9ee6e8b2007-11-11 00:04:49 +00007843 }
7844 break;
7845 }
pbrook6ddbc6e2008-03-31 03:46:33 +00007846 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007847 } else {
7848 imm = ((insn & 0x04000000) >> 15)
7849 | ((insn & 0x7000) >> 4) | (insn & 0xff);
7850 if (insn & (1 << 22)) {
7851 /* 16-bit immediate. */
7852 imm |= (insn >> 4) & 0xf000;
7853 if (insn & (1 << 23)) {
7854 /* movt */
pbrook5e3f8782008-03-31 03:47:34 +00007855 tmp = load_reg(s, rd);
pbrook86831432008-05-11 12:22:01 +00007856 tcg_gen_ext16u_i32(tmp, tmp);
pbrook5e3f8782008-03-31 03:47:34 +00007857 tcg_gen_ori_i32(tmp, tmp, imm << 16);
pbrook9ee6e8b2007-11-11 00:04:49 +00007858 } else {
7859 /* movw */
pbrook5e3f8782008-03-31 03:47:34 +00007860 tmp = new_tmp();
7861 tcg_gen_movi_i32(tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007862 }
7863 } else {
7864 /* Add/sub 12-bit immediate. */
7865 if (rn == 15) {
pbrookb0109802008-03-31 03:47:03 +00007866 offset = s->pc & ~(uint32_t)3;
pbrook9ee6e8b2007-11-11 00:04:49 +00007867 if (insn & (1 << 23))
pbrookb0109802008-03-31 03:47:03 +00007868 offset -= imm;
pbrook9ee6e8b2007-11-11 00:04:49 +00007869 else
pbrookb0109802008-03-31 03:47:03 +00007870 offset += imm;
pbrook5e3f8782008-03-31 03:47:34 +00007871 tmp = new_tmp();
7872 tcg_gen_movi_i32(tmp, offset);
pbrook9ee6e8b2007-11-11 00:04:49 +00007873 } else {
pbrook5e3f8782008-03-31 03:47:34 +00007874 tmp = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007875 if (insn & (1 << 23))
pbrook5e3f8782008-03-31 03:47:34 +00007876 tcg_gen_subi_i32(tmp, tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007877 else
pbrook5e3f8782008-03-31 03:47:34 +00007878 tcg_gen_addi_i32(tmp, tmp, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007879 }
7880 }
pbrook5e3f8782008-03-31 03:47:34 +00007881 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007882 }
7883 } else {
7884 int shifter_out = 0;
7885 /* modified 12-bit immediate. */
7886 shift = ((insn & 0x04000000) >> 23) | ((insn & 0x7000) >> 12);
7887 imm = (insn & 0xff);
7888 switch (shift) {
7889 case 0: /* XY */
7890 /* Nothing to do. */
7891 break;
7892 case 1: /* 00XY00XY */
7893 imm |= imm << 16;
7894 break;
7895 case 2: /* XY00XY00 */
7896 imm |= imm << 16;
7897 imm <<= 8;
7898 break;
7899 case 3: /* XYXYXYXY */
7900 imm |= imm << 16;
7901 imm |= imm << 8;
7902 break;
7903 default: /* Rotated constant. */
7904 shift = (shift << 1) | (imm >> 7);
7905 imm |= 0x80;
7906 imm = imm << (32 - shift);
7907 shifter_out = 1;
7908 break;
7909 }
7910 gen_op_movl_T1_im(imm);
7911 rn = (insn >> 16) & 0xf;
7912 if (rn == 15)
7913 gen_op_movl_T0_im(0);
7914 else
7915 gen_movl_T0_reg(s, rn);
7916 op = (insn >> 21) & 0xf;
7917 if (gen_thumb2_data_op(s, op, (insn & (1 << 20)) != 0,
7918 shifter_out))
7919 goto illegal_op;
7920 rd = (insn >> 8) & 0xf;
7921 if (rd != 15) {
7922 gen_movl_reg_T0(s, rd);
7923 }
7924 }
7925 }
7926 break;
7927 case 12: /* Load/store single data item. */
7928 {
7929 int postinc = 0;
7930 int writeback = 0;
pbrookb0109802008-03-31 03:47:03 +00007931 int user;
pbrook9ee6e8b2007-11-11 00:04:49 +00007932 if ((insn & 0x01100000) == 0x01000000) {
7933 if (disas_neon_ls_insn(env, s, insn))
7934 goto illegal_op;
7935 break;
7936 }
pbrookb0109802008-03-31 03:47:03 +00007937 user = IS_USER(s);
pbrook9ee6e8b2007-11-11 00:04:49 +00007938 if (rn == 15) {
pbrookb0109802008-03-31 03:47:03 +00007939 addr = new_tmp();
pbrook9ee6e8b2007-11-11 00:04:49 +00007940 /* PC relative. */
7941 /* s->pc has already been incremented by 4. */
7942 imm = s->pc & 0xfffffffc;
7943 if (insn & (1 << 23))
7944 imm += insn & 0xfff;
7945 else
7946 imm -= insn & 0xfff;
pbrookb0109802008-03-31 03:47:03 +00007947 tcg_gen_movi_i32(addr, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007948 } else {
pbrookb0109802008-03-31 03:47:03 +00007949 addr = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00007950 if (insn & (1 << 23)) {
7951 /* Positive offset. */
7952 imm = insn & 0xfff;
pbrookb0109802008-03-31 03:47:03 +00007953 tcg_gen_addi_i32(addr, addr, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007954 } else {
7955 op = (insn >> 8) & 7;
7956 imm = insn & 0xff;
7957 switch (op) {
7958 case 0: case 8: /* Shifted Register. */
7959 shift = (insn >> 4) & 0xf;
7960 if (shift > 3)
7961 goto illegal_op;
pbrookb26eefb2008-03-31 03:44:26 +00007962 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007963 if (shift)
pbrookb26eefb2008-03-31 03:44:26 +00007964 tcg_gen_shli_i32(tmp, tmp, shift);
pbrookb0109802008-03-31 03:47:03 +00007965 tcg_gen_add_i32(addr, addr, tmp);
pbrookb26eefb2008-03-31 03:44:26 +00007966 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00007967 break;
7968 case 4: /* Negative offset. */
pbrookb0109802008-03-31 03:47:03 +00007969 tcg_gen_addi_i32(addr, addr, -imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007970 break;
7971 case 6: /* User privilege. */
pbrookb0109802008-03-31 03:47:03 +00007972 tcg_gen_addi_i32(addr, addr, imm);
7973 user = 1;
pbrook9ee6e8b2007-11-11 00:04:49 +00007974 break;
7975 case 1: /* Post-decrement. */
7976 imm = -imm;
7977 /* Fall through. */
7978 case 3: /* Post-increment. */
pbrook9ee6e8b2007-11-11 00:04:49 +00007979 postinc = 1;
7980 writeback = 1;
7981 break;
7982 case 5: /* Pre-decrement. */
7983 imm = -imm;
7984 /* Fall through. */
7985 case 7: /* Pre-increment. */
pbrookb0109802008-03-31 03:47:03 +00007986 tcg_gen_addi_i32(addr, addr, imm);
pbrook9ee6e8b2007-11-11 00:04:49 +00007987 writeback = 1;
7988 break;
7989 default:
7990 goto illegal_op;
7991 }
7992 }
7993 }
7994 op = ((insn >> 21) & 3) | ((insn >> 22) & 4);
7995 if (insn & (1 << 20)) {
7996 /* Load. */
7997 if (rs == 15 && op != 2) {
7998 if (op & 2)
7999 goto illegal_op;
8000 /* Memory hint. Implemented as NOP. */
8001 } else {
8002 switch (op) {
pbrookb0109802008-03-31 03:47:03 +00008003 case 0: tmp = gen_ld8u(addr, user); break;
8004 case 4: tmp = gen_ld8s(addr, user); break;
8005 case 1: tmp = gen_ld16u(addr, user); break;
8006 case 5: tmp = gen_ld16s(addr, user); break;
8007 case 2: tmp = gen_ld32(addr, user); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008008 default: goto illegal_op;
8009 }
8010 if (rs == 15) {
pbrookb0109802008-03-31 03:47:03 +00008011 gen_bx(s, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00008012 } else {
pbrookb0109802008-03-31 03:47:03 +00008013 store_reg(s, rs, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00008014 }
8015 }
8016 } else {
8017 /* Store. */
8018 if (rs == 15)
8019 goto illegal_op;
pbrookb0109802008-03-31 03:47:03 +00008020 tmp = load_reg(s, rs);
pbrook9ee6e8b2007-11-11 00:04:49 +00008021 switch (op) {
pbrookb0109802008-03-31 03:47:03 +00008022 case 0: gen_st8(tmp, addr, user); break;
8023 case 1: gen_st16(tmp, addr, user); break;
8024 case 2: gen_st32(tmp, addr, user); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008025 default: goto illegal_op;
8026 }
8027 }
8028 if (postinc)
pbrookb0109802008-03-31 03:47:03 +00008029 tcg_gen_addi_i32(addr, addr, imm);
8030 if (writeback) {
8031 store_reg(s, rn, addr);
8032 } else {
8033 dead_tmp(addr);
8034 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008035 }
8036 break;
8037 default:
8038 goto illegal_op;
8039 }
8040 return 0;
8041illegal_op:
8042 return 1;
8043}
8044
8045static void disas_thumb_insn(CPUState *env, DisasContext *s)
bellard99c475a2005-01-31 20:45:13 +00008046{
8047 uint32_t val, insn, op, rm, rn, rd, shift, cond;
8048 int32_t offset;
8049 int i;
pbrookb26eefb2008-03-31 03:44:26 +00008050 TCGv tmp;
pbrookd9ba4832008-03-31 03:46:50 +00008051 TCGv tmp2;
pbrookb0109802008-03-31 03:47:03 +00008052 TCGv addr;
bellard99c475a2005-01-31 20:45:13 +00008053
pbrook9ee6e8b2007-11-11 00:04:49 +00008054 if (s->condexec_mask) {
8055 cond = s->condexec_cond;
8056 s->condlabel = gen_new_label();
pbrookd9ba4832008-03-31 03:46:50 +00008057 gen_test_cc(cond ^ 1, s->condlabel);
pbrook9ee6e8b2007-11-11 00:04:49 +00008058 s->condjmp = 1;
8059 }
8060
bellardb5ff1b32005-11-26 10:38:39 +00008061 insn = lduw_code(s->pc);
bellard99c475a2005-01-31 20:45:13 +00008062 s->pc += 2;
bellardb5ff1b32005-11-26 10:38:39 +00008063
bellard99c475a2005-01-31 20:45:13 +00008064 switch (insn >> 12) {
8065 case 0: case 1:
8066 rd = insn & 7;
8067 op = (insn >> 11) & 3;
8068 if (op == 3) {
8069 /* add/subtract */
8070 rn = (insn >> 3) & 7;
8071 gen_movl_T0_reg(s, rn);
8072 if (insn & (1 << 10)) {
8073 /* immediate */
8074 gen_op_movl_T1_im((insn >> 6) & 7);
8075 } else {
8076 /* reg */
8077 rm = (insn >> 6) & 7;
8078 gen_movl_T1_reg(s, rm);
8079 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008080 if (insn & (1 << 9)) {
8081 if (s->condexec_mask)
8082 gen_op_subl_T0_T1();
8083 else
8084 gen_op_subl_T0_T1_cc();
8085 } else {
8086 if (s->condexec_mask)
8087 gen_op_addl_T0_T1();
8088 else
8089 gen_op_addl_T0_T1_cc();
8090 }
bellard99c475a2005-01-31 20:45:13 +00008091 gen_movl_reg_T0(s, rd);
8092 } else {
8093 /* shift immediate */
8094 rm = (insn >> 3) & 7;
8095 shift = (insn >> 6) & 0x1f;
pbrook9a119ff2008-03-31 03:45:35 +00008096 tmp = load_reg(s, rm);
8097 gen_arm_shift_im(tmp, op, shift, s->condexec_mask == 0);
8098 if (!s->condexec_mask)
8099 gen_logic_CC(tmp);
8100 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008101 }
8102 break;
8103 case 2: case 3:
8104 /* arithmetic large immediate */
8105 op = (insn >> 11) & 3;
8106 rd = (insn >> 8) & 0x7;
8107 if (op == 0) {
8108 gen_op_movl_T0_im(insn & 0xff);
8109 } else {
8110 gen_movl_T0_reg(s, rd);
8111 gen_op_movl_T1_im(insn & 0xff);
8112 }
8113 switch (op) {
8114 case 0: /* mov */
pbrook9ee6e8b2007-11-11 00:04:49 +00008115 if (!s->condexec_mask)
8116 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008117 break;
8118 case 1: /* cmp */
8119 gen_op_subl_T0_T1_cc();
8120 break;
8121 case 2: /* add */
pbrook9ee6e8b2007-11-11 00:04:49 +00008122 if (s->condexec_mask)
8123 gen_op_addl_T0_T1();
8124 else
8125 gen_op_addl_T0_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008126 break;
8127 case 3: /* sub */
pbrook9ee6e8b2007-11-11 00:04:49 +00008128 if (s->condexec_mask)
8129 gen_op_subl_T0_T1();
8130 else
8131 gen_op_subl_T0_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008132 break;
8133 }
8134 if (op != 1)
8135 gen_movl_reg_T0(s, rd);
8136 break;
8137 case 4:
8138 if (insn & (1 << 11)) {
8139 rd = (insn >> 8) & 7;
bellard5899f382005-04-27 20:25:20 +00008140 /* load pc-relative. Bit 1 of PC is ignored. */
8141 val = s->pc + 2 + ((insn & 0xff) * 4);
8142 val &= ~(uint32_t)2;
pbrookb0109802008-03-31 03:47:03 +00008143 addr = new_tmp();
8144 tcg_gen_movi_i32(addr, val);
8145 tmp = gen_ld32(addr, IS_USER(s));
8146 dead_tmp(addr);
8147 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008148 break;
8149 }
8150 if (insn & (1 << 10)) {
8151 /* data processing extended or blx */
8152 rd = (insn & 7) | ((insn >> 4) & 8);
8153 rm = (insn >> 3) & 0xf;
8154 op = (insn >> 8) & 3;
8155 switch (op) {
8156 case 0: /* add */
8157 gen_movl_T0_reg(s, rd);
8158 gen_movl_T1_reg(s, rm);
8159 gen_op_addl_T0_T1();
8160 gen_movl_reg_T0(s, rd);
8161 break;
8162 case 1: /* cmp */
8163 gen_movl_T0_reg(s, rd);
8164 gen_movl_T1_reg(s, rm);
8165 gen_op_subl_T0_T1_cc();
8166 break;
8167 case 2: /* mov/cpy */
8168 gen_movl_T0_reg(s, rm);
8169 gen_movl_reg_T0(s, rd);
8170 break;
8171 case 3:/* branch [and link] exchange thumb register */
pbrookb0109802008-03-31 03:47:03 +00008172 tmp = load_reg(s, rm);
bellard99c475a2005-01-31 20:45:13 +00008173 if (insn & (1 << 7)) {
8174 val = (uint32_t)s->pc | 1;
pbrookb0109802008-03-31 03:47:03 +00008175 tmp2 = new_tmp();
8176 tcg_gen_movi_i32(tmp2, val);
8177 store_reg(s, 14, tmp2);
bellard99c475a2005-01-31 20:45:13 +00008178 }
pbrookd9ba4832008-03-31 03:46:50 +00008179 gen_bx(s, tmp);
bellard99c475a2005-01-31 20:45:13 +00008180 break;
8181 }
8182 break;
8183 }
8184
8185 /* data processing register */
8186 rd = insn & 7;
8187 rm = (insn >> 3) & 7;
8188 op = (insn >> 6) & 0xf;
8189 if (op == 2 || op == 3 || op == 4 || op == 7) {
8190 /* the shift/rotate ops want the operands backwards */
8191 val = rm;
8192 rm = rd;
8193 rd = val;
8194 val = 1;
8195 } else {
8196 val = 0;
8197 }
8198
8199 if (op == 9) /* neg */
8200 gen_op_movl_T0_im(0);
8201 else if (op != 0xf) /* mvn doesn't read its first operand */
8202 gen_movl_T0_reg(s, rd);
8203
8204 gen_movl_T1_reg(s, rm);
bellard5899f382005-04-27 20:25:20 +00008205 switch (op) {
bellard99c475a2005-01-31 20:45:13 +00008206 case 0x0: /* and */
8207 gen_op_andl_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008208 if (!s->condexec_mask)
8209 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008210 break;
8211 case 0x1: /* eor */
8212 gen_op_xorl_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008213 if (!s->condexec_mask)
8214 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008215 break;
8216 case 0x2: /* lsl */
pbrook9ee6e8b2007-11-11 00:04:49 +00008217 if (s->condexec_mask) {
pbrook8984bd22008-03-31 03:47:48 +00008218 gen_helper_shl(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008219 } else {
pbrook8984bd22008-03-31 03:47:48 +00008220 gen_helper_shl_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008221 gen_op_logic_T1_cc();
8222 }
bellard99c475a2005-01-31 20:45:13 +00008223 break;
8224 case 0x3: /* lsr */
pbrook9ee6e8b2007-11-11 00:04:49 +00008225 if (s->condexec_mask) {
pbrook8984bd22008-03-31 03:47:48 +00008226 gen_helper_shr(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008227 } else {
pbrook8984bd22008-03-31 03:47:48 +00008228 gen_helper_shr_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008229 gen_op_logic_T1_cc();
8230 }
bellard99c475a2005-01-31 20:45:13 +00008231 break;
8232 case 0x4: /* asr */
pbrook9ee6e8b2007-11-11 00:04:49 +00008233 if (s->condexec_mask) {
pbrook8984bd22008-03-31 03:47:48 +00008234 gen_helper_sar(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008235 } else {
pbrook8984bd22008-03-31 03:47:48 +00008236 gen_helper_sar_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008237 gen_op_logic_T1_cc();
8238 }
bellard99c475a2005-01-31 20:45:13 +00008239 break;
8240 case 0x5: /* adc */
pbrook9ee6e8b2007-11-11 00:04:49 +00008241 if (s->condexec_mask)
pbrookb26eefb2008-03-31 03:44:26 +00008242 gen_adc_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008243 else
8244 gen_op_adcl_T0_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008245 break;
8246 case 0x6: /* sbc */
pbrook9ee6e8b2007-11-11 00:04:49 +00008247 if (s->condexec_mask)
pbrook36706692008-03-31 03:46:19 +00008248 gen_sbc_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008249 else
8250 gen_op_sbcl_T0_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008251 break;
8252 case 0x7: /* ror */
pbrook9ee6e8b2007-11-11 00:04:49 +00008253 if (s->condexec_mask) {
pbrook8984bd22008-03-31 03:47:48 +00008254 gen_helper_ror(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008255 } else {
pbrook8984bd22008-03-31 03:47:48 +00008256 gen_helper_ror_cc(cpu_T[1], cpu_T[1], cpu_T[0]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008257 gen_op_logic_T1_cc();
8258 }
bellard99c475a2005-01-31 20:45:13 +00008259 break;
8260 case 0x8: /* tst */
8261 gen_op_andl_T0_T1();
8262 gen_op_logic_T0_cc();
8263 rd = 16;
bellard5899f382005-04-27 20:25:20 +00008264 break;
bellard99c475a2005-01-31 20:45:13 +00008265 case 0x9: /* neg */
pbrook9ee6e8b2007-11-11 00:04:49 +00008266 if (s->condexec_mask)
pbrook390efc52008-05-11 14:35:37 +00008267 tcg_gen_neg_i32(cpu_T[0], cpu_T[1]);
pbrook9ee6e8b2007-11-11 00:04:49 +00008268 else
8269 gen_op_subl_T0_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008270 break;
8271 case 0xa: /* cmp */
8272 gen_op_subl_T0_T1_cc();
8273 rd = 16;
8274 break;
8275 case 0xb: /* cmn */
8276 gen_op_addl_T0_T1_cc();
8277 rd = 16;
8278 break;
8279 case 0xc: /* orr */
8280 gen_op_orl_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008281 if (!s->condexec_mask)
8282 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008283 break;
8284 case 0xd: /* mul */
8285 gen_op_mull_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008286 if (!s->condexec_mask)
8287 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008288 break;
8289 case 0xe: /* bic */
8290 gen_op_bicl_T0_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008291 if (!s->condexec_mask)
8292 gen_op_logic_T0_cc();
bellard99c475a2005-01-31 20:45:13 +00008293 break;
8294 case 0xf: /* mvn */
8295 gen_op_notl_T1();
pbrook9ee6e8b2007-11-11 00:04:49 +00008296 if (!s->condexec_mask)
8297 gen_op_logic_T1_cc();
bellard99c475a2005-01-31 20:45:13 +00008298 val = 1;
bellard5899f382005-04-27 20:25:20 +00008299 rm = rd;
bellard99c475a2005-01-31 20:45:13 +00008300 break;
8301 }
8302 if (rd != 16) {
8303 if (val)
bellard5899f382005-04-27 20:25:20 +00008304 gen_movl_reg_T1(s, rm);
bellard99c475a2005-01-31 20:45:13 +00008305 else
8306 gen_movl_reg_T0(s, rd);
8307 }
8308 break;
8309
8310 case 5:
8311 /* load/store register offset. */
8312 rd = insn & 7;
8313 rn = (insn >> 3) & 7;
8314 rm = (insn >> 6) & 7;
8315 op = (insn >> 9) & 7;
pbrookb0109802008-03-31 03:47:03 +00008316 addr = load_reg(s, rn);
pbrookb26eefb2008-03-31 03:44:26 +00008317 tmp = load_reg(s, rm);
pbrookb0109802008-03-31 03:47:03 +00008318 tcg_gen_add_i32(addr, addr, tmp);
pbrookb26eefb2008-03-31 03:44:26 +00008319 dead_tmp(tmp);
bellard99c475a2005-01-31 20:45:13 +00008320
8321 if (op < 3) /* store */
pbrookb0109802008-03-31 03:47:03 +00008322 tmp = load_reg(s, rd);
bellard99c475a2005-01-31 20:45:13 +00008323
8324 switch (op) {
8325 case 0: /* str */
pbrookb0109802008-03-31 03:47:03 +00008326 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008327 break;
8328 case 1: /* strh */
pbrookb0109802008-03-31 03:47:03 +00008329 gen_st16(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008330 break;
8331 case 2: /* strb */
pbrookb0109802008-03-31 03:47:03 +00008332 gen_st8(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008333 break;
8334 case 3: /* ldrsb */
pbrookb0109802008-03-31 03:47:03 +00008335 tmp = gen_ld8s(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008336 break;
8337 case 4: /* ldr */
pbrookb0109802008-03-31 03:47:03 +00008338 tmp = gen_ld32(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008339 break;
8340 case 5: /* ldrh */
pbrookb0109802008-03-31 03:47:03 +00008341 tmp = gen_ld16u(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008342 break;
8343 case 6: /* ldrb */
pbrookb0109802008-03-31 03:47:03 +00008344 tmp = gen_ld8u(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008345 break;
8346 case 7: /* ldrsh */
pbrookb0109802008-03-31 03:47:03 +00008347 tmp = gen_ld16s(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008348 break;
8349 }
8350 if (op >= 3) /* load */
pbrookb0109802008-03-31 03:47:03 +00008351 store_reg(s, rd, tmp);
8352 dead_tmp(addr);
bellard99c475a2005-01-31 20:45:13 +00008353 break;
8354
8355 case 6:
8356 /* load/store word immediate offset */
8357 rd = insn & 7;
8358 rn = (insn >> 3) & 7;
pbrookb0109802008-03-31 03:47:03 +00008359 addr = load_reg(s, rn);
bellard99c475a2005-01-31 20:45:13 +00008360 val = (insn >> 4) & 0x7c;
pbrookb0109802008-03-31 03:47:03 +00008361 tcg_gen_addi_i32(addr, addr, val);
bellard99c475a2005-01-31 20:45:13 +00008362
8363 if (insn & (1 << 11)) {
8364 /* load */
pbrookb0109802008-03-31 03:47:03 +00008365 tmp = gen_ld32(addr, IS_USER(s));
8366 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008367 } else {
8368 /* store */
pbrookb0109802008-03-31 03:47:03 +00008369 tmp = load_reg(s, rd);
8370 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008371 }
pbrookb0109802008-03-31 03:47:03 +00008372 dead_tmp(addr);
bellard99c475a2005-01-31 20:45:13 +00008373 break;
8374
8375 case 7:
8376 /* load/store byte immediate offset */
8377 rd = insn & 7;
8378 rn = (insn >> 3) & 7;
pbrookb0109802008-03-31 03:47:03 +00008379 addr = load_reg(s, rn);
bellard99c475a2005-01-31 20:45:13 +00008380 val = (insn >> 6) & 0x1f;
pbrookb0109802008-03-31 03:47:03 +00008381 tcg_gen_addi_i32(addr, addr, val);
bellard99c475a2005-01-31 20:45:13 +00008382
8383 if (insn & (1 << 11)) {
8384 /* load */
pbrookb0109802008-03-31 03:47:03 +00008385 tmp = gen_ld8u(addr, IS_USER(s));
8386 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008387 } else {
8388 /* store */
pbrookb0109802008-03-31 03:47:03 +00008389 tmp = load_reg(s, rd);
8390 gen_st8(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008391 }
pbrookb0109802008-03-31 03:47:03 +00008392 dead_tmp(addr);
bellard99c475a2005-01-31 20:45:13 +00008393 break;
8394
8395 case 8:
8396 /* load/store halfword immediate offset */
8397 rd = insn & 7;
8398 rn = (insn >> 3) & 7;
pbrookb0109802008-03-31 03:47:03 +00008399 addr = load_reg(s, rn);
bellard99c475a2005-01-31 20:45:13 +00008400 val = (insn >> 5) & 0x3e;
pbrookb0109802008-03-31 03:47:03 +00008401 tcg_gen_addi_i32(addr, addr, val);
bellard99c475a2005-01-31 20:45:13 +00008402
8403 if (insn & (1 << 11)) {
8404 /* load */
pbrookb0109802008-03-31 03:47:03 +00008405 tmp = gen_ld16u(addr, IS_USER(s));
8406 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008407 } else {
8408 /* store */
pbrookb0109802008-03-31 03:47:03 +00008409 tmp = load_reg(s, rd);
8410 gen_st16(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008411 }
pbrookb0109802008-03-31 03:47:03 +00008412 dead_tmp(addr);
bellard99c475a2005-01-31 20:45:13 +00008413 break;
8414
8415 case 9:
8416 /* load/store from stack */
8417 rd = (insn >> 8) & 7;
pbrookb0109802008-03-31 03:47:03 +00008418 addr = load_reg(s, 13);
bellard99c475a2005-01-31 20:45:13 +00008419 val = (insn & 0xff) * 4;
pbrookb0109802008-03-31 03:47:03 +00008420 tcg_gen_addi_i32(addr, addr, val);
bellard99c475a2005-01-31 20:45:13 +00008421
8422 if (insn & (1 << 11)) {
8423 /* load */
pbrookb0109802008-03-31 03:47:03 +00008424 tmp = gen_ld32(addr, IS_USER(s));
8425 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008426 } else {
8427 /* store */
pbrookb0109802008-03-31 03:47:03 +00008428 tmp = load_reg(s, rd);
8429 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008430 }
pbrookb0109802008-03-31 03:47:03 +00008431 dead_tmp(addr);
bellard99c475a2005-01-31 20:45:13 +00008432 break;
8433
8434 case 10:
8435 /* add to high reg */
8436 rd = (insn >> 8) & 7;
bellard5899f382005-04-27 20:25:20 +00008437 if (insn & (1 << 11)) {
8438 /* SP */
pbrook5e3f8782008-03-31 03:47:34 +00008439 tmp = load_reg(s, 13);
bellard5899f382005-04-27 20:25:20 +00008440 } else {
8441 /* PC. bit 1 is ignored. */
pbrook5e3f8782008-03-31 03:47:34 +00008442 tmp = new_tmp();
8443 tcg_gen_movi_i32(tmp, (s->pc + 2) & ~(uint32_t)2);
bellard5899f382005-04-27 20:25:20 +00008444 }
bellard99c475a2005-01-31 20:45:13 +00008445 val = (insn & 0xff) * 4;
pbrook5e3f8782008-03-31 03:47:34 +00008446 tcg_gen_addi_i32(tmp, tmp, val);
8447 store_reg(s, rd, tmp);
bellard99c475a2005-01-31 20:45:13 +00008448 break;
8449
8450 case 11:
8451 /* misc */
8452 op = (insn >> 8) & 0xf;
8453 switch (op) {
8454 case 0:
8455 /* adjust stack pointer */
pbrookb26eefb2008-03-31 03:44:26 +00008456 tmp = load_reg(s, 13);
bellard99c475a2005-01-31 20:45:13 +00008457 val = (insn & 0x7f) * 4;
8458 if (insn & (1 << 7))
balrog6a0d8a12008-04-13 13:25:31 +00008459 val = -(int32_t)val;
pbrookb26eefb2008-03-31 03:44:26 +00008460 tcg_gen_addi_i32(tmp, tmp, val);
8461 store_reg(s, 13, tmp);
bellard99c475a2005-01-31 20:45:13 +00008462 break;
8463
pbrook9ee6e8b2007-11-11 00:04:49 +00008464 case 2: /* sign/zero extend. */
8465 ARCH(6);
8466 rd = insn & 7;
8467 rm = (insn >> 3) & 7;
pbrookb0109802008-03-31 03:47:03 +00008468 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00008469 switch ((insn >> 6) & 3) {
pbrookb0109802008-03-31 03:47:03 +00008470 case 0: gen_sxth(tmp); break;
8471 case 1: gen_sxtb(tmp); break;
8472 case 2: gen_uxth(tmp); break;
8473 case 3: gen_uxtb(tmp); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008474 }
pbrookb0109802008-03-31 03:47:03 +00008475 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00008476 break;
bellard99c475a2005-01-31 20:45:13 +00008477 case 4: case 5: case 0xc: case 0xd:
8478 /* push/pop */
pbrookb0109802008-03-31 03:47:03 +00008479 addr = load_reg(s, 13);
bellard5899f382005-04-27 20:25:20 +00008480 if (insn & (1 << 8))
8481 offset = 4;
bellard99c475a2005-01-31 20:45:13 +00008482 else
bellard5899f382005-04-27 20:25:20 +00008483 offset = 0;
8484 for (i = 0; i < 8; i++) {
8485 if (insn & (1 << i))
8486 offset += 4;
8487 }
8488 if ((insn & (1 << 11)) == 0) {
pbrookb0109802008-03-31 03:47:03 +00008489 tcg_gen_addi_i32(addr, addr, -offset);
bellard5899f382005-04-27 20:25:20 +00008490 }
bellard99c475a2005-01-31 20:45:13 +00008491 for (i = 0; i < 8; i++) {
8492 if (insn & (1 << i)) {
8493 if (insn & (1 << 11)) {
8494 /* pop */
pbrookb0109802008-03-31 03:47:03 +00008495 tmp = gen_ld32(addr, IS_USER(s));
8496 store_reg(s, i, tmp);
bellard99c475a2005-01-31 20:45:13 +00008497 } else {
8498 /* push */
pbrookb0109802008-03-31 03:47:03 +00008499 tmp = load_reg(s, i);
8500 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008501 }
bellard5899f382005-04-27 20:25:20 +00008502 /* advance to the next address. */
pbrookb0109802008-03-31 03:47:03 +00008503 tcg_gen_addi_i32(addr, addr, 4);
bellard99c475a2005-01-31 20:45:13 +00008504 }
8505 }
pbrooka50f5b92008-06-29 15:25:29 +00008506 TCGV_UNUSED(tmp);
bellard99c475a2005-01-31 20:45:13 +00008507 if (insn & (1 << 8)) {
8508 if (insn & (1 << 11)) {
8509 /* pop pc */
pbrookb0109802008-03-31 03:47:03 +00008510 tmp = gen_ld32(addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008511 /* don't set the pc until the rest of the instruction
8512 has completed */
8513 } else {
8514 /* push lr */
pbrookb0109802008-03-31 03:47:03 +00008515 tmp = load_reg(s, 14);
8516 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008517 }
pbrookb0109802008-03-31 03:47:03 +00008518 tcg_gen_addi_i32(addr, addr, 4);
bellard99c475a2005-01-31 20:45:13 +00008519 }
bellard5899f382005-04-27 20:25:20 +00008520 if ((insn & (1 << 11)) == 0) {
pbrookb0109802008-03-31 03:47:03 +00008521 tcg_gen_addi_i32(addr, addr, -offset);
bellard5899f382005-04-27 20:25:20 +00008522 }
bellard99c475a2005-01-31 20:45:13 +00008523 /* write back the new stack pointer */
pbrookb0109802008-03-31 03:47:03 +00008524 store_reg(s, 13, addr);
bellard99c475a2005-01-31 20:45:13 +00008525 /* set the new PC value */
8526 if ((insn & 0x0900) == 0x0900)
pbrookb0109802008-03-31 03:47:03 +00008527 gen_bx(s, tmp);
bellard99c475a2005-01-31 20:45:13 +00008528 break;
8529
pbrook9ee6e8b2007-11-11 00:04:49 +00008530 case 1: case 3: case 9: case 11: /* czb */
8531 rm = insn & 7;
pbrookd9ba4832008-03-31 03:46:50 +00008532 tmp = load_reg(s, rm);
pbrook9ee6e8b2007-11-11 00:04:49 +00008533 s->condlabel = gen_new_label();
8534 s->condjmp = 1;
8535 if (insn & (1 << 11))
pbrookcb636692008-05-24 02:22:00 +00008536 tcg_gen_brcondi_i32(TCG_COND_EQ, tmp, 0, s->condlabel);
pbrook9ee6e8b2007-11-11 00:04:49 +00008537 else
pbrookcb636692008-05-24 02:22:00 +00008538 tcg_gen_brcondi_i32(TCG_COND_NE, tmp, 0, s->condlabel);
pbrookd9ba4832008-03-31 03:46:50 +00008539 dead_tmp(tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00008540 offset = ((insn & 0xf8) >> 2) | (insn & 0x200) >> 3;
8541 val = (uint32_t)s->pc + 2;
8542 val += offset;
8543 gen_jmp(s, val);
8544 break;
8545
8546 case 15: /* IT, nop-hint. */
8547 if ((insn & 0xf) == 0) {
8548 gen_nop_hint(s, (insn >> 4) & 0xf);
8549 break;
8550 }
8551 /* If Then. */
8552 s->condexec_cond = (insn >> 4) & 0xe;
8553 s->condexec_mask = insn & 0x1f;
8554 /* No actual code generated for this insn, just setup state. */
8555 break;
8556
pbrook06c949e2006-02-04 19:35:26 +00008557 case 0xe: /* bkpt */
pbrook9ee6e8b2007-11-11 00:04:49 +00008558 gen_set_condexec(s);
pbrook5e3f8782008-03-31 03:47:34 +00008559 gen_set_pc_im(s->pc - 2);
pbrookd9ba4832008-03-31 03:46:50 +00008560 gen_exception(EXCP_BKPT);
pbrook06c949e2006-02-04 19:35:26 +00008561 s->is_jmp = DISAS_JUMP;
8562 break;
8563
pbrook9ee6e8b2007-11-11 00:04:49 +00008564 case 0xa: /* rev */
8565 ARCH(6);
8566 rn = (insn >> 3) & 0x7;
8567 rd = insn & 0x7;
pbrookb0109802008-03-31 03:47:03 +00008568 tmp = load_reg(s, rn);
pbrook9ee6e8b2007-11-11 00:04:49 +00008569 switch ((insn >> 6) & 3) {
aurel3266896cb2009-03-13 09:34:48 +00008570 case 0: tcg_gen_bswap32_i32(tmp, tmp); break;
pbrookb0109802008-03-31 03:47:03 +00008571 case 1: gen_rev16(tmp); break;
8572 case 3: gen_revsh(tmp); break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008573 default: goto illegal_op;
8574 }
pbrookb0109802008-03-31 03:47:03 +00008575 store_reg(s, rd, tmp);
pbrook9ee6e8b2007-11-11 00:04:49 +00008576 break;
8577
8578 case 6: /* cps */
8579 ARCH(6);
8580 if (IS_USER(s))
8581 break;
8582 if (IS_M(env)) {
pbrook8984bd22008-03-31 03:47:48 +00008583 tmp = tcg_const_i32((insn & (1 << 4)) != 0);
pbrook9ee6e8b2007-11-11 00:04:49 +00008584 /* PRIMASK */
pbrook8984bd22008-03-31 03:47:48 +00008585 if (insn & 1) {
8586 addr = tcg_const_i32(16);
8587 gen_helper_v7m_msr(cpu_env, addr, tmp);
8588 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008589 /* FAULTMASK */
pbrook8984bd22008-03-31 03:47:48 +00008590 if (insn & 2) {
8591 addr = tcg_const_i32(17);
8592 gen_helper_v7m_msr(cpu_env, addr, tmp);
8593 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008594 gen_lookup_tb(s);
8595 } else {
8596 if (insn & (1 << 4))
8597 shift = CPSR_A | CPSR_I | CPSR_F;
8598 else
8599 shift = 0;
8600
8601 val = ((insn & 7) << 6) & shift;
8602 gen_op_movl_T0_im(val);
8603 gen_set_psr_T0(s, shift, 0);
8604 }
8605 break;
8606
bellard99c475a2005-01-31 20:45:13 +00008607 default:
8608 goto undef;
8609 }
8610 break;
8611
8612 case 12:
8613 /* load/store multiple */
8614 rn = (insn >> 8) & 0x7;
pbrookb0109802008-03-31 03:47:03 +00008615 addr = load_reg(s, rn);
bellard99c475a2005-01-31 20:45:13 +00008616 for (i = 0; i < 8; i++) {
8617 if (insn & (1 << i)) {
bellard99c475a2005-01-31 20:45:13 +00008618 if (insn & (1 << 11)) {
8619 /* load */
pbrookb0109802008-03-31 03:47:03 +00008620 tmp = gen_ld32(addr, IS_USER(s));
8621 store_reg(s, i, tmp);
bellard99c475a2005-01-31 20:45:13 +00008622 } else {
8623 /* store */
pbrookb0109802008-03-31 03:47:03 +00008624 tmp = load_reg(s, i);
8625 gen_st32(tmp, addr, IS_USER(s));
bellard99c475a2005-01-31 20:45:13 +00008626 }
bellard5899f382005-04-27 20:25:20 +00008627 /* advance to the next address */
pbrookb0109802008-03-31 03:47:03 +00008628 tcg_gen_addi_i32(addr, addr, 4);
bellard99c475a2005-01-31 20:45:13 +00008629 }
8630 }
bellard5899f382005-04-27 20:25:20 +00008631 /* Base register writeback. */
pbrookb0109802008-03-31 03:47:03 +00008632 if ((insn & (1 << rn)) == 0) {
8633 store_reg(s, rn, addr);
8634 } else {
8635 dead_tmp(addr);
8636 }
bellard99c475a2005-01-31 20:45:13 +00008637 break;
8638
8639 case 13:
8640 /* conditional branch or swi */
8641 cond = (insn >> 8) & 0xf;
8642 if (cond == 0xe)
8643 goto undef;
8644
8645 if (cond == 0xf) {
8646 /* swi */
pbrook9ee6e8b2007-11-11 00:04:49 +00008647 gen_set_condexec(s);
balrog422ebf62008-04-16 23:17:02 +00008648 gen_set_pc_im(s->pc);
pbrook9ee6e8b2007-11-11 00:04:49 +00008649 s->is_jmp = DISAS_SWI;
bellard99c475a2005-01-31 20:45:13 +00008650 break;
8651 }
8652 /* generate a conditional jump to next instruction */
bellarde50e6a22005-04-26 20:36:11 +00008653 s->condlabel = gen_new_label();
pbrookd9ba4832008-03-31 03:46:50 +00008654 gen_test_cc(cond ^ 1, s->condlabel);
bellarde50e6a22005-04-26 20:36:11 +00008655 s->condjmp = 1;
bellard99c475a2005-01-31 20:45:13 +00008656 gen_movl_T1_reg(s, 15);
8657
8658 /* jump to the offset */
bellard5899f382005-04-27 20:25:20 +00008659 val = (uint32_t)s->pc + 2;
bellard99c475a2005-01-31 20:45:13 +00008660 offset = ((int32_t)insn << 24) >> 24;
bellard5899f382005-04-27 20:25:20 +00008661 val += offset << 1;
bellard8aaca4c2005-04-23 18:27:52 +00008662 gen_jmp(s, val);
bellard99c475a2005-01-31 20:45:13 +00008663 break;
8664
8665 case 14:
pbrook358bf292006-04-09 14:38:57 +00008666 if (insn & (1 << 11)) {
pbrook9ee6e8b2007-11-11 00:04:49 +00008667 if (disas_thumb2_insn(env, s, insn))
8668 goto undef32;
pbrook358bf292006-04-09 14:38:57 +00008669 break;
8670 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008671 /* unconditional branch */
bellard99c475a2005-01-31 20:45:13 +00008672 val = (uint32_t)s->pc;
8673 offset = ((int32_t)insn << 21) >> 21;
8674 val += (offset << 1) + 2;
bellard8aaca4c2005-04-23 18:27:52 +00008675 gen_jmp(s, val);
bellard99c475a2005-01-31 20:45:13 +00008676 break;
8677
8678 case 15:
pbrook9ee6e8b2007-11-11 00:04:49 +00008679 if (disas_thumb2_insn(env, s, insn))
balrog6a0d8a12008-04-13 13:25:31 +00008680 goto undef32;
pbrook9ee6e8b2007-11-11 00:04:49 +00008681 break;
bellard99c475a2005-01-31 20:45:13 +00008682 }
8683 return;
pbrook9ee6e8b2007-11-11 00:04:49 +00008684undef32:
8685 gen_set_condexec(s);
pbrook5e3f8782008-03-31 03:47:34 +00008686 gen_set_pc_im(s->pc - 4);
pbrookd9ba4832008-03-31 03:46:50 +00008687 gen_exception(EXCP_UDEF);
pbrook9ee6e8b2007-11-11 00:04:49 +00008688 s->is_jmp = DISAS_JUMP;
8689 return;
8690illegal_op:
bellard99c475a2005-01-31 20:45:13 +00008691undef:
pbrook9ee6e8b2007-11-11 00:04:49 +00008692 gen_set_condexec(s);
pbrook5e3f8782008-03-31 03:47:34 +00008693 gen_set_pc_im(s->pc - 2);
pbrookd9ba4832008-03-31 03:46:50 +00008694 gen_exception(EXCP_UDEF);
bellard99c475a2005-01-31 20:45:13 +00008695 s->is_jmp = DISAS_JUMP;
8696}
8697
bellard2c0262a2003-09-30 20:34:21 +00008698/* generate intermediate code in gen_opc_buf and gen_opparam_buf for
8699 basic block 'tb'. If search_pc is TRUE, also generate PC
8700 information for each intermediate instruction. */
ths2cfc5f12008-07-18 18:01:29 +00008701static inline void gen_intermediate_code_internal(CPUState *env,
8702 TranslationBlock *tb,
8703 int search_pc)
bellard2c0262a2003-09-30 20:34:21 +00008704{
8705 DisasContext dc1, *dc = &dc1;
aliguoria1d1bb32008-11-18 20:07:32 +00008706 CPUBreakpoint *bp;
bellard2c0262a2003-09-30 20:34:21 +00008707 uint16_t *gen_opc_end;
8708 int j, lj;
bellard0fa85d42005-01-03 23:43:32 +00008709 target_ulong pc_start;
bellardb5ff1b32005-11-26 10:38:39 +00008710 uint32_t next_page_start;
pbrook2e70f6e2008-06-29 01:03:05 +00008711 int num_insns;
8712 int max_insns;
ths3b46e622007-09-17 08:09:54 +00008713
bellard2c0262a2003-09-30 20:34:21 +00008714 /* generate intermediate code */
pbrookb26eefb2008-03-31 03:44:26 +00008715 num_temps = 0;
8716 memset(temps, 0, sizeof(temps));
8717
bellard0fa85d42005-01-03 23:43:32 +00008718 pc_start = tb->pc;
ths3b46e622007-09-17 08:09:54 +00008719
bellard2c0262a2003-09-30 20:34:21 +00008720 dc->tb = tb;
8721
bellard2c0262a2003-09-30 20:34:21 +00008722 gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
bellard2c0262a2003-09-30 20:34:21 +00008723
8724 dc->is_jmp = DISAS_NEXT;
8725 dc->pc = pc_start;
bellard8aaca4c2005-04-23 18:27:52 +00008726 dc->singlestep_enabled = env->singlestep_enabled;
bellarde50e6a22005-04-26 20:36:11 +00008727 dc->condjmp = 0;
bellard5899f382005-04-27 20:25:20 +00008728 dc->thumb = env->thumb;
pbrook9ee6e8b2007-11-11 00:04:49 +00008729 dc->condexec_mask = (env->condexec_bits & 0xf) << 1;
8730 dc->condexec_cond = env->condexec_bits >> 4;
bellardb5ff1b32005-11-26 10:38:39 +00008731#if !defined(CONFIG_USER_ONLY)
pbrook9ee6e8b2007-11-11 00:04:49 +00008732 if (IS_M(env)) {
8733 dc->user = ((env->v7m.exception == 0) && (env->v7m.control & 1));
8734 } else {
8735 dc->user = (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_USR;
8736 }
bellardb5ff1b32005-11-26 10:38:39 +00008737#endif
pbrooka7812ae2008-11-17 14:43:54 +00008738 cpu_F0s = tcg_temp_new_i32();
8739 cpu_F1s = tcg_temp_new_i32();
8740 cpu_F0d = tcg_temp_new_i64();
8741 cpu_F1d = tcg_temp_new_i64();
pbrookad694712008-03-31 03:48:30 +00008742 cpu_V0 = cpu_F0d;
8743 cpu_V1 = cpu_F1d;
pbrooke6771372008-03-31 03:49:05 +00008744 /* FIXME: cpu_M0 can probably be the same as cpu_V0. */
pbrooka7812ae2008-11-17 14:43:54 +00008745 cpu_M0 = tcg_temp_new_i64();
bellardb5ff1b32005-11-26 10:38:39 +00008746 next_page_start = (pc_start & TARGET_PAGE_MASK) + TARGET_PAGE_SIZE;
bellard2c0262a2003-09-30 20:34:21 +00008747 lj = -1;
pbrook2e70f6e2008-06-29 01:03:05 +00008748 num_insns = 0;
8749 max_insns = tb->cflags & CF_COUNT_MASK;
8750 if (max_insns == 0)
8751 max_insns = CF_COUNT_MASK;
8752
8753 gen_icount_start();
pbrook9ee6e8b2007-11-11 00:04:49 +00008754 /* Reset the conditional execution bits immediately. This avoids
8755 complications trying to do it at the end of the block. */
8756 if (env->condexec_bits)
pbrook8f012452008-03-31 03:46:03 +00008757 {
8758 TCGv tmp = new_tmp();
8759 tcg_gen_movi_i32(tmp, 0);
pbrookd9ba4832008-03-31 03:46:50 +00008760 store_cpu_field(tmp, condexec_bits);
pbrook8f012452008-03-31 03:46:03 +00008761 }
bellard2c0262a2003-09-30 20:34:21 +00008762 do {
pbrookfbb4a2e2008-05-29 00:20:44 +00008763#ifdef CONFIG_USER_ONLY
8764 /* Intercept jump to the magic kernel page. */
8765 if (dc->pc >= 0xffff0000) {
8766 /* We always get here via a jump, so know we are not in a
8767 conditional execution block. */
8768 gen_exception(EXCP_KERNEL_TRAP);
8769 dc->is_jmp = DISAS_UPDATE;
8770 break;
8771 }
8772#else
pbrook9ee6e8b2007-11-11 00:04:49 +00008773 if (dc->pc >= 0xfffffff0 && IS_M(env)) {
8774 /* We always get here via a jump, so know we are not in a
8775 conditional execution block. */
pbrookd9ba4832008-03-31 03:46:50 +00008776 gen_exception(EXCP_EXCEPTION_EXIT);
pbrookd60bb012008-07-11 00:27:19 +00008777 dc->is_jmp = DISAS_UPDATE;
8778 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008779 }
8780#endif
8781
aliguoric0ce9982008-11-25 22:13:57 +00008782 if (unlikely(!TAILQ_EMPTY(&env->breakpoints))) {
8783 TAILQ_FOREACH(bp, &env->breakpoints, entry) {
aliguoria1d1bb32008-11-18 20:07:32 +00008784 if (bp->pc == dc->pc) {
pbrook9ee6e8b2007-11-11 00:04:49 +00008785 gen_set_condexec(dc);
pbrook5e3f8782008-03-31 03:47:34 +00008786 gen_set_pc_im(dc->pc);
pbrookd9ba4832008-03-31 03:46:50 +00008787 gen_exception(EXCP_DEBUG);
bellard1fddef42005-04-17 19:16:13 +00008788 dc->is_jmp = DISAS_JUMP;
pbrook9ee6e8b2007-11-11 00:04:49 +00008789 /* Advance PC so that clearing the breakpoint will
8790 invalidate this TB. */
8791 dc->pc += 2;
8792 goto done_generating;
bellard1fddef42005-04-17 19:16:13 +00008793 break;
8794 }
8795 }
8796 }
bellard2c0262a2003-09-30 20:34:21 +00008797 if (search_pc) {
8798 j = gen_opc_ptr - gen_opc_buf;
8799 if (lj < j) {
8800 lj++;
8801 while (lj < j)
8802 gen_opc_instr_start[lj++] = 0;
8803 }
bellard0fa85d42005-01-03 23:43:32 +00008804 gen_opc_pc[lj] = dc->pc;
bellard2c0262a2003-09-30 20:34:21 +00008805 gen_opc_instr_start[lj] = 1;
pbrook2e70f6e2008-06-29 01:03:05 +00008806 gen_opc_icount[lj] = num_insns;
bellard2c0262a2003-09-30 20:34:21 +00008807 }
bellarde50e6a22005-04-26 20:36:11 +00008808
pbrook2e70f6e2008-06-29 01:03:05 +00008809 if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO))
8810 gen_io_start();
8811
pbrook9ee6e8b2007-11-11 00:04:49 +00008812 if (env->thumb) {
8813 disas_thumb_insn(env, dc);
8814 if (dc->condexec_mask) {
8815 dc->condexec_cond = (dc->condexec_cond & 0xe)
8816 | ((dc->condexec_mask >> 4) & 1);
8817 dc->condexec_mask = (dc->condexec_mask << 1) & 0x1f;
8818 if (dc->condexec_mask == 0) {
8819 dc->condexec_cond = 0;
8820 }
8821 }
8822 } else {
8823 disas_arm_insn(env, dc);
8824 }
pbrookb26eefb2008-03-31 03:44:26 +00008825 if (num_temps) {
8826 fprintf(stderr, "Internal resource leak before %08x\n", dc->pc);
8827 num_temps = 0;
8828 }
bellarde50e6a22005-04-26 20:36:11 +00008829
8830 if (dc->condjmp && !dc->is_jmp) {
8831 gen_set_label(dc->condlabel);
8832 dc->condjmp = 0;
8833 }
balrogaaf2d972008-12-07 13:20:16 +00008834 /* Translation stops when a conditional branch is encountered.
bellarde50e6a22005-04-26 20:36:11 +00008835 * Otherwise the subsequent code could get translated several times.
bellardb5ff1b32005-11-26 10:38:39 +00008836 * Also stop translation when a page boundary is reached. This
thsbf20dc02008-06-30 17:22:19 +00008837 * ensures prefetch aborts occur at the right place. */
pbrook2e70f6e2008-06-29 01:03:05 +00008838 num_insns ++;
bellard1fddef42005-04-17 19:16:13 +00008839 } while (!dc->is_jmp && gen_opc_ptr < gen_opc_end &&
8840 !env->singlestep_enabled &&
aurel321b530a62009-04-05 20:08:59 +00008841 !singlestep &&
pbrook2e70f6e2008-06-29 01:03:05 +00008842 dc->pc < next_page_start &&
8843 num_insns < max_insns);
8844
8845 if (tb->cflags & CF_LAST_IO) {
8846 if (dc->condjmp) {
8847 /* FIXME: This can theoretically happen with self-modifying
8848 code. */
8849 cpu_abort(env, "IO on conditional branch instruction");
8850 }
8851 gen_io_end();
8852 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008853
bellardb5ff1b32005-11-26 10:38:39 +00008854 /* At this stage dc->condjmp will only be set when the skipped
pbrook9ee6e8b2007-11-11 00:04:49 +00008855 instruction was a conditional branch or trap, and the PC has
8856 already been written. */
ths551bd272008-07-03 17:57:36 +00008857 if (unlikely(env->singlestep_enabled)) {
bellard8aaca4c2005-04-23 18:27:52 +00008858 /* Make sure the pc is updated, and raise a debug exception. */
bellarde50e6a22005-04-26 20:36:11 +00008859 if (dc->condjmp) {
pbrook9ee6e8b2007-11-11 00:04:49 +00008860 gen_set_condexec(dc);
8861 if (dc->is_jmp == DISAS_SWI) {
pbrookd9ba4832008-03-31 03:46:50 +00008862 gen_exception(EXCP_SWI);
pbrook9ee6e8b2007-11-11 00:04:49 +00008863 } else {
pbrookd9ba4832008-03-31 03:46:50 +00008864 gen_exception(EXCP_DEBUG);
pbrook9ee6e8b2007-11-11 00:04:49 +00008865 }
bellarde50e6a22005-04-26 20:36:11 +00008866 gen_set_label(dc->condlabel);
8867 }
8868 if (dc->condjmp || !dc->is_jmp) {
pbrook5e3f8782008-03-31 03:47:34 +00008869 gen_set_pc_im(dc->pc);
bellarde50e6a22005-04-26 20:36:11 +00008870 dc->condjmp = 0;
bellard8aaca4c2005-04-23 18:27:52 +00008871 }
pbrook9ee6e8b2007-11-11 00:04:49 +00008872 gen_set_condexec(dc);
8873 if (dc->is_jmp == DISAS_SWI && !dc->condjmp) {
pbrookd9ba4832008-03-31 03:46:50 +00008874 gen_exception(EXCP_SWI);
pbrook9ee6e8b2007-11-11 00:04:49 +00008875 } else {
8876 /* FIXME: Single stepping a WFI insn will not halt
8877 the CPU. */
pbrookd9ba4832008-03-31 03:46:50 +00008878 gen_exception(EXCP_DEBUG);
pbrook9ee6e8b2007-11-11 00:04:49 +00008879 }
bellard8aaca4c2005-04-23 18:27:52 +00008880 } else {
pbrook9ee6e8b2007-11-11 00:04:49 +00008881 /* While branches must always occur at the end of an IT block,
8882 there are a few other things that can cause us to terminate
8883 the TB in the middel of an IT block:
8884 - Exception generating instructions (bkpt, swi, undefined).
8885 - Page boundaries.
8886 - Hardware watchpoints.
8887 Hardware breakpoints have already been handled and skip this code.
8888 */
8889 gen_set_condexec(dc);
bellard8aaca4c2005-04-23 18:27:52 +00008890 switch(dc->is_jmp) {
bellard8aaca4c2005-04-23 18:27:52 +00008891 case DISAS_NEXT:
bellard6e256c92005-11-20 10:32:05 +00008892 gen_goto_tb(dc, 1, dc->pc);
bellard8aaca4c2005-04-23 18:27:52 +00008893 break;
8894 default:
8895 case DISAS_JUMP:
8896 case DISAS_UPDATE:
8897 /* indicate that the hash table must be used to find the next TB */
bellard57fec1f2008-02-01 10:50:11 +00008898 tcg_gen_exit_tb(0);
bellard8aaca4c2005-04-23 18:27:52 +00008899 break;
8900 case DISAS_TB_JUMP:
8901 /* nothing more to generate */
8902 break;
pbrook9ee6e8b2007-11-11 00:04:49 +00008903 case DISAS_WFI:
pbrookd9ba4832008-03-31 03:46:50 +00008904 gen_helper_wfi();
pbrook9ee6e8b2007-11-11 00:04:49 +00008905 break;
8906 case DISAS_SWI:
pbrookd9ba4832008-03-31 03:46:50 +00008907 gen_exception(EXCP_SWI);
pbrook9ee6e8b2007-11-11 00:04:49 +00008908 break;
bellard8aaca4c2005-04-23 18:27:52 +00008909 }
bellarde50e6a22005-04-26 20:36:11 +00008910 if (dc->condjmp) {
8911 gen_set_label(dc->condlabel);
pbrook9ee6e8b2007-11-11 00:04:49 +00008912 gen_set_condexec(dc);
bellard6e256c92005-11-20 10:32:05 +00008913 gen_goto_tb(dc, 1, dc->pc);
bellarde50e6a22005-04-26 20:36:11 +00008914 dc->condjmp = 0;
8915 }
bellard2c0262a2003-09-30 20:34:21 +00008916 }
pbrook2e70f6e2008-06-29 01:03:05 +00008917
pbrook9ee6e8b2007-11-11 00:04:49 +00008918done_generating:
pbrook2e70f6e2008-06-29 01:03:05 +00008919 gen_icount_end(tb, num_insns);
bellard2c0262a2003-09-30 20:34:21 +00008920 *gen_opc_ptr = INDEX_op_end;
8921
8922#ifdef DEBUG_DISAS
aliguori8fec2b82009-01-15 22:36:53 +00008923 if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) {
aliguori93fcfe32009-01-15 22:34:14 +00008924 qemu_log("----------------\n");
8925 qemu_log("IN: %s\n", lookup_symbol(pc_start));
8926 log_target_disas(pc_start, dc->pc - pc_start, env->thumb);
8927 qemu_log("\n");
bellard2c0262a2003-09-30 20:34:21 +00008928 }
8929#endif
bellardb5ff1b32005-11-26 10:38:39 +00008930 if (search_pc) {
8931 j = gen_opc_ptr - gen_opc_buf;
8932 lj++;
8933 while (lj <= j)
8934 gen_opc_instr_start[lj++] = 0;
bellardb5ff1b32005-11-26 10:38:39 +00008935 } else {
bellard2c0262a2003-09-30 20:34:21 +00008936 tb->size = dc->pc - pc_start;
pbrook2e70f6e2008-06-29 01:03:05 +00008937 tb->icount = num_insns;
bellardb5ff1b32005-11-26 10:38:39 +00008938 }
bellard2c0262a2003-09-30 20:34:21 +00008939}
8940
ths2cfc5f12008-07-18 18:01:29 +00008941void gen_intermediate_code(CPUState *env, TranslationBlock *tb)
bellard2c0262a2003-09-30 20:34:21 +00008942{
ths2cfc5f12008-07-18 18:01:29 +00008943 gen_intermediate_code_internal(env, tb, 0);
bellard2c0262a2003-09-30 20:34:21 +00008944}
8945
ths2cfc5f12008-07-18 18:01:29 +00008946void gen_intermediate_code_pc(CPUState *env, TranslationBlock *tb)
bellard2c0262a2003-09-30 20:34:21 +00008947{
ths2cfc5f12008-07-18 18:01:29 +00008948 gen_intermediate_code_internal(env, tb, 1);
bellard2c0262a2003-09-30 20:34:21 +00008949}
8950
bellardb5ff1b32005-11-26 10:38:39 +00008951static const char *cpu_mode_names[16] = {
8952 "usr", "fiq", "irq", "svc", "???", "???", "???", "abt",
8953 "???", "???", "???", "und", "???", "???", "???", "sys"
8954};
pbrook9ee6e8b2007-11-11 00:04:49 +00008955
ths5fafdf22007-09-16 21:08:06 +00008956void cpu_dump_state(CPUState *env, FILE *f,
bellard7fe48482004-10-09 18:08:01 +00008957 int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
8958 int flags)
bellard2c0262a2003-09-30 20:34:21 +00008959{
8960 int i;
ths06e80fc2008-07-03 16:40:06 +00008961#if 0
bellardbc380d12005-05-13 22:50:47 +00008962 union {
bellardb7bcbe92005-02-22 19:27:29 +00008963 uint32_t i;
8964 float s;
8965 } s0, s1;
8966 CPU_DoubleU d;
pbrooka94a6ab2006-10-25 17:43:33 +00008967 /* ??? This assumes float64 and double have the same layout.
8968 Oh well, it's only debug dumps. */
8969 union {
8970 float64 f64;
8971 double d;
8972 } d0;
ths06e80fc2008-07-03 16:40:06 +00008973#endif
bellardb5ff1b32005-11-26 10:38:39 +00008974 uint32_t psr;
bellard2c0262a2003-09-30 20:34:21 +00008975
8976 for(i=0;i<16;i++) {
bellard7fe48482004-10-09 18:08:01 +00008977 cpu_fprintf(f, "R%02d=%08x", i, env->regs[i]);
bellard2c0262a2003-09-30 20:34:21 +00008978 if ((i % 4) == 3)
bellard7fe48482004-10-09 18:08:01 +00008979 cpu_fprintf(f, "\n");
bellard2c0262a2003-09-30 20:34:21 +00008980 else
bellard7fe48482004-10-09 18:08:01 +00008981 cpu_fprintf(f, " ");
bellard2c0262a2003-09-30 20:34:21 +00008982 }
bellardb5ff1b32005-11-26 10:38:39 +00008983 psr = cpsr_read(env);
ths687fa642007-04-02 08:18:36 +00008984 cpu_fprintf(f, "PSR=%08x %c%c%c%c %c %s%d\n",
8985 psr,
bellardb5ff1b32005-11-26 10:38:39 +00008986 psr & (1 << 31) ? 'N' : '-',
8987 psr & (1 << 30) ? 'Z' : '-',
8988 psr & (1 << 29) ? 'C' : '-',
8989 psr & (1 << 28) ? 'V' : '-',
ths5fafdf22007-09-16 21:08:06 +00008990 psr & CPSR_T ? 'T' : 'A',
bellardb5ff1b32005-11-26 10:38:39 +00008991 cpu_mode_names[psr & 0xf], (psr & 0x10) ? 32 : 26);
bellardb7bcbe92005-02-22 19:27:29 +00008992
pbrook5e3f8782008-03-31 03:47:34 +00008993#if 0
bellardb7bcbe92005-02-22 19:27:29 +00008994 for (i = 0; i < 16; i++) {
bellard8e960052005-04-07 19:42:46 +00008995 d.d = env->vfp.regs[i];
8996 s0.i = d.l.lower;
8997 s1.i = d.l.upper;
pbrooka94a6ab2006-10-25 17:43:33 +00008998 d0.f64 = d.d;
8999 cpu_fprintf(f, "s%02d=%08x(%8g) s%02d=%08x(%8g) d%02d=%08x%08x(%8g)\n",
bellardb7bcbe92005-02-22 19:27:29 +00009000 i * 2, (int)s0.i, s0.s,
pbrooka94a6ab2006-10-25 17:43:33 +00009001 i * 2 + 1, (int)s1.i, s1.s,
bellardb7bcbe92005-02-22 19:27:29 +00009002 i, (int)(uint32_t)d.l.upper, (int)(uint32_t)d.l.lower,
pbrooka94a6ab2006-10-25 17:43:33 +00009003 d0.d);
bellardb7bcbe92005-02-22 19:27:29 +00009004 }
pbrook40f137e2006-02-20 00:33:36 +00009005 cpu_fprintf(f, "FPSCR: %08x\n", (int)env->vfp.xregs[ARM_VFP_FPSCR]);
pbrook5e3f8782008-03-31 03:47:34 +00009006#endif
bellard2c0262a2003-09-30 20:34:21 +00009007}
bellarda6b025d2004-01-24 15:18:16 +00009008
aurel32d2856f12008-04-28 00:32:32 +00009009void gen_pc_load(CPUState *env, TranslationBlock *tb,
9010 unsigned long searched_pc, int pc_pos, void *puc)
9011{
9012 env->regs[15] = gen_opc_pc[pc_pos];
9013}