op_helper.c revision 85c62200dbdb7ced04b34cb228098b888a8cd828
1/*
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
17 * License along with this library; if not, see <http://www.gnu.org/licenses/>.
18 */
19#include "exec.h"
20#include "helper.h"
21
22#define SIGNBIT (uint32_t)0x80000000
23#define SIGNBIT64 ((uint64_t)1 << 63)
24
25void raise_exception(int tt)
26{
27    env->exception_index = tt;
28    cpu_loop_exit(env);
29}
30
31uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def,
32                          uint32_t rn, uint32_t maxindex)
33{
34    uint32_t val;
35    uint32_t tmp;
36    int index;
37    int shift;
38    uint64_t *table;
39    table = (uint64_t *)&env->vfp.regs[rn];
40    val = 0;
41    for (shift = 0; shift < 32; shift += 8) {
42        index = (ireg >> shift) & 0xff;
43        if (index < maxindex) {
44            tmp = (table[index >> 3] >> ((index & 7) << 3)) & 0xff;
45            val |= tmp << shift;
46        } else {
47            val |= def & (0xff << shift);
48        }
49    }
50    return val;
51}
52
53#if !defined(CONFIG_USER_ONLY)
54
55#define MMUSUFFIX _mmu
56
57#define SHIFT 0
58#include "exec/softmmu_template.h"
59
60#define SHIFT 1
61#include "exec/softmmu_template.h"
62
63#define SHIFT 2
64#include "exec/softmmu_template.h"
65
66#define SHIFT 3
67#include "exec/softmmu_template.h"
68
69/* try to fill the TLB and return an exception if error. If retaddr is
70   NULL, it means that the function was called in C code (i.e. not
71   from generated code or from helper.c) */
72/* XXX: fix it to restore all registers */
73void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr)
74{
75    TranslationBlock *tb;
76    CPUARMState *saved_env;
77    unsigned long pc;
78    int ret;
79
80    /* XXX: hack to restore env in all cases, even if not called from
81       generated code */
82    saved_env = env;
83    env = cpu_single_env;
84    ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1);
85    if (unlikely(ret)) {
86        if (retaddr) {
87            /* now we have a real cpu fault */
88            pc = (unsigned long)retaddr;
89            tb = tb_find_pc(pc);
90            if (tb) {
91                /* the PC is inside the translated code. It means that we have
92                   a virtual CPU fault */
93                cpu_restore_state(tb, env, pc);
94            }
95        }
96        raise_exception(env->exception_index);
97    }
98    env = saved_env;
99}
100
101void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val)
102{
103    int cp_num = (insn >> 8) & 0xf;
104    int cp_info = (insn >> 5) & 7;
105    int src = (insn >> 16) & 0xf;
106    int operand = insn & 0xf;
107
108    if (env->cp[cp_num].cp_write)
109        env->cp[cp_num].cp_write(env->cp[cp_num].opaque,
110                                 cp_info, src, operand, val, GETPC());
111        }
112
113uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn)
114{
115    int cp_num = (insn >> 8) & 0xf;
116    int cp_info = (insn >> 5) & 7;
117    int dest = (insn >> 16) & 0xf;
118    int operand = insn & 0xf;
119
120    if (env->cp[cp_num].cp_read)
121        return env->cp[cp_num].cp_read(env->cp[cp_num].opaque,
122                                       cp_info, dest, operand, GETPC());
123        return 0;
124}
125
126#else
127
128void HELPER(set_cp)(CPUARMState *env, uint32_t insn, uint32_t val)
129{
130    int op1 = (insn >> 8) & 0xf;
131    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
132    return;
133}
134
135uint32_t HELPER(get_cp)(CPUARMState *env, uint32_t insn)
136{
137    int op1 = (insn >> 8) & 0xf;
138    cpu_abort(env, "cp%i insn %08x\n", op1, insn);
139    return 0;
140}
141
142#endif
143
144/* FIXME: Pass an axplicit pointer to QF to CPUARMState, and move saturating
145   instructions into helper.c  */
146uint32_t HELPER(add_setq)(uint32_t a, uint32_t b)
147{
148    uint32_t res = a + b;
149    if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT))
150        env->QF = 1;
151    return res;
152}
153
154uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b)
155{
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
164uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b)
165{
166    uint32_t res = a - b;
167    if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) {
168        env->QF = 1;
169        res = ~(((int32_t)a >> 31) ^ SIGNBIT);
170    }
171    return res;
172}
173
174uint32_t HELPER(double_saturate)(int32_t val)
175{
176    uint32_t res;
177    if (val >= 0x40000000) {
178        res = ~SIGNBIT;
179        env->QF = 1;
180    } else if (val <= (int32_t)0xc0000000) {
181        res = SIGNBIT;
182        env->QF = 1;
183    } else {
184        res = val << 1;
185    }
186    return res;
187}
188
189uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b)
190{
191    uint32_t res = a + b;
192    if (res < a) {
193        env->QF = 1;
194        res = ~0;
195    }
196    return res;
197}
198
199uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b)
200{
201    uint32_t res = a - b;
202    if (res > a) {
203        env->QF = 1;
204        res = 0;
205    }
206    return res;
207}
208
209/* Signed saturation.  */
210static inline uint32_t do_ssat(int32_t val, int shift)
211{
212    int32_t top;
213    uint32_t mask;
214
215    top = val >> shift;
216    mask = (1u << shift) - 1;
217    if (top > 0) {
218        env->QF = 1;
219        return mask;
220    } else if (top < -1) {
221        env->QF = 1;
222        return ~mask;
223    }
224    return val;
225}
226
227/* Unsigned saturation.  */
228static inline uint32_t do_usat(int32_t val, int shift)
229{
230    uint32_t max;
231
232    max = (1u << shift) - 1;
233    if (val < 0) {
234        env->QF = 1;
235        return 0;
236    } else if (val > max) {
237        env->QF = 1;
238        return max;
239    }
240    return val;
241}
242
243/* Signed saturate.  */
244uint32_t HELPER(ssat)(uint32_t x, uint32_t shift)
245{
246    return do_ssat(x, shift);
247}
248
249/* Dual halfword signed saturate.  */
250uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift)
251{
252    uint32_t res;
253
254    res = (uint16_t)do_ssat((int16_t)x, shift);
255    res |= do_ssat(((int32_t)x) >> 16, shift) << 16;
256    return res;
257}
258
259/* Unsigned saturate.  */
260uint32_t HELPER(usat)(uint32_t x, uint32_t shift)
261{
262    return do_usat(x, shift);
263}
264
265/* Dual halfword unsigned saturate.  */
266uint32_t HELPER(usat16)(uint32_t x, uint32_t shift)
267{
268    uint32_t res;
269
270    res = (uint16_t)do_usat((int16_t)x, shift);
271    res |= do_usat(((int32_t)x) >> 16, shift) << 16;
272    return res;
273}
274
275void HELPER(wfi)(void)
276{
277    env->exception_index = EXCP_HLT;
278    env->halted = 1;
279    cpu_loop_exit(env);
280}
281
282void HELPER(exception)(uint32_t excp)
283{
284    env->exception_index = excp;
285    cpu_loop_exit(env);
286}
287
288uint32_t HELPER(cpsr_read)(void)
289{
290    return cpsr_read(env) & ~CPSR_EXEC;
291}
292
293void HELPER(cpsr_write)(uint32_t val, uint32_t mask)
294{
295    cpsr_write(env, val, mask);
296}
297
298/* Access to user mode registers from privileged modes.  */
299uint32_t HELPER(get_user_reg)(uint32_t regno)
300{
301    uint32_t val;
302
303    if (regno == 13) {
304        val = env->banked_r13[0];
305    } else if (regno == 14) {
306        val = env->banked_r14[0];
307    } else if (regno >= 8
308               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
309        val = env->usr_regs[regno - 8];
310    } else {
311        val = env->regs[regno];
312    }
313    return val;
314}
315
316void HELPER(set_user_reg)(uint32_t regno, uint32_t val)
317{
318    if (regno == 13) {
319        env->banked_r13[0] = val;
320    } else if (regno == 14) {
321        env->banked_r14[0] = val;
322    } else if (regno >= 8
323               && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) {
324        env->usr_regs[regno - 8] = val;
325    } else {
326        env->regs[regno] = val;
327    }
328}
329
330/* ??? Flag setting arithmetic is awkward because we need to do comparisons.
331   The only way to do that in TCG is a conditional branch, which clobbers
332   all our temporaries.  For now implement these as helper functions.  */
333
334uint32_t HELPER (add_cc)(uint32_t a, uint32_t b)
335{
336    uint32_t result;
337    result = a + b;
338    env->NF = env->ZF = result;
339    env->CF = result < a;
340    env->VF = (a ^ b ^ -1) & (a ^ result);
341    return result;
342}
343
344uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b)
345{
346    uint32_t result;
347    if (!env->CF) {
348        result = a + b;
349        env->CF = result < a;
350    } else {
351        result = a + b + 1;
352        env->CF = result <= a;
353    }
354    env->VF = (a ^ b ^ -1) & (a ^ result);
355    env->NF = env->ZF = result;
356    return result;
357}
358
359uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b)
360{
361    uint32_t result;
362    result = a - b;
363    env->NF = env->ZF = result;
364    env->CF = a >= b;
365    env->VF = (a ^ b) & (a ^ result);
366    return result;
367}
368
369uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b)
370{
371    uint32_t result;
372    if (!env->CF) {
373        result = a - b - 1;
374        env->CF = a > b;
375    } else {
376        result = a - b;
377        env->CF = a >= b;
378    }
379    env->VF = (a ^ b) & (a ^ result);
380    env->NF = env->ZF = result;
381    return result;
382}
383
384/* Similarly for variable shift instructions.  */
385
386uint32_t HELPER(shl)(uint32_t x, uint32_t i)
387{
388    int shift = i & 0xff;
389    if (shift >= 32)
390        return 0;
391    return x << shift;
392}
393
394uint32_t HELPER(shr)(uint32_t x, uint32_t i)
395{
396    int shift = i & 0xff;
397    if (shift >= 32)
398        return 0;
399    return (uint32_t)x >> shift;
400}
401
402uint32_t HELPER(sar)(uint32_t x, uint32_t i)
403{
404    int shift = i & 0xff;
405    if (shift >= 32)
406        shift = 31;
407    return (int32_t)x >> shift;
408}
409
410uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i)
411{
412    int shift = i & 0xff;
413    if (shift >= 32) {
414        if (shift == 32)
415            env->CF = x & 1;
416        else
417            env->CF = 0;
418        return 0;
419    } else if (shift != 0) {
420        env->CF = (x >> (32 - shift)) & 1;
421        return x << shift;
422    }
423    return x;
424}
425
426uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i)
427{
428    int shift = i & 0xff;
429    if (shift >= 32) {
430        if (shift == 32)
431            env->CF = (x >> 31) & 1;
432        else
433            env->CF = 0;
434        return 0;
435    } else if (shift != 0) {
436        env->CF = (x >> (shift - 1)) & 1;
437        return x >> shift;
438    }
439    return x;
440}
441
442uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i)
443{
444    int shift = i & 0xff;
445    if (shift >= 32) {
446        env->CF = (x >> 31) & 1;
447        return (int32_t)x >> 31;
448    } else if (shift != 0) {
449        env->CF = (x >> (shift - 1)) & 1;
450        return (int32_t)x >> shift;
451    }
452    return x;
453}
454
455uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i)
456{
457    int shift1, shift;
458    shift1 = i & 0xff;
459    shift = shift1 & 0x1f;
460    if (shift == 0) {
461        if (shift1 != 0)
462            env->CF = (x >> 31) & 1;
463        return x;
464    } else {
465        env->CF = (x >> (shift - 1)) & 1;
466        return ((uint32_t)x >> shift) | (x << (32 - shift));
467    }
468}
469
470void HELPER(neon_vldst_all)(uint32_t insn)
471{
472#if defined(CONFIG_USER_ONLY)
473#define LDB(addr) ldub(addr)
474#define LDW(addr) lduw(addr)
475#define LDL(addr) ldl(addr)
476#define LDQ(addr) ldq(addr)
477#define STB(addr, val) stb(addr, val)
478#define STW(addr, val) stw(addr, val)
479#define STL(addr, val) stl(addr, val)
480#define STQ(addr, val) stq(addr, val)
481#else
482    int user = cpu_mmu_index(env);
483#define LDB(addr) slow_ldb_mmu(addr, user, GETPC())
484#define LDW(addr) slow_ldw_mmu(addr, user, GETPC())
485#define LDL(addr) slow_ldl_mmu(addr, user, GETPC())
486#define LDQ(addr) slow_ldq_mmu(addr, user, GETPC())
487#define STB(addr, val) slow_stb_mmu(addr, val, user, GETPC())
488#define STW(addr, val) slow_stw_mmu(addr, val, user, GETPC())
489#define STL(addr, val) slow_stl_mmu(addr, val, user, GETPC())
490#define STQ(addr, val) slow_stq_mmu(addr, val, user, GETPC())
491#endif
492    static const struct {
493        int nregs;
494        int interleave;
495        int spacing;
496    } neon_ls_element_type[11] = {
497        {4, 4, 1},
498        {4, 4, 2},
499        {4, 1, 1},
500        {4, 2, 1},
501        {3, 3, 1},
502        {3, 3, 2},
503        {3, 1, 1},
504        {1, 1, 1},
505        {2, 2, 1},
506        {2, 2, 2},
507        {2, 1, 1}
508    };
509
510    const int op = (insn >> 8) & 0xf;
511    const int size = (insn >> 6) & 3;
512    int rd = ((insn >> 12) & 0x0f) | ((insn >> 18) & 0x10);
513    const int rn = (insn >> 16) & 0xf;
514    const int load = (insn & (1 << 21)) != 0;
515    const int nregs = neon_ls_element_type[op].nregs;
516    const int interleave = neon_ls_element_type[op].interleave;
517    const int spacing = neon_ls_element_type[op].spacing;
518    uint32_t addr = env->regs[rn];
519    const int stride = (1 << size) * interleave;
520    int i, reg;
521    uint64_t tmp64;
522
523    for (reg = 0; reg < nregs; reg++) {
524        if (interleave > 2 || (interleave == 2 && nregs == 2)) {
525            addr = env->regs[rn] + (1 << size) * reg;
526        } else if (interleave == 2 && nregs == 4 && reg == 2) {
527            addr = env->regs[rn] + (1 << size);
528        }
529        switch (size) {
530            case 3:
531                if (load) {
532                    env->vfp.regs[rd] = make_float64(LDQ(addr));
533                } else {
534                    STQ(addr, float64_val(env->vfp.regs[rd]));
535                }
536                addr += stride;
537                break;
538            case 2:
539                if (load) {
540                    tmp64 = (uint32_t)LDL(addr);
541                    addr += stride;
542                    tmp64 |= (uint64_t)LDL(addr) << 32;
543                    addr += stride;
544                    env->vfp.regs[rd] = make_float64(tmp64);
545                } else {
546                    tmp64 = float64_val(env->vfp.regs[rd]);
547                    STL(addr, tmp64);
548                    addr += stride;
549                    STL(addr, tmp64 >> 32);
550                    addr += stride;
551                }
552                break;
553            case 1:
554                if (load) {
555                    tmp64 = 0ull;
556                    for (i = 0; i < 4; i++, addr += stride) {
557                        tmp64 |= (uint64_t)LDW(addr) << (i * 16);
558                    }
559                    env->vfp.regs[rd] = make_float64(tmp64);
560                } else {
561                    tmp64 = float64_val(env->vfp.regs[rd]);
562                    for (i = 0; i < 4; i++, addr += stride, tmp64 >>= 16) {
563                        STW(addr, tmp64);
564                    }
565                }
566                break;
567            case 0:
568                if (load) {
569                    tmp64 = 0ull;
570                    for (i = 0; i < 8; i++, addr += stride) {
571                        tmp64 |= (uint64_t)LDB(addr) << (i * 8);
572                    }
573                    env->vfp.regs[rd] = make_float64(tmp64);
574                } else {
575                    tmp64 = float64_val(env->vfp.regs[rd]);
576                    for (i = 0; i < 8; i++, addr += stride, tmp64 >>= 8) {
577                        STB(addr, tmp64);
578                    }
579                }
580                break;
581        }
582        rd += spacing;
583    }
584#undef LDB
585#undef LDW
586#undef LDL
587#undef LDQ
588#undef STB
589#undef STW
590#undef STL
591#undef STQ
592}
593