diff options
Diffstat (limited to 'target/mips/tcg/op_helper.c')
-rw-r--r-- | target/mips/tcg/op_helper.c | 420 |
1 files changed, 420 insertions, 0 deletions
diff --git a/target/mips/tcg/op_helper.c b/target/mips/tcg/op_helper.c new file mode 100644 index 0000000000..ce1549c985 --- /dev/null +++ b/target/mips/tcg/op_helper.c @@ -0,0 +1,420 @@ +/* + * MIPS emulation helpers for qemu. + * + * Copyright (c) 2004-2005 Jocelyn Mayer + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * 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, see <http://www.gnu.org/licenses/>. + * + */ + +#include "qemu/osdep.h" +#include "cpu.h" +#include "internal.h" +#include "exec/helper-proto.h" +#include "exec/exec-all.h" +#include "exec/memop.h" +#include "fpu_helper.h" + +/* 64 bits arithmetic for 32 bits hosts */ +static inline uint64_t get_HILO(CPUMIPSState *env) +{ + return ((uint64_t)(env->active_tc.HI[0]) << 32) | + (uint32_t)env->active_tc.LO[0]; +} + +static inline target_ulong set_HIT0_LO(CPUMIPSState *env, uint64_t HILO) +{ + env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); + return env->active_tc.HI[0] = (int32_t)(HILO >> 32); +} + +static inline target_ulong set_HI_LOT0(CPUMIPSState *env, uint64_t HILO) +{ + target_ulong tmp = env->active_tc.LO[0] = (int32_t)(HILO & 0xFFFFFFFF); + env->active_tc.HI[0] = (int32_t)(HILO >> 32); + return tmp; +} + +/* Multiplication variants of the vr54xx. */ +target_ulong helper_muls(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, 0 - ((int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2)); +} + +target_ulong helper_mulsu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, 0 - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macc(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_macchi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)get_HILO(env) + (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_maccu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (uint64_t)get_HILO(env) + + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_macchiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)get_HILO(env) + + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msac(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_msachi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)get_HILO(env) - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_msacu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HI_LOT0(env, (uint64_t)get_HILO(env) - + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_msachiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)get_HILO(env) - + (uint64_t)(uint32_t)arg1 * (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulhi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (int64_t)(int32_t)arg1 * (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulhiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +target_ulong helper_mulshi(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, 0 - (int64_t)(int32_t)arg1 * + (int64_t)(int32_t)arg2); +} + +target_ulong helper_mulshiu(CPUMIPSState *env, target_ulong arg1, + target_ulong arg2) +{ + return set_HIT0_LO(env, 0 - (uint64_t)(uint32_t)arg1 * + (uint64_t)(uint32_t)arg2); +} + +static inline target_ulong bitswap(target_ulong v) +{ + v = ((v >> 1) & (target_ulong)0x5555555555555555ULL) | + ((v & (target_ulong)0x5555555555555555ULL) << 1); + v = ((v >> 2) & (target_ulong)0x3333333333333333ULL) | + ((v & (target_ulong)0x3333333333333333ULL) << 2); + v = ((v >> 4) & (target_ulong)0x0F0F0F0F0F0F0F0FULL) | + ((v & (target_ulong)0x0F0F0F0F0F0F0F0FULL) << 4); + return v; +} + +#ifdef TARGET_MIPS64 +target_ulong helper_dbitswap(target_ulong rt) +{ + return bitswap(rt); +} +#endif + +target_ulong helper_bitswap(target_ulong rt) +{ + return (int32_t)bitswap(rt); +} + +target_ulong helper_rotx(target_ulong rs, uint32_t shift, uint32_t shiftx, + uint32_t stripe) +{ + int i; + uint64_t tmp0 = ((uint64_t)rs) << 32 | ((uint64_t)rs & 0xffffffff); + uint64_t tmp1 = tmp0; + for (i = 0; i <= 46; i++) { + int s; + if (i & 0x8) { + s = shift; + } else { + s = shiftx; + } + + if (stripe != 0 && !(i & 0x4)) { + s = ~s; + } + if (s & 0x10) { + if (tmp0 & (1LL << (i + 16))) { + tmp1 |= 1LL << i; + } else { + tmp1 &= ~(1LL << i); + } + } + } + + uint64_t tmp2 = tmp1; + for (i = 0; i <= 38; i++) { + int s; + if (i & 0x4) { + s = shift; + } else { + s = shiftx; + } + + if (s & 0x8) { + if (tmp1 & (1LL << (i + 8))) { + tmp2 |= 1LL << i; + } else { + tmp2 &= ~(1LL << i); + } + } + } + + uint64_t tmp3 = tmp2; + for (i = 0; i <= 34; i++) { + int s; + if (i & 0x2) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x4) { + if (tmp2 & (1LL << (i + 4))) { + tmp3 |= 1LL << i; + } else { + tmp3 &= ~(1LL << i); + } + } + } + + uint64_t tmp4 = tmp3; + for (i = 0; i <= 32; i++) { + int s; + if (i & 0x1) { + s = shift; + } else { + s = shiftx; + } + if (s & 0x2) { + if (tmp3 & (1LL << (i + 2))) { + tmp4 |= 1LL << i; + } else { + tmp4 &= ~(1LL << i); + } + } + } + + uint64_t tmp5 = tmp4; + for (i = 0; i <= 31; i++) { + int s; + s = shift; + if (s & 0x1) { + if (tmp4 & (1LL << (i + 1))) { + tmp5 |= 1LL << i; + } else { + tmp5 &= ~(1LL << i); + } + } + } + + return (int64_t)(int32_t)(uint32_t)tmp5; +} + +void helper_fork(target_ulong arg1, target_ulong arg2) +{ + /* + * arg1 = rt, arg2 = rs + * TODO: store to TC register + */ +} + +target_ulong helper_yield(CPUMIPSState *env, target_ulong arg) +{ + target_long arg1 = arg; + + if (arg1 < 0) { + /* No scheduling policy implemented. */ + if (arg1 != -2) { + if (env->CP0_VPEControl & (1 << CP0VPECo_YSI) && + env->active_tc.CP0_TCStatus & (1 << CP0TCSt_DT)) { + env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); + env->CP0_VPEControl |= 4 << CP0VPECo_EXCPT; + do_raise_exception(env, EXCP_THREAD, GETPC()); + } + } + } else if (arg1 == 0) { + if (0) { + /* TODO: TC underflow */ + env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); + do_raise_exception(env, EXCP_THREAD, GETPC()); + } else { + /* TODO: Deallocate TC */ + } + } else if (arg1 > 0) { + /* Yield qualifier inputs not implemented. */ + env->CP0_VPEControl &= ~(0x7 << CP0VPECo_EXCPT); + env->CP0_VPEControl |= 2 << CP0VPECo_EXCPT; + do_raise_exception(env, EXCP_THREAD, GETPC()); + } + return env->CP0_YQMask; +} + +static inline void check_hwrena(CPUMIPSState *env, int reg, uintptr_t pc) +{ + if ((env->hflags & MIPS_HFLAG_CP0) || (env->CP0_HWREna & (1 << reg))) { + return; + } + do_raise_exception(env, EXCP_RI, pc); +} + +target_ulong helper_rdhwr_cpunum(CPUMIPSState *env) +{ + check_hwrena(env, 0, GETPC()); + return env->CP0_EBase & 0x3ff; +} + +target_ulong helper_rdhwr_synci_step(CPUMIPSState *env) +{ + check_hwrena(env, 1, GETPC()); + return env->SYNCI_Step; +} + +target_ulong helper_rdhwr_cc(CPUMIPSState *env) +{ + check_hwrena(env, 2, GETPC()); +#ifdef CONFIG_USER_ONLY + return env->CP0_Count; +#else + return (int32_t)cpu_mips_get_count(env); +#endif +} + +target_ulong helper_rdhwr_ccres(CPUMIPSState *env) +{ + check_hwrena(env, 3, GETPC()); + return env->CCRes; +} + +target_ulong helper_rdhwr_performance(CPUMIPSState *env) +{ + check_hwrena(env, 4, GETPC()); + return env->CP0_Performance0; +} + +target_ulong helper_rdhwr_xnp(CPUMIPSState *env) +{ + check_hwrena(env, 5, GETPC()); + return (env->CP0_Config5 >> CP0C5_XNP) & 1; +} + +void helper_pmon(CPUMIPSState *env, int function) +{ + function /= 2; + switch (function) { + case 2: /* TODO: char inbyte(int waitflag); */ + if (env->active_tc.gpr[4] == 0) { + env->active_tc.gpr[2] = -1; + } + /* Fall through */ + case 11: /* TODO: char inbyte (void); */ + env->active_tc.gpr[2] = -1; + break; + case 3: + case 12: + printf("%c", (char)(env->active_tc.gpr[4] & 0xFF)); + break; + case 17: + break; + case 158: + { + unsigned char *fmt = (void *)(uintptr_t)env->active_tc.gpr[4]; + printf("%s", fmt); + } + break; + } +} + +#if !defined(CONFIG_USER_ONLY) + +void mips_cpu_do_unaligned_access(CPUState *cs, vaddr addr, + MMUAccessType access_type, + int mmu_idx, uintptr_t retaddr) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + int error_code = 0; + int excp; + + if (!(env->hflags & MIPS_HFLAG_DM)) { + env->CP0_BadVAddr = addr; + } + + if (access_type == MMU_DATA_STORE) { + excp = EXCP_AdES; + } else { + excp = EXCP_AdEL; + if (access_type == MMU_INST_FETCH) { + error_code |= EXCP_INST_NOTAVAIL; + } + } + + do_raise_exception_err(env, excp, error_code, retaddr); +} + +void mips_cpu_do_transaction_failed(CPUState *cs, hwaddr physaddr, + vaddr addr, unsigned size, + MMUAccessType access_type, + int mmu_idx, MemTxAttrs attrs, + MemTxResult response, uintptr_t retaddr) +{ + MIPSCPU *cpu = MIPS_CPU(cs); + CPUMIPSState *env = &cpu->env; + + if (access_type == MMU_INST_FETCH) { + do_raise_exception(env, EXCP_IBE, retaddr); + } else { + do_raise_exception(env, EXCP_DBE, retaddr); + } +} +#endif /* !CONFIG_USER_ONLY */ |