From 060629c6ef0b7e5c267d84c91600113264d33120 Mon Sep 17 00:00:00 2001 From: Anas Nashif Date: Tue, 6 Nov 2012 07:50:24 -0800 Subject: Imported Upstream version 1.2.0 --- target-alpha/Makefile.objs | 3 + target-alpha/STATUS | 28 + target-alpha/cpu-qom.h | 71 + target-alpha/cpu.c | 58 + target-alpha/cpu.h | 535 +++++++ target-alpha/fpu_helper.c | 822 ++++++++++ target-alpha/helper.c | 532 +++++++ target-alpha/helper.h | 122 ++ target-alpha/int_helper.c | 319 ++++ target-alpha/machine.c | 87 ++ target-alpha/mem_helper.c | 151 ++ target-alpha/sys_helper.c | 87 ++ target-alpha/translate.c | 3560 ++++++++++++++++++++++++++++++++++++++++++++ 13 files changed, 6375 insertions(+) create mode 100644 target-alpha/Makefile.objs create mode 100644 target-alpha/STATUS create mode 100644 target-alpha/cpu-qom.h create mode 100644 target-alpha/cpu.c create mode 100644 target-alpha/cpu.h create mode 100644 target-alpha/fpu_helper.c create mode 100644 target-alpha/helper.c create mode 100644 target-alpha/helper.h create mode 100644 target-alpha/int_helper.c create mode 100644 target-alpha/machine.c create mode 100644 target-alpha/mem_helper.c create mode 100644 target-alpha/sys_helper.c create mode 100644 target-alpha/translate.c (limited to 'target-alpha') diff --git a/target-alpha/Makefile.objs b/target-alpha/Makefile.objs new file mode 100644 index 000000000..590304cc6 --- /dev/null +++ b/target-alpha/Makefile.objs @@ -0,0 +1,3 @@ +obj-$(CONFIG_SOFTMMU) += machine.o +obj-y += translate.o helper.o cpu.o +obj-y += int_helper.o fpu_helper.o sys_helper.o mem_helper.o diff --git a/target-alpha/STATUS b/target-alpha/STATUS new file mode 100644 index 000000000..6c9744569 --- /dev/null +++ b/target-alpha/STATUS @@ -0,0 +1,28 @@ +(to be completed) + +Alpha emulation structure: +cpu.h : CPU definitions globally exported +exec.h : CPU definitions used only for translated code execution +helper.c : helpers that can be called either by the translated code + or the QEMU core, including the exception handler. +op_helper.c : helpers that can be called only from TCG +helper.h : TCG helpers prototypes +translate.c : Alpha instructions to micro-operations translator + +Code translator status: +The Alpha CPU instruction emulation should be quite complete with the +limitation that the VAX floating-point load and stores are not tested. +The 4 MMU modes are implemented. + +Linux user mode emulation status: +a few programs start to run. Most crash at a certain point, dereferencing a +NULL pointer. It seems that the UNIQUE register is not initialized properly. +It may appear that old executables, not relying on TLS support, run but +this is to be proved... + +Full system emulation status: +* Alpha PALCode emulation is in a very early stage and is not sufficient + to run any real OS. The alpha-softmmu target is not enabled for now. +* no hardware platform description is implemented +* there might be problems in the Alpha PALCode dedicated instructions + that would prevent to use a native PALCode image. diff --git a/target-alpha/cpu-qom.h b/target-alpha/cpu-qom.h new file mode 100644 index 000000000..6b4ca6d1d --- /dev/null +++ b/target-alpha/cpu-qom.h @@ -0,0 +1,71 @@ +/* + * QEMU Alpha CPU + * + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * 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 + * + */ +#ifndef QEMU_ALPHA_CPU_QOM_H +#define QEMU_ALPHA_CPU_QOM_H + +#include "qemu/cpu.h" +#include "cpu.h" + +#define TYPE_ALPHA_CPU "alpha-cpu" + +#define ALPHA_CPU_CLASS(klass) \ + OBJECT_CLASS_CHECK(AlphaCPUClass, (klass), TYPE_ALPHA_CPU) +#define ALPHA_CPU(obj) \ + OBJECT_CHECK(AlphaCPU, (obj), TYPE_ALPHA_CPU) +#define ALPHA_CPU_GET_CLASS(obj) \ + OBJECT_GET_CLASS(AlphaCPUClass, (obj), TYPE_ALPHA_CPU) + +/** + * AlphaCPUClass: + * @parent_reset: The parent class' reset handler. + * + * An Alpha CPU model. + */ +typedef struct AlphaCPUClass { + /*< private >*/ + CPUClass parent_class; + /*< public >*/ + + void (*parent_reset)(CPUState *cpu); +} AlphaCPUClass; + +/** + * AlphaCPU: + * @env: #CPUAlphaState + * + * An Alpha CPU. + */ +typedef struct AlphaCPU { + /*< private >*/ + CPUState parent_obj; + /*< public >*/ + + CPUAlphaState env; +} AlphaCPU; + +static inline AlphaCPU *alpha_env_get_cpu(CPUAlphaState *env) +{ + return ALPHA_CPU(container_of(env, AlphaCPU, env)); +} + +#define ENV_GET_CPU(e) CPU(alpha_env_get_cpu(e)) + + +#endif diff --git a/target-alpha/cpu.c b/target-alpha/cpu.c new file mode 100644 index 000000000..62d2a669a --- /dev/null +++ b/target-alpha/cpu.c @@ -0,0 +1,58 @@ +/* + * QEMU Alpha CPU + * + * Copyright (c) 2007 Jocelyn Mayer + * Copyright (c) 2012 SUSE LINUX Products GmbH + * + * 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 + * + */ + +#include "cpu-qom.h" +#include "qemu-common.h" + + +static void alpha_cpu_initfn(Object *obj) +{ + AlphaCPU *cpu = ALPHA_CPU(obj); + CPUAlphaState *env = &cpu->env; + + cpu_exec_init(env); + tlb_flush(env, 1); + +#if defined(CONFIG_USER_ONLY) + env->ps = PS_USER_MODE; + cpu_alpha_store_fpcr(env, (FPCR_INVD | FPCR_DZED | FPCR_OVFD + | FPCR_UNFD | FPCR_INED | FPCR_DNOD + | FPCR_DYN_NORMAL)); +#endif + env->lock_addr = -1; + env->fen = 1; +} + +static const TypeInfo alpha_cpu_type_info = { + .name = TYPE_ALPHA_CPU, + .parent = TYPE_CPU, + .instance_size = sizeof(AlphaCPU), + .instance_init = alpha_cpu_initfn, + .abstract = false, + .class_size = sizeof(AlphaCPUClass), +}; + +static void alpha_cpu_register_types(void) +{ + type_register_static(&alpha_cpu_type_info); +} + +type_init(alpha_cpu_register_types) diff --git a/target-alpha/cpu.h b/target-alpha/cpu.h new file mode 100644 index 000000000..5689760ce --- /dev/null +++ b/target-alpha/cpu.h @@ -0,0 +1,535 @@ +/* + * Alpha emulation cpu definitions for qemu. + * + * Copyright (c) 2007 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 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 . + */ + +#if !defined (__CPU_ALPHA_H__) +#define __CPU_ALPHA_H__ + +#include "config.h" +#include "qemu-common.h" + +#define TARGET_LONG_BITS 64 + +#define CPUArchState struct CPUAlphaState + +#include "cpu-defs.h" + +#include "softfloat.h" + +#define TARGET_HAS_ICE 1 + +#define ELF_MACHINE EM_ALPHA + +#define ICACHE_LINE_SIZE 32 +#define DCACHE_LINE_SIZE 32 + +#define TARGET_PAGE_BITS 13 + +#ifdef CONFIG_USER_ONLY +/* ??? The kernel likes to give addresses in high memory. If the host has + more virtual address space than the guest, this can lead to impossible + allocations. Honor the long-standing assumption that only kernel addrs + are negative, but otherwise allow allocations anywhere. This could lead + to tricky emulation problems for programs doing tagged addressing, but + that's far fewer than encounter the impossible allocation problem. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 63 +#define TARGET_VIRT_ADDR_SPACE_BITS 63 +#else +/* ??? EV4 has 34 phys addr bits, EV5 has 40, EV6 has 44. */ +#define TARGET_PHYS_ADDR_SPACE_BITS 44 +#define TARGET_VIRT_ADDR_SPACE_BITS (30 + TARGET_PAGE_BITS) +#endif + +/* Alpha major type */ +enum { + ALPHA_EV3 = 1, + ALPHA_EV4 = 2, + ALPHA_SIM = 3, + ALPHA_LCA = 4, + ALPHA_EV5 = 5, /* 21164 */ + ALPHA_EV45 = 6, /* 21064A */ + ALPHA_EV56 = 7, /* 21164A */ +}; + +/* EV4 minor type */ +enum { + ALPHA_EV4_2 = 0, + ALPHA_EV4_3 = 1, +}; + +/* LCA minor type */ +enum { + ALPHA_LCA_1 = 1, /* 21066 */ + ALPHA_LCA_2 = 2, /* 20166 */ + ALPHA_LCA_3 = 3, /* 21068 */ + ALPHA_LCA_4 = 4, /* 21068 */ + ALPHA_LCA_5 = 5, /* 21066A */ + ALPHA_LCA_6 = 6, /* 21068A */ +}; + +/* EV5 minor type */ +enum { + ALPHA_EV5_1 = 1, /* Rev BA, CA */ + ALPHA_EV5_2 = 2, /* Rev DA, EA */ + ALPHA_EV5_3 = 3, /* Pass 3 */ + ALPHA_EV5_4 = 4, /* Pass 3.2 */ + ALPHA_EV5_5 = 5, /* Pass 4 */ +}; + +/* EV45 minor type */ +enum { + ALPHA_EV45_1 = 1, /* Pass 1 */ + ALPHA_EV45_2 = 2, /* Pass 1.1 */ + ALPHA_EV45_3 = 3, /* Pass 2 */ +}; + +/* EV56 minor type */ +enum { + ALPHA_EV56_1 = 1, /* Pass 1 */ + ALPHA_EV56_2 = 2, /* Pass 2 */ +}; + +enum { + IMPLVER_2106x = 0, /* EV4, EV45 & LCA45 */ + IMPLVER_21164 = 1, /* EV5, EV56 & PCA45 */ + IMPLVER_21264 = 2, /* EV6, EV67 & EV68x */ + IMPLVER_21364 = 3, /* EV7 & EV79 */ +}; + +enum { + AMASK_BWX = 0x00000001, + AMASK_FIX = 0x00000002, + AMASK_CIX = 0x00000004, + AMASK_MVI = 0x00000100, + AMASK_TRAP = 0x00000200, + AMASK_PREFETCH = 0x00001000, +}; + +enum { + VAX_ROUND_NORMAL = 0, + VAX_ROUND_CHOPPED, +}; + +enum { + IEEE_ROUND_NORMAL = 0, + IEEE_ROUND_DYNAMIC, + IEEE_ROUND_PLUS, + IEEE_ROUND_MINUS, + IEEE_ROUND_CHOPPED, +}; + +/* IEEE floating-point operations encoding */ +/* Trap mode */ +enum { + FP_TRAP_I = 0x0, + FP_TRAP_U = 0x1, + FP_TRAP_S = 0x4, + FP_TRAP_SU = 0x5, + FP_TRAP_SUI = 0x7, +}; + +/* Rounding mode */ +enum { + FP_ROUND_CHOPPED = 0x0, + FP_ROUND_MINUS = 0x1, + FP_ROUND_NORMAL = 0x2, + FP_ROUND_DYNAMIC = 0x3, +}; + +/* FPCR bits */ +#define FPCR_SUM (1ULL << 63) +#define FPCR_INED (1ULL << 62) +#define FPCR_UNFD (1ULL << 61) +#define FPCR_UNDZ (1ULL << 60) +#define FPCR_DYN_SHIFT 58 +#define FPCR_DYN_CHOPPED (0ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MINUS (1ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_NORMAL (2ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_PLUS (3ULL << FPCR_DYN_SHIFT) +#define FPCR_DYN_MASK (3ULL << FPCR_DYN_SHIFT) +#define FPCR_IOV (1ULL << 57) +#define FPCR_INE (1ULL << 56) +#define FPCR_UNF (1ULL << 55) +#define FPCR_OVF (1ULL << 54) +#define FPCR_DZE (1ULL << 53) +#define FPCR_INV (1ULL << 52) +#define FPCR_OVFD (1ULL << 51) +#define FPCR_DZED (1ULL << 50) +#define FPCR_INVD (1ULL << 49) +#define FPCR_DNZ (1ULL << 48) +#define FPCR_DNOD (1ULL << 47) +#define FPCR_STATUS_MASK (FPCR_IOV | FPCR_INE | FPCR_UNF \ + | FPCR_OVF | FPCR_DZE | FPCR_INV) + +/* The silly software trap enables implemented by the kernel emulation. + These are more or less architecturally required, since the real hardware + has read-as-zero bits in the FPCR when the features aren't implemented. + For the purposes of QEMU, we pretend the FPCR can hold everything. */ +#define SWCR_TRAP_ENABLE_INV (1ULL << 1) +#define SWCR_TRAP_ENABLE_DZE (1ULL << 2) +#define SWCR_TRAP_ENABLE_OVF (1ULL << 3) +#define SWCR_TRAP_ENABLE_UNF (1ULL << 4) +#define SWCR_TRAP_ENABLE_INE (1ULL << 5) +#define SWCR_TRAP_ENABLE_DNO (1ULL << 6) +#define SWCR_TRAP_ENABLE_MASK ((1ULL << 7) - (1ULL << 1)) + +#define SWCR_MAP_DMZ (1ULL << 12) +#define SWCR_MAP_UMZ (1ULL << 13) +#define SWCR_MAP_MASK (SWCR_MAP_DMZ | SWCR_MAP_UMZ) + +#define SWCR_STATUS_INV (1ULL << 17) +#define SWCR_STATUS_DZE (1ULL << 18) +#define SWCR_STATUS_OVF (1ULL << 19) +#define SWCR_STATUS_UNF (1ULL << 20) +#define SWCR_STATUS_INE (1ULL << 21) +#define SWCR_STATUS_DNO (1ULL << 22) +#define SWCR_STATUS_MASK ((1ULL << 23) - (1ULL << 17)) + +#define SWCR_MASK (SWCR_TRAP_ENABLE_MASK | SWCR_MAP_MASK | SWCR_STATUS_MASK) + +/* MMU modes definitions */ + +/* Alpha has 5 MMU modes: PALcode, kernel, executive, supervisor, and user. + The Unix PALcode only exposes the kernel and user modes; presumably + executive and supervisor are used by VMS. + + PALcode itself uses physical mode for code and kernel mode for data; + there are PALmode instructions that can access data via physical mode + or via an os-installed "alternate mode", which is one of the 4 above. + + QEMU does not currently properly distinguish between code/data when + looking up addresses. To avoid having to address this issue, our + emulated PALcode will cheat and use the KSEG mapping for its code+data + rather than physical addresses. + + Moreover, we're only emulating Unix PALcode, and not attempting VMS. + + All of which allows us to drop all but kernel and user modes. + Elide the unused MMU modes to save space. */ + +#define NB_MMU_MODES 2 + +#define MMU_MODE0_SUFFIX _kernel +#define MMU_MODE1_SUFFIX _user +#define MMU_KERNEL_IDX 0 +#define MMU_USER_IDX 1 + +typedef struct CPUAlphaState CPUAlphaState; + +struct CPUAlphaState { + uint64_t ir[31]; + float64 fir[31]; + uint64_t pc; + uint64_t unique; + uint64_t lock_addr; + uint64_t lock_st_addr; + uint64_t lock_value; + float_status fp_status; + /* The following fields make up the FPCR, but in FP_STATUS format. */ + uint8_t fpcr_exc_status; + uint8_t fpcr_exc_mask; + uint8_t fpcr_dyn_round; + uint8_t fpcr_flush_to_zero; + uint8_t fpcr_dnod; + uint8_t fpcr_undz; + + /* The Internal Processor Registers. Some of these we assume always + exist for use in user-mode. */ + uint8_t ps; + uint8_t intr_flag; + uint8_t pal_mode; + uint8_t fen; + + uint32_t pcc_ofs; + + /* These pass data from the exception logic in the translator and + helpers to the OS entry point. This is used for both system + emulation and user-mode. */ + uint64_t trap_arg0; + uint64_t trap_arg1; + uint64_t trap_arg2; + +#if !defined(CONFIG_USER_ONLY) + /* The internal data required by our emulation of the Unix PALcode. */ + uint64_t exc_addr; + uint64_t palbr; + uint64_t ptbr; + uint64_t vptptr; + uint64_t sysval; + uint64_t usp; + uint64_t shadow[8]; + uint64_t scratch[24]; +#endif + + /* This alarm doesn't exist in real hardware; we wish it did. */ + struct QEMUTimer *alarm_timer; + uint64_t alarm_expire; + +#if TARGET_LONG_BITS > HOST_LONG_BITS + /* temporary fixed-point registers + * used to emulate 64 bits target on 32 bits hosts + */ + target_ulong t0, t1; +#endif + + /* Those resources are used only in QEMU core */ + CPU_COMMON + + int error_code; + + uint32_t features; + uint32_t amask; + int implver; +}; + +#define cpu_init cpu_alpha_init +#define cpu_exec cpu_alpha_exec +#define cpu_gen_code cpu_alpha_gen_code +#define cpu_signal_handler cpu_alpha_signal_handler + +#include "cpu-all.h" +#include "cpu-qom.h" + +enum { + FEATURE_ASN = 0x00000001, + FEATURE_SPS = 0x00000002, + FEATURE_VIRBND = 0x00000004, + FEATURE_TBCHK = 0x00000008, +}; + +enum { + EXCP_RESET, + EXCP_MCHK, + EXCP_SMP_INTERRUPT, + EXCP_CLK_INTERRUPT, + EXCP_DEV_INTERRUPT, + EXCP_MMFAULT, + EXCP_UNALIGN, + EXCP_OPCDEC, + EXCP_ARITH, + EXCP_FEN, + EXCP_CALL_PAL, + /* For Usermode emulation. */ + EXCP_STL_C, + EXCP_STQ_C, +}; + +/* Alpha-specific interrupt pending bits. */ +#define CPU_INTERRUPT_TIMER CPU_INTERRUPT_TGT_EXT_0 +#define CPU_INTERRUPT_SMP CPU_INTERRUPT_TGT_EXT_1 +#define CPU_INTERRUPT_MCHK CPU_INTERRUPT_TGT_EXT_2 + +/* OSF/1 Page table bits. */ +enum { + PTE_VALID = 0x0001, + PTE_FOR = 0x0002, /* used for page protection (fault on read) */ + PTE_FOW = 0x0004, /* used for page protection (fault on write) */ + PTE_FOE = 0x0008, /* used for page protection (fault on exec) */ + PTE_ASM = 0x0010, + PTE_KRE = 0x0100, + PTE_URE = 0x0200, + PTE_KWE = 0x1000, + PTE_UWE = 0x2000 +}; + +/* Hardware interrupt (entInt) constants. */ +enum { + INT_K_IP, + INT_K_CLK, + INT_K_MCHK, + INT_K_DEV, + INT_K_PERF, +}; + +/* Memory management (entMM) constants. */ +enum { + MM_K_TNV, + MM_K_ACV, + MM_K_FOR, + MM_K_FOE, + MM_K_FOW +}; + +/* Arithmetic exception (entArith) constants. */ +enum { + EXC_M_SWC = 1, /* Software completion */ + EXC_M_INV = 2, /* Invalid operation */ + EXC_M_DZE = 4, /* Division by zero */ + EXC_M_FOV = 8, /* Overflow */ + EXC_M_UNF = 16, /* Underflow */ + EXC_M_INE = 32, /* Inexact result */ + EXC_M_IOV = 64 /* Integer Overflow */ +}; + +/* Processor status constants. */ +enum { + /* Low 3 bits are interrupt mask level. */ + PS_INT_MASK = 7, + + /* Bits 4 and 5 are the mmu mode. The VMS PALcode uses all 4 modes; + The Unix PALcode only uses bit 4. */ + PS_USER_MODE = 8 +}; + +static inline int cpu_mmu_index(CPUAlphaState *env) +{ + if (env->pal_mode) { + return MMU_KERNEL_IDX; + } else if (env->ps & PS_USER_MODE) { + return MMU_USER_IDX; + } else { + return MMU_KERNEL_IDX; + } +} + +enum { + IR_V0 = 0, + IR_T0 = 1, + IR_T1 = 2, + IR_T2 = 3, + IR_T3 = 4, + IR_T4 = 5, + IR_T5 = 6, + IR_T6 = 7, + IR_T7 = 8, + IR_S0 = 9, + IR_S1 = 10, + IR_S2 = 11, + IR_S3 = 12, + IR_S4 = 13, + IR_S5 = 14, + IR_S6 = 15, + IR_FP = IR_S6, + IR_A0 = 16, + IR_A1 = 17, + IR_A2 = 18, + IR_A3 = 19, + IR_A4 = 20, + IR_A5 = 21, + IR_T8 = 22, + IR_T9 = 23, + IR_T10 = 24, + IR_T11 = 25, + IR_RA = 26, + IR_T12 = 27, + IR_PV = IR_T12, + IR_AT = 28, + IR_GP = 29, + IR_SP = 30, + IR_ZERO = 31, +}; + +CPUAlphaState * cpu_alpha_init (const char *cpu_model); +int cpu_alpha_exec(CPUAlphaState *s); +/* you can call this signal handler from your SIGBUS and SIGSEGV + signal handlers to inform the virtual CPU of exceptions. non zero + is returned if the signal was handled by the virtual CPU. */ +int cpu_alpha_signal_handler(int host_signum, void *pinfo, + void *puc); +int cpu_alpha_handle_mmu_fault (CPUAlphaState *env, uint64_t address, int rw, + int mmu_idx); +#define cpu_handle_mmu_fault cpu_alpha_handle_mmu_fault +void do_interrupt (CPUAlphaState *env); +void do_restore_state(CPUAlphaState *, uintptr_t retaddr); +void QEMU_NORETURN dynamic_excp(CPUAlphaState *, uintptr_t, int, int); +void QEMU_NORETURN arith_excp(CPUAlphaState *, uintptr_t, int, uint64_t); + +uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env); +void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val); +#ifndef CONFIG_USER_ONLY +void swap_shadow_regs(CPUAlphaState *env); +QEMU_NORETURN void cpu_unassigned_access(CPUAlphaState *env1, + target_phys_addr_t addr, int is_write, + int is_exec, int unused, int size); +#endif + +/* Bits in TB->FLAGS that control how translation is processed. */ +enum { + TB_FLAGS_PAL_MODE = 1, + TB_FLAGS_FEN = 2, + TB_FLAGS_USER_MODE = 8, + + TB_FLAGS_AMASK_SHIFT = 4, + TB_FLAGS_AMASK_BWX = AMASK_BWX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_FIX = AMASK_FIX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_CIX = AMASK_CIX << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_MVI = AMASK_MVI << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_TRAP = AMASK_TRAP << TB_FLAGS_AMASK_SHIFT, + TB_FLAGS_AMASK_PREFETCH = AMASK_PREFETCH << TB_FLAGS_AMASK_SHIFT, +}; + +static inline void cpu_get_tb_cpu_state(CPUAlphaState *env, target_ulong *pc, + target_ulong *cs_base, int *pflags) +{ + int flags = 0; + + *pc = env->pc; + *cs_base = 0; + + if (env->pal_mode) { + flags = TB_FLAGS_PAL_MODE; + } else { + flags = env->ps & PS_USER_MODE; + } + if (env->fen) { + flags |= TB_FLAGS_FEN; + } + flags |= env->amask << TB_FLAGS_AMASK_SHIFT; + + *pflags = flags; +} + +#if defined(CONFIG_USER_ONLY) +static inline void cpu_clone_regs(CPUAlphaState *env, target_ulong newsp) +{ + if (newsp) { + env->ir[IR_SP] = newsp; + } + env->ir[IR_V0] = 0; + env->ir[IR_A3] = 0; +} + +static inline void cpu_set_tls(CPUAlphaState *env, target_ulong newtls) +{ + env->unique = newtls; +} +#endif + +static inline bool cpu_has_work(CPUAlphaState *env) +{ + /* Here we are checking to see if the CPU should wake up from HALT. + We will have gotten into this state only for WTINT from PALmode. */ + /* ??? I'm not sure how the IPL state works with WTINT to keep a CPU + asleep even if (some) interrupts have been asserted. For now, + assume that if a CPU really wants to stay asleep, it will mask + interrupts at the chipset level, which will prevent these bits + from being set in the first place. */ + return env->interrupt_request & (CPU_INTERRUPT_HARD + | CPU_INTERRUPT_TIMER + | CPU_INTERRUPT_SMP + | CPU_INTERRUPT_MCHK); +} + +#include "exec-all.h" + +static inline void cpu_pc_from_tb(CPUAlphaState *env, TranslationBlock *tb) +{ + env->pc = tb->pc; +} + +#endif /* !defined (__CPU_ALPHA_H__) */ diff --git a/target-alpha/fpu_helper.c b/target-alpha/fpu_helper.c new file mode 100644 index 000000000..fe988ec45 --- /dev/null +++ b/target-alpha/fpu_helper.c @@ -0,0 +1,822 @@ +/* + * Helpers for floating point instructions. + * + * Copyright (c) 2007 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 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 . + */ + +#include "cpu.h" +#include "helper.h" +#include "softfloat.h" + +#define FP_STATUS (env->fp_status) + + +void helper_setroundmode(CPUAlphaState *env, uint32_t val) +{ + set_float_rounding_mode(val, &FP_STATUS); +} + +void helper_setflushzero(CPUAlphaState *env, uint32_t val) +{ + set_flush_to_zero(val, &FP_STATUS); +} + +void helper_fp_exc_clear(CPUAlphaState *env) +{ + set_float_exception_flags(0, &FP_STATUS); +} + +uint32_t helper_fp_exc_get(CPUAlphaState *env) +{ + return get_float_exception_flags(&FP_STATUS); +} + +static inline void inline_fp_exc_raise(CPUAlphaState *env, uintptr_t retaddr, + uint32_t exc, uint32_t regno) +{ + if (exc) { + uint32_t hw_exc = 0; + + if (exc & float_flag_invalid) { + hw_exc |= EXC_M_INV; + } + if (exc & float_flag_divbyzero) { + hw_exc |= EXC_M_DZE; + } + if (exc & float_flag_overflow) { + hw_exc |= EXC_M_FOV; + } + if (exc & float_flag_underflow) { + hw_exc |= EXC_M_UNF; + } + if (exc & float_flag_inexact) { + hw_exc |= EXC_M_INE; + } + + arith_excp(env, retaddr, hw_exc, 1ull << regno); + } +} + +/* Raise exceptions for ieee fp insns without software completion. + In that case there are no exceptions that don't trap; the mask + doesn't apply. */ +void helper_fp_exc_raise(CPUAlphaState *env, uint32_t exc, uint32_t regno) +{ + inline_fp_exc_raise(env, GETPC(), exc, regno); +} + +/* Raise exceptions for ieee fp insns with software completion. */ +void helper_fp_exc_raise_s(CPUAlphaState *env, uint32_t exc, uint32_t regno) +{ + if (exc) { + env->fpcr_exc_status |= exc; + exc &= ~env->fpcr_exc_mask; + inline_fp_exc_raise(env, GETPC(), exc, regno); + } +} + +/* Input handing without software completion. Trap for all + non-finite numbers. */ +void helper_ieee_input(CPUAlphaState *env, uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + /* Denormals without DNZ set raise an exception. */ + if (frac != 0 && !env->fp_status.flush_inputs_to_zero) { + arith_excp(env, GETPC(), EXC_M_UNF, 0); + } + } else if (exp == 0x7ff) { + /* Infinity or NaN. */ + /* ??? I'm not sure these exception bit flags are correct. I do + know that the Linux kernel, at least, doesn't rely on them and + just emulates the insn to figure out what exception to use. */ + arith_excp(env, GETPC(), frac ? EXC_M_INV : EXC_M_FOV, 0); + } +} + +/* Similar, but does not trap for infinities. Used for comparisons. */ +void helper_ieee_input_cmp(CPUAlphaState *env, uint64_t val) +{ + uint32_t exp = (uint32_t)(val >> 52) & 0x7ff; + uint64_t frac = val & 0xfffffffffffffull; + + if (exp == 0) { + /* Denormals without DNZ set raise an exception. */ + if (frac != 0 && !env->fp_status.flush_inputs_to_zero) { + arith_excp(env, GETPC(), EXC_M_UNF, 0); + } + } else if (exp == 0x7ff && frac) { + /* NaN. */ + arith_excp(env, GETPC(), EXC_M_INV, 0); + } +} + +/* F floating (VAX) */ +static uint64_t float32_to_f(float32 fa) +{ + uint64_t r, exp, mant, sig; + CPU_FloatU a; + + a.f = fa; + sig = ((uint64_t)a.l & 0x80000000) << 32; + exp = (a.l >> 23) & 0xff; + mant = ((uint64_t)a.l & 0x007fffff) << 29; + + if (exp == 255) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 253) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; +} + +static float32 f_to_float32(CPUAlphaState *env, uintptr_t retaddr, uint64_t a) +{ + uint32_t exp, mant_sig; + CPU_FloatU r; + + exp = ((a >> 55) & 0x80) | ((a >> 52) & 0x7f); + mant_sig = ((a >> 32) & 0x80000000) | ((a >> 29) & 0x007fffff); + + if (unlikely(!exp && mant_sig)) { + /* Reserved operands / Dirty zero */ + dynamic_excp(env, retaddr, EXCP_OPCDEC, 0); + } + + if (exp < 3) { + /* Underflow */ + r.l = 0; + } else { + r.l = ((exp - 2) << 23) | mant_sig; + } + + return r.f; +} + +uint32_t helper_f_to_memory(uint64_t a) +{ + uint32_t r; + r = (a & 0x00001fffe0000000ull) >> 13; + r |= (a & 0x07ffe00000000000ull) >> 45; + r |= (a & 0xc000000000000000ull) >> 48; + return r; +} + +uint64_t helper_memory_to_f(uint32_t a) +{ + uint64_t r; + r = ((uint64_t)(a & 0x0000c000)) << 48; + r |= ((uint64_t)(a & 0x003fffff)) << 45; + r |= ((uint64_t)(a & 0xffff0000)) << 13; + if (!(a & 0x00004000)) { + r |= 0x7ll << 59; + } + return r; +} + +/* ??? Emulating VAX arithmetic with IEEE arithmetic is wrong. We should + either implement VAX arithmetic properly or just signal invalid opcode. */ + +uint64_t helper_addf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_subf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_mulf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_divf(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = f_to_float32(env, GETPC(), a); + fb = f_to_float32(env, GETPC(), b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_sqrtf(CPUAlphaState *env, uint64_t t) +{ + float32 ft, fr; + + ft = f_to_float32(env, GETPC(), t); + fr = float32_sqrt(ft, &FP_STATUS); + return float32_to_f(fr); +} + + +/* G floating (VAX) */ +static uint64_t float64_to_g(float64 fa) +{ + uint64_t r, exp, mant, sig; + CPU_DoubleU a; + + a.d = fa; + sig = a.ll & 0x8000000000000000ull; + exp = (a.ll >> 52) & 0x7ff; + mant = a.ll & 0x000fffffffffffffull; + + if (exp == 2047) { + /* NaN or infinity */ + r = 1; /* VAX dirty zero */ + } else if (exp == 0) { + if (mant == 0) { + /* Zero */ + r = 0; + } else { + /* Denormalized */ + r = sig | ((exp + 1) << 52) | mant; + } + } else { + if (exp >= 2045) { + /* Overflow */ + r = 1; /* VAX dirty zero */ + } else { + r = sig | ((exp + 2) << 52); + } + } + + return r; +} + +static float64 g_to_float64(CPUAlphaState *env, uintptr_t retaddr, uint64_t a) +{ + uint64_t exp, mant_sig; + CPU_DoubleU r; + + exp = (a >> 52) & 0x7ff; + mant_sig = a & 0x800fffffffffffffull; + + if (!exp && mant_sig) { + /* Reserved operands / Dirty zero */ + dynamic_excp(env, retaddr, EXCP_OPCDEC, 0); + } + + if (exp < 3) { + /* Underflow */ + r.ll = 0; + } else { + r.ll = ((exp - 2) << 52) | mant_sig; + } + + return r.d; +} + +uint64_t helper_g_to_memory(uint64_t a) +{ + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; +} + +uint64_t helper_memory_to_g(uint64_t a) +{ + uint64_t r; + r = (a & 0x000000000000ffffull) << 48; + r |= (a & 0x00000000ffff0000ull) << 16; + r |= (a & 0x0000ffff00000000ull) >> 16; + r |= (a & 0xffff000000000000ull) >> 48; + return r; +} + +uint64_t helper_addg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_subg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_mulg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_divg(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_g(fr); +} + +uint64_t helper_sqrtg(CPUAlphaState *env, uint64_t a) +{ + float64 fa, fr; + + fa = g_to_float64(env, GETPC(), a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_g(fr); +} + + +/* S floating (single) */ + +/* Taken from linux/arch/alpha/kernel/traps.c, s_mem_to_reg. */ +static inline uint64_t float32_to_s_int(uint32_t fi) +{ + uint32_t frac = fi & 0x7fffff; + uint32_t sign = fi >> 31; + uint32_t exp_msb = (fi >> 30) & 1; + uint32_t exp_low = (fi >> 23) & 0x7f; + uint32_t exp; + + exp = (exp_msb << 10) | exp_low; + if (exp_msb) { + if (exp_low == 0x7f) { + exp = 0x7ff; + } + } else { + if (exp_low != 0x00) { + exp |= 0x380; + } + } + + return (((uint64_t)sign << 63) + | ((uint64_t)exp << 52) + | ((uint64_t)frac << 29)); +} + +static inline uint64_t float32_to_s(float32 fa) +{ + CPU_FloatU a; + a.f = fa; + return float32_to_s_int(a.l); +} + +static inline uint32_t s_to_float32_int(uint64_t a) +{ + return ((a >> 32) & 0xc0000000) | ((a >> 29) & 0x3fffffff); +} + +static inline float32 s_to_float32(uint64_t a) +{ + CPU_FloatU r; + r.l = s_to_float32_int(a); + return r.f; +} + +uint32_t helper_s_to_memory(uint64_t a) +{ + return s_to_float32_int(a); +} + +uint64_t helper_memory_to_s(uint32_t a) +{ + return float32_to_s_int(a); +} + +uint64_t helper_adds(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_add(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_subs(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_sub(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_muls(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_mul(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_divs(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float32 fa, fb, fr; + + fa = s_to_float32(a); + fb = s_to_float32(b); + fr = float32_div(fa, fb, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_sqrts(CPUAlphaState *env, uint64_t a) +{ + float32 fa, fr; + + fa = s_to_float32(a); + fr = float32_sqrt(fa, &FP_STATUS); + return float32_to_s(fr); +} + + +/* T floating (double) */ +static inline float64 t_to_float64(uint64_t a) +{ + /* Memory format is the same as float64 */ + CPU_DoubleU r; + r.ll = a; + return r.d; +} + +static inline uint64_t float64_to_t(float64 fa) +{ + /* Memory format is the same as float64 */ + CPU_DoubleU r; + r.d = fa; + return r.ll; +} + +uint64_t helper_addt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_add(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_subt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_sub(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_mult(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_mul(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_divt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb, fr; + + fa = t_to_float64(a); + fb = t_to_float64(b); + fr = float64_div(fa, fb, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_sqrtt(CPUAlphaState *env, uint64_t a) +{ + float64 fa, fr; + + fa = t_to_float64(a); + fr = float64_sqrt(fa, &FP_STATUS); + return float64_to_t(fr); +} + +/* Comparisons */ +uint64_t helper_cmptun(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_unordered_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpteq(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_eq_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmptle(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_le(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmptlt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = t_to_float64(a); + fb = t_to_float64(b); + + if (float64_lt(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpgeq(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_eq_quiet(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpgle(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_le(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +uint64_t helper_cmpglt(CPUAlphaState *env, uint64_t a, uint64_t b) +{ + float64 fa, fb; + + fa = g_to_float64(env, GETPC(), a); + fb = g_to_float64(env, GETPC(), b); + + if (float64_lt(fa, fb, &FP_STATUS)) { + return 0x4000000000000000ULL; + } else { + return 0; + } +} + +/* Floating point format conversion */ +uint64_t helper_cvtts(CPUAlphaState *env, uint64_t a) +{ + float64 fa; + float32 fr; + + fa = t_to_float64(a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_s(fr); +} + +uint64_t helper_cvtst(CPUAlphaState *env, uint64_t a) +{ + float32 fa; + float64 fr; + + fa = s_to_float32(a); + fr = float32_to_float64(fa, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_cvtqs(CPUAlphaState *env, uint64_t a) +{ + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_s(fr); +} + +/* Implement float64 to uint64 conversion without saturation -- we must + supply the truncated result. This behaviour is used by the compiler + to get unsigned conversion for free with the same instruction. + + The VI flag is set when overflow or inexact exceptions should be raised. */ + +static inline uint64_t inline_cvttq(CPUAlphaState *env, uint64_t a, + int roundmode, int VI) +{ + uint64_t frac, ret = 0; + uint32_t exp, sign, exc = 0; + int shift; + + sign = (a >> 63); + exp = (uint32_t)(a >> 52) & 0x7ff; + frac = a & 0xfffffffffffffull; + + if (exp == 0) { + if (unlikely(frac != 0)) { + goto do_underflow; + } + } else if (exp == 0x7ff) { + exc = (frac ? float_flag_invalid : VI ? float_flag_overflow : 0); + } else { + /* Restore implicit bit. */ + frac |= 0x10000000000000ull; + + shift = exp - 1023 - 52; + if (shift >= 0) { + /* In this case the number is so large that we must shift + the fraction left. There is no rounding to do. */ + if (shift < 63) { + ret = frac << shift; + if (VI && (ret >> shift) != frac) { + exc = float_flag_overflow; + } + } + } else { + uint64_t round; + + /* In this case the number is smaller than the fraction as + represented by the 52 bit number. Here we must think + about rounding the result. Handle this by shifting the + fractional part of the number into the high bits of ROUND. + This will let us efficiently handle round-to-nearest. */ + shift = -shift; + if (shift < 63) { + ret = frac >> shift; + round = frac << (64 - shift); + } else { + /* The exponent is so small we shift out everything. + Leave a sticky bit for proper rounding below. */ + do_underflow: + round = 1; + } + + if (round) { + exc = (VI ? float_flag_inexact : 0); + switch (roundmode) { + case float_round_nearest_even: + if (round == (1ull << 63)) { + /* Fraction is exactly 0.5; round to even. */ + ret += (ret & 1); + } else if (round > (1ull << 63)) { + ret += 1; + } + break; + case float_round_to_zero: + break; + case float_round_up: + ret += 1 - sign; + break; + case float_round_down: + ret += sign; + break; + } + } + } + if (sign) { + ret = -ret; + } + } + if (unlikely(exc)) { + float_raise(exc, &FP_STATUS); + } + + return ret; +} + +uint64_t helper_cvttq(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, FP_STATUS.float_rounding_mode, 1); +} + +uint64_t helper_cvttq_c(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, float_round_to_zero, 0); +} + +uint64_t helper_cvttq_svic(CPUAlphaState *env, uint64_t a) +{ + return inline_cvttq(env, a, float_round_to_zero, 1); +} + +uint64_t helper_cvtqt(CPUAlphaState *env, uint64_t a) +{ + float64 fr = int64_to_float64(a, &FP_STATUS); + return float64_to_t(fr); +} + +uint64_t helper_cvtqf(CPUAlphaState *env, uint64_t a) +{ + float32 fr = int64_to_float32(a, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_cvtgf(CPUAlphaState *env, uint64_t a) +{ + float64 fa; + float32 fr; + + fa = g_to_float64(env, GETPC(), a); + fr = float64_to_float32(fa, &FP_STATUS); + return float32_to_f(fr); +} + +uint64_t helper_cvtgq(CPUAlphaState *env, uint64_t a) +{ + float64 fa = g_to_float64(env, GETPC(), a); + return float64_to_int64_round_to_zero(fa, &FP_STATUS); +} + +uint64_t helper_cvtqg(CPUAlphaState *env, uint64_t a) +{ + float64 fr; + fr = int64_to_float64(a, &FP_STATUS); + return float64_to_g(fr); +} diff --git a/target-alpha/helper.c b/target-alpha/helper.c new file mode 100644 index 000000000..81d4763b4 --- /dev/null +++ b/target-alpha/helper.c @@ -0,0 +1,532 @@ +/* + * Alpha emulation cpu helpers for qemu. + * + * Copyright (c) 2007 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 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 . + */ + +#include +#include +#include + +#include "cpu.h" +#include "softfloat.h" +#include "helper.h" + +uint64_t cpu_alpha_load_fpcr (CPUAlphaState *env) +{ + uint64_t r = 0; + uint8_t t; + + t = env->fpcr_exc_status; + if (t) { + r = FPCR_SUM; + if (t & float_flag_invalid) { + r |= FPCR_INV; + } + if (t & float_flag_divbyzero) { + r |= FPCR_DZE; + } + if (t & float_flag_overflow) { + r |= FPCR_OVF; + } + if (t & float_flag_underflow) { + r |= FPCR_UNF; + } + if (t & float_flag_inexact) { + r |= FPCR_INE; + } + } + + t = env->fpcr_exc_mask; + if (t & float_flag_invalid) { + r |= FPCR_INVD; + } + if (t & float_flag_divbyzero) { + r |= FPCR_DZED; + } + if (t & float_flag_overflow) { + r |= FPCR_OVFD; + } + if (t & float_flag_underflow) { + r |= FPCR_UNFD; + } + if (t & float_flag_inexact) { + r |= FPCR_INED; + } + + switch (env->fpcr_dyn_round) { + case float_round_nearest_even: + r |= FPCR_DYN_NORMAL; + break; + case float_round_down: + r |= FPCR_DYN_MINUS; + break; + case float_round_up: + r |= FPCR_DYN_PLUS; + break; + case float_round_to_zero: + r |= FPCR_DYN_CHOPPED; + break; + } + + if (env->fp_status.flush_inputs_to_zero) { + r |= FPCR_DNZ; + } + if (env->fpcr_dnod) { + r |= FPCR_DNOD; + } + if (env->fpcr_undz) { + r |= FPCR_UNDZ; + } + + return r; +} + +void cpu_alpha_store_fpcr (CPUAlphaState *env, uint64_t val) +{ + uint8_t t; + + t = 0; + if (val & FPCR_INV) { + t |= float_flag_invalid; + } + if (val & FPCR_DZE) { + t |= float_flag_divbyzero; + } + if (val & FPCR_OVF) { + t |= float_flag_overflow; + } + if (val & FPCR_UNF) { + t |= float_flag_underflow; + } + if (val & FPCR_INE) { + t |= float_flag_inexact; + } + env->fpcr_exc_status = t; + + t = 0; + if (val & FPCR_INVD) { + t |= float_flag_invalid; + } + if (val & FPCR_DZED) { + t |= float_flag_divbyzero; + } + if (val & FPCR_OVFD) { + t |= float_flag_overflow; + } + if (val & FPCR_UNFD) { + t |= float_flag_underflow; + } + if (val & FPCR_INED) { + t |= float_flag_inexact; + } + env->fpcr_exc_mask = t; + + switch (val & FPCR_DYN_MASK) { + case FPCR_DYN_CHOPPED: + t = float_round_to_zero; + break; + case FPCR_DYN_MINUS: + t = float_round_down; + break; + case FPCR_DYN_NORMAL: + t = float_round_nearest_even; + break; + case FPCR_DYN_PLUS: + t = float_round_up; + break; + } + env->fpcr_dyn_round = t; + + env->fpcr_dnod = (val & FPCR_DNOD) != 0; + env->fpcr_undz = (val & FPCR_UNDZ) != 0; + env->fpcr_flush_to_zero = env->fpcr_dnod & env->fpcr_undz; + env->fp_status.flush_inputs_to_zero = (val & FPCR_DNZ) != 0; +} + +uint64_t helper_load_fpcr(CPUAlphaState *env) +{ + return cpu_alpha_load_fpcr(env); +} + +void helper_store_fpcr(CPUAlphaState *env, uint64_t val) +{ + cpu_alpha_store_fpcr(env, val); +} + +#if defined(CONFIG_USER_ONLY) +int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong address, + int rw, int mmu_idx) +{ + env->exception_index = EXCP_MMFAULT; + env->trap_arg0 = address; + return 1; +} +#else +void swap_shadow_regs(CPUAlphaState *env) +{ + uint64_t i0, i1, i2, i3, i4, i5, i6, i7; + + i0 = env->ir[8]; + i1 = env->ir[9]; + i2 = env->ir[10]; + i3 = env->ir[11]; + i4 = env->ir[12]; + i5 = env->ir[13]; + i6 = env->ir[14]; + i7 = env->ir[25]; + + env->ir[8] = env->shadow[0]; + env->ir[9] = env->shadow[1]; + env->ir[10] = env->shadow[2]; + env->ir[11] = env->shadow[3]; + env->ir[12] = env->shadow[4]; + env->ir[13] = env->shadow[5]; + env->ir[14] = env->shadow[6]; + env->ir[25] = env->shadow[7]; + + env->shadow[0] = i0; + env->shadow[1] = i1; + env->shadow[2] = i2; + env->shadow[3] = i3; + env->shadow[4] = i4; + env->shadow[5] = i5; + env->shadow[6] = i6; + env->shadow[7] = i7; +} + +/* Returns the OSF/1 entMM failure indication, or -1 on success. */ +static int get_physical_address(CPUAlphaState *env, target_ulong addr, + int prot_need, int mmu_idx, + target_ulong *pphys, int *pprot) +{ + target_long saddr = addr; + target_ulong phys = 0; + target_ulong L1pte, L2pte, L3pte; + target_ulong pt, index; + int prot = 0; + int ret = MM_K_ACV; + + /* Ensure that the virtual address is properly sign-extended from + the last implemented virtual address bit. */ + if (saddr >> TARGET_VIRT_ADDR_SPACE_BITS != saddr >> 63) { + goto exit; + } + + /* Translate the superpage. */ + /* ??? When we do more than emulate Unix PALcode, we'll need to + determine which KSEG is actually active. */ + if (saddr < 0 && ((saddr >> 41) & 3) == 2) { + /* User-space cannot access KSEG addresses. */ + if (mmu_idx != MMU_KERNEL_IDX) { + goto exit; + } + + /* For the benefit of the Typhoon chipset, move bit 40 to bit 43. + We would not do this if the 48-bit KSEG is enabled. */ + phys = saddr & ((1ull << 40) - 1); + phys |= (saddr & (1ull << 40)) << 3; + + prot = PAGE_READ | PAGE_WRITE | PAGE_EXEC; + ret = -1; + goto exit; + } + + /* Interpret the page table exactly like PALcode does. */ + + pt = env->ptbr; + + /* L1 page table read. */ + index = (addr >> (TARGET_PAGE_BITS + 20)) & 0x3ff; + L1pte = ldq_phys(pt + index*8); + + if (unlikely((L1pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + if (unlikely((L1pte & PTE_KRE) == 0)) { + goto exit; + } + pt = L1pte >> 32 << TARGET_PAGE_BITS; + + /* L2 page table read. */ + index = (addr >> (TARGET_PAGE_BITS + 10)) & 0x3ff; + L2pte = ldq_phys(pt + index*8); + + if (unlikely((L2pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + if (unlikely((L2pte & PTE_KRE) == 0)) { + goto exit; + } + pt = L2pte >> 32 << TARGET_PAGE_BITS; + + /* L3 page table read. */ + index = (addr >> TARGET_PAGE_BITS) & 0x3ff; + L3pte = ldq_phys(pt + index*8); + + phys = L3pte >> 32 << TARGET_PAGE_BITS; + if (unlikely((L3pte & PTE_VALID) == 0)) { + ret = MM_K_TNV; + goto exit; + } + +#if PAGE_READ != 1 || PAGE_WRITE != 2 || PAGE_EXEC != 4 +# error page bits out of date +#endif + + /* Check access violations. */ + if (L3pte & (PTE_KRE << mmu_idx)) { + prot |= PAGE_READ | PAGE_EXEC; + } + if (L3pte & (PTE_KWE << mmu_idx)) { + prot |= PAGE_WRITE; + } + if (unlikely((prot & prot_need) == 0 && prot_need)) { + goto exit; + } + + /* Check fault-on-operation violations. */ + prot &= ~(L3pte >> 1); + ret = -1; + if (unlikely((prot & prot_need) == 0)) { + ret = (prot_need & PAGE_EXEC ? MM_K_FOE : + prot_need & PAGE_WRITE ? MM_K_FOW : + prot_need & PAGE_READ ? MM_K_FOR : -1); + } + + exit: + *pphys = phys; + *pprot = prot; + return ret; +} + +target_phys_addr_t cpu_get_phys_page_debug(CPUAlphaState *env, target_ulong addr) +{ + target_ulong phys; + int prot, fail; + + fail = get_physical_address(env, addr, 0, 0, &phys, &prot); + return (fail >= 0 ? -1 : phys); +} + +int cpu_alpha_handle_mmu_fault(CPUAlphaState *env, target_ulong addr, int rw, + int mmu_idx) +{ + target_ulong phys; + int prot, fail; + + fail = get_physical_address(env, addr, 1 << rw, mmu_idx, &phys, &prot); + if (unlikely(fail >= 0)) { + env->exception_index = EXCP_MMFAULT; + env->trap_arg0 = addr; + env->trap_arg1 = fail; + env->trap_arg2 = (rw == 2 ? -1 : rw); + return 1; + } + + tlb_set_page(env, addr & TARGET_PAGE_MASK, phys & TARGET_PAGE_MASK, + prot, mmu_idx, TARGET_PAGE_SIZE); + return 0; +} +#endif /* USER_ONLY */ + +void do_interrupt (CPUAlphaState *env) +{ + int i = env->exception_index; + + if (qemu_loglevel_mask(CPU_LOG_INT)) { + static int count; + const char *name = ""; + + switch (i) { + case EXCP_RESET: + name = "reset"; + break; + case EXCP_MCHK: + name = "mchk"; + break; + case EXCP_SMP_INTERRUPT: + name = "smp_interrupt"; + break; + case EXCP_CLK_INTERRUPT: + name = "clk_interrupt"; + break; + case EXCP_DEV_INTERRUPT: + name = "dev_interrupt"; + break; + case EXCP_MMFAULT: + name = "mmfault"; + break; + case EXCP_UNALIGN: + name = "unalign"; + break; + case EXCP_OPCDEC: + name = "opcdec"; + break; + case EXCP_ARITH: + name = "arith"; + break; + case EXCP_FEN: + name = "fen"; + break; + case EXCP_CALL_PAL: + name = "call_pal"; + break; + case EXCP_STL_C: + name = "stl_c"; + break; + case EXCP_STQ_C: + name = "stq_c"; + break; + } + qemu_log("INT %6d: %s(%#x) pc=%016" PRIx64 " sp=%016" PRIx64 "\n", + ++count, name, env->error_code, env->pc, env->ir[IR_SP]); + } + + env->exception_index = -1; + +#if !defined(CONFIG_USER_ONLY) + switch (i) { + case EXCP_RESET: + i = 0x0000; + break; + case EXCP_MCHK: + i = 0x0080; + break; + case EXCP_SMP_INTERRUPT: + i = 0x0100; + break; + case EXCP_CLK_INTERRUPT: + i = 0x0180; + break; + case EXCP_DEV_INTERRUPT: + i = 0x0200; + break; + case EXCP_MMFAULT: + i = 0x0280; + break; + case EXCP_UNALIGN: + i = 0x0300; + break; + case EXCP_OPCDEC: + i = 0x0380; + break; + case EXCP_ARITH: + i = 0x0400; + break; + case EXCP_FEN: + i = 0x0480; + break; + case EXCP_CALL_PAL: + i = env->error_code; + /* There are 64 entry points for both privileged and unprivileged, + with bit 0x80 indicating unprivileged. Each entry point gets + 64 bytes to do its job. */ + if (i & 0x80) { + i = 0x2000 + (i - 0x80) * 64; + } else { + i = 0x1000 + i * 64; + } + break; + default: + cpu_abort(env, "Unhandled CPU exception"); + } + + /* Remember where the exception happened. Emulate real hardware in + that the low bit of the PC indicates PALmode. */ + env->exc_addr = env->pc | env->pal_mode; + + /* Continue execution at the PALcode entry point. */ + env->pc = env->palbr + i; + + /* Switch to PALmode. */ + if (!env->pal_mode) { + env->pal_mode = 1; + swap_shadow_regs(env); + } +#endif /* !USER_ONLY */ +} + +void cpu_dump_state (CPUAlphaState *env, FILE *f, fprintf_function cpu_fprintf, + int flags) +{ + static const char *linux_reg_names[] = { + "v0 ", "t0 ", "t1 ", "t2 ", "t3 ", "t4 ", "t5 ", "t6 ", + "t7 ", "s0 ", "s1 ", "s2 ", "s3 ", "s4 ", "s5 ", "fp ", + "a0 ", "a1 ", "a2 ", "a3 ", "a4 ", "a5 ", "t8 ", "t9 ", + "t10", "t11", "ra ", "t12", "at ", "gp ", "sp ", "zero", + }; + int i; + + cpu_fprintf(f, " PC " TARGET_FMT_lx " PS %02x\n", + env->pc, env->ps); + for (i = 0; i < 31; i++) { + cpu_fprintf(f, "IR%02d %s " TARGET_FMT_lx " ", i, + linux_reg_names[i], env->ir[i]); + if ((i % 3) == 2) + cpu_fprintf(f, "\n"); + } + + cpu_fprintf(f, "lock_a " TARGET_FMT_lx " lock_v " TARGET_FMT_lx "\n", + env->lock_addr, env->lock_value); + + for (i = 0; i < 31; i++) { + cpu_fprintf(f, "FIR%02d " TARGET_FMT_lx " ", i, + *((uint64_t *)(&env->fir[i]))); + if ((i % 3) == 2) + cpu_fprintf(f, "\n"); + } + cpu_fprintf(f, "\n"); +} + +void do_restore_state(CPUAlphaState *env, uintptr_t retaddr) +{ + if (retaddr) { + TranslationBlock *tb = tb_find_pc(retaddr); + if (tb) { + cpu_restore_state(tb, env, retaddr); + } + } +} + +/* This should only be called from translate, via gen_excp. + We expect that ENV->PC has already been updated. */ +void QEMU_NORETURN helper_excp(CPUAlphaState *env, int excp, int error) +{ + env->exception_index = excp; + env->error_code = error; + cpu_loop_exit(env); +} + +/* This may be called from any of the helpers to set up EXCEPTION_INDEX. */ +void QEMU_NORETURN dynamic_excp(CPUAlphaState *env, uintptr_t retaddr, + int excp, int error) +{ + env->exception_index = excp; + env->error_code = error; + do_restore_state(env, retaddr); + cpu_loop_exit(env); +} + +void QEMU_NORETURN arith_excp(CPUAlphaState *env, uintptr_t retaddr, + int exc, uint64_t mask) +{ + env->trap_arg0 = exc; + env->trap_arg1 = mask; + dynamic_excp(env, retaddr, EXCP_ARITH, 0); +} diff --git a/target-alpha/helper.h b/target-alpha/helper.h new file mode 100644 index 000000000..a184def3e --- /dev/null +++ b/target-alpha/helper.h @@ -0,0 +1,122 @@ +#include "def-helper.h" + +DEF_HELPER_3(excp, noreturn, env, int, int) +DEF_HELPER_FLAGS_1(load_pcc, TCG_CALL_CONST | TCG_CALL_PURE, i64, env) + +DEF_HELPER_3(addqv, i64, env, i64, i64) +DEF_HELPER_3(addlv, i64, env, i64, i64) +DEF_HELPER_3(subqv, i64, env, i64, i64) +DEF_HELPER_3(sublv, i64, env, i64, i64) +DEF_HELPER_3(mullv, i64, env, i64, i64) +DEF_HELPER_3(mulqv, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(umulh, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_1(ctpop, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(ctlz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(cttz, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) + +DEF_HELPER_FLAGS_2(zap, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(zapnot, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(cmpbge, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) + +DEF_HELPER_FLAGS_2(minub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(minsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxub8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxsb8, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxuw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(maxsw4, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_2(perr, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64, i64) +DEF_HELPER_FLAGS_1(pklb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(pkwb, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(unpkbl, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(unpkbw, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) + +DEF_HELPER_FLAGS_1(load_fpcr, TCG_CALL_CONST | TCG_CALL_PURE, i64, env) +DEF_HELPER_FLAGS_2(store_fpcr, TCG_CALL_CONST, void, env, i64) + +DEF_HELPER_FLAGS_1(f_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) +DEF_HELPER_FLAGS_1(memory_to_f, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) +DEF_HELPER_FLAGS_3(addf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mulf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divf, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtf, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_1(g_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_1(memory_to_g, TCG_CALL_CONST | TCG_CALL_PURE, i64, i64) +DEF_HELPER_FLAGS_3(addg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mulg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divg, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtg, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_1(s_to_memory, TCG_CALL_CONST | TCG_CALL_PURE, i32, i64) +DEF_HELPER_FLAGS_1(memory_to_s, TCG_CALL_CONST | TCG_CALL_PURE, i64, i32) +DEF_HELPER_FLAGS_3(adds, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subs, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(muls, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divs, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrts, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_3(addt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(subt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(mult, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(divt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_2(sqrtt, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_3(cmptun, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpteq, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmptle, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmptlt, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpgeq, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpgle, TCG_CALL_CONST, i64, env, i64, i64) +DEF_HELPER_FLAGS_3(cmpglt, TCG_CALL_CONST, i64, env, i64, i64) + +DEF_HELPER_FLAGS_2(cvtts, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtst, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqs, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqt, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqf, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtgf, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtgq, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvtqg, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_2(cvttq, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvttq_c, TCG_CALL_CONST, i64, env, i64) +DEF_HELPER_FLAGS_2(cvttq_svic, TCG_CALL_CONST, i64, env, i64) + +DEF_HELPER_FLAGS_2(setroundmode, TCG_CALL_CONST, void, env, i32) +DEF_HELPER_FLAGS_2(setflushzero, TCG_CALL_CONST, void, env, i32) +DEF_HELPER_FLAGS_1(fp_exc_clear, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_1(fp_exc_get, TCG_CALL_CONST | TCG_CALL_PURE, i32, env) +DEF_HELPER_3(fp_exc_raise, void, env, i32, i32) +DEF_HELPER_3(fp_exc_raise_s, void, env, i32, i32) + +DEF_HELPER_2(ieee_input, void, env, i64) +DEF_HELPER_2(ieee_input_cmp, void, env, i64) + +#if !defined (CONFIG_USER_ONLY) +DEF_HELPER_2(hw_ret, void, env, i64) + +DEF_HELPER_1(ldl_phys, i64, i64) +DEF_HELPER_1(ldq_phys, i64, i64) +DEF_HELPER_2(ldl_l_phys, i64, env, i64) +DEF_HELPER_2(ldq_l_phys, i64, env, i64) +DEF_HELPER_2(stl_phys, void, i64, i64) +DEF_HELPER_2(stq_phys, void, i64, i64) +DEF_HELPER_3(stl_c_phys, i64, env, i64, i64) +DEF_HELPER_3(stq_c_phys, i64, env, i64, i64) + +DEF_HELPER_FLAGS_1(tbia, TCG_CALL_CONST, void, env) +DEF_HELPER_FLAGS_2(tbis, TCG_CALL_CONST, void, env, i64) + +DEF_HELPER_1(halt, void, i64); + +DEF_HELPER_FLAGS_0(get_time, TCG_CALL_CONST, i64) +DEF_HELPER_FLAGS_2(set_alarm, TCG_CALL_CONST, void, env, i64) +#endif + +#include "def-helper.h" diff --git a/target-alpha/int_helper.c b/target-alpha/int_helper.c new file mode 100644 index 000000000..1d832f0b5 --- /dev/null +++ b/target-alpha/int_helper.c @@ -0,0 +1,319 @@ +/* + * Helpers for integer and multimedia instructions. + * + * Copyright (c) 2007 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 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 . + */ + +#include "cpu.h" +#include "helper.h" +#include "host-utils.h" + + +uint64_t helper_umulh(uint64_t op1, uint64_t op2) +{ + uint64_t tl, th; + mulu64(&tl, &th, op1, op2); + return th; +} + +uint64_t helper_ctpop(uint64_t arg) +{ + return ctpop64(arg); +} + +uint64_t helper_ctlz(uint64_t arg) +{ + return clz64(arg); +} + +uint64_t helper_cttz(uint64_t arg) +{ + return ctz64(arg); +} + +static inline uint64_t byte_zap(uint64_t op, uint8_t mskb) +{ + uint64_t mask; + + mask = 0; + mask |= ((mskb >> 0) & 1) * 0x00000000000000FFULL; + mask |= ((mskb >> 1) & 1) * 0x000000000000FF00ULL; + mask |= ((mskb >> 2) & 1) * 0x0000000000FF0000ULL; + mask |= ((mskb >> 3) & 1) * 0x00000000FF000000ULL; + mask |= ((mskb >> 4) & 1) * 0x000000FF00000000ULL; + mask |= ((mskb >> 5) & 1) * 0x0000FF0000000000ULL; + mask |= ((mskb >> 6) & 1) * 0x00FF000000000000ULL; + mask |= ((mskb >> 7) & 1) * 0xFF00000000000000ULL; + + return op & ~mask; +} + +uint64_t helper_zap(uint64_t val, uint64_t mask) +{ + return byte_zap(val, mask); +} + +uint64_t helper_zapnot(uint64_t val, uint64_t mask) +{ + return byte_zap(val, ~mask); +} + +uint64_t helper_cmpbge(uint64_t op1, uint64_t op2) +{ + uint8_t opa, opb, res; + int i; + + res = 0; + for (i = 0; i < 8; i++) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) { + res |= 1 << i; + } + } + return res; +} + +uint64_t helper_minub8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_minsb8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_minuw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_minsw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa < opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_maxub8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_maxsb8(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int8_t opa, opb; + uint8_t opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 8); + } + return res; +} + +uint64_t helper_maxuw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint16_t opa, opb, opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_maxsw4(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + int16_t opa, opb; + uint16_t opr; + int i; + + for (i = 0; i < 4; ++i) { + opa = op1 >> (i * 16); + opb = op2 >> (i * 16); + opr = opa > opb ? opa : opb; + res |= (uint64_t)opr << (i * 16); + } + return res; +} + +uint64_t helper_perr(uint64_t op1, uint64_t op2) +{ + uint64_t res = 0; + uint8_t opa, opb, opr; + int i; + + for (i = 0; i < 8; ++i) { + opa = op1 >> (i * 8); + opb = op2 >> (i * 8); + if (opa >= opb) { + opr = opa - opb; + } else { + opr = opb - opa; + } + res += opr; + } + return res; +} + +uint64_t helper_pklb(uint64_t op1) +{ + return (op1 & 0xff) | ((op1 >> 24) & 0xff00); +} + +uint64_t helper_pkwb(uint64_t op1) +{ + return ((op1 & 0xff) + | ((op1 >> 8) & 0xff00) + | ((op1 >> 16) & 0xff0000) + | ((op1 >> 24) & 0xff000000)); +} + +uint64_t helper_unpkbl(uint64_t op1) +{ + return (op1 & 0xff) | ((op1 & 0xff00) << 24); +} + +uint64_t helper_unpkbw(uint64_t op1) +{ + return ((op1 & 0xff) + | ((op1 & 0xff00) << 8) + | ((op1 & 0xff0000) << 16) + | ((op1 & 0xff000000) << 24)); +} + +uint64_t helper_addqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tmp = op1; + op1 += op2; + if (unlikely((tmp ^ op2 ^ (-1ULL)) & (tmp ^ op1) & (1ULL << 63))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return op1; +} + +uint64_t helper_addlv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tmp = op1; + op1 = (uint32_t)(op1 + op2); + if (unlikely((tmp ^ op2 ^ (-1UL)) & (tmp ^ op1) & (1UL << 31))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return op1; +} + +uint64_t helper_subqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t res; + res = op1 - op2; + if (unlikely((op1 ^ op2) & (res ^ op1) & (1ULL << 63))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return res; +} + +uint64_t helper_sublv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint32_t res; + res = op1 - op2; + if (unlikely((op1 ^ op2) & (res ^ op1) & (1UL << 31))) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return res; +} + +uint64_t helper_mullv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + int64_t res = (int64_t)op1 * (int64_t)op2; + + if (unlikely((int32_t)res != res)) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return (int64_t)((int32_t)res); +} + +uint64_t helper_mulqv(CPUAlphaState *env, uint64_t op1, uint64_t op2) +{ + uint64_t tl, th; + + muls64(&tl, &th, op1, op2); + /* If th != 0 && th != -1, then we had an overflow */ + if (unlikely((th + 1) > 1)) { + arith_excp(env, GETPC(), EXC_M_IOV, 0); + } + return tl; +} diff --git a/target-alpha/machine.c b/target-alpha/machine.c new file mode 100644 index 000000000..1c9edd12c --- /dev/null +++ b/target-alpha/machine.c @@ -0,0 +1,87 @@ +#include "hw/hw.h" +#include "hw/boards.h" + +static int get_fpcr(QEMUFile *f, void *opaque, size_t size) +{ + CPUAlphaState *env = opaque; + cpu_alpha_store_fpcr(env, qemu_get_be64(f)); + return 0; +} + +static void put_fpcr(QEMUFile *f, void *opaque, size_t size) +{ + CPUAlphaState *env = opaque; + qemu_put_be64(f, cpu_alpha_load_fpcr(env)); +} + +static const VMStateInfo vmstate_fpcr = { + .name = "fpcr", + .get = get_fpcr, + .put = put_fpcr, +}; + +static VMStateField vmstate_cpu_fields[] = { + VMSTATE_UINTTL_ARRAY(ir, CPUAlphaState, 31), + VMSTATE_UINTTL_ARRAY(fir, CPUAlphaState, 31), + /* Save the architecture value of the fpcr, not the internally + expanded version. Since this architecture value does not + exist in memory to be stored, this requires a but of hoop + jumping. We want OFFSET=0 so that we effectively pass ENV + to the helper functions, and we need to fill in the name by + hand since there's no field of that name. */ + { + .name = "fpcr", + .version_id = 0, + .size = sizeof(uint64_t), + .info = &vmstate_fpcr, + .flags = VMS_SINGLE, + .offset = 0 + }, + VMSTATE_UINTTL(pc, CPUAlphaState), + VMSTATE_UINTTL(unique, CPUAlphaState), + VMSTATE_UINTTL(lock_addr, CPUAlphaState), + VMSTATE_UINTTL(lock_value, CPUAlphaState), + /* Note that lock_st_addr is not saved; it is a temporary + used during the execution of the st[lq]_c insns. */ + + VMSTATE_UINT8(ps, CPUAlphaState), + VMSTATE_UINT8(intr_flag, CPUAlphaState), + VMSTATE_UINT8(pal_mode, CPUAlphaState), + VMSTATE_UINT8(fen, CPUAlphaState), + + VMSTATE_UINT32(pcc_ofs, CPUAlphaState), + + VMSTATE_UINTTL(trap_arg0, CPUAlphaState), + VMSTATE_UINTTL(trap_arg1, CPUAlphaState), + VMSTATE_UINTTL(trap_arg2, CPUAlphaState), + + VMSTATE_UINTTL(exc_addr, CPUAlphaState), + VMSTATE_UINTTL(palbr, CPUAlphaState), + VMSTATE_UINTTL(ptbr, CPUAlphaState), + VMSTATE_UINTTL(vptptr, CPUAlphaState), + VMSTATE_UINTTL(sysval, CPUAlphaState), + VMSTATE_UINTTL(usp, CPUAlphaState), + + VMSTATE_UINTTL_ARRAY(shadow, CPUAlphaState, 8), + VMSTATE_UINTTL_ARRAY(scratch, CPUAlphaState, 24), + + VMSTATE_END_OF_LIST() +}; + +static const VMStateDescription vmstate_cpu = { + .name = "cpu", + .version_id = 1, + .minimum_version_id = 1, + .minimum_version_id_old = 1, + .fields = vmstate_cpu_fields, +}; + +void cpu_save(QEMUFile *f, void *opaque) +{ + vmstate_save_state(f, &vmstate_cpu, opaque); +} + +int cpu_load(QEMUFile *f, void *opaque, int version_id) +{ + return vmstate_load_state(f, &vmstate_cpu, opaque, version_id); +} diff --git a/target-alpha/mem_helper.c b/target-alpha/mem_helper.c new file mode 100644 index 000000000..87cada48f --- /dev/null +++ b/target-alpha/mem_helper.c @@ -0,0 +1,151 @@ +/* + * Helpers for loads and stores + * + * Copyright (c) 2007 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 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 . + */ + +#include "cpu.h" +#include "helper.h" + + +/* Softmmu support */ +#ifndef CONFIG_USER_ONLY + +uint64_t helper_ldl_phys(uint64_t p) +{ + return (int32_t)ldl_phys(p); +} + +uint64_t helper_ldq_phys(uint64_t p) +{ + return ldq_phys(p); +} + +uint64_t helper_ldl_l_phys(CPUAlphaState *env, uint64_t p) +{ + env->lock_addr = p; + return env->lock_value = (int32_t)ldl_phys(p); +} + +uint64_t helper_ldq_l_phys(CPUAlphaState *env, uint64_t p) +{ + env->lock_addr = p; + return env->lock_value = ldq_phys(p); +} + +void helper_stl_phys(uint64_t p, uint64_t v) +{ + stl_phys(p, v); +} + +void helper_stq_phys(uint64_t p, uint64_t v) +{ + stq_phys(p, v); +} + +uint64_t helper_stl_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) +{ + uint64_t ret = 0; + + if (p == env->lock_addr) { + int32_t old = ldl_phys(p); + if (old == (int32_t)env->lock_value) { + stl_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; + + return ret; +} + +uint64_t helper_stq_c_phys(CPUAlphaState *env, uint64_t p, uint64_t v) +{ + uint64_t ret = 0; + + if (p == env->lock_addr) { + uint64_t old = ldq_phys(p); + if (old == env->lock_value) { + stq_phys(p, v); + ret = 1; + } + } + env->lock_addr = -1; + + return ret; +} + +static void do_unaligned_access(CPUAlphaState *env, target_ulong addr, + int is_write, int is_user, uintptr_t retaddr) +{ + uint64_t pc; + uint32_t insn; + + do_restore_state(env, retaddr); + + pc = env->pc; + insn = cpu_ldl_code(env, pc); + + env->trap_arg0 = addr; + env->trap_arg1 = insn >> 26; /* opcode */ + env->trap_arg2 = (insn >> 21) & 31; /* dest regno */ + env->exception_index = EXCP_UNALIGN; + env->error_code = 0; + cpu_loop_exit(env); +} + +void cpu_unassigned_access(CPUAlphaState *env, target_phys_addr_t addr, + int is_write, int is_exec, int unused, int size) +{ + env->trap_arg0 = addr; + env->trap_arg1 = is_write; + dynamic_excp(env, 0, EXCP_MCHK, 0); +} + +#include "softmmu_exec.h" + +#define MMUSUFFIX _mmu +#define ALIGNED_ONLY + +#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(CPUAlphaState *env, target_ulong addr, int is_write, + int mmu_idx, uintptr_t retaddr) +{ + int ret; + + ret = cpu_alpha_handle_mmu_fault(env, addr, is_write, mmu_idx); + if (unlikely(ret != 0)) { + do_restore_state(env, retaddr); + /* Exception index and error code are already set */ + cpu_loop_exit(env); + } +} +#endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/sys_helper.c b/target-alpha/sys_helper.c new file mode 100644 index 000000000..40ca49c88 --- /dev/null +++ b/target-alpha/sys_helper.c @@ -0,0 +1,87 @@ +/* + * Helpers for system instructions. + * + * Copyright (c) 2007 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 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 . + */ + +#include "cpu.h" +#include "helper.h" +#include "sysemu.h" +#include "qemu-timer.h" + + +uint64_t helper_load_pcc(CPUAlphaState *env) +{ +#ifndef CONFIG_USER_ONLY + /* In system mode we have access to a decent high-resolution clock. + In order to make OS-level time accounting work with the RPCC, + present it with a well-timed clock fixed at 250MHz. */ + return (((uint64_t)env->pcc_ofs << 32) + | (uint32_t)(qemu_get_clock_ns(vm_clock) >> 2)); +#else + /* In user-mode, vm_clock doesn't exist. Just pass through the host cpu + clock ticks. Also, don't bother taking PCC_OFS into account. */ + return (uint32_t)cpu_get_real_ticks(); +#endif +} + +/* PALcode support special instructions */ +#ifndef CONFIG_USER_ONLY +void helper_hw_ret(CPUAlphaState *env, uint64_t a) +{ + env->pc = a & ~3; + env->intr_flag = 0; + env->lock_addr = -1; + if ((a & 1) == 0) { + env->pal_mode = 0; + swap_shadow_regs(env); + } +} + +void helper_tbia(CPUAlphaState *env) +{ + tlb_flush(env, 1); +} + +void helper_tbis(CPUAlphaState *env, uint64_t p) +{ + tlb_flush_page(env, p); +} + +void helper_halt(uint64_t restart) +{ + if (restart) { + qemu_system_reset_request(); + } else { + qemu_system_shutdown_request(); + } +} + +uint64_t helper_get_time(void) +{ + return qemu_get_clock_ns(rtc_clock); +} + +void helper_set_alarm(CPUAlphaState *env, uint64_t expire) +{ + if (expire) { + env->alarm_expire = expire; + qemu_mod_timer(env->alarm_timer, expire); + } else { + qemu_del_timer(env->alarm_timer); + } +} +#endif /* CONFIG_USER_ONLY */ diff --git a/target-alpha/translate.c b/target-alpha/translate.c new file mode 100644 index 000000000..12de6a3fb --- /dev/null +++ b/target-alpha/translate.c @@ -0,0 +1,3560 @@ +/* + * Alpha emulation cpu translation for qemu. + * + * Copyright (c) 2007 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 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 . + */ + +#include "cpu.h" +#include "disas.h" +#include "host-utils.h" +#include "tcg-op.h" + +#include "helper.h" +#define GEN_HELPER 1 +#include "helper.h" + +#undef ALPHA_DEBUG_DISAS +#define CONFIG_SOFTFLOAT_INLINE + +#ifdef ALPHA_DEBUG_DISAS +# define LOG_DISAS(...) qemu_log_mask(CPU_LOG_TB_IN_ASM, ## __VA_ARGS__) +#else +# define LOG_DISAS(...) do { } while (0) +#endif + +typedef struct DisasContext DisasContext; +struct DisasContext { + struct TranslationBlock *tb; + CPUAlphaState *env; + uint64_t pc; + int mem_idx; + + /* Current rounding mode for this TB. */ + int tb_rm; + /* Current flush-to-zero setting for this TB. */ + int tb_ftz; +}; + +/* Return values from translate_one, indicating the state of the TB. + Note that zero indicates that we are not exiting the TB. */ + +typedef enum { + NO_EXIT, + + /* We have emitted one or more goto_tb. No fixup required. */ + EXIT_GOTO_TB, + + /* We are not using a goto_tb (for whatever reason), but have updated + the PC (for whatever reason), so there's no need to do it again on + exiting the TB. */ + EXIT_PC_UPDATED, + + /* We are exiting the TB, but have neither emitted a goto_tb, nor + updated the PC for the next instruction to be executed. */ + EXIT_PC_STALE, + + /* We are ending the TB with a noreturn function call, e.g. longjmp. + No following code will be executed. */ + EXIT_NORETURN, +} ExitStatus; + +/* global register indexes */ +static TCGv_ptr cpu_env; +static TCGv cpu_ir[31]; +static TCGv cpu_fir[31]; +static TCGv cpu_pc; +static TCGv cpu_lock_addr; +static TCGv cpu_lock_st_addr; +static TCGv cpu_lock_value; +static TCGv cpu_unique; +#ifndef CONFIG_USER_ONLY +static TCGv cpu_sysval; +static TCGv cpu_usp; +#endif + +/* register names */ +static char cpu_reg_names[10*4+21*5 + 10*5+21*6]; + +#include "gen-icount.h" + +static void alpha_translate_init(void) +{ + int i; + char *p; + static int done_init = 0; + + if (done_init) + return; + + cpu_env = tcg_global_reg_new_ptr(TCG_AREG0, "env"); + + p = cpu_reg_names; + for (i = 0; i < 31; i++) { + sprintf(p, "ir%d", i); + cpu_ir[i] = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, ir[i]), p); + p += (i < 10) ? 4 : 5; + + sprintf(p, "fir%d", i); + cpu_fir[i] = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, fir[i]), p); + p += (i < 10) ? 5 : 6; + } + + cpu_pc = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, pc), "pc"); + + cpu_lock_addr = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, lock_addr), + "lock_addr"); + cpu_lock_st_addr = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, lock_st_addr), + "lock_st_addr"); + cpu_lock_value = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, lock_value), + "lock_value"); + + cpu_unique = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, unique), "unique"); +#ifndef CONFIG_USER_ONLY + cpu_sysval = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, sysval), "sysval"); + cpu_usp = tcg_global_mem_new_i64(TCG_AREG0, + offsetof(CPUAlphaState, usp), "usp"); +#endif + + /* register helpers */ +#define GEN_HELPER 2 +#include "helper.h" + + done_init = 1; +} + +static void gen_excp_1(int exception, int error_code) +{ + TCGv_i32 tmp1, tmp2; + + tmp1 = tcg_const_i32(exception); + tmp2 = tcg_const_i32(error_code); + gen_helper_excp(cpu_env, tmp1, tmp2); + tcg_temp_free_i32(tmp2); + tcg_temp_free_i32(tmp1); +} + +static ExitStatus gen_excp(DisasContext *ctx, int exception, int error_code) +{ + tcg_gen_movi_i64(cpu_pc, ctx->pc); + gen_excp_1(exception, error_code); + return EXIT_NORETURN; +} + +static inline ExitStatus gen_invalid(DisasContext *ctx) +{ + return gen_excp(ctx, EXCP_OPCDEC, 0); +} + +static inline void gen_qemu_ldf(TCGv t0, TCGv t1, int flags) +{ + TCGv tmp = tcg_temp_new(); + TCGv_i32 tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, t1, flags); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_memory_to_f(t0, tmp32); + tcg_temp_free_i32(tmp32); + tcg_temp_free(tmp); +} + +static inline void gen_qemu_ldg(TCGv t0, TCGv t1, int flags) +{ + TCGv tmp = tcg_temp_new(); + tcg_gen_qemu_ld64(tmp, t1, flags); + gen_helper_memory_to_g(t0, tmp); + tcg_temp_free(tmp); +} + +static inline void gen_qemu_lds(TCGv t0, TCGv t1, int flags) +{ + TCGv tmp = tcg_temp_new(); + TCGv_i32 tmp32 = tcg_temp_new_i32(); + tcg_gen_qemu_ld32u(tmp, t1, flags); + tcg_gen_trunc_i64_i32(tmp32, tmp); + gen_helper_memory_to_s(t0, tmp32); + tcg_temp_free_i32(tmp32); + tcg_temp_free(tmp); +} + +static inline void gen_qemu_ldl_l(TCGv t0, TCGv t1, int flags) +{ + tcg_gen_qemu_ld32s(t0, t1, flags); + tcg_gen_mov_i64(cpu_lock_addr, t1); + tcg_gen_mov_i64(cpu_lock_value, t0); +} + +static inline void gen_qemu_ldq_l(TCGv t0, TCGv t1, int flags) +{ + tcg_gen_qemu_ld64(t0, t1, flags); + tcg_gen_mov_i64(cpu_lock_addr, t1); + tcg_gen_mov_i64(cpu_lock_value, t0); +} + +static inline void gen_load_mem(DisasContext *ctx, + void (*tcg_gen_qemu_load)(TCGv t0, TCGv t1, + int flags), + int ra, int rb, int32_t disp16, int fp, + int clear) +{ + TCGv addr, va; + + /* LDQ_U with ra $31 is UNOP. Other various loads are forms of + prefetches, which we can treat as nops. No worries about + missed exceptions here. */ + if (unlikely(ra == 31)) { + return; + } + + addr = tcg_temp_new(); + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); + if (clear) { + tcg_gen_andi_i64(addr, addr, ~0x7); + } + } else { + if (clear) { + disp16 &= ~0x7; + } + tcg_gen_movi_i64(addr, disp16); + } + + va = (fp ? cpu_fir[ra] : cpu_ir[ra]); + tcg_gen_qemu_load(va, addr, ctx->mem_idx); + + tcg_temp_free(addr); +} + +static inline void gen_qemu_stf(TCGv t0, TCGv t1, int flags) +{ + TCGv_i32 tmp32 = tcg_temp_new_i32(); + TCGv tmp = tcg_temp_new(); + gen_helper_f_to_memory(tmp32, t0); + tcg_gen_extu_i32_i64(tmp, tmp32); + tcg_gen_qemu_st32(tmp, t1, flags); + tcg_temp_free(tmp); + tcg_temp_free_i32(tmp32); +} + +static inline void gen_qemu_stg(TCGv t0, TCGv t1, int flags) +{ + TCGv tmp = tcg_temp_new(); + gen_helper_g_to_memory(tmp, t0); + tcg_gen_qemu_st64(tmp, t1, flags); + tcg_temp_free(tmp); +} + +static inline void gen_qemu_sts(TCGv t0, TCGv t1, int flags) +{ + TCGv_i32 tmp32 = tcg_temp_new_i32(); + TCGv tmp = tcg_temp_new(); + gen_helper_s_to_memory(tmp32, t0); + tcg_gen_extu_i32_i64(tmp, tmp32); + tcg_gen_qemu_st32(tmp, t1, flags); + tcg_temp_free(tmp); + tcg_temp_free_i32(tmp32); +} + +static inline void gen_store_mem(DisasContext *ctx, + void (*tcg_gen_qemu_store)(TCGv t0, TCGv t1, + int flags), + int ra, int rb, int32_t disp16, int fp, + int clear) +{ + TCGv addr, va; + + addr = tcg_temp_new(); + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); + if (clear) { + tcg_gen_andi_i64(addr, addr, ~0x7); + } + } else { + if (clear) { + disp16 &= ~0x7; + } + tcg_gen_movi_i64(addr, disp16); + } + + if (ra == 31) { + va = tcg_const_i64(0); + } else { + va = (fp ? cpu_fir[ra] : cpu_ir[ra]); + } + tcg_gen_qemu_store(va, addr, ctx->mem_idx); + + tcg_temp_free(addr); + if (ra == 31) { + tcg_temp_free(va); + } +} + +static ExitStatus gen_store_conditional(DisasContext *ctx, int ra, int rb, + int32_t disp16, int quad) +{ + TCGv addr; + + if (ra == 31) { + /* ??? Don't bother storing anything. The user can't tell + the difference, since the zero register always reads zero. */ + return NO_EXIT; + } + +#if defined(CONFIG_USER_ONLY) + addr = cpu_lock_st_addr; +#else + addr = tcg_temp_local_new(); +#endif + + if (rb != 31) { + tcg_gen_addi_i64(addr, cpu_ir[rb], disp16); + } else { + tcg_gen_movi_i64(addr, disp16); + } + +#if defined(CONFIG_USER_ONLY) + /* ??? This is handled via a complicated version of compare-and-swap + in the cpu_loop. Hopefully one day we'll have a real CAS opcode + in TCG so that this isn't necessary. */ + return gen_excp(ctx, quad ? EXCP_STQ_C : EXCP_STL_C, ra); +#else + /* ??? In system mode we are never multi-threaded, so CAS can be + implemented via a non-atomic load-compare-store sequence. */ + { + int lab_fail, lab_done; + TCGv val; + + lab_fail = gen_new_label(); + lab_done = gen_new_label(); + tcg_gen_brcond_i64(TCG_COND_NE, addr, cpu_lock_addr, lab_fail); + + val = tcg_temp_new(); + if (quad) { + tcg_gen_qemu_ld64(val, addr, ctx->mem_idx); + } else { + tcg_gen_qemu_ld32s(val, addr, ctx->mem_idx); + } + tcg_gen_brcond_i64(TCG_COND_NE, val, cpu_lock_value, lab_fail); + + if (quad) { + tcg_gen_qemu_st64(cpu_ir[ra], addr, ctx->mem_idx); + } else { + tcg_gen_qemu_st32(cpu_ir[ra], addr, ctx->mem_idx); + } + tcg_gen_movi_i64(cpu_ir[ra], 1); + tcg_gen_br(lab_done); + + gen_set_label(lab_fail); + tcg_gen_movi_i64(cpu_ir[ra], 0); + + gen_set_label(lab_done); + tcg_gen_movi_i64(cpu_lock_addr, -1); + + tcg_temp_free(addr); + return NO_EXIT; + } +#endif +} + +static int use_goto_tb(DisasContext *ctx, uint64_t dest) +{ + /* Check for the dest on the same page as the start of the TB. We + also want to suppress goto_tb in the case of single-steping and IO. */ + return (((ctx->tb->pc ^ dest) & TARGET_PAGE_MASK) == 0 + && !ctx->env->singlestep_enabled + && !(ctx->tb->cflags & CF_LAST_IO)); +} + +static ExitStatus gen_bdirect(DisasContext *ctx, int ra, int32_t disp) +{ + uint64_t dest = ctx->pc + (disp << 2); + + if (ra != 31) { + tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); + } + + /* Notice branch-to-next; used to initialize RA with the PC. */ + if (disp == 0) { + return 0; + } else if (use_goto_tb(ctx, dest)) { + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_exit_tb((tcg_target_long)ctx->tb); + return EXIT_GOTO_TB; + } else { + tcg_gen_movi_i64(cpu_pc, dest); + return EXIT_PC_UPDATED; + } +} + +static ExitStatus gen_bcond_internal(DisasContext *ctx, TCGCond cond, + TCGv cmp, int32_t disp) +{ + uint64_t dest = ctx->pc + (disp << 2); + int lab_true = gen_new_label(); + + if (use_goto_tb(ctx, dest)) { + tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); + + tcg_gen_goto_tb(0); + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tcg_gen_exit_tb((tcg_target_long)ctx->tb); + + gen_set_label(lab_true); + tcg_gen_goto_tb(1); + tcg_gen_movi_i64(cpu_pc, dest); + tcg_gen_exit_tb((tcg_target_long)ctx->tb + 1); + + return EXIT_GOTO_TB; + } else { + int lab_over = gen_new_label(); + + /* ??? Consider using either + movi pc, next + addi tmp, pc, disp + movcond pc, cond, 0, tmp, pc + or + setcond tmp, cond, 0 + movi pc, next + neg tmp, tmp + andi tmp, tmp, disp + add pc, pc, tmp + The current diamond subgraph surely isn't efficient. */ + + tcg_gen_brcondi_i64(cond, cmp, 0, lab_true); + tcg_gen_movi_i64(cpu_pc, ctx->pc); + tcg_gen_br(lab_over); + gen_set_label(lab_true); + tcg_gen_movi_i64(cpu_pc, dest); + gen_set_label(lab_over); + + return EXIT_PC_UPDATED; + } +} + +static ExitStatus gen_bcond(DisasContext *ctx, TCGCond cond, int ra, + int32_t disp, int mask) +{ + TCGv cmp_tmp; + + if (unlikely(ra == 31)) { + cmp_tmp = tcg_const_i64(0); + } else { + cmp_tmp = tcg_temp_new(); + if (mask) { + tcg_gen_andi_i64(cmp_tmp, cpu_ir[ra], 1); + } else { + tcg_gen_mov_i64(cmp_tmp, cpu_ir[ra]); + } + } + + return gen_bcond_internal(ctx, cond, cmp_tmp, disp); +} + +/* Fold -0.0 for comparison with COND. */ + +static void gen_fold_mzero(TCGCond cond, TCGv dest, TCGv src) +{ + uint64_t mzero = 1ull << 63; + + switch (cond) { + case TCG_COND_LE: + case TCG_COND_GT: + /* For <= or >, the -0.0 value directly compares the way we want. */ + tcg_gen_mov_i64(dest, src); + break; + + case TCG_COND_EQ: + case TCG_COND_NE: + /* For == or !=, we can simply mask off the sign bit and compare. */ + tcg_gen_andi_i64(dest, src, mzero - 1); + break; + + case TCG_COND_GE: + case TCG_COND_LT: + /* For >= or <, map -0.0 to +0.0 via comparison and mask. */ + tcg_gen_setcondi_i64(TCG_COND_NE, dest, src, mzero); + tcg_gen_neg_i64(dest, dest); + tcg_gen_and_i64(dest, dest, src); + break; + + default: + abort(); + } +} + +static ExitStatus gen_fbcond(DisasContext *ctx, TCGCond cond, int ra, + int32_t disp) +{ + TCGv cmp_tmp; + + if (unlikely(ra == 31)) { + /* Very uncommon case, but easier to optimize it to an integer + comparison than continuing with the floating point comparison. */ + return gen_bcond(ctx, cond, ra, disp, 0); + } + + cmp_tmp = tcg_temp_new(); + gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]); + return gen_bcond_internal(ctx, cond, cmp_tmp, disp); +} + +static void gen_cmov(TCGCond cond, int ra, int rb, int rc, + int islit, uint8_t lit, int mask) +{ + TCGCond inv_cond = tcg_invert_cond(cond); + int l1; + + if (unlikely(rc == 31)) + return; + + l1 = gen_new_label(); + + if (ra != 31) { + if (mask) { + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, cpu_ir[ra], 1); + tcg_gen_brcondi_i64(inv_cond, tmp, 0, l1); + tcg_temp_free(tmp); + } else + tcg_gen_brcondi_i64(inv_cond, cpu_ir[ra], 0, l1); + } else { + /* Very uncommon case - Do not bother to optimize. */ + TCGv tmp = tcg_const_i64(0); + tcg_gen_brcondi_i64(inv_cond, tmp, 0, l1); + tcg_temp_free(tmp); + } + + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + gen_set_label(l1); +} + +static void gen_fcmov(TCGCond cond, int ra, int rb, int rc) +{ + TCGv cmp_tmp; + int l1; + + if (unlikely(rc == 31)) { + return; + } + + cmp_tmp = tcg_temp_new(); + if (unlikely(ra == 31)) { + tcg_gen_movi_i64(cmp_tmp, 0); + } else { + gen_fold_mzero(cond, cmp_tmp, cpu_fir[ra]); + } + + l1 = gen_new_label(); + tcg_gen_brcondi_i64(tcg_invert_cond(cond), cmp_tmp, 0, l1); + tcg_temp_free(cmp_tmp); + + if (rb != 31) + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[rb]); + else + tcg_gen_movi_i64(cpu_fir[rc], 0); + gen_set_label(l1); +} + +#define QUAL_RM_N 0x080 /* Round mode nearest even */ +#define QUAL_RM_C 0x000 /* Round mode chopped */ +#define QUAL_RM_M 0x040 /* Round mode minus infinity */ +#define QUAL_RM_D 0x0c0 /* Round mode dynamic */ +#define QUAL_RM_MASK 0x0c0 + +#define QUAL_U 0x100 /* Underflow enable (fp output) */ +#define QUAL_V 0x100 /* Overflow enable (int output) */ +#define QUAL_S 0x400 /* Software completion enable */ +#define QUAL_I 0x200 /* Inexact detection enable */ + +static void gen_qual_roundmode(DisasContext *ctx, int fn11) +{ + TCGv_i32 tmp; + + fn11 &= QUAL_RM_MASK; + if (fn11 == ctx->tb_rm) { + return; + } + ctx->tb_rm = fn11; + + tmp = tcg_temp_new_i32(); + switch (fn11) { + case QUAL_RM_N: + tcg_gen_movi_i32(tmp, float_round_nearest_even); + break; + case QUAL_RM_C: + tcg_gen_movi_i32(tmp, float_round_to_zero); + break; + case QUAL_RM_M: + tcg_gen_movi_i32(tmp, float_round_down); + break; + case QUAL_RM_D: + tcg_gen_ld8u_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fpcr_dyn_round)); + break; + } + +#if defined(CONFIG_SOFTFLOAT_INLINE) + /* ??? The "softfloat.h" interface is to call set_float_rounding_mode. + With CONFIG_SOFTFLOAT that expands to an out-of-line call that just + sets the one field. */ + tcg_gen_st8_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fp_status.float_rounding_mode)); +#else + gen_helper_setroundmode(tmp); +#endif + + tcg_temp_free_i32(tmp); +} + +static void gen_qual_flushzero(DisasContext *ctx, int fn11) +{ + TCGv_i32 tmp; + + fn11 &= QUAL_U; + if (fn11 == ctx->tb_ftz) { + return; + } + ctx->tb_ftz = fn11; + + tmp = tcg_temp_new_i32(); + if (fn11) { + /* Underflow is enabled, use the FPCR setting. */ + tcg_gen_ld8u_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fpcr_flush_to_zero)); + } else { + /* Underflow is disabled, force flush-to-zero. */ + tcg_gen_movi_i32(tmp, 1); + } + +#if defined(CONFIG_SOFTFLOAT_INLINE) + tcg_gen_st8_i32(tmp, cpu_env, + offsetof(CPUAlphaState, fp_status.flush_to_zero)); +#else + gen_helper_setflushzero(tmp); +#endif + + tcg_temp_free_i32(tmp); +} + +static TCGv gen_ieee_input(int reg, int fn11, int is_cmp) +{ + TCGv val; + if (reg == 31) { + val = tcg_const_i64(0); + } else { + if ((fn11 & QUAL_S) == 0) { + if (is_cmp) { + gen_helper_ieee_input_cmp(cpu_env, cpu_fir[reg]); + } else { + gen_helper_ieee_input(cpu_env, cpu_fir[reg]); + } + } + val = tcg_temp_new(); + tcg_gen_mov_i64(val, cpu_fir[reg]); + } + return val; +} + +static void gen_fp_exc_clear(void) +{ +#if defined(CONFIG_SOFTFLOAT_INLINE) + TCGv_i32 zero = tcg_const_i32(0); + tcg_gen_st8_i32(zero, cpu_env, + offsetof(CPUAlphaState, fp_status.float_exception_flags)); + tcg_temp_free_i32(zero); +#else + gen_helper_fp_exc_clear(cpu_env); +#endif +} + +static void gen_fp_exc_raise_ignore(int rc, int fn11, int ignore) +{ + /* ??? We ought to be able to do something with imprecise exceptions. + E.g. notice we're still in the trap shadow of something within the + TB and do not generate the code to signal the exception; end the TB + when an exception is forced to arrive, either by consumption of a + register value or TRAPB or EXCB. */ + TCGv_i32 exc = tcg_temp_new_i32(); + TCGv_i32 reg; + +#if defined(CONFIG_SOFTFLOAT_INLINE) + tcg_gen_ld8u_i32(exc, cpu_env, + offsetof(CPUAlphaState, fp_status.float_exception_flags)); +#else + gen_helper_fp_exc_get(exc, cpu_env); +#endif + + if (ignore) { + tcg_gen_andi_i32(exc, exc, ~ignore); + } + + /* ??? Pass in the regno of the destination so that the helper can + set EXC_MASK, which contains a bitmask of destination registers + that have caused arithmetic traps. A simple userspace emulation + does not require this. We do need it for a guest kernel's entArith, + or if we were to do something clever with imprecise exceptions. */ + reg = tcg_const_i32(rc + 32); + + if (fn11 & QUAL_S) { + gen_helper_fp_exc_raise_s(cpu_env, exc, reg); + } else { + gen_helper_fp_exc_raise(cpu_env, exc, reg); + } + + tcg_temp_free_i32(reg); + tcg_temp_free_i32(exc); +} + +static inline void gen_fp_exc_raise(int rc, int fn11) +{ + gen_fp_exc_raise_ignore(rc, fn11, fn11 & QUAL_I ? 0 : float_flag_inexact); +} + +static void gen_fcvtlq(int rb, int rc) +{ + if (unlikely(rc == 31)) { + return; + } + if (unlikely(rb == 31)) { + tcg_gen_movi_i64(cpu_fir[rc], 0); + } else { + TCGv tmp = tcg_temp_new(); + + /* The arithmetic right shift here, plus the sign-extended mask below + yields a sign-extended result without an explicit ext32s_i64. */ + tcg_gen_sari_i64(tmp, cpu_fir[rb], 32); + tcg_gen_shri_i64(cpu_fir[rc], cpu_fir[rb], 29); + tcg_gen_andi_i64(tmp, tmp, (int32_t)0xc0000000); + tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rc], 0x3fffffff); + tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp); + + tcg_temp_free(tmp); + } +} + +static void gen_fcvtql(int rb, int rc) +{ + if (unlikely(rc == 31)) { + return; + } + if (unlikely(rb == 31)) { + tcg_gen_movi_i64(cpu_fir[rc], 0); + } else { + TCGv tmp = tcg_temp_new(); + + tcg_gen_andi_i64(tmp, cpu_fir[rb], 0xC0000000); + tcg_gen_andi_i64(cpu_fir[rc], cpu_fir[rb], 0x3FFFFFFF); + tcg_gen_shli_i64(tmp, tmp, 32); + tcg_gen_shli_i64(cpu_fir[rc], cpu_fir[rc], 29); + tcg_gen_or_i64(cpu_fir[rc], cpu_fir[rc], tmp); + + tcg_temp_free(tmp); + } +} + +static void gen_fcvtql_v(DisasContext *ctx, int rb, int rc) +{ + if (rb != 31) { + int lab = gen_new_label(); + TCGv tmp = tcg_temp_new(); + + tcg_gen_ext32s_i64(tmp, cpu_fir[rb]); + tcg_gen_brcond_i64(TCG_COND_EQ, tmp, cpu_fir[rb], lab); + gen_excp(ctx, EXCP_ARITH, EXC_M_IOV); + + gen_set_label(lab); + } + gen_fcvtql(rb, rc); +} + +#define FARITH2(name) \ + static inline void glue(gen_f, name)(int rb, int rc) \ + { \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (rb != 31) { \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, cpu_fir[rb]); \ + } else { \ + TCGv tmp = tcg_const_i64(0); \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, tmp); \ + tcg_temp_free(tmp); \ + } \ + } + +/* ??? VAX instruction qualifiers ignored. */ +FARITH2(sqrtf) +FARITH2(sqrtg) +FARITH2(cvtgf) +FARITH2(cvtgq) +FARITH2(cvtqf) +FARITH2(cvtqg) + +static void gen_ieee_arith2(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv), + int rb, int rc, int fn11) +{ + TCGv vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + gen_qual_flushzero(ctx, fn11); + gen_fp_exc_clear(); + + vb = gen_ieee_input(rb, fn11, 0); + helper(cpu_fir[rc], cpu_env, vb); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_ARITH2(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int rb, int rc, int fn11) \ +{ \ + gen_ieee_arith2(ctx, gen_helper_##name, rb, rc, fn11); \ +} +IEEE_ARITH2(sqrts) +IEEE_ARITH2(sqrtt) +IEEE_ARITH2(cvtst) +IEEE_ARITH2(cvtts) + +static void gen_fcvttq(DisasContext *ctx, int rb, int rc, int fn11) +{ + TCGv vb; + int ignore = 0; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + /* No need to set flushzero, since we have an integer output. */ + gen_fp_exc_clear(); + vb = gen_ieee_input(rb, fn11, 0); + + /* Almost all integer conversions use cropped rounding, and most + also do not have integer overflow enabled. Special case that. */ + switch (fn11) { + case QUAL_RM_C: + gen_helper_cvttq_c(cpu_fir[rc], cpu_env, vb); + break; + case QUAL_V | QUAL_RM_C: + case QUAL_S | QUAL_V | QUAL_RM_C: + ignore = float_flag_inexact; + /* FALLTHRU */ + case QUAL_S | QUAL_V | QUAL_I | QUAL_RM_C: + gen_helper_cvttq_svic(cpu_fir[rc], cpu_env, vb); + break; + default: + gen_qual_roundmode(ctx, fn11); + gen_helper_cvttq(cpu_fir[rc], cpu_env, vb); + ignore |= (fn11 & QUAL_V ? 0 : float_flag_overflow); + ignore |= (fn11 & QUAL_I ? 0 : float_flag_inexact); + break; + } + tcg_temp_free(vb); + + gen_fp_exc_raise_ignore(rc, fn11, ignore); +} + +static void gen_ieee_intcvt(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv), + int rb, int rc, int fn11) +{ + TCGv vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + + if (rb == 31) { + vb = tcg_const_i64(0); + } else { + vb = cpu_fir[rb]; + } + + /* The only exception that can be raised by integer conversion + is inexact. Thus we only need to worry about exceptions when + inexact handling is requested. */ + if (fn11 & QUAL_I) { + gen_fp_exc_clear(); + helper(cpu_fir[rc], cpu_env, vb); + gen_fp_exc_raise(rc, fn11); + } else { + helper(cpu_fir[rc], cpu_env, vb); + } + + if (rb == 31) { + tcg_temp_free(vb); + } +} + +#define IEEE_INTCVT(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int rb, int rc, int fn11) \ +{ \ + gen_ieee_intcvt(ctx, gen_helper_##name, rb, rc, fn11); \ +} +IEEE_INTCVT(cvtqs) +IEEE_INTCVT(cvtqt) + +static void gen_cpys_internal(int ra, int rb, int rc, int inv_a, uint64_t mask) +{ + TCGv va, vb, vmask; + int za = 0, zb = 0; + + if (unlikely(rc == 31)) { + return; + } + + vmask = tcg_const_i64(mask); + + TCGV_UNUSED_I64(va); + if (ra == 31) { + if (inv_a) { + va = vmask; + } else { + za = 1; + } + } else { + va = tcg_temp_new_i64(); + tcg_gen_mov_i64(va, cpu_fir[ra]); + if (inv_a) { + tcg_gen_andc_i64(va, vmask, va); + } else { + tcg_gen_and_i64(va, va, vmask); + } + } + + TCGV_UNUSED_I64(vb); + if (rb == 31) { + zb = 1; + } else { + vb = tcg_temp_new_i64(); + tcg_gen_andc_i64(vb, cpu_fir[rb], vmask); + } + + switch (za << 1 | zb) { + case 0 | 0: + tcg_gen_or_i64(cpu_fir[rc], va, vb); + break; + case 0 | 1: + tcg_gen_mov_i64(cpu_fir[rc], va); + break; + case 2 | 0: + tcg_gen_mov_i64(cpu_fir[rc], vb); + break; + case 2 | 1: + tcg_gen_movi_i64(cpu_fir[rc], 0); + break; + } + + tcg_temp_free(vmask); + if (ra != 31) { + tcg_temp_free(va); + } + if (rb != 31) { + tcg_temp_free(vb); + } +} + +static inline void gen_fcpys(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 0, 0x8000000000000000ULL); +} + +static inline void gen_fcpysn(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 1, 0x8000000000000000ULL); +} + +static inline void gen_fcpyse(int ra, int rb, int rc) +{ + gen_cpys_internal(ra, rb, rc, 0, 0xFFF0000000000000ULL); +} + +#define FARITH3(name) \ + static inline void glue(gen_f, name)(int ra, int rb, int rc) \ + { \ + TCGv va, vb; \ + \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (ra == 31) { \ + va = tcg_const_i64(0); \ + } else { \ + va = cpu_fir[ra]; \ + } \ + if (rb == 31) { \ + vb = tcg_const_i64(0); \ + } else { \ + vb = cpu_fir[rb]; \ + } \ + \ + gen_helper_ ## name(cpu_fir[rc], cpu_env, va, vb); \ + \ + if (ra == 31) { \ + tcg_temp_free(va); \ + } \ + if (rb == 31) { \ + tcg_temp_free(vb); \ + } \ + } + +/* ??? VAX instruction qualifiers ignored. */ +FARITH3(addf) +FARITH3(subf) +FARITH3(mulf) +FARITH3(divf) +FARITH3(addg) +FARITH3(subg) +FARITH3(mulg) +FARITH3(divg) +FARITH3(cmpgeq) +FARITH3(cmpglt) +FARITH3(cmpgle) + +static void gen_ieee_arith3(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv), + int ra, int rb, int rc, int fn11) +{ + TCGv va, vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_qual_roundmode(ctx, fn11); + gen_qual_flushzero(ctx, fn11); + gen_fp_exc_clear(); + + va = gen_ieee_input(ra, fn11, 0); + vb = gen_ieee_input(rb, fn11, 0); + helper(cpu_fir[rc], cpu_env, va, vb); + tcg_temp_free(va); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_ARITH3(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int ra, int rb, int rc, int fn11) \ +{ \ + gen_ieee_arith3(ctx, gen_helper_##name, ra, rb, rc, fn11); \ +} +IEEE_ARITH3(adds) +IEEE_ARITH3(subs) +IEEE_ARITH3(muls) +IEEE_ARITH3(divs) +IEEE_ARITH3(addt) +IEEE_ARITH3(subt) +IEEE_ARITH3(mult) +IEEE_ARITH3(divt) + +static void gen_ieee_compare(DisasContext *ctx, + void (*helper)(TCGv, TCGv_ptr, TCGv, TCGv), + int ra, int rb, int rc, int fn11) +{ + TCGv va, vb; + + /* ??? This is wrong: the instruction is not a nop, it still may + raise exceptions. */ + if (unlikely(rc == 31)) { + return; + } + + gen_fp_exc_clear(); + + va = gen_ieee_input(ra, fn11, 1); + vb = gen_ieee_input(rb, fn11, 1); + helper(cpu_fir[rc], cpu_env, va, vb); + tcg_temp_free(va); + tcg_temp_free(vb); + + gen_fp_exc_raise(rc, fn11); +} + +#define IEEE_CMP3(name) \ +static inline void glue(gen_f, name)(DisasContext *ctx, \ + int ra, int rb, int rc, int fn11) \ +{ \ + gen_ieee_compare(ctx, gen_helper_##name, ra, rb, rc, fn11); \ +} +IEEE_CMP3(cmptun) +IEEE_CMP3(cmpteq) +IEEE_CMP3(cmptlt) +IEEE_CMP3(cmptle) + +static inline uint64_t zapnot_mask(uint8_t lit) +{ + uint64_t mask = 0; + int i; + + for (i = 0; i < 8; ++i) { + if ((lit >> i) & 1) + mask |= 0xffull << (i * 8); + } + return mask; +} + +/* Implement zapnot with an immediate operand, which expands to some + form of immediate AND. This is a basic building block in the + definition of many of the other byte manipulation instructions. */ +static void gen_zapnoti(TCGv dest, TCGv src, uint8_t lit) +{ + switch (lit) { + case 0x00: + tcg_gen_movi_i64(dest, 0); + break; + case 0x01: + tcg_gen_ext8u_i64(dest, src); + break; + case 0x03: + tcg_gen_ext16u_i64(dest, src); + break; + case 0x0f: + tcg_gen_ext32u_i64(dest, src); + break; + case 0xff: + tcg_gen_mov_i64(dest, src); + break; + default: + tcg_gen_andi_i64 (dest, src, zapnot_mask (lit)); + break; + } +} + +static inline void gen_zapnot(int ra, int rb, int rc, int islit, uint8_t lit) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + gen_zapnoti(cpu_ir[rc], cpu_ir[ra], lit); + else + gen_helper_zapnot (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); +} + +static inline void gen_zap(int ra, int rb, int rc, int islit, uint8_t lit) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + gen_zapnoti(cpu_ir[rc], cpu_ir[ra], ~lit); + else + gen_helper_zap (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); +} + + +/* EXTWH, EXTLH, EXTQH */ +static void gen_ext_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + if (islit) { + lit = (64 - (lit & 7) * 8) & 0x3f; + tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], lit); + } else { + TCGv tmp1 = tcg_temp_new(); + tcg_gen_andi_i64(tmp1, cpu_ir[rb], 7); + tcg_gen_shli_i64(tmp1, tmp1, 3); + tcg_gen_neg_i64(tmp1, tmp1); + tcg_gen_andi_i64(tmp1, tmp1, 0x3f); + tcg_gen_shl_i64(cpu_ir[rc], cpu_ir[ra], tmp1); + tcg_temp_free(tmp1); + } + gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask); + } +} + +/* EXTBL, EXTWL, EXTLL, EXTQL */ +static void gen_ext_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + if (islit) { + tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], (lit & 7) * 8); + } else { + TCGv tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, cpu_ir[rb], 7); + tcg_gen_shli_i64(tmp, tmp, 3); + tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], tmp); + tcg_temp_free(tmp); + } + gen_zapnoti(cpu_ir[rc], cpu_ir[rc], byte_mask); + } +} + +/* INSWH, INSLH, INSQH */ +static void gen_ins_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31) || (islit && (lit & 7) == 0)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + TCGv tmp = tcg_temp_new(); + + /* The instruction description has us left-shift the byte mask + and extract bits <15:8> and apply that zap at the end. This + is equivalent to simply performing the zap first and shifting + afterward. */ + gen_zapnoti (tmp, cpu_ir[ra], byte_mask); + + if (islit) { + /* Note that we have handled the lit==0 case above. */ + tcg_gen_shri_i64 (cpu_ir[rc], tmp, 64 - (lit & 7) * 8); + } else { + TCGv shift = tcg_temp_new(); + + /* If (B & 7) == 0, we need to shift by 64 and leave a zero. + Do this portably by splitting the shift into two parts: + shift_count-1 and 1. Arrange for the -1 by using + ones-complement instead of twos-complement in the negation: + ~((B & 7) * 8) & 63. */ + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_not_i64(shift, shift); + tcg_gen_andi_i64(shift, shift, 0x3f); + + tcg_gen_shr_i64(cpu_ir[rc], tmp, shift); + tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[rc], 1); + tcg_temp_free(shift); + } + tcg_temp_free(tmp); + } +} + +/* INSBL, INSWL, INSLL, INSQL */ +static void gen_ins_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + TCGv tmp = tcg_temp_new(); + + /* The instruction description has us left-shift the byte mask + the same number of byte slots as the data and apply the zap + at the end. This is equivalent to simply performing the zap + first and shifting afterward. */ + gen_zapnoti (tmp, cpu_ir[ra], byte_mask); + + if (islit) { + tcg_gen_shli_i64(cpu_ir[rc], tmp, (lit & 7) * 8); + } else { + TCGv shift = tcg_temp_new(); + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_shl_i64(cpu_ir[rc], tmp, shift); + tcg_temp_free(shift); + } + tcg_temp_free(tmp); + } +} + +/* MSKWH, MSKLH, MSKQH */ +static void gen_msk_h(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) { + gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~((byte_mask << (lit & 7)) >> 8)); + } else { + TCGv shift = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + + /* The instruction description is as above, where the byte_mask + is shifted left, and then we extract bits <15:8>. This can be + emulated with a right-shift on the expanded byte mask. This + requires extra care because for an input <2:0> == 0 we need a + shift of 64 bits in order to generate a zero. This is done by + splitting the shift into two parts, the variable shift - 1 + followed by a constant 1 shift. The code we expand below is + equivalent to ~((B & 7) * 8) & 63. */ + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_not_i64(shift, shift); + tcg_gen_andi_i64(shift, shift, 0x3f); + tcg_gen_movi_i64(mask, zapnot_mask (byte_mask)); + tcg_gen_shr_i64(mask, mask, shift); + tcg_gen_shri_i64(mask, mask, 1); + + tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask); + + tcg_temp_free(mask); + tcg_temp_free(shift); + } +} + +/* MSKBL, MSKWL, MSKLL, MSKQL */ +static void gen_msk_l(int ra, int rb, int rc, int islit, + uint8_t lit, uint8_t byte_mask) +{ + if (unlikely(rc == 31)) + return; + else if (unlikely(ra == 31)) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) { + gen_zapnoti (cpu_ir[rc], cpu_ir[ra], ~(byte_mask << (lit & 7))); + } else { + TCGv shift = tcg_temp_new(); + TCGv mask = tcg_temp_new(); + + tcg_gen_andi_i64(shift, cpu_ir[rb], 7); + tcg_gen_shli_i64(shift, shift, 3); + tcg_gen_movi_i64(mask, zapnot_mask (byte_mask)); + tcg_gen_shl_i64(mask, mask, shift); + + tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], mask); + + tcg_temp_free(mask); + tcg_temp_free(shift); + } +} + +/* Code to call arith3 helpers */ +#define ARITH3(name) \ +static inline void glue(gen_, name)(int ra, int rb, int rc, int islit,\ + uint8_t lit) \ +{ \ + if (unlikely(rc == 31)) \ + return; \ + \ + if (ra != 31) { \ + if (islit) { \ + TCGv tmp = tcg_const_i64(lit); \ + gen_helper_ ## name(cpu_ir[rc], cpu_ir[ra], tmp); \ + tcg_temp_free(tmp); \ + } else \ + gen_helper_ ## name (cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); \ + } else { \ + TCGv tmp1 = tcg_const_i64(0); \ + if (islit) { \ + TCGv tmp2 = tcg_const_i64(lit); \ + gen_helper_ ## name (cpu_ir[rc], tmp1, tmp2); \ + tcg_temp_free(tmp2); \ + } else \ + gen_helper_ ## name (cpu_ir[rc], tmp1, cpu_ir[rb]); \ + tcg_temp_free(tmp1); \ + } \ +} +ARITH3(umulh) +ARITH3(cmpbge) +ARITH3(minub8) +ARITH3(minsb8) +ARITH3(minuw4) +ARITH3(minsw4) +ARITH3(maxub8) +ARITH3(maxsb8) +ARITH3(maxuw4) +ARITH3(maxsw4) +ARITH3(perr) + +/* Code to call arith3 helpers */ +#define ARITH3_EX(name) \ + static inline void glue(gen_, name)(int ra, int rb, int rc, \ + int islit, uint8_t lit) \ + { \ + if (unlikely(rc == 31)) { \ + return; \ + } \ + if (ra != 31) { \ + if (islit) { \ + TCGv tmp = tcg_const_i64(lit); \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, \ + cpu_ir[ra], tmp); \ + tcg_temp_free(tmp); \ + } else { \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, \ + cpu_ir[ra], cpu_ir[rb]); \ + } \ + } else { \ + TCGv tmp1 = tcg_const_i64(0); \ + if (islit) { \ + TCGv tmp2 = tcg_const_i64(lit); \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, tmp1, tmp2); \ + tcg_temp_free(tmp2); \ + } else { \ + gen_helper_ ## name(cpu_ir[rc], cpu_env, tmp1, cpu_ir[rb]); \ + } \ + tcg_temp_free(tmp1); \ + } \ + } +ARITH3_EX(addlv) +ARITH3_EX(sublv) +ARITH3_EX(addqv) +ARITH3_EX(subqv) +ARITH3_EX(mullv) +ARITH3_EX(mulqv) + +#define MVIOP2(name) \ +static inline void glue(gen_, name)(int rb, int rc) \ +{ \ + if (unlikely(rc == 31)) \ + return; \ + if (unlikely(rb == 31)) \ + tcg_gen_movi_i64(cpu_ir[rc], 0); \ + else \ + gen_helper_ ## name (cpu_ir[rc], cpu_ir[rb]); \ +} +MVIOP2(pklb) +MVIOP2(pkwb) +MVIOP2(unpkbl) +MVIOP2(unpkbw) + +static void gen_cmp(TCGCond cond, int ra, int rb, int rc, + int islit, uint8_t lit) +{ + TCGv va, vb; + + if (unlikely(rc == 31)) { + return; + } + + if (ra == 31) { + va = tcg_const_i64(0); + } else { + va = cpu_ir[ra]; + } + if (islit) { + vb = tcg_const_i64(lit); + } else { + vb = cpu_ir[rb]; + } + + tcg_gen_setcond_i64(cond, cpu_ir[rc], va, vb); + + if (ra == 31) { + tcg_temp_free(va); + } + if (islit) { + tcg_temp_free(vb); + } +} + +static void gen_rx(int ra, int set) +{ + TCGv_i32 tmp; + + if (ra != 31) { + tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, offsetof(CPUAlphaState, intr_flag)); + } + + tmp = tcg_const_i32(set); + tcg_gen_st8_i32(tmp, cpu_env, offsetof(CPUAlphaState, intr_flag)); + tcg_temp_free_i32(tmp); +} + +static ExitStatus gen_call_pal(DisasContext *ctx, int palcode) +{ + /* We're emulating OSF/1 PALcode. Many of these are trivial access + to internal cpu registers. */ + + /* Unprivileged PAL call */ + if (palcode >= 0x80 && palcode < 0xC0) { + switch (palcode) { + case 0x86: + /* IMB */ + /* No-op inside QEMU. */ + break; + case 0x9E: + /* RDUNIQUE */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_unique); + break; + case 0x9F: + /* WRUNIQUE */ + tcg_gen_mov_i64(cpu_unique, cpu_ir[IR_A0]); + break; + default: + return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0xbf); + } + return NO_EXIT; + } + +#ifndef CONFIG_USER_ONLY + /* Privileged PAL code */ + if (palcode < 0x40 && (ctx->tb->flags & TB_FLAGS_USER_MODE) == 0) { + switch (palcode) { + case 0x01: + /* CFLUSH */ + /* No-op inside QEMU. */ + break; + case 0x02: + /* DRAINA */ + /* No-op inside QEMU. */ + break; + case 0x2D: + /* WRVPTPTR */ + tcg_gen_st_i64(cpu_ir[IR_A0], cpu_env, offsetof(CPUAlphaState, vptptr)); + break; + case 0x31: + /* WRVAL */ + tcg_gen_mov_i64(cpu_sysval, cpu_ir[IR_A0]); + break; + case 0x32: + /* RDVAL */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_sysval); + break; + + case 0x35: { + /* SWPIPL */ + TCGv tmp; + + /* Note that we already know we're in kernel mode, so we know + that PS only contains the 3 IPL bits. */ + tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, offsetof(CPUAlphaState, ps)); + + /* But make sure and store only the 3 IPL bits from the user. */ + tmp = tcg_temp_new(); + tcg_gen_andi_i64(tmp, cpu_ir[IR_A0], PS_INT_MASK); + tcg_gen_st8_i64(tmp, cpu_env, offsetof(CPUAlphaState, ps)); + tcg_temp_free(tmp); + break; + } + + case 0x36: + /* RDPS */ + tcg_gen_ld8u_i64(cpu_ir[IR_V0], cpu_env, offsetof(CPUAlphaState, ps)); + break; + case 0x38: + /* WRUSP */ + tcg_gen_mov_i64(cpu_usp, cpu_ir[IR_A0]); + break; + case 0x3A: + /* RDUSP */ + tcg_gen_mov_i64(cpu_ir[IR_V0], cpu_usp); + break; + case 0x3C: + /* WHAMI */ + tcg_gen_ld32s_i64(cpu_ir[IR_V0], cpu_env, + offsetof(CPUAlphaState, cpu_index)); + break; + + default: + return gen_excp(ctx, EXCP_CALL_PAL, palcode & 0x3f); + } + return NO_EXIT; + } +#endif + + return gen_invalid(ctx); +} + +#ifndef CONFIG_USER_ONLY + +#define PR_BYTE 0x100000 +#define PR_LONG 0x200000 + +static int cpu_pr_data(int pr) +{ + switch (pr) { + case 0: return offsetof(CPUAlphaState, ps) | PR_BYTE; + case 1: return offsetof(CPUAlphaState, fen) | PR_BYTE; + case 2: return offsetof(CPUAlphaState, pcc_ofs) | PR_LONG; + case 3: return offsetof(CPUAlphaState, trap_arg0); + case 4: return offsetof(CPUAlphaState, trap_arg1); + case 5: return offsetof(CPUAlphaState, trap_arg2); + case 6: return offsetof(CPUAlphaState, exc_addr); + case 7: return offsetof(CPUAlphaState, palbr); + case 8: return offsetof(CPUAlphaState, ptbr); + case 9: return offsetof(CPUAlphaState, vptptr); + case 10: return offsetof(CPUAlphaState, unique); + case 11: return offsetof(CPUAlphaState, sysval); + case 12: return offsetof(CPUAlphaState, usp); + + case 32 ... 39: + return offsetof(CPUAlphaState, shadow[pr - 32]); + case 40 ... 63: + return offsetof(CPUAlphaState, scratch[pr - 40]); + + case 251: + return offsetof(CPUAlphaState, alarm_expire); + } + return 0; +} + +static ExitStatus gen_mfpr(int ra, int regno) +{ + int data = cpu_pr_data(regno); + + /* In our emulated PALcode, these processor registers have no + side effects from reading. */ + if (ra == 31) { + return NO_EXIT; + } + + if (regno == 250) { + /* WALL_TIME */ + if (use_icount) { + gen_io_start(); + gen_helper_get_time(cpu_ir[ra]); + gen_io_end(); + return EXIT_PC_STALE; + } else { + gen_helper_get_time(cpu_ir[ra]); + return NO_EXIT; + } + } + + /* The basic registers are data only, and unknown registers + are read-zero, write-ignore. */ + if (data == 0) { + tcg_gen_movi_i64(cpu_ir[ra], 0); + } else if (data & PR_BYTE) { + tcg_gen_ld8u_i64(cpu_ir[ra], cpu_env, data & ~PR_BYTE); + } else if (data & PR_LONG) { + tcg_gen_ld32s_i64(cpu_ir[ra], cpu_env, data & ~PR_LONG); + } else { + tcg_gen_ld_i64(cpu_ir[ra], cpu_env, data); + } + return NO_EXIT; +} + +static ExitStatus gen_mtpr(DisasContext *ctx, int rb, int regno) +{ + TCGv tmp; + int data; + + if (rb == 31) { + tmp = tcg_const_i64(0); + } else { + tmp = cpu_ir[rb]; + } + + switch (regno) { + case 255: + /* TBIA */ + gen_helper_tbia(cpu_env); + break; + + case 254: + /* TBIS */ + gen_helper_tbis(cpu_env, tmp); + break; + + case 253: + /* WAIT */ + tmp = tcg_const_i64(1); + tcg_gen_st32_i64(tmp, cpu_env, offsetof(CPUAlphaState, halted)); + return gen_excp(ctx, EXCP_HLT, 0); + + case 252: + /* HALT */ + gen_helper_halt(tmp); + return EXIT_PC_STALE; + + case 251: + /* ALARM */ + gen_helper_set_alarm(cpu_env, tmp); + break; + + default: + /* The basic registers are data only, and unknown registers + are read-zero, write-ignore. */ + data = cpu_pr_data(regno); + if (data != 0) { + if (data & PR_BYTE) { + tcg_gen_st8_i64(tmp, cpu_env, data & ~PR_BYTE); + } else if (data & PR_LONG) { + tcg_gen_st32_i64(tmp, cpu_env, data & ~PR_LONG); + } else { + tcg_gen_st_i64(tmp, cpu_env, data); + } + } + break; + } + + if (rb == 31) { + tcg_temp_free(tmp); + } + + return NO_EXIT; +} +#endif /* !USER_ONLY*/ + +static ExitStatus translate_one(DisasContext *ctx, uint32_t insn) +{ + uint32_t palcode; + int32_t disp21, disp16; +#ifndef CONFIG_USER_ONLY + int32_t disp12; +#endif + uint16_t fn11; + uint8_t opc, ra, rb, rc, fpfn, fn7, islit, real_islit; + uint8_t lit; + ExitStatus ret; + + /* Decode all instruction fields */ + opc = insn >> 26; + ra = (insn >> 21) & 0x1F; + rb = (insn >> 16) & 0x1F; + rc = insn & 0x1F; + real_islit = islit = (insn >> 12) & 1; + if (rb == 31 && !islit) { + islit = 1; + lit = 0; + } else + lit = (insn >> 13) & 0xFF; + palcode = insn & 0x03FFFFFF; + disp21 = ((int32_t)((insn & 0x001FFFFF) << 11)) >> 11; + disp16 = (int16_t)(insn & 0x0000FFFF); +#ifndef CONFIG_USER_ONLY + disp12 = (int32_t)((insn & 0x00000FFF) << 20) >> 20; +#endif + fn11 = (insn >> 5) & 0x000007FF; + fpfn = fn11 & 0x3F; + fn7 = (insn >> 5) & 0x0000007F; + LOG_DISAS("opc %02x ra %2d rb %2d rc %2d disp16 %6d\n", + opc, ra, rb, rc, disp16); + + ret = NO_EXIT; + switch (opc) { + case 0x00: + /* CALL_PAL */ + ret = gen_call_pal(ctx, palcode); + break; + case 0x01: + /* OPC01 */ + goto invalid_opc; + case 0x02: + /* OPC02 */ + goto invalid_opc; + case 0x03: + /* OPC03 */ + goto invalid_opc; + case 0x04: + /* OPC04 */ + goto invalid_opc; + case 0x05: + /* OPC05 */ + goto invalid_opc; + case 0x06: + /* OPC06 */ + goto invalid_opc; + case 0x07: + /* OPC07 */ + goto invalid_opc; + case 0x08: + /* LDA */ + if (likely(ra != 31)) { + if (rb != 31) + tcg_gen_addi_i64(cpu_ir[ra], cpu_ir[rb], disp16); + else + tcg_gen_movi_i64(cpu_ir[ra], disp16); + } + break; + case 0x09: + /* LDAH */ + if (likely(ra != 31)) { + if (rb != 31) + tcg_gen_addi_i64(cpu_ir[ra], cpu_ir[rb], disp16 << 16); + else + tcg_gen_movi_i64(cpu_ir[ra], disp16 << 16); + } + break; + case 0x0A: + /* LDBU */ + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + gen_load_mem(ctx, &tcg_gen_qemu_ld8u, ra, rb, disp16, 0, 0); + break; + } + goto invalid_opc; + case 0x0B: + /* LDQ_U */ + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 1); + break; + case 0x0C: + /* LDWU */ + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + gen_load_mem(ctx, &tcg_gen_qemu_ld16u, ra, rb, disp16, 0, 0); + break; + } + goto invalid_opc; + case 0x0D: + /* STW */ + gen_store_mem(ctx, &tcg_gen_qemu_st16, ra, rb, disp16, 0, 0); + break; + case 0x0E: + /* STB */ + gen_store_mem(ctx, &tcg_gen_qemu_st8, ra, rb, disp16, 0, 0); + break; + case 0x0F: + /* STQ_U */ + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 1); + break; + case 0x10: + switch (fn7) { + case 0x00: + /* ADDL */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) { + tcg_gen_addi_i64(cpu_ir[rc], cpu_ir[ra], lit); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } else { + tcg_gen_add_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x02: + /* S4ADDL */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 2); + if (islit) + tcg_gen_addi_i64(tmp, tmp, lit); + else + tcg_gen_add_i64(tmp, tmp, cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], tmp); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x09: + /* SUBL */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_subi_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_sub_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else { + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } + } + break; + case 0x0B: + /* S4SUBL */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 2); + if (islit) + tcg_gen_subi_i64(tmp, tmp, lit); + else + tcg_gen_sub_i64(tmp, tmp, cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], tmp); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else { + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } + } + } + break; + case 0x0F: + /* CMPBGE */ + gen_cmpbge(ra, rb, rc, islit, lit); + break; + case 0x12: + /* S8ADDL */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 3); + if (islit) + tcg_gen_addi_i64(tmp, tmp, lit); + else + tcg_gen_add_i64(tmp, tmp, cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], tmp); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x1B: + /* S8SUBL */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 3); + if (islit) + tcg_gen_subi_i64(tmp, tmp, lit); + else + tcg_gen_sub_i64(tmp, tmp, cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], tmp); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } + } + } + break; + case 0x1D: + /* CMPULT */ + gen_cmp(TCG_COND_LTU, ra, rb, rc, islit, lit); + break; + case 0x20: + /* ADDQ */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_addi_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_add_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x22: + /* S4ADDQ */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 2); + if (islit) + tcg_gen_addi_i64(cpu_ir[rc], tmp, lit); + else + tcg_gen_add_i64(cpu_ir[rc], tmp, cpu_ir[rb]); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x29: + /* SUBQ */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_subi_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_sub_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x2B: + /* S4SUBQ */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 2); + if (islit) + tcg_gen_subi_i64(cpu_ir[rc], tmp, lit); + else + tcg_gen_sub_i64(cpu_ir[rc], tmp, cpu_ir[rb]); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x2D: + /* CMPEQ */ + gen_cmp(TCG_COND_EQ, ra, rb, rc, islit, lit); + break; + case 0x32: + /* S8ADDQ */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 3); + if (islit) + tcg_gen_addi_i64(cpu_ir[rc], tmp, lit); + else + tcg_gen_add_i64(cpu_ir[rc], tmp, cpu_ir[rb]); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x3B: + /* S8SUBQ */ + if (likely(rc != 31)) { + if (ra != 31) { + TCGv tmp = tcg_temp_new(); + tcg_gen_shli_i64(tmp, cpu_ir[ra], 3); + if (islit) + tcg_gen_subi_i64(cpu_ir[rc], tmp, lit); + else + tcg_gen_sub_i64(cpu_ir[rc], tmp, cpu_ir[rb]); + tcg_temp_free(tmp); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], -lit); + else + tcg_gen_neg_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x3D: + /* CMPULE */ + gen_cmp(TCG_COND_LEU, ra, rb, rc, islit, lit); + break; + case 0x40: + /* ADDL/V */ + gen_addlv(ra, rb, rc, islit, lit); + break; + case 0x49: + /* SUBL/V */ + gen_sublv(ra, rb, rc, islit, lit); + break; + case 0x4D: + /* CMPLT */ + gen_cmp(TCG_COND_LT, ra, rb, rc, islit, lit); + break; + case 0x60: + /* ADDQ/V */ + gen_addqv(ra, rb, rc, islit, lit); + break; + case 0x69: + /* SUBQ/V */ + gen_subqv(ra, rb, rc, islit, lit); + break; + case 0x6D: + /* CMPLE */ + gen_cmp(TCG_COND_LE, ra, rb, rc, islit, lit); + break; + default: + goto invalid_opc; + } + break; + case 0x11: + switch (fn7) { + case 0x00: + /* AND */ + if (likely(rc != 31)) { + if (ra == 31) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_and_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } + break; + case 0x08: + /* BIC */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[ra], ~lit); + else + tcg_gen_andc_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } + break; + case 0x14: + /* CMOVLBS */ + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 1); + break; + case 0x16: + /* CMOVLBC */ + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 1); + break; + case 0x20: + /* BIS */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_ori_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_or_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x24: + /* CMOVEQ */ + gen_cmov(TCG_COND_EQ, ra, rb, rc, islit, lit, 0); + break; + case 0x26: + /* CMOVNE */ + gen_cmov(TCG_COND_NE, ra, rb, rc, islit, lit, 0); + break; + case 0x28: + /* ORNOT */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_ori_i64(cpu_ir[rc], cpu_ir[ra], ~lit); + else + tcg_gen_orc_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], ~lit); + else + tcg_gen_not_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x40: + /* XOR */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_xori_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_xor_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], lit); + else + tcg_gen_mov_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x44: + /* CMOVLT */ + gen_cmov(TCG_COND_LT, ra, rb, rc, islit, lit, 0); + break; + case 0x46: + /* CMOVGE */ + gen_cmov(TCG_COND_GE, ra, rb, rc, islit, lit, 0); + break; + case 0x48: + /* EQV */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_xori_i64(cpu_ir[rc], cpu_ir[ra], ~lit); + else + tcg_gen_eqv_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } else { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], ~lit); + else + tcg_gen_not_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + case 0x61: + /* AMASK */ + if (likely(rc != 31)) { + uint64_t amask = ctx->tb->flags >> TB_FLAGS_AMASK_SHIFT; + + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], lit & ~amask); + } else { + tcg_gen_andi_i64(cpu_ir[rc], cpu_ir[rb], ~amask); + } + } + break; + case 0x64: + /* CMOVLE */ + gen_cmov(TCG_COND_LE, ra, rb, rc, islit, lit, 0); + break; + case 0x66: + /* CMOVGT */ + gen_cmov(TCG_COND_GT, ra, rb, rc, islit, lit, 0); + break; + case 0x6C: + /* IMPLVER */ + if (rc != 31) + tcg_gen_movi_i64(cpu_ir[rc], ctx->env->implver); + break; + default: + goto invalid_opc; + } + break; + case 0x12: + switch (fn7) { + case 0x02: + /* MSKBL */ + gen_msk_l(ra, rb, rc, islit, lit, 0x01); + break; + case 0x06: + /* EXTBL */ + gen_ext_l(ra, rb, rc, islit, lit, 0x01); + break; + case 0x0B: + /* INSBL */ + gen_ins_l(ra, rb, rc, islit, lit, 0x01); + break; + case 0x12: + /* MSKWL */ + gen_msk_l(ra, rb, rc, islit, lit, 0x03); + break; + case 0x16: + /* EXTWL */ + gen_ext_l(ra, rb, rc, islit, lit, 0x03); + break; + case 0x1B: + /* INSWL */ + gen_ins_l(ra, rb, rc, islit, lit, 0x03); + break; + case 0x22: + /* MSKLL */ + gen_msk_l(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x26: + /* EXTLL */ + gen_ext_l(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x2B: + /* INSLL */ + gen_ins_l(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x30: + /* ZAP */ + gen_zap(ra, rb, rc, islit, lit); + break; + case 0x31: + /* ZAPNOT */ + gen_zapnot(ra, rb, rc, islit, lit); + break; + case 0x32: + /* MSKQL */ + gen_msk_l(ra, rb, rc, islit, lit, 0xff); + break; + case 0x34: + /* SRL */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_shri_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f); + else { + TCGv shift = tcg_temp_new(); + tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f); + tcg_gen_shr_i64(cpu_ir[rc], cpu_ir[ra], shift); + tcg_temp_free(shift); + } + } else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } + break; + case 0x36: + /* EXTQL */ + gen_ext_l(ra, rb, rc, islit, lit, 0xff); + break; + case 0x39: + /* SLL */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_shli_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f); + else { + TCGv shift = tcg_temp_new(); + tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f); + tcg_gen_shl_i64(cpu_ir[rc], cpu_ir[ra], shift); + tcg_temp_free(shift); + } + } else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } + break; + case 0x3B: + /* INSQL */ + gen_ins_l(ra, rb, rc, islit, lit, 0xff); + break; + case 0x3C: + /* SRA */ + if (likely(rc != 31)) { + if (ra != 31) { + if (islit) + tcg_gen_sari_i64(cpu_ir[rc], cpu_ir[ra], lit & 0x3f); + else { + TCGv shift = tcg_temp_new(); + tcg_gen_andi_i64(shift, cpu_ir[rb], 0x3f); + tcg_gen_sar_i64(cpu_ir[rc], cpu_ir[ra], shift); + tcg_temp_free(shift); + } + } else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } + break; + case 0x52: + /* MSKWH */ + gen_msk_h(ra, rb, rc, islit, lit, 0x03); + break; + case 0x57: + /* INSWH */ + gen_ins_h(ra, rb, rc, islit, lit, 0x03); + break; + case 0x5A: + /* EXTWH */ + gen_ext_h(ra, rb, rc, islit, lit, 0x03); + break; + case 0x62: + /* MSKLH */ + gen_msk_h(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x67: + /* INSLH */ + gen_ins_h(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x6A: + /* EXTLH */ + gen_ext_h(ra, rb, rc, islit, lit, 0x0f); + break; + case 0x72: + /* MSKQH */ + gen_msk_h(ra, rb, rc, islit, lit, 0xff); + break; + case 0x77: + /* INSQH */ + gen_ins_h(ra, rb, rc, islit, lit, 0xff); + break; + case 0x7A: + /* EXTQH */ + gen_ext_h(ra, rb, rc, islit, lit, 0xff); + break; + default: + goto invalid_opc; + } + break; + case 0x13: + switch (fn7) { + case 0x00: + /* MULL */ + if (likely(rc != 31)) { + if (ra == 31) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else { + if (islit) + tcg_gen_muli_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_mul_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + tcg_gen_ext32s_i64(cpu_ir[rc], cpu_ir[rc]); + } + } + break; + case 0x20: + /* MULQ */ + if (likely(rc != 31)) { + if (ra == 31) + tcg_gen_movi_i64(cpu_ir[rc], 0); + else if (islit) + tcg_gen_muli_i64(cpu_ir[rc], cpu_ir[ra], lit); + else + tcg_gen_mul_i64(cpu_ir[rc], cpu_ir[ra], cpu_ir[rb]); + } + break; + case 0x30: + /* UMULH */ + gen_umulh(ra, rb, rc, islit, lit); + break; + case 0x40: + /* MULL/V */ + gen_mullv(ra, rb, rc, islit, lit); + break; + case 0x60: + /* MULQ/V */ + gen_mulqv(ra, rb, rc, islit, lit); + break; + default: + goto invalid_opc; + } + break; + case 0x14: + switch (fpfn) { /* fn11 & 0x3F */ + case 0x04: + /* ITOFS */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { + goto invalid_opc; + } + if (likely(rc != 31)) { + if (ra != 31) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]); + gen_helper_memory_to_s(cpu_fir[rc], tmp); + tcg_temp_free_i32(tmp); + } else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } + break; + case 0x0A: + /* SQRTF */ + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtf(rb, rc); + break; + } + goto invalid_opc; + case 0x0B: + /* SQRTS */ + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrts(ctx, rb, rc, fn11); + break; + } + goto invalid_opc; + case 0x14: + /* ITOFF */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { + goto invalid_opc; + } + if (likely(rc != 31)) { + if (ra != 31) { + TCGv_i32 tmp = tcg_temp_new_i32(); + tcg_gen_trunc_i64_i32(tmp, cpu_ir[ra]); + gen_helper_memory_to_f(cpu_fir[rc], tmp); + tcg_temp_free_i32(tmp); + } else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } + break; + case 0x24: + /* ITOFT */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { + goto invalid_opc; + } + if (likely(rc != 31)) { + if (ra != 31) + tcg_gen_mov_i64(cpu_fir[rc], cpu_ir[ra]); + else + tcg_gen_movi_i64(cpu_fir[rc], 0); + } + break; + case 0x2A: + /* SQRTG */ + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtg(rb, rc); + break; + } + goto invalid_opc; + case 0x02B: + /* SQRTT */ + if (ctx->tb->flags & TB_FLAGS_AMASK_FIX) { + gen_fsqrtt(ctx, rb, rc, fn11); + break; + } + goto invalid_opc; + default: + goto invalid_opc; + } + break; + case 0x15: + /* VAX floating point */ + /* XXX: rounding mode and trap are ignored (!) */ + switch (fpfn) { /* fn11 & 0x3F */ + case 0x00: + /* ADDF */ + gen_faddf(ra, rb, rc); + break; + case 0x01: + /* SUBF */ + gen_fsubf(ra, rb, rc); + break; + case 0x02: + /* MULF */ + gen_fmulf(ra, rb, rc); + break; + case 0x03: + /* DIVF */ + gen_fdivf(ra, rb, rc); + break; + case 0x1E: + /* CVTDG */ +#if 0 // TODO + gen_fcvtdg(rb, rc); +#else + goto invalid_opc; +#endif + break; + case 0x20: + /* ADDG */ + gen_faddg(ra, rb, rc); + break; + case 0x21: + /* SUBG */ + gen_fsubg(ra, rb, rc); + break; + case 0x22: + /* MULG */ + gen_fmulg(ra, rb, rc); + break; + case 0x23: + /* DIVG */ + gen_fdivg(ra, rb, rc); + break; + case 0x25: + /* CMPGEQ */ + gen_fcmpgeq(ra, rb, rc); + break; + case 0x26: + /* CMPGLT */ + gen_fcmpglt(ra, rb, rc); + break; + case 0x27: + /* CMPGLE */ + gen_fcmpgle(ra, rb, rc); + break; + case 0x2C: + /* CVTGF */ + gen_fcvtgf(rb, rc); + break; + case 0x2D: + /* CVTGD */ +#if 0 // TODO + gen_fcvtgd(rb, rc); +#else + goto invalid_opc; +#endif + break; + case 0x2F: + /* CVTGQ */ + gen_fcvtgq(rb, rc); + break; + case 0x3C: + /* CVTQF */ + gen_fcvtqf(rb, rc); + break; + case 0x3E: + /* CVTQG */ + gen_fcvtqg(rb, rc); + break; + default: + goto invalid_opc; + } + break; + case 0x16: + /* IEEE floating-point */ + switch (fpfn) { /* fn11 & 0x3F */ + case 0x00: + /* ADDS */ + gen_fadds(ctx, ra, rb, rc, fn11); + break; + case 0x01: + /* SUBS */ + gen_fsubs(ctx, ra, rb, rc, fn11); + break; + case 0x02: + /* MULS */ + gen_fmuls(ctx, ra, rb, rc, fn11); + break; + case 0x03: + /* DIVS */ + gen_fdivs(ctx, ra, rb, rc, fn11); + break; + case 0x20: + /* ADDT */ + gen_faddt(ctx, ra, rb, rc, fn11); + break; + case 0x21: + /* SUBT */ + gen_fsubt(ctx, ra, rb, rc, fn11); + break; + case 0x22: + /* MULT */ + gen_fmult(ctx, ra, rb, rc, fn11); + break; + case 0x23: + /* DIVT */ + gen_fdivt(ctx, ra, rb, rc, fn11); + break; + case 0x24: + /* CMPTUN */ + gen_fcmptun(ctx, ra, rb, rc, fn11); + break; + case 0x25: + /* CMPTEQ */ + gen_fcmpteq(ctx, ra, rb, rc, fn11); + break; + case 0x26: + /* CMPTLT */ + gen_fcmptlt(ctx, ra, rb, rc, fn11); + break; + case 0x27: + /* CMPTLE */ + gen_fcmptle(ctx, ra, rb, rc, fn11); + break; + case 0x2C: + if (fn11 == 0x2AC || fn11 == 0x6AC) { + /* CVTST */ + gen_fcvtst(ctx, rb, rc, fn11); + } else { + /* CVTTS */ + gen_fcvtts(ctx, rb, rc, fn11); + } + break; + case 0x2F: + /* CVTTQ */ + gen_fcvttq(ctx, rb, rc, fn11); + break; + case 0x3C: + /* CVTQS */ + gen_fcvtqs(ctx, rb, rc, fn11); + break; + case 0x3E: + /* CVTQT */ + gen_fcvtqt(ctx, rb, rc, fn11); + break; + default: + goto invalid_opc; + } + break; + case 0x17: + switch (fn11) { + case 0x010: + /* CVTLQ */ + gen_fcvtlq(rb, rc); + break; + case 0x020: + if (likely(rc != 31)) { + if (ra == rb) { + /* FMOV */ + if (ra == 31) + tcg_gen_movi_i64(cpu_fir[rc], 0); + else + tcg_gen_mov_i64(cpu_fir[rc], cpu_fir[ra]); + } else { + /* CPYS */ + gen_fcpys(ra, rb, rc); + } + } + break; + case 0x021: + /* CPYSN */ + gen_fcpysn(ra, rb, rc); + break; + case 0x022: + /* CPYSE */ + gen_fcpyse(ra, rb, rc); + break; + case 0x024: + /* MT_FPCR */ + if (likely(ra != 31)) + gen_helper_store_fpcr(cpu_env, cpu_fir[ra]); + else { + TCGv tmp = tcg_const_i64(0); + gen_helper_store_fpcr(cpu_env, tmp); + tcg_temp_free(tmp); + } + break; + case 0x025: + /* MF_FPCR */ + if (likely(ra != 31)) + gen_helper_load_fpcr(cpu_fir[ra], cpu_env); + break; + case 0x02A: + /* FCMOVEQ */ + gen_fcmov(TCG_COND_EQ, ra, rb, rc); + break; + case 0x02B: + /* FCMOVNE */ + gen_fcmov(TCG_COND_NE, ra, rb, rc); + break; + case 0x02C: + /* FCMOVLT */ + gen_fcmov(TCG_COND_LT, ra, rb, rc); + break; + case 0x02D: + /* FCMOVGE */ + gen_fcmov(TCG_COND_GE, ra, rb, rc); + break; + case 0x02E: + /* FCMOVLE */ + gen_fcmov(TCG_COND_LE, ra, rb, rc); + break; + case 0x02F: + /* FCMOVGT */ + gen_fcmov(TCG_COND_GT, ra, rb, rc); + break; + case 0x030: + /* CVTQL */ + gen_fcvtql(rb, rc); + break; + case 0x130: + /* CVTQL/V */ + case 0x530: + /* CVTQL/SV */ + /* ??? I'm pretty sure there's nothing that /sv needs to do that + /v doesn't do. The only thing I can think is that /sv is a + valid instruction merely for completeness in the ISA. */ + gen_fcvtql_v(ctx, rb, rc); + break; + default: + goto invalid_opc; + } + break; + case 0x18: + switch ((uint16_t)disp16) { + case 0x0000: + /* TRAPB */ + /* No-op. */ + break; + case 0x0400: + /* EXCB */ + /* No-op. */ + break; + case 0x4000: + /* MB */ + /* No-op */ + break; + case 0x4400: + /* WMB */ + /* No-op */ + break; + case 0x8000: + /* FETCH */ + /* No-op */ + break; + case 0xA000: + /* FETCH_M */ + /* No-op */ + break; + case 0xC000: + /* RPCC */ + if (ra != 31) { + if (use_icount) { + gen_io_start(); + gen_helper_load_pcc(cpu_ir[ra], cpu_env); + gen_io_end(); + ret = EXIT_PC_STALE; + } else { + gen_helper_load_pcc(cpu_ir[ra], cpu_env); + } + } + break; + case 0xE000: + /* RC */ + gen_rx(ra, 0); + break; + case 0xE800: + /* ECB */ + break; + case 0xF000: + /* RS */ + gen_rx(ra, 1); + break; + case 0xF800: + /* WH64 */ + /* No-op */ + break; + default: + goto invalid_opc; + } + break; + case 0x19: + /* HW_MFPR (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + return gen_mfpr(ra, insn & 0xffff); + } +#endif + goto invalid_opc; + case 0x1A: + /* JMP, JSR, RET, JSR_COROUTINE. These only differ by the branch + prediction stack action, which of course we don't implement. */ + if (rb != 31) { + tcg_gen_andi_i64(cpu_pc, cpu_ir[rb], ~3); + } else { + tcg_gen_movi_i64(cpu_pc, 0); + } + if (ra != 31) { + tcg_gen_movi_i64(cpu_ir[ra], ctx->pc); + } + ret = EXIT_PC_UPDATED; + break; + case 0x1B: + /* HW_LD (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + TCGv addr; + + if (ra == 31) { + break; + } + + addr = tcg_temp_new(); + if (rb != 31) + tcg_gen_addi_i64(addr, cpu_ir[rb], disp12); + else + tcg_gen_movi_i64(addr, disp12); + switch ((insn >> 12) & 0xF) { + case 0x0: + /* Longword physical access (hw_ldl/p) */ + gen_helper_ldl_phys(cpu_ir[ra], addr); + break; + case 0x1: + /* Quadword physical access (hw_ldq/p) */ + gen_helper_ldq_phys(cpu_ir[ra], addr); + break; + case 0x2: + /* Longword physical access with lock (hw_ldl_l/p) */ + gen_helper_ldl_l_phys(cpu_ir[ra], cpu_env, addr); + break; + case 0x3: + /* Quadword physical access with lock (hw_ldq_l/p) */ + gen_helper_ldq_l_phys(cpu_ir[ra], cpu_env, addr); + break; + case 0x4: + /* Longword virtual PTE fetch (hw_ldl/v) */ + goto invalid_opc; + case 0x5: + /* Quadword virtual PTE fetch (hw_ldq/v) */ + goto invalid_opc; + break; + case 0x6: + /* Incpu_ir[ra]id */ + goto invalid_opc; + case 0x7: + /* Incpu_ir[ra]id */ + goto invalid_opc; + case 0x8: + /* Longword virtual access (hw_ldl) */ + goto invalid_opc; + case 0x9: + /* Quadword virtual access (hw_ldq) */ + goto invalid_opc; + case 0xA: + /* Longword virtual access with protection check (hw_ldl/w) */ + tcg_gen_qemu_ld32s(cpu_ir[ra], addr, MMU_KERNEL_IDX); + break; + case 0xB: + /* Quadword virtual access with protection check (hw_ldq/w) */ + tcg_gen_qemu_ld64(cpu_ir[ra], addr, MMU_KERNEL_IDX); + break; + case 0xC: + /* Longword virtual access with alt access mode (hw_ldl/a)*/ + goto invalid_opc; + case 0xD: + /* Quadword virtual access with alt access mode (hw_ldq/a) */ + goto invalid_opc; + case 0xE: + /* Longword virtual access with alternate access mode and + protection checks (hw_ldl/wa) */ + tcg_gen_qemu_ld32s(cpu_ir[ra], addr, MMU_USER_IDX); + break; + case 0xF: + /* Quadword virtual access with alternate access mode and + protection checks (hw_ldq/wa) */ + tcg_gen_qemu_ld64(cpu_ir[ra], addr, MMU_USER_IDX); + break; + } + tcg_temp_free(addr); + break; + } +#endif + goto invalid_opc; + case 0x1C: + switch (fn7) { + case 0x00: + /* SEXTB */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_BWX) == 0) { + goto invalid_opc; + } + if (likely(rc != 31)) { + if (islit) + tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int8_t)lit)); + else + tcg_gen_ext8s_i64(cpu_ir[rc], cpu_ir[rb]); + } + break; + case 0x01: + /* SEXTW */ + if (ctx->tb->flags & TB_FLAGS_AMASK_BWX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], (int64_t)((int16_t)lit)); + } else { + tcg_gen_ext16s_i64(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + } + goto invalid_opc; + case 0x30: + /* CTPOP */ + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], ctpop64(lit)); + } else { + gen_helper_ctpop(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + } + goto invalid_opc; + case 0x31: + /* PERR */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_perr(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x32: + /* CTLZ */ + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], clz64(lit)); + } else { + gen_helper_ctlz(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + } + goto invalid_opc; + case 0x33: + /* CTTZ */ + if (ctx->tb->flags & TB_FLAGS_AMASK_CIX) { + if (likely(rc != 31)) { + if (islit) { + tcg_gen_movi_i64(cpu_ir[rc], ctz64(lit)); + } else { + gen_helper_cttz(cpu_ir[rc], cpu_ir[rb]); + } + } + break; + } + goto invalid_opc; + case 0x34: + /* UNPKBW */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_unpkbw(rb, rc); + break; + } + goto invalid_opc; + case 0x35: + /* UNPKBL */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_unpkbl(rb, rc); + break; + } + goto invalid_opc; + case 0x36: + /* PKWB */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_pkwb(rb, rc); + break; + } + goto invalid_opc; + case 0x37: + /* PKLB */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + if (real_islit || ra != 31) { + goto invalid_opc; + } + gen_pklb(rb, rc); + break; + } + goto invalid_opc; + case 0x38: + /* MINSB8 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minsb8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x39: + /* MINSW4 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minsw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3A: + /* MINUB8 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minub8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3B: + /* MINUW4 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_minuw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3C: + /* MAXUB8 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxub8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3D: + /* MAXUW4 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxuw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3E: + /* MAXSB8 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxsb8(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x3F: + /* MAXSW4 */ + if (ctx->tb->flags & TB_FLAGS_AMASK_MVI) { + gen_maxsw4(ra, rb, rc, islit, lit); + break; + } + goto invalid_opc; + case 0x70: + /* FTOIT */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { + goto invalid_opc; + } + if (likely(rc != 31)) { + if (ra != 31) + tcg_gen_mov_i64(cpu_ir[rc], cpu_fir[ra]); + else + tcg_gen_movi_i64(cpu_ir[rc], 0); + } + break; + case 0x78: + /* FTOIS */ + if ((ctx->tb->flags & TB_FLAGS_AMASK_FIX) == 0) { + goto invalid_opc; + } + if (rc != 31) { + TCGv_i32 tmp1 = tcg_temp_new_i32(); + if (ra != 31) + gen_helper_s_to_memory(tmp1, cpu_fir[ra]); + else { + TCGv tmp2 = tcg_const_i64(0); + gen_helper_s_to_memory(tmp1, tmp2); + tcg_temp_free(tmp2); + } + tcg_gen_ext_i32_i64(cpu_ir[rc], tmp1); + tcg_temp_free_i32(tmp1); + } + break; + default: + goto invalid_opc; + } + break; + case 0x1D: + /* HW_MTPR (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + return gen_mtpr(ctx, rb, insn & 0xffff); + } +#endif + goto invalid_opc; + case 0x1E: + /* HW_RET (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + if (rb == 31) { + /* Pre-EV6 CPUs interpreted this as HW_REI, loading the return + address from EXC_ADDR. This turns out to be useful for our + emulation PALcode, so continue to accept it. */ + TCGv tmp = tcg_temp_new(); + tcg_gen_ld_i64(tmp, cpu_env, offsetof(CPUAlphaState, exc_addr)); + gen_helper_hw_ret(cpu_env, tmp); + tcg_temp_free(tmp); + } else { + gen_helper_hw_ret(cpu_env, cpu_ir[rb]); + } + ret = EXIT_PC_UPDATED; + break; + } +#endif + goto invalid_opc; + case 0x1F: + /* HW_ST (PALcode) */ +#ifndef CONFIG_USER_ONLY + if (ctx->tb->flags & TB_FLAGS_PAL_MODE) { + TCGv addr, val; + addr = tcg_temp_new(); + if (rb != 31) + tcg_gen_addi_i64(addr, cpu_ir[rb], disp12); + else + tcg_gen_movi_i64(addr, disp12); + if (ra != 31) + val = cpu_ir[ra]; + else { + val = tcg_temp_new(); + tcg_gen_movi_i64(val, 0); + } + switch ((insn >> 12) & 0xF) { + case 0x0: + /* Longword physical access */ + gen_helper_stl_phys(addr, val); + break; + case 0x1: + /* Quadword physical access */ + gen_helper_stq_phys(addr, val); + break; + case 0x2: + /* Longword physical access with lock */ + gen_helper_stl_c_phys(val, cpu_env, addr, val); + break; + case 0x3: + /* Quadword physical access with lock */ + gen_helper_stq_c_phys(val, cpu_env, addr, val); + break; + case 0x4: + /* Longword virtual access */ + goto invalid_opc; + case 0x5: + /* Quadword virtual access */ + goto invalid_opc; + case 0x6: + /* Invalid */ + goto invalid_opc; + case 0x7: + /* Invalid */ + goto invalid_opc; + case 0x8: + /* Invalid */ + goto invalid_opc; + case 0x9: + /* Invalid */ + goto invalid_opc; + case 0xA: + /* Invalid */ + goto invalid_opc; + case 0xB: + /* Invalid */ + goto invalid_opc; + case 0xC: + /* Longword virtual access with alternate access mode */ + goto invalid_opc; + case 0xD: + /* Quadword virtual access with alternate access mode */ + goto invalid_opc; + case 0xE: + /* Invalid */ + goto invalid_opc; + case 0xF: + /* Invalid */ + goto invalid_opc; + } + if (ra == 31) + tcg_temp_free(val); + tcg_temp_free(addr); + break; + } +#endif + goto invalid_opc; + case 0x20: + /* LDF */ + gen_load_mem(ctx, &gen_qemu_ldf, ra, rb, disp16, 1, 0); + break; + case 0x21: + /* LDG */ + gen_load_mem(ctx, &gen_qemu_ldg, ra, rb, disp16, 1, 0); + break; + case 0x22: + /* LDS */ + gen_load_mem(ctx, &gen_qemu_lds, ra, rb, disp16, 1, 0); + break; + case 0x23: + /* LDT */ + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 1, 0); + break; + case 0x24: + /* STF */ + gen_store_mem(ctx, &gen_qemu_stf, ra, rb, disp16, 1, 0); + break; + case 0x25: + /* STG */ + gen_store_mem(ctx, &gen_qemu_stg, ra, rb, disp16, 1, 0); + break; + case 0x26: + /* STS */ + gen_store_mem(ctx, &gen_qemu_sts, ra, rb, disp16, 1, 0); + break; + case 0x27: + /* STT */ + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 1, 0); + break; + case 0x28: + /* LDL */ + gen_load_mem(ctx, &tcg_gen_qemu_ld32s, ra, rb, disp16, 0, 0); + break; + case 0x29: + /* LDQ */ + gen_load_mem(ctx, &tcg_gen_qemu_ld64, ra, rb, disp16, 0, 0); + break; + case 0x2A: + /* LDL_L */ + gen_load_mem(ctx, &gen_qemu_ldl_l, ra, rb, disp16, 0, 0); + break; + case 0x2B: + /* LDQ_L */ + gen_load_mem(ctx, &gen_qemu_ldq_l, ra, rb, disp16, 0, 0); + break; + case 0x2C: + /* STL */ + gen_store_mem(ctx, &tcg_gen_qemu_st32, ra, rb, disp16, 0, 0); + break; + case 0x2D: + /* STQ */ + gen_store_mem(ctx, &tcg_gen_qemu_st64, ra, rb, disp16, 0, 0); + break; + case 0x2E: + /* STL_C */ + ret = gen_store_conditional(ctx, ra, rb, disp16, 0); + break; + case 0x2F: + /* STQ_C */ + ret = gen_store_conditional(ctx, ra, rb, disp16, 1); + break; + case 0x30: + /* BR */ + ret = gen_bdirect(ctx, ra, disp21); + break; + case 0x31: /* FBEQ */ + ret = gen_fbcond(ctx, TCG_COND_EQ, ra, disp21); + break; + case 0x32: /* FBLT */ + ret = gen_fbcond(ctx, TCG_COND_LT, ra, disp21); + break; + case 0x33: /* FBLE */ + ret = gen_fbcond(ctx, TCG_COND_LE, ra, disp21); + break; + case 0x34: + /* BSR */ + ret = gen_bdirect(ctx, ra, disp21); + break; + case 0x35: /* FBNE */ + ret = gen_fbcond(ctx, TCG_COND_NE, ra, disp21); + break; + case 0x36: /* FBGE */ + ret = gen_fbcond(ctx, TCG_COND_GE, ra, disp21); + break; + case 0x37: /* FBGT */ + ret = gen_fbcond(ctx, TCG_COND_GT, ra, disp21); + break; + case 0x38: + /* BLBC */ + ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 1); + break; + case 0x39: + /* BEQ */ + ret = gen_bcond(ctx, TCG_COND_EQ, ra, disp21, 0); + break; + case 0x3A: + /* BLT */ + ret = gen_bcond(ctx, TCG_COND_LT, ra, disp21, 0); + break; + case 0x3B: + /* BLE */ + ret = gen_bcond(ctx, TCG_COND_LE, ra, disp21, 0); + break; + case 0x3C: + /* BLBS */ + ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 1); + break; + case 0x3D: + /* BNE */ + ret = gen_bcond(ctx, TCG_COND_NE, ra, disp21, 0); + break; + case 0x3E: + /* BGE */ + ret = gen_bcond(ctx, TCG_COND_GE, ra, disp21, 0); + break; + case 0x3F: + /* BGT */ + ret = gen_bcond(ctx, TCG_COND_GT, ra, disp21, 0); + break; + invalid_opc: + ret = gen_invalid(ctx); + break; + } + + return ret; +} + +static inline void gen_intermediate_code_internal(CPUAlphaState *env, + TranslationBlock *tb, + int search_pc) +{ + DisasContext ctx, *ctxp = &ctx; + target_ulong pc_start; + uint32_t insn; + uint16_t *gen_opc_end; + CPUBreakpoint *bp; + int j, lj = -1; + ExitStatus ret; + int num_insns; + int max_insns; + + pc_start = tb->pc; + gen_opc_end = gen_opc_buf + OPC_MAX_SIZE; + + ctx.tb = tb; + ctx.env = env; + ctx.pc = pc_start; + ctx.mem_idx = cpu_mmu_index(env); + + /* ??? Every TB begins with unset rounding mode, to be initialized on + the first fp insn of the TB. Alternately we could define a proper + default for every TB (e.g. QUAL_RM_N or QUAL_RM_D) and make sure + to reset the FP_STATUS to that default at the end of any TB that + changes the default. We could even (gasp) dynamiclly figure out + what default would be most efficient given the running program. */ + ctx.tb_rm = -1; + /* Similarly for flush-to-zero. */ + ctx.tb_ftz = -1; + + num_insns = 0; + max_insns = tb->cflags & CF_COUNT_MASK; + if (max_insns == 0) + max_insns = CF_COUNT_MASK; + + gen_icount_start(); + do { + if (unlikely(!QTAILQ_EMPTY(&env->breakpoints))) { + QTAILQ_FOREACH(bp, &env->breakpoints, entry) { + if (bp->pc == ctx.pc) { + gen_excp(&ctx, EXCP_DEBUG, 0); + break; + } + } + } + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + if (lj < j) { + lj++; + while (lj < j) + gen_opc_instr_start[lj++] = 0; + } + gen_opc_pc[lj] = ctx.pc; + gen_opc_instr_start[lj] = 1; + gen_opc_icount[lj] = num_insns; + } + if (num_insns + 1 == max_insns && (tb->cflags & CF_LAST_IO)) + gen_io_start(); + insn = cpu_ldl_code(env, ctx.pc); + num_insns++; + + if (unlikely(qemu_loglevel_mask(CPU_LOG_TB_OP))) { + tcg_gen_debug_insn_start(ctx.pc); + } + + ctx.pc += 4; + ret = translate_one(ctxp, insn); + + /* If we reach a page boundary, are single stepping, + or exhaust instruction count, stop generation. */ + if (ret == NO_EXIT + && ((ctx.pc & (TARGET_PAGE_SIZE - 1)) == 0 + || gen_opc_ptr >= gen_opc_end + || num_insns >= max_insns + || singlestep + || env->singlestep_enabled)) { + ret = EXIT_PC_STALE; + } + } while (ret == NO_EXIT); + + if (tb->cflags & CF_LAST_IO) { + gen_io_end(); + } + + switch (ret) { + case EXIT_GOTO_TB: + case EXIT_NORETURN: + break; + case EXIT_PC_STALE: + tcg_gen_movi_i64(cpu_pc, ctx.pc); + /* FALLTHRU */ + case EXIT_PC_UPDATED: + if (env->singlestep_enabled) { + gen_excp_1(EXCP_DEBUG, 0); + } else { + tcg_gen_exit_tb(0); + } + break; + default: + abort(); + } + + gen_icount_end(tb, num_insns); + *gen_opc_ptr = INDEX_op_end; + if (search_pc) { + j = gen_opc_ptr - gen_opc_buf; + lj++; + while (lj <= j) + gen_opc_instr_start[lj++] = 0; + } else { + tb->size = ctx.pc - pc_start; + tb->icount = num_insns; + } + +#ifdef DEBUG_DISAS + if (qemu_loglevel_mask(CPU_LOG_TB_IN_ASM)) { + qemu_log("IN: %s\n", lookup_symbol(pc_start)); + log_target_disas(pc_start, ctx.pc - pc_start, 1); + qemu_log("\n"); + } +#endif +} + +void gen_intermediate_code (CPUAlphaState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 0); +} + +void gen_intermediate_code_pc (CPUAlphaState *env, struct TranslationBlock *tb) +{ + gen_intermediate_code_internal(env, tb, 1); +} + +struct cpu_def_t { + const char *name; + int implver, amask; +}; + +static const struct cpu_def_t cpu_defs[] = { + { "ev4", IMPLVER_2106x, 0 }, + { "ev5", IMPLVER_21164, 0 }, + { "ev56", IMPLVER_21164, AMASK_BWX }, + { "pca56", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, + { "ev6", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, + { "ev67", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, + { "ev68", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), }, + { "21064", IMPLVER_2106x, 0 }, + { "21164", IMPLVER_21164, 0 }, + { "21164a", IMPLVER_21164, AMASK_BWX }, + { "21164pc", IMPLVER_21164, AMASK_BWX | AMASK_MVI }, + { "21264", IMPLVER_21264, AMASK_BWX | AMASK_FIX | AMASK_MVI | AMASK_TRAP }, + { "21264a", IMPLVER_21264, (AMASK_BWX | AMASK_FIX | AMASK_CIX + | AMASK_MVI | AMASK_TRAP | AMASK_PREFETCH), } +}; + +CPUAlphaState * cpu_alpha_init (const char *cpu_model) +{ + AlphaCPU *cpu; + CPUAlphaState *env; + int implver, amask, i, max; + + cpu = ALPHA_CPU(object_new(TYPE_ALPHA_CPU)); + env = &cpu->env; + + alpha_translate_init(); + + /* Default to ev67; no reason not to emulate insns by default. */ + implver = IMPLVER_21264; + amask = (AMASK_BWX | AMASK_FIX | AMASK_CIX | AMASK_MVI + | AMASK_TRAP | AMASK_PREFETCH); + + max = ARRAY_SIZE(cpu_defs); + for (i = 0; i < max; i++) { + if (strcmp (cpu_model, cpu_defs[i].name) == 0) { + implver = cpu_defs[i].implver; + amask = cpu_defs[i].amask; + break; + } + } + env->implver = implver; + env->amask = amask; + + qemu_init_vcpu(env); + return env; +} + +void restore_state_to_opc(CPUAlphaState *env, TranslationBlock *tb, int pc_pos) +{ + env->pc = gen_opc_pc[pc_pos]; +} -- cgit v1.2.3