summaryrefslogtreecommitdiff
path: root/target-alpha
diff options
context:
space:
mode:
authorAnas Nashif <anas.nashif@intel.com>2012-11-06 07:50:24 -0800
committerAnas Nashif <anas.nashif@intel.com>2012-11-06 07:50:24 -0800
commit060629c6ef0b7e5c267d84c91600113264d33120 (patch)
tree18fcb144ac71b9c4d08ee5d1dc58e2b16c109a5a /target-alpha
downloadqemu-060629c6ef0b7e5c267d84c91600113264d33120.tar.gz
qemu-060629c6ef0b7e5c267d84c91600113264d33120.tar.bz2
qemu-060629c6ef0b7e5c267d84c91600113264d33120.zip
Imported Upstream version 1.2.0upstream/1.2.0
Diffstat (limited to 'target-alpha')
-rw-r--r--target-alpha/Makefile.objs3
-rw-r--r--target-alpha/STATUS28
-rw-r--r--target-alpha/cpu-qom.h71
-rw-r--r--target-alpha/cpu.c58
-rw-r--r--target-alpha/cpu.h535
-rw-r--r--target-alpha/fpu_helper.c822
-rw-r--r--target-alpha/helper.c532
-rw-r--r--target-alpha/helper.h122
-rw-r--r--target-alpha/int_helper.c319
-rw-r--r--target-alpha/machine.c87
-rw-r--r--target-alpha/mem_helper.c151
-rw-r--r--target-alpha/sys_helper.c87
-rw-r--r--target-alpha/translate.c3560
13 files changed, 6375 insertions, 0 deletions
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
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+#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
+ * <http://www.gnu.org/licenses/lgpl-2.1.html>
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdio.h>
+
+#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 = "<unknown>";
+
+ 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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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 <http://www.gnu.org/licenses/>.
+ */
+
+#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];
+}