blob: e3c46440181f303149f0ae0d2810108de753d077 [file] [log] [blame]
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -08001/*
2 * ARM helper routines
3 *
4 * Copyright (c) 2005-2007 CodeSourcery, LLC
5 *
6 * This library is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU Lesser General Public
8 * License as published by the Free Software Foundation; either
9 * version 2 of the License, or (at your option) any later version.
10 *
11 * This library is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 * Lesser General Public License for more details.
15 *
16 * You should have received a copy of the GNU Lesser General Public
David 'Digit' Turner52858642011-06-03 13:41:05 +020017 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080018 */
David 'Digit' Turnera889d352014-03-20 12:35:32 +010019#include "cpu.h"
David 'Digit' Turner86b1fb02014-03-21 15:20:21 +010020#include "tcg.h"
David 'Digit' Turner52858642011-06-03 13:41:05 +020021#include "helper.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080022
23#define SIGNBIT (uint32_t)0x80000000
24#define SIGNBIT64 ((uint64_t)1 << 63)
25
David 'Digit' Turner66576782014-03-24 16:57:57 +010026#if !defined(CONFIG_USER_ONLY)
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +020027static void raise_exception(CPUARMState *env, int tt)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080028{
29 env->exception_index = tt;
David 'Digit' Turner85c62202014-02-16 20:53:40 +010030 cpu_loop_exit(env);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080031}
David 'Digit' Turner66576782014-03-24 16:57:57 +010032#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080033
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +020034uint32_t HELPER(neon_tbl)(CPUARMState *env, uint32_t ireg, uint32_t def,
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080035 uint32_t rn, uint32_t maxindex)
36{
37 uint32_t val;
38 uint32_t tmp;
39 int index;
40 int shift;
41 uint64_t *table;
42 table = (uint64_t *)&env->vfp.regs[rn];
43 val = 0;
44 for (shift = 0; shift < 32; shift += 8) {
45 index = (ireg >> shift) & 0xff;
46 if (index < maxindex) {
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -070047 tmp = (table[index >> 3] >> ((index & 7) << 3)) & 0xff;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080048 val |= tmp << shift;
49 } else {
50 val |= def & (0xff << shift);
51 }
52 }
53 return val;
54}
55
David 'Digit' Turner66576782014-03-24 16:57:57 +010056#undef env
57
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080058#if !defined(CONFIG_USER_ONLY)
59
David 'Digit' Turnera889d352014-03-20 12:35:32 +010060
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080061#define MMUSUFFIX _mmu
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080062
63#define SHIFT 0
David 'Digit' Turner852088c2013-12-14 23:04:12 +010064#include "exec/softmmu_template.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080065
66#define SHIFT 1
David 'Digit' Turner852088c2013-12-14 23:04:12 +010067#include "exec/softmmu_template.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080068
69#define SHIFT 2
David 'Digit' Turner852088c2013-12-14 23:04:12 +010070#include "exec/softmmu_template.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080071
72#define SHIFT 3
David 'Digit' Turner852088c2013-12-14 23:04:12 +010073#include "exec/softmmu_template.h"
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080074
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080075/* try to fill the TLB and return an exception if error. If retaddr is
76 NULL, it means that the function was called in C code (i.e. not
77 from generated code or from helper.c) */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +020078void tlb_fill(CPUARMState *env, target_ulong addr, int is_write, int mmu_idx,
79 uintptr_t retaddr)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080080{
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080081 int ret;
82
David 'Digit' Turner0d8b2352014-03-20 17:13:13 +010083 ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080084 if (unlikely(ret)) {
85 if (retaddr) {
86 /* now we have a real cpu fault */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +020087 cpu_restore_state(env, retaddr);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080088 }
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +020089 raise_exception(env, env->exception_index);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080090 }
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -080091}
92
David 'Digit' Turnere2678e12014-01-16 15:56:43 +010093void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val)
David 'Digit' Turner52858642011-06-03 13:41:05 +020094{
95 int cp_num = (insn >> 8) & 0xf;
96 int cp_info = (insn >> 5) & 7;
97 int src = (insn >> 16) & 0xf;
98 int operand = insn & 0xf;
99
100 if (env->cp[cp_num].cp_write)
101 env->cp[cp_num].cp_write(env->cp[cp_num].opaque,
David 'Digit' Turnereb3bc462014-03-21 11:09:04 +0100102 cp_info, src, operand, val, (void*)GETPC());
David 'Digit' Turner52858642011-06-03 13:41:05 +0200103 }
104
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100105uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn)
David 'Digit' Turner52858642011-06-03 13:41:05 +0200106{
107 int cp_num = (insn >> 8) & 0xf;
108 int cp_info = (insn >> 5) & 7;
109 int dest = (insn >> 16) & 0xf;
110 int operand = insn & 0xf;
111
112 if (env->cp[cp_num].cp_read)
113 return env->cp[cp_num].cp_read(env->cp[cp_num].opaque,
David 'Digit' Turnereb3bc462014-03-21 11:09:04 +0100114 cp_info, dest, operand, (void*)GETPC());
David 'Digit' Turner52858642011-06-03 13:41:05 +0200115 return 0;
116}
117
118#else
119
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100120void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val)
David 'Digit' Turner52858642011-06-03 13:41:05 +0200121{
122 int op1 = (insn >> 8) & 0xf;
123 cpu_abort(env, "cp%i insn %08x\n", op1, insn);
124 return;
125}
126
David 'Digit' Turnere2678e12014-01-16 15:56:43 +0100127uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn)
David 'Digit' Turner52858642011-06-03 13:41:05 +0200128{
129 int op1 = (insn >> 8) & 0xf;
130 cpu_abort(env, "cp%i insn %08x\n", op1, insn);
131 return 0;
132}
133
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800134#endif
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800135
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200136uint32_t HELPER(add_setq)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800137{
138 uint32_t res = a + b;
139 if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT))
140 env->QF = 1;
141 return res;
142}
143
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200144uint32_t HELPER(add_saturate)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800145{
146 uint32_t res = a + b;
147 if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) {
148 env->QF = 1;
149 res = ~(((int32_t)a >> 31) ^ SIGNBIT);
150 }
151 return res;
152}
153
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200154uint32_t HELPER(sub_saturate)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800155{
156 uint32_t res = a - b;
157 if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
158 env->QF = 1;
159 res = ~(((int32_t)a >> 31) ^ SIGNBIT);
160 }
161 return res;
162}
163
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200164uint32_t HELPER(double_saturate)(CPUARMState *env, int32_t val)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800165{
166 uint32_t res;
167 if (val >= 0x40000000) {
168 res = ~SIGNBIT;
169 env->QF = 1;
170 } else if (val <= (int32_t)0xc0000000) {
171 res = SIGNBIT;
172 env->QF = 1;
173 } else {
174 res = val << 1;
175 }
176 return res;
177}
178
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200179uint32_t HELPER(add_usaturate)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800180{
181 uint32_t res = a + b;
182 if (res < a) {
183 env->QF = 1;
184 res = ~0;
185 }
186 return res;
187}
188
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200189uint32_t HELPER(sub_usaturate)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800190{
191 uint32_t res = a - b;
192 if (res > a) {
193 env->QF = 1;
194 res = 0;
195 }
196 return res;
197}
198
199/* Signed saturation. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200200static inline uint32_t do_ssat(CPUARMState *env, int32_t val, int shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800201{
202 int32_t top;
203 uint32_t mask;
204
205 top = val >> shift;
206 mask = (1u << shift) - 1;
207 if (top > 0) {
208 env->QF = 1;
209 return mask;
210 } else if (top < -1) {
211 env->QF = 1;
212 return ~mask;
213 }
214 return val;
215}
216
217/* Unsigned saturation. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200218static inline uint32_t do_usat(CPUARMState *env, int32_t val, int shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800219{
220 uint32_t max;
221
222 max = (1u << shift) - 1;
223 if (val < 0) {
224 env->QF = 1;
225 return 0;
226 } else if (val > max) {
227 env->QF = 1;
228 return max;
229 }
230 return val;
231}
232
233/* Signed saturate. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200234uint32_t HELPER(ssat)(CPUARMState *env, uint32_t x, uint32_t shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800235{
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200236 return do_ssat(env, x, shift);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800237}
238
239/* Dual halfword signed saturate. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200240uint32_t HELPER(ssat16)(CPUARMState *env, uint32_t x, uint32_t shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800241{
242 uint32_t res;
243
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200244 res = (uint16_t)do_ssat(env, (int16_t)x, shift);
245 res |= do_ssat(env, ((int32_t)x) >> 16, shift) << 16;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800246 return res;
247}
248
249/* Unsigned saturate. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200250uint32_t HELPER(usat)(CPUARMState *env, uint32_t x, uint32_t shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800251{
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200252 return do_usat(env, x, shift);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800253}
254
255/* Dual halfword unsigned saturate. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200256uint32_t HELPER(usat16)(CPUARMState *env, uint32_t x, uint32_t shift)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800257{
258 uint32_t res;
259
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200260 res = (uint16_t)do_usat(env, (int16_t)x, shift);
261 res |= do_usat(env, ((int32_t)x) >> 16, shift) << 16;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800262 return res;
263}
264
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200265void HELPER(wfi)(CPUARMState *env)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800266{
267 env->exception_index = EXCP_HLT;
David 'Digit' Turner66576782014-03-24 16:57:57 +0100268 ENV_GET_CPU(env)->halted = 1;
David 'Digit' Turner85c62202014-02-16 20:53:40 +0100269 cpu_loop_exit(env);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800270}
271
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200272void HELPER(exception)(CPUARMState *env, uint32_t excp)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800273{
274 env->exception_index = excp;
David 'Digit' Turner85c62202014-02-16 20:53:40 +0100275 cpu_loop_exit(env);
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800276}
277
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200278uint32_t HELPER(cpsr_read)(CPUARMState *env)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800279{
280 return cpsr_read(env) & ~CPSR_EXEC;
281}
282
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200283void HELPER(cpsr_write)(CPUARMState *env, uint32_t val, uint32_t mask)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800284{
285 cpsr_write(env, val, mask);
286}
287
288/* Access to user mode registers from privileged modes. */
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200289uint32_t HELPER(get_user_reg)(CPUARMState *env, uint32_t regno)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800290{
291 uint32_t val;
292
293 if (regno == 13) {
294 val = env->banked_r13[0];
295 } else if (regno == 14) {
296 val = env->banked_r14[0];
297 } else if (regno >= 8
298 && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
299 val = env->usr_regs[regno - 8];
300 } else {
301 val = env->regs[regno];
302 }
303 return val;
304}
305
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200306void HELPER(set_user_reg)(CPUARMState *env, uint32_t regno, uint32_t val)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800307{
308 if (regno == 13) {
309 env->banked_r13[0] = val;
310 } else if (regno == 14) {
311 env->banked_r14[0] = val;
312 } else if (regno >= 8
313 && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
314 env->usr_regs[regno - 8] = val;
315 } else {
316 env->regs[regno] = val;
317 }
318}
319
320/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
321 The only way to do that in TCG is a conditional branch, which clobbers
322 all our temporaries. For now implement these as helper functions. */
323
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200324uint32_t HELPER (add_cc)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800325{
326 uint32_t result;
David 'Digit' Turner5d8f37a2009-09-14 14:32:27 -0700327 result = a + b;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800328 env->NF = env->ZF = result;
329 env->CF = result < a;
330 env->VF = (a ^ b ^ -1) & (a ^ result);
331 return result;
332}
333
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200334uint32_t HELPER(adc_cc)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800335{
336 uint32_t result;
337 if (!env->CF) {
338 result = a + b;
339 env->CF = result < a;
340 } else {
341 result = a + b + 1;
342 env->CF = result <= a;
343 }
344 env->VF = (a ^ b ^ -1) & (a ^ result);
345 env->NF = env->ZF = result;
346 return result;
347}
348
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200349uint32_t HELPER(sub_cc)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800350{
351 uint32_t result;
352 result = a - b;
353 env->NF = env->ZF = result;
354 env->CF = a >= b;
355 env->VF = (a ^ b) & (a ^ result);
356 return result;
357}
358
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200359uint32_t HELPER(sbc_cc)(CPUARMState *env, uint32_t a, uint32_t b)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800360{
361 uint32_t result;
362 if (!env->CF) {
363 result = a - b - 1;
364 env->CF = a > b;
365 } else {
366 result = a - b;
367 env->CF = a >= b;
368 }
369 env->VF = (a ^ b) & (a ^ result);
370 env->NF = env->ZF = result;
371 return result;
372}
373
374/* Similarly for variable shift instructions. */
375
376uint32_t HELPER(shl)(uint32_t x, uint32_t i)
377{
378 int shift = i & 0xff;
379 if (shift >= 32)
380 return 0;
381 return x << shift;
382}
383
384uint32_t HELPER(shr)(uint32_t x, uint32_t i)
385{
386 int shift = i & 0xff;
387 if (shift >= 32)
388 return 0;
389 return (uint32_t)x >> shift;
390}
391
392uint32_t HELPER(sar)(uint32_t x, uint32_t i)
393{
394 int shift = i & 0xff;
395 if (shift >= 32)
396 shift = 31;
397 return (int32_t)x >> shift;
398}
399
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200400uint32_t HELPER(shl_cc)(CPUARMState *env, uint32_t x, uint32_t i)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800401{
402 int shift = i & 0xff;
403 if (shift >= 32) {
404 if (shift == 32)
405 env->CF = x & 1;
406 else
407 env->CF = 0;
408 return 0;
409 } else if (shift != 0) {
410 env->CF = (x >> (32 - shift)) & 1;
411 return x << shift;
412 }
413 return x;
414}
415
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200416uint32_t HELPER(shr_cc)(CPUARMState *env, uint32_t x, uint32_t i)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800417{
418 int shift = i & 0xff;
419 if (shift >= 32) {
420 if (shift == 32)
421 env->CF = (x >> 31) & 1;
422 else
423 env->CF = 0;
424 return 0;
425 } else if (shift != 0) {
426 env->CF = (x >> (shift - 1)) & 1;
427 return x >> shift;
428 }
429 return x;
430}
431
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200432uint32_t HELPER(sar_cc)(CPUARMState *env, uint32_t x, uint32_t i)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800433{
434 int shift = i & 0xff;
435 if (shift >= 32) {
436 env->CF = (x >> 31) & 1;
437 return (int32_t)x >> 31;
438 } else if (shift != 0) {
439 env->CF = (x >> (shift - 1)) & 1;
440 return (int32_t)x >> shift;
441 }
442 return x;
443}
444
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200445uint32_t HELPER(ror_cc)(CPUARMState *env, uint32_t x, uint32_t i)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800446{
447 int shift1, shift;
448 shift1 = i & 0xff;
449 shift = shift1 & 0x1f;
450 if (shift == 0) {
451 if (shift1 != 0)
452 env->CF = (x >> 31) & 1;
453 return x;
454 } else {
455 env->CF = (x >> (shift - 1)) & 1;
456 return ((uint32_t)x >> shift) | (x << (32 - shift));
457 }
458}
459
David 'Digit' Turner0b4c9e82014-04-03 00:37:18 +0200460void HELPER(neon_vldst_all)(CPUARMState *env, uint32_t insn)
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800461{
David 'Digit' Turner52858642011-06-03 13:41:05 +0200462#if defined(CONFIG_USER_ONLY)
463#define LDB(addr) ldub(addr)
464#define LDW(addr) lduw(addr)
465#define LDL(addr) ldl(addr)
466#define LDQ(addr) ldq(addr)
467#define STB(addr, val) stb(addr, val)
468#define STW(addr, val) stw(addr, val)
469#define STL(addr, val) stl(addr, val)
470#define STQ(addr, val) stq(addr, val)
471#else
472 int user = cpu_mmu_index(env);
David 'Digit' Turner86b1fb02014-03-21 15:20:21 +0100473#define LDB(addr) helper_ldb_mmu(env, addr, user)
474#define LDW(addr) helper_le_lduw_mmu(env, addr, user, GETPC())
475#define LDL(addr) helper_le_ldul_mmu(env, addr, user, GETPC())
476#define LDQ(addr) helper_le_ldq_mmu(env, addr, user, GETPC())
477#define STB(addr, val) helper_stb_mmu(env, addr, val, user)
478#define STW(addr, val) helper_le_stw_mmu(env, addr, val, user, GETPC())
479#define STL(addr, val) helper_le_stl_mmu(env, addr, val, user, GETPC())
480#define STQ(addr, val) helper_le_stq_mmu(env, addr, val, user, GETPC())
David 'Digit' Turner52858642011-06-03 13:41:05 +0200481#endif
482 static const struct {
483 int nregs;
484 int interleave;
485 int spacing;
486 } neon_ls_element_type[11] = {
487 {4, 4, 1},
488 {4, 4, 2},
489 {4, 1, 1},
490 {4, 2, 1},
491 {3, 3, 1},
492 {3, 3, 2},
493 {3, 1, 1},
494 {1, 1, 1},
495 {2, 2, 1},
496 {2, 2, 2},
497 {2, 1, 1}
498 };
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800499
David 'Digit' Turner52858642011-06-03 13:41:05 +0200500 const int op = (insn >> 8) & 0xf;
501 const int size = (insn >> 6) & 3;
502 int rd = ((insn >> 12) & 0x0f) | ((insn >> 18) & 0x10);
503 const int rn = (insn >> 16) & 0xf;
504 const int load = (insn & (1 << 21)) != 0;
505 const int nregs = neon_ls_element_type[op].nregs;
506 const int interleave = neon_ls_element_type[op].interleave;
507 const int spacing = neon_ls_element_type[op].spacing;
508 uint32_t addr = env->regs[rn];
509 const int stride = (1 << size) * interleave;
510 int i, reg;
511 uint64_t tmp64;
512
513 for (reg = 0; reg < nregs; reg++) {
514 if (interleave > 2 || (interleave == 2 && nregs == 2)) {
515 addr = env->regs[rn] + (1 << size) * reg;
516 } else if (interleave == 2 && nregs == 4 && reg == 2) {
517 addr = env->regs[rn] + (1 << size);
518 }
519 switch (size) {
520 case 3:
521 if (load) {
522 env->vfp.regs[rd] = make_float64(LDQ(addr));
523 } else {
524 STQ(addr, float64_val(env->vfp.regs[rd]));
525 }
526 addr += stride;
527 break;
528 case 2:
529 if (load) {
530 tmp64 = (uint32_t)LDL(addr);
531 addr += stride;
532 tmp64 |= (uint64_t)LDL(addr) << 32;
533 addr += stride;
534 env->vfp.regs[rd] = make_float64(tmp64);
535 } else {
536 tmp64 = float64_val(env->vfp.regs[rd]);
537 STL(addr, tmp64);
538 addr += stride;
539 STL(addr, tmp64 >> 32);
540 addr += stride;
541 }
542 break;
543 case 1:
544 if (load) {
545 tmp64 = 0ull;
546 for (i = 0; i < 4; i++, addr += stride) {
547 tmp64 |= (uint64_t)LDW(addr) << (i * 16);
548 }
549 env->vfp.regs[rd] = make_float64(tmp64);
550 } else {
551 tmp64 = float64_val(env->vfp.regs[rd]);
552 for (i = 0; i < 4; i++, addr += stride, tmp64 >>= 16) {
553 STW(addr, tmp64);
554 }
555 }
556 break;
557 case 0:
558 if (load) {
559 tmp64 = 0ull;
560 for (i = 0; i < 8; i++, addr += stride) {
561 tmp64 |= (uint64_t)LDB(addr) << (i * 8);
562 }
563 env->vfp.regs[rd] = make_float64(tmp64);
564 } else {
565 tmp64 = float64_val(env->vfp.regs[rd]);
566 for (i = 0; i < 8; i++, addr += stride, tmp64 >>= 8) {
567 STB(addr, tmp64);
568 }
569 }
570 break;
571 }
572 rd += spacing;
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800573 }
David 'Digit' Turner52858642011-06-03 13:41:05 +0200574#undef LDB
575#undef LDW
576#undef LDL
577#undef LDQ
578#undef STB
579#undef STW
580#undef STL
581#undef STQ
The Android Open Source Project8b23a6c2009-03-03 19:30:32 -0800582}