X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=target-arm%2Fop_helper.c;h=46358844c59706438fcec8154b2f4a56c42b8454;hb=6fea2ea462f4f726249f0e646012d21f24c024b5;hp=c075b53fcba3de612c98e9ed295c98681a41205c;hpb=b5ff1b3127119aa430a6fd309591d584803b7b6e;p=qemu.git diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index c075b53fc..46358844c 100644 --- a/target-arm/op_helper.c +++ b/target-arm/op_helper.c @@ -1,7 +1,7 @@ /* * ARM helper routines - * - * Copyright (c) 2005 CodeSourcery, LLC + * + * Copyright (c) 2005-2007 CodeSourcery, LLC * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -14,212 +14,413 @@ * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + * License along with this library; if not, see . */ #include "exec.h" +#include "helper.h" + +#define SIGNBIT (uint32_t)0x80000000 +#define SIGNBIT64 ((uint64_t)1 << 63) void raise_exception(int tt) { env->exception_index = tt; - cpu_loop_exit(); + cpu_loop_exit(env); } -/* thread support */ +uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, + uint32_t rn, uint32_t maxindex) +{ + uint32_t val; + uint32_t tmp; + int index; + int shift; + uint64_t *table; + table = (uint64_t *)&env->vfp.regs[rn]; + val = 0; + for (shift = 0; shift < 32; shift += 8) { + index = (ireg >> shift) & 0xff; + if (index < maxindex) { + tmp = (table[index >> 3] >> ((index & 7) << 3)) & 0xff; + val |= tmp << shift; + } else { + val |= def & (0xff << shift); + } + } + return val; +} + +#if !defined(CONFIG_USER_ONLY) + +#define MMUSUFFIX _mmu + +#define SHIFT 0 +#include "softmmu_template.h" + +#define SHIFT 1 +#include "softmmu_template.h" + +#define SHIFT 2 +#include "softmmu_template.h" + +#define SHIFT 3 +#include "softmmu_template.h" + +/* try to fill the TLB and return an exception if error. If retaddr is + NULL, it means that the function was called in C code (i.e. not + from generated code or from helper.c) */ +/* XXX: fix it to restore all registers */ +void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) +{ + TranslationBlock *tb; + CPUState *saved_env; + unsigned long pc; + int ret; + + /* XXX: hack to restore env in all cases, even if not called from + generated code */ + saved_env = env; + env = cpu_single_env; + ret = cpu_arm_handle_mmu_fault(env, addr, is_write, mmu_idx, 1); + if (unlikely(ret)) { + if (retaddr) { + /* now we have a real cpu fault */ + pc = (unsigned long)retaddr; + tb = tb_find_pc(pc); + if (tb) { + /* the PC is inside the translated code. It means that we have + a virtual CPU fault */ + cpu_restore_state(tb, env, pc); + } + } + raise_exception(env->exception_index); + } + env = saved_env; +} +#endif -spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; +/* FIXME: Pass an axplicit pointer to QF to CPUState, and move saturating + instructions into helper.c */ +uint32_t HELPER(add_setq)(uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) + env->QF = 1; + return res; +} -void cpu_lock(void) +uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b) { - spin_lock(&global_cpu_lock); + uint32_t res = a + b; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; } -void cpu_unlock(void) +uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b) { - spin_unlock(&global_cpu_lock); + uint32_t res = a - b; + if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; } -/* VFP support. */ +uint32_t HELPER(double_saturate)(int32_t val) +{ + uint32_t res; + if (val >= 0x40000000) { + res = ~SIGNBIT; + env->QF = 1; + } else if (val <= (int32_t)0xc0000000) { + res = SIGNBIT; + env->QF = 1; + } else { + res = val << 1; + } + return res; +} -void do_vfp_abss(void) +uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b) { - FT0s = float32_abs(FT0s); + uint32_t res = a + b; + if (res < a) { + env->QF = 1; + res = ~0; + } + return res; } -void do_vfp_absd(void) +uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b) { - FT0d = float64_abs(FT0d); + uint32_t res = a - b; + if (res > a) { + env->QF = 1; + res = 0; + } + return res; } -void do_vfp_sqrts(void) +/* Signed saturation. */ +static inline uint32_t do_ssat(int32_t val, int shift) { - FT0s = float32_sqrt(FT0s, &env->vfp.fp_status); + int32_t top; + uint32_t mask; + + top = val >> shift; + mask = (1u << shift) - 1; + if (top > 0) { + env->QF = 1; + return mask; + } else if (top < -1) { + env->QF = 1; + return ~mask; + } + return val; } -void do_vfp_sqrtd(void) +/* Unsigned saturation. */ +static inline uint32_t do_usat(int32_t val, int shift) { - FT0d = float64_sqrt(FT0d, &env->vfp.fp_status); + uint32_t max; + + max = (1u << shift) - 1; + if (val < 0) { + env->QF = 1; + return 0; + } else if (val > max) { + env->QF = 1; + return max; + } + return val; } -/* XXX: check quiet/signaling case */ -#define DO_VFP_cmp(p, size) \ -void do_vfp_cmp##p(void) \ -{ \ - uint32_t flags; \ - switch(float ## size ## _compare_quiet(FT0##p, FT1##p, &env->vfp.fp_status)) {\ - case 0: flags = 0x6; break;\ - case -1: flags = 0x8; break;\ - case 1: flags = 0x2; break;\ - default: case 2: flags = 0x3; break;\ - }\ - env->vfp.fpscr = (flags << 28) | (env->vfp.fpscr & 0x0fffffff); \ - FORCE_RET(); \ -}\ -\ -void do_vfp_cmpe##p(void) \ -{ \ - uint32_t flags; \ - switch(float ## size ## _compare(FT0##p, FT1##p, &env->vfp.fp_status)) {\ - case 0: flags = 0x6; break;\ - case -1: flags = 0x8; break;\ - case 1: flags = 0x2; break;\ - default: case 2: flags = 0x3; break;\ - }\ - env->vfp.fpscr = (flags << 28) | (env->vfp.fpscr & 0x0fffffff); \ - FORCE_RET(); \ +/* Signed saturate. */ +uint32_t HELPER(ssat)(uint32_t x, uint32_t shift) +{ + return do_ssat(x, shift); } -DO_VFP_cmp(s, 32) -DO_VFP_cmp(d, 64) -#undef DO_VFP_cmp -/* Convert host exception flags to vfp form. */ -static inline int vfp_exceptbits_from_host(int host_bits) +/* Dual halfword signed saturate. */ +uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift) { - int target_bits = 0; + uint32_t res; + + res = (uint16_t)do_ssat((int16_t)x, shift); + res |= do_ssat(((int32_t)x) >> 16, shift) << 16; + return res; +} - if (host_bits & float_flag_invalid) - target_bits |= 1; - if (host_bits & float_flag_divbyzero) - target_bits |= 2; - if (host_bits & float_flag_overflow) - target_bits |= 4; - if (host_bits & float_flag_underflow) - target_bits |= 8; - if (host_bits & float_flag_inexact) - target_bits |= 0x10; - return target_bits; +/* Unsigned saturate. */ +uint32_t HELPER(usat)(uint32_t x, uint32_t shift) +{ + return do_usat(x, shift); } -/* Convert vfp exception flags to target form. */ -static inline int vfp_exceptbits_to_host(int target_bits) +/* Dual halfword unsigned saturate. */ +uint32_t HELPER(usat16)(uint32_t x, uint32_t shift) { - int host_bits = 0; + uint32_t res; - if (target_bits & 1) - host_bits |= float_flag_invalid; - if (target_bits & 2) - host_bits |= float_flag_divbyzero; - if (target_bits & 4) - host_bits |= float_flag_overflow; - if (target_bits & 8) - host_bits |= float_flag_underflow; - if (target_bits & 0x10) - host_bits |= float_flag_inexact; - return host_bits; + res = (uint16_t)do_usat((int16_t)x, shift); + res |= do_usat(((int32_t)x) >> 16, shift) << 16; + return res; } -void do_vfp_set_fpscr(void) +void HELPER(wfi)(void) { - int i; - uint32_t changed; + env->exception_index = EXCP_HLT; + env->halted = 1; + cpu_loop_exit(env); +} - changed = env->vfp.fpscr; - env->vfp.fpscr = (T0 & 0xffc8ffff); - env->vfp.vec_len = (T0 >> 16) & 7; - env->vfp.vec_stride = (T0 >> 20) & 3; - - changed ^= T0; - if (changed & (3 << 22)) { - i = (T0 >> 22) & 3; - switch (i) { - case 0: - i = float_round_nearest_even; - break; - case 1: - i = float_round_up; - break; - case 2: - i = float_round_down; - break; - case 3: - i = float_round_to_zero; - break; - } - set_float_rounding_mode(i, &env->vfp.fp_status); +void HELPER(exception)(uint32_t excp) +{ + env->exception_index = excp; + cpu_loop_exit(env); +} + +uint32_t HELPER(cpsr_read)(void) +{ + return cpsr_read(env) & ~CPSR_EXEC; +} + +void HELPER(cpsr_write)(uint32_t val, uint32_t mask) +{ + cpsr_write(env, val, mask); +} + +/* Access to user mode registers from privileged modes. */ +uint32_t HELPER(get_user_reg)(uint32_t regno) +{ + uint32_t val; + + if (regno == 13) { + val = env->banked_r13[0]; + } else if (regno == 14) { + val = env->banked_r14[0]; + } else if (regno >= 8 + && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { + val = env->usr_regs[regno - 8]; + } else { + val = env->regs[regno]; } + return val; +} - i = vfp_exceptbits_to_host((T0 >> 8) & 0x1f); - set_float_exception_flags(i, &env->vfp.fp_status); - /* XXX: FZ and DN are not implemented. */ +void HELPER(set_user_reg)(uint32_t regno, uint32_t val) +{ + if (regno == 13) { + env->banked_r13[0] = val; + } else if (regno == 14) { + env->banked_r14[0] = val; + } else if (regno >= 8 + && (env->uncached_cpsr & 0x1f) == ARM_CPU_MODE_FIQ) { + env->usr_regs[regno - 8] = val; + } else { + env->regs[regno] = val; + } } -void do_vfp_get_fpscr(void) +/* ??? Flag setting arithmetic is awkward because we need to do comparisons. + The only way to do that in TCG is a conditional branch, which clobbers + all our temporaries. For now implement these as helper functions. */ + +uint32_t HELPER (add_cc)(uint32_t a, uint32_t b) { - int i; + uint32_t result; + result = a + b; + env->NF = env->ZF = result; + env->CF = result < a; + env->VF = (a ^ b ^ -1) & (a ^ result); + return result; +} - T0 = (env->vfp.fpscr & 0xffc8ffff) | (env->vfp.vec_len << 16) - | (env->vfp.vec_stride << 20); - i = get_float_exception_flags(&env->vfp.fp_status); - T0 |= vfp_exceptbits_from_host(i); +uint32_t HELPER(adc_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + if (!env->CF) { + result = a + b; + env->CF = result < a; + } else { + result = a + b + 1; + env->CF = result <= a; + } + env->VF = (a ^ b ^ -1) & (a ^ result); + env->NF = env->ZF = result; + return result; } -#if !defined(CONFIG_USER_ONLY) +uint32_t HELPER(sub_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + result = a - b; + env->NF = env->ZF = result; + env->CF = a >= b; + env->VF = (a ^ b) & (a ^ result); + return result; +} -#define MMUSUFFIX _mmu -#define GETPC() (__builtin_return_address(0)) +uint32_t HELPER(sbc_cc)(uint32_t a, uint32_t b) +{ + uint32_t result; + if (!env->CF) { + result = a - b - 1; + env->CF = a > b; + } else { + result = a - b; + env->CF = a >= b; + } + env->VF = (a ^ b) & (a ^ result); + env->NF = env->ZF = result; + return result; +} -#define SHIFT 0 -#include "softmmu_template.h" +/* Similarly for variable shift instructions. */ -#define SHIFT 1 -#include "softmmu_template.h" +uint32_t HELPER(shl)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + return 0; + return x << shift; +} -#define SHIFT 2 -#include "softmmu_template.h" +uint32_t HELPER(shr)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + return 0; + return (uint32_t)x >> shift; +} -#define SHIFT 3 -#include "softmmu_template.h" +uint32_t HELPER(sar)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + shift = 31; + return (int32_t)x >> shift; +} -/* try to fill the TLB and return an exception if error. If retaddr is - NULL, it means that the function was called in C code (i.e. not - from generated code or from helper.c) */ -/* XXX: fix it to restore all registers */ -void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) +uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i) { - TranslationBlock *tb; - CPUState *saved_env; - target_phys_addr_t pc; - int ret; + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = x & 1; + else + env->CF = 0; + return 0; + } else if (shift != 0) { + env->CF = (x >> (32 - shift)) & 1; + return x << shift; + } + return x; +} - /* XXX: hack to restore env in all cases, even if not called from - generated code */ - saved_env = env; - env = cpu_single_env; - ret = cpu_arm_handle_mmu_fault(env, addr, is_write, is_user, 1); - if (__builtin_expect(ret, 0)) { - if (retaddr) { - /* now we have a real cpu fault */ - pc = (target_phys_addr_t)retaddr; - tb = tb_find_pc(pc); - if (tb) { - /* the PC is inside the translated code. It means that we have - a virtual CPU fault */ - cpu_restore_state(tb, env, pc, NULL); - } - } - raise_exception(env->exception_index); +uint32_t HELPER(shr_cc)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + if (shift == 32) + env->CF = (x >> 31) & 1; + else + env->CF = 0; + return 0; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return x >> shift; } - env = saved_env; + return x; } -#endif +uint32_t HELPER(sar_cc)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) { + env->CF = (x >> 31) & 1; + return (int32_t)x >> 31; + } else if (shift != 0) { + env->CF = (x >> (shift - 1)) & 1; + return (int32_t)x >> shift; + } + return x; +} + +uint32_t HELPER(ror_cc)(uint32_t x, uint32_t i) +{ + int shift1, shift; + shift1 = i & 0xff; + shift = shift1 & 0x1f; + if (shift == 0) { + if (shift1 != 0) + env->CF = (x >> 31) & 1; + return x; + } else { + env->CF = (x >> (shift - 1)) & 1; + return ((uint32_t)x >> shift) | (x << (32 - shift)); + } +}