diff options
Diffstat (limited to 'target-arm/op_helper.c')
-rw-r--r-- | target-arm/op_helper.c | 641 |
1 files changed, 488 insertions, 153 deletions
diff --git a/target-arm/op_helper.c b/target-arm/op_helper.c index acc83ba..36de55b 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 @@ -18,6 +18,10 @@ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ #include "exec.h" +#include "helpers.h" + +#define SIGNBIT (uint32_t)0x80000000 +#define SIGNBIT64 ((uint64_t)1 << 63) void raise_exception(int tt) { @@ -39,140 +43,26 @@ void cpu_unlock(void) spin_unlock(&global_cpu_lock); } -/* VFP support. */ - -void do_vfp_abss(void) -{ - FT0s = float32_abs(FT0s); -} - -void do_vfp_absd(void) +uint32_t HELPER(neon_tbl)(uint32_t ireg, uint32_t def, + uint32_t rn, uint32_t maxindex) { - FT0d = float64_abs(FT0d); -} - -void do_vfp_sqrts(void) -{ - FT0s = float32_sqrt(FT0s, &env->vfp.fp_status); -} - -void do_vfp_sqrtd(void) -{ - FT0d = float64_sqrt(FT0d, &env->vfp.fp_status); -} - -/* 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.xregs[ARM_VFP_FPSCR] = (flags << 28)\ - | (env->vfp.xregs[ARM_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.xregs[ARM_VFP_FPSCR] = (flags << 28)\ - | (env->vfp.xregs[ARM_VFP_FPSCR] & 0x0fffffff); \ - FORCE_RET(); \ -} -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) -{ - int target_bits = 0; - - 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; -} - -/* Convert vfp exception flags to target form. */ -static inline int vfp_exceptbits_to_host(int target_bits) -{ - int host_bits = 0; - - 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; -} - -void do_vfp_set_fpscr(void) -{ - int i; - uint32_t changed; - - changed = env->vfp.xregs[ARM_VFP_FPSCR]; - env->vfp.xregs[ARM_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; + 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)) & 0xff; + val |= tmp << shift; + } else { + val |= def & (0xff << shift); } - set_float_rounding_mode(i, &env->vfp.fp_status); } - - 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 do_vfp_get_fpscr(void) -{ - int i; - - T0 = (env->vfp.xregs[ARM_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); + return val; } #if !defined(CONFIG_USER_ONLY) @@ -180,7 +70,6 @@ void do_vfp_get_fpscr(void) static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr); #define MMUSUFFIX _mmu -#define GETPC() (__builtin_return_address(0)) #define ALIGNED_ONLY 1 #define SHIFT 0 @@ -195,10 +84,10 @@ static void do_unaligned_access (target_ulong addr, int is_write, int is_user, v #define SHIFT 3 #include "softmmu_template.h" -static void do_unaligned_access (target_ulong addr, int is_write, int is_user, void *retaddr) +static void do_unaligned_access (target_ulong addr, int is_write, int mmu_idx, void *retaddr) { //printf("::UNALIGNED:: addr=%lx is_write=%d is_user=%d retaddr=%p\n", addr, is_write, is_user, retaddr); - if (is_user) + if (mmu_idx) { env = cpu_single_env; env->cp15.c5_data = 0x00000001; /* corresponds to an alignment fault */ @@ -212,22 +101,22 @@ static void do_unaligned_access (target_ulong addr, int is_write, int is_user, v 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) +void tlb_fill (target_ulong addr, int is_write, int mmu_idx, void *retaddr) { TranslationBlock *tb; CPUState *saved_env; - target_phys_addr_t pc; + 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, is_user, 1); - if (__builtin_expect(ret, 0)) { + 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 = (target_phys_addr_t)retaddr; + pc = (unsigned long)retaddr; tb = tb_find_pc(pc); if (tb) { /* the PC is inside the translated code. It means that we have @@ -246,7 +135,7 @@ void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr) * The following functions are address translation helper functions * for fast memory access in QEMU. */ -static unsigned long v2p_mmu(target_ulong addr, int is_user) +static target_phys_addr_t v2p_mmu(target_ulong addr, int mmu_idx) { int index; target_ulong tlb_addr; @@ -255,13 +144,13 @@ static unsigned long v2p_mmu(target_ulong addr, int is_user) index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); redo: - tlb_addr = env->tlb_table[is_user][index].addr_read; + tlb_addr = env->tlb_table[mmu_idx][index].addr_read; if ((addr & TARGET_PAGE_MASK) == (tlb_addr & (TARGET_PAGE_MASK | TLB_INVALID_MASK))) { - physaddr = addr + env->tlb_table[is_user][index].addend; + physaddr = addr + env->tlb_table[mmu_idx][index].addend; } else { /* the page is not in the TLB : fill it */ retaddr = GETPC(); - tlb_fill(addr, 0, is_user, retaddr); + tlb_fill(addr, 0, mmu_idx, retaddr); goto redo; } return physaddr; @@ -272,22 +161,23 @@ redo: * to the address of simulation host (not the physical * address of simulated OS. */ -unsigned long v2p(target_ulong ptr, int is_user) +target_phys_addr_t v2p(target_ulong ptr, int mmu_idx) { CPUState *saved_env; int index; target_ulong addr; - unsigned long physaddr; + target_phys_addr_t physaddr; saved_env = env; env = cpu_single_env; addr = ptr; index = (addr >> TARGET_PAGE_BITS) & (CPU_TLB_SIZE - 1); - if (__builtin_expect(env->tlb_table[is_user][index].addr_read != - (addr & TARGET_PAGE_MASK), 0)) { - return v2p_mmu(addr, is_user); + if (__builtin_expect(env->tlb_table[mmu_idx][index].addr_read != + (addr & TARGET_PAGE_MASK), 0)) + { + physaddr = v2p_mmu(addr, mmu_idx); } else { - physaddr = addr + env->tlb_table[is_user][index].addend; + physaddr = (target_phys_addr_t)addr + env->tlb_table[mmu_idx][index].addend; } env = saved_env; return physaddr; @@ -311,7 +201,7 @@ void vmemcpy(target_ulong ptr, char *buf, int size) } /* copy memory from the QEMU buffer to simulated virtual space */ -void pmemcpy(target_ulong ptr, char *buf, int size) +void pmemcpy(target_ulong ptr, const char *buf, int size) { if (buf == NULL) return; while (size) { @@ -349,5 +239,450 @@ void vstrcpy(target_ulong ptr, char *buf, int max) } } #endif - #endif + +/* 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; +} + +uint32_t HELPER(add_saturate)(uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (((res ^ a) & SIGNBIT) && !((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +uint32_t HELPER(sub_saturate)(uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (((res ^ a) & SIGNBIT) && ((a ^ b) & SIGNBIT)) { + env->QF = 1; + res = ~(((int32_t)a >> 31) ^ SIGNBIT); + } + return res; +} + +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; +} + +uint32_t HELPER(add_usaturate)(uint32_t a, uint32_t b) +{ + uint32_t res = a + b; + if (res < a) { + env->QF = 1; + res = ~0; + } + return res; +} + +uint32_t HELPER(sub_usaturate)(uint32_t a, uint32_t b) +{ + uint32_t res = a - b; + if (res > a) { + env->QF = 1; + res = 0; + } + return res; +} + +/* Signed saturation. */ +static inline uint32_t do_ssat(int32_t val, int shift) +{ + 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; +} + +/* Unsigned saturation. */ +static inline uint32_t do_usat(int32_t val, int shift) +{ + 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; +} + +/* Signed saturate. */ +uint32_t HELPER(ssat)(uint32_t x, uint32_t shift) +{ + return do_ssat(x, shift); +} + +/* Dual halfword signed saturate. */ +uint32_t HELPER(ssat16)(uint32_t x, uint32_t shift) +{ + uint32_t res; + + res = (uint16_t)do_ssat((int16_t)x, shift); + res |= do_ssat(((int32_t)x) >> 16, shift) << 16; + return res; +} + +/* Unsigned saturate. */ +uint32_t HELPER(usat)(uint32_t x, uint32_t shift) +{ + return do_usat(x, shift); +} + +/* Dual halfword unsigned saturate. */ +uint32_t HELPER(usat16)(uint32_t x, uint32_t shift) +{ + uint32_t res; + + res = (uint16_t)do_usat((int16_t)x, shift); + res |= do_usat(((int32_t)x) >> 16, shift) << 16; + return res; +} + +void HELPER(wfi)(void) +{ + env->exception_index = EXCP_HLT; + env->halted = 1; + cpu_loop_exit(); +} + +void HELPER(exception)(uint32_t excp) +{ + env->exception_index = excp; + cpu_loop_exit(); +} + +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; +} + +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; + } +} + +/* ??? 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) +{ + uint32_t result; + result = T0 + T1; + env->NF = env->ZF = result; + env->CF = result < a; + env->VF = (a ^ b ^ -1) & (a ^ result); + return result; +} + +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; +} + +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; +} + +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; +} + +/* Similarly for variable shift instructions. */ + +uint32_t HELPER(shl)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + return 0; + return x << shift; +} + +uint32_t HELPER(shr)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + return 0; + return (uint32_t)x >> shift; +} + +uint32_t HELPER(sar)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift >= 32) + shift = 31; + return (int32_t)x >> shift; +} + +uint32_t HELPER(ror)(uint32_t x, uint32_t i) +{ + int shift = i & 0xff; + if (shift == 0) + return x; + return (x >> shift) | (x << (32 - shift)); +} + +uint32_t HELPER(shl_cc)(uint32_t x, uint32_t i) +{ + 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; +} + +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; + } + return x; +} + +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)); + } +} + +uint64_t HELPER(neon_add_saturate_s64)(uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 + src2; + if (((res ^ src1) & SIGNBIT64) && !((src1 ^ src2) & SIGNBIT64)) { + env->QF = 1; + res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; + } + return res; +} + +uint64_t HELPER(neon_add_saturate_u64)(uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 + src2; + if (res < src1) { + env->QF = 1; + res = ~(uint64_t)0; + } + return res; +} + +uint64_t HELPER(neon_sub_saturate_s64)(uint64_t src1, uint64_t src2) +{ + uint64_t res; + + res = src1 - src2; + if (((res ^ src1) & SIGNBIT64) && ((src1 ^ src2) & SIGNBIT64)) { + env->QF = 1; + res = ((int64_t)src1 >> 63) ^ ~SIGNBIT64; + } + return res; +} + +uint64_t HELPER(neon_sub_saturate_u64)(uint64_t src1, uint64_t src2) +{ + uint64_t res; + + if (src1 < src2) { + env->QF = 1; + res = 0; + } else { + res = src1 - src2; + } + return res; +} + +/* These need to return a pair of value, so still use T0/T1. */ +/* Transpose. Argument order is rather strange to avoid special casing + the tranlation code. + On input T0 = rm, T1 = rd. On output T0 = rd, T1 = rm */ +void HELPER(neon_trn_u8)(void) +{ + uint32_t rd; + uint32_t rm; + rd = ((T0 & 0x00ff00ff) << 8) | (T1 & 0x00ff00ff); + rm = ((T1 & 0xff00ff00) >> 8) | (T0 & 0xff00ff00); + T0 = rd; + T1 = rm; + FORCE_RET(); +} + +void HELPER(neon_trn_u16)(void) +{ + uint32_t rd; + uint32_t rm; + rd = (T0 << 16) | (T1 & 0xffff); + rm = (T1 >> 16) | (T0 & 0xffff0000); + T0 = rd; + T1 = rm; + FORCE_RET(); +} + +/* Worker routines for zip and unzip. */ +void HELPER(neon_unzip_u8)(void) +{ + uint32_t rd; + uint32_t rm; + rd = (T0 & 0xff) | ((T0 >> 8) & 0xff00) + | ((T1 << 16) & 0xff0000) | ((T1 << 8) & 0xff000000); + rm = ((T0 >> 8) & 0xff) | ((T0 >> 16) & 0xff00) + | ((T1 << 8) & 0xff0000) | (T1 & 0xff000000); + T0 = rd; + T1 = rm; + FORCE_RET(); +} + +void HELPER(neon_zip_u8)(void) +{ + uint32_t rd; + uint32_t rm; + rd = (T0 & 0xff) | ((T1 << 8) & 0xff00) + | ((T0 << 16) & 0xff0000) | ((T1 << 24) & 0xff000000); + rm = ((T0 >> 16) & 0xff) | ((T1 >> 8) & 0xff00) + | ((T0 >> 8) & 0xff0000) | (T1 & 0xff000000); + T0 = rd; + T1 = rm; + FORCE_RET(); +} + +void HELPER(neon_zip_u16)(void) +{ + uint32_t tmp; + + tmp = (T0 & 0xffff) | (T1 << 16); + T1 = (T1 & 0xffff0000) | (T0 >> 16); + T0 = tmp; + FORCE_RET(); +} |