aboutsummaryrefslogtreecommitdiffstats
path: root/target-ppc
diff options
context:
space:
mode:
Diffstat (limited to 'target-ppc')
-rw-r--r--target-ppc/cpu.h1025
-rw-r--r--target-ppc/exec.h90
-rw-r--r--target-ppc/helper.c1458
-rw-r--r--target-ppc/op.c1296
-rw-r--r--target-ppc/op_helper.c589
-rw-r--r--target-ppc/op_helper_mem.h100
-rw-r--r--target-ppc/op_mem.h371
-rw-r--r--target-ppc/op_template.h183
-rw-r--r--target-ppc/translate.c2701
-rw-r--r--target-ppc/translate_init.c2067
10 files changed, 9880 insertions, 0 deletions
diff --git a/target-ppc/cpu.h b/target-ppc/cpu.h
new file mode 100644
index 0000000..88d9135
--- /dev/null
+++ b/target-ppc/cpu.h
@@ -0,0 +1,1025 @@
+/*
+ * PowerPC emulation cpu definitions for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if !defined (__CPU_PPC_H__)
+#define __CPU_PPC_H__
+
+#include "config.h"
+
+#define TARGET_LONG_BITS 32
+
+#include "cpu-defs.h"
+
+#include <setjmp.h>
+
+#include "softfloat.h"
+
+#define TARGET_HAS_ICE 1
+
+/* XXX: this should be tunable: PowerPC 601 & 64 bits PowerPC
+ * have different cache line sizes
+ */
+#define ICACHE_LINE_SIZE 32
+#define DCACHE_LINE_SIZE 32
+
+/* XXX: put this in a common place */
+#define likely(x) __builtin_expect(!!(x), 1)
+
+/*****************************************************************************/
+/* PVR definitions for most known PowerPC */
+enum {
+ /* PowerPC 401 cores */
+ CPU_PPC_401A1 = 0x00210000,
+ CPU_PPC_401B2 = 0x00220000,
+ CPU_PPC_401C2 = 0x00230000,
+ CPU_PPC_401D2 = 0x00240000,
+ CPU_PPC_401E2 = 0x00250000,
+ CPU_PPC_401F2 = 0x00260000,
+ CPU_PPC_401G2 = 0x00270000,
+ CPU_PPC_IOP480 = 0x40100000,
+ /* PowerPC 403 cores */
+ CPU_PPC_403GA = 0x00200000,
+ CPU_PPC_403GB = 0x00200100,
+ CPU_PPC_403GC = 0x00200200,
+ CPU_PPC_403GCX = 0x00201400,
+ /* PowerPC 405 cores */
+ CPU_PPC_405 = 0x40110000,
+ CPU_PPC_405EP = 0x51210000,
+ CPU_PPC_405GPR = 0x50910000,
+ CPU_PPC_405D2 = 0x20010000,
+ CPU_PPC_405D4 = 0x41810000,
+ CPU_PPC_NPE405H = 0x41410000,
+ CPU_PPC_NPE405L = 0x41610000,
+#if 0
+ CPU_PPC_STB02 = xxx,
+#endif
+ CPU_PPC_STB03 = 0x40310000,
+#if 0
+ CPU_PPC_STB04 = xxx,
+#endif
+ CPU_PPC_STB25 = 0x51510000,
+#if 0
+ CPU_PPC_STB130 = xxx,
+#endif
+ /* PowerPC 440 cores */
+ CPU_PPC_440EP = 0x42220000,
+ CPU_PPC_440GP = 0x40120400,
+ CPU_PPC_440GX = 0x51B20000,
+ /* PowerPC MPC 8xx cores */
+ CPU_PPC_8540 = 0x80200000,
+ CPU_PPC_8xx = 0x00500000,
+ CPU_PPC_8240 = 0x00810100,
+ CPU_PPC_8245 = 0x00811014,
+ /* PowerPC 6xx cores */
+ CPU_PPC_601 = 0x00010000,
+ CPU_PPC_602 = 0x00050000,
+ CPU_PPC_603 = 0x00030000,
+ CPU_PPC_603E = 0x00060000,
+ CPU_PPC_603EV = 0x00070000,
+ CPU_PPC_603R = 0x00071000,
+ CPU_PPC_G2 = 0x80810000,
+ CPU_PPC_G2LE = 0x80820000,
+ CPU_PPC_604 = 0x00040000,
+ CPU_PPC_604E = 0x00090000,
+ CPU_PPC_604R = 0x000a0000,
+ /* PowerPC 74x/75x cores (aka G3) */
+ CPU_PPC_74x = 0x00080000,
+ CPU_PPC_755 = 0x00083000,
+ CPU_PPC_74xP = 0x10080000,
+ CPU_PPC_750CXE22 = 0x00082202,
+ CPU_PPC_750CXE24 = 0x00082214,
+ CPU_PPC_750CXE24b = 0x00083214,
+ CPU_PPC_750CXE31 = 0x00083211,
+ CPU_PPC_750CXE31b = 0x00083311,
+#define CPU_PPC_750CXE CPU_PPC_750CXE31b
+ CPU_PPC_750FX = 0x70000000,
+ CPU_PPC_750GX = 0x70020000,
+ /* PowerPC 74xx cores (aka G4) */
+ CPU_PPC_7400 = 0x000C0000,
+ CPU_PPC_7410 = 0x800C0000,
+ CPU_PPC_7441 = 0x80000200,
+ CPU_PPC_7450 = 0x80000000,
+ CPU_PPC_7451 = 0x80000203,
+ CPU_PPC_7455 = 0x80010000,
+ CPU_PPC_7457 = 0x80020000,
+ CPU_PPC_7457A = 0x80030000,
+ /* 64 bits PowerPC */
+ CPU_PPC_620 = 0x00140000,
+ CPU_PPC_630 = 0x00400000,
+ CPU_PPC_631 = 0x00410000,
+ CPU_PPC_POWER4 = 0x00350000,
+ CPU_PPC_POWER4P = 0x00380000,
+ CPU_PPC_POWER5 = 0x003A0000,
+ CPU_PPC_POWER5P = 0x003B0000,
+ CPU_PPC_970 = 0x00390000,
+ CPU_PPC_970FX = 0x003C0000,
+ CPU_PPC_RS64 = 0x00330000,
+ CPU_PPC_RS64II = 0x00340000,
+ CPU_PPC_RS64III = 0x00360000,
+ CPU_PPC_RS64IV = 0x00370000,
+ /* Original POWER */
+ /* XXX: should be POWER (RIOS), RSC3308, RSC4608,
+ * POWER2 (RIOS2) & RSC2 (P2SC) here
+ */
+#if 0
+ CPU_POWER = xxx,
+#endif
+#if 0
+ CPU_POWER2 = xxx,
+#endif
+};
+
+/* System version register (used on MPC 8xx) */
+enum {
+ PPC_SVR_8540 = 0x80300000,
+ PPC_SVR_8541E = 0x807A0000,
+ PPC_SVR_8555E = 0x80790000,
+ PPC_SVR_8560 = 0x80700000,
+};
+
+/*****************************************************************************/
+/* Instruction types */
+enum {
+ PPC_NONE = 0x00000000,
+ /* integer operations instructions */
+ /* flow control instructions */
+ /* virtual memory instructions */
+ /* ld/st with reservation instructions */
+ /* cache control instructions */
+ /* spr/msr access instructions */
+ PPC_INSNS_BASE = 0x00000001,
+#define PPC_INTEGER PPC_INSNS_BASE
+#define PPC_FLOW PPC_INSNS_BASE
+#define PPC_MEM PPC_INSNS_BASE
+#define PPC_RES PPC_INSNS_BASE
+#define PPC_CACHE PPC_INSNS_BASE
+#define PPC_MISC PPC_INSNS_BASE
+ /* floating point operations instructions */
+ PPC_FLOAT = 0x00000002,
+ /* more floating point operations instructions */
+ PPC_FLOAT_EXT = 0x00000004,
+ /* external control instructions */
+ PPC_EXTERN = 0x00000008,
+ /* segment register access instructions */
+ PPC_SEGMENT = 0x00000010,
+ /* Optional cache control instructions */
+ PPC_CACHE_OPT = 0x00000020,
+ /* Optional floating point op instructions */
+ PPC_FLOAT_OPT = 0x00000040,
+ /* Optional memory control instructions */
+ PPC_MEM_TLBIA = 0x00000080,
+ PPC_MEM_TLBIE = 0x00000100,
+ PPC_MEM_TLBSYNC = 0x00000200,
+ /* eieio & sync */
+ PPC_MEM_SYNC = 0x00000400,
+ /* PowerPC 6xx TLB management instructions */
+ PPC_6xx_TLB = 0x00000800,
+ /* Altivec support */
+ PPC_ALTIVEC = 0x00001000,
+ /* Time base support */
+ PPC_TB = 0x00002000,
+ /* Embedded PowerPC dedicated instructions */
+ PPC_4xx_COMMON = 0x00004000,
+ /* PowerPC 40x exception model */
+ PPC_40x_EXCP = 0x00008000,
+ /* PowerPC 40x specific instructions */
+ PPC_40x_SPEC = 0x00010000,
+ /* PowerPC 405 Mac instructions */
+ PPC_405_MAC = 0x00020000,
+ /* PowerPC 440 specific instructions */
+ PPC_440_SPEC = 0x00040000,
+ /* Specific extensions */
+ /* Power-to-PowerPC bridge (601) */
+ PPC_POWER_BR = 0x00080000,
+ /* PowerPC 602 specific */
+ PPC_602_SPEC = 0x00100000,
+ /* Deprecated instructions */
+ /* Original POWER instruction set */
+ PPC_POWER = 0x00200000,
+ /* POWER2 instruction set extension */
+ PPC_POWER2 = 0x00400000,
+ /* Power RTC support */
+ PPC_POWER_RTC = 0x00800000,
+ /* 64 bits PowerPC instructions */
+ /* 64 bits PowerPC instruction set */
+ PPC_64B = 0x01000000,
+ /* 64 bits hypervisor extensions */
+ PPC_64H = 0x02000000,
+ /* 64 bits PowerPC "bridge" features */
+ PPC_64_BRIDGE = 0x04000000,
+};
+
+/* CPU run-time flags (MMU and exception model) */
+enum {
+ /* MMU model */
+#define PPC_FLAGS_MMU_MASK (0x0000000F)
+ /* Standard 32 bits PowerPC MMU */
+ PPC_FLAGS_MMU_32B = 0x00000000,
+ /* Standard 64 bits PowerPC MMU */
+ PPC_FLAGS_MMU_64B = 0x00000001,
+ /* PowerPC 601 MMU */
+ PPC_FLAGS_MMU_601 = 0x00000002,
+ /* PowerPC 6xx MMU with software TLB */
+ PPC_FLAGS_MMU_SOFT_6xx = 0x00000003,
+ /* PowerPC 4xx MMU with software TLB */
+ PPC_FLAGS_MMU_SOFT_4xx = 0x00000004,
+ /* PowerPC 403 MMU */
+ PPC_FLAGS_MMU_403 = 0x00000005,
+ /* Exception model */
+#define PPC_FLAGS_EXCP_MASK (0x000000F0)
+ /* Standard PowerPC exception model */
+ PPC_FLAGS_EXCP_STD = 0x00000000,
+ /* PowerPC 40x exception model */
+ PPC_FLAGS_EXCP_40x = 0x00000010,
+ /* PowerPC 601 exception model */
+ PPC_FLAGS_EXCP_601 = 0x00000020,
+ /* PowerPC 602 exception model */
+ PPC_FLAGS_EXCP_602 = 0x00000030,
+ /* PowerPC 603 exception model */
+ PPC_FLAGS_EXCP_603 = 0x00000040,
+ /* PowerPC 604 exception model */
+ PPC_FLAGS_EXCP_604 = 0x00000050,
+ /* PowerPC 7x0 exception model */
+ PPC_FLAGS_EXCP_7x0 = 0x00000060,
+ /* PowerPC 7x5 exception model */
+ PPC_FLAGS_EXCP_7x5 = 0x00000070,
+ /* PowerPC 74xx exception model */
+ PPC_FLAGS_EXCP_74xx = 0x00000080,
+ /* PowerPC 970 exception model */
+ PPC_FLAGS_EXCP_970 = 0x00000090,
+};
+
+#define PPC_MMU(env) (env->flags & PPC_FLAGS_MMU_MASK)
+#define PPC_EXCP(env) (env->flags & PPC_FLAGS_EXCP_MASK)
+
+/*****************************************************************************/
+/* Supported instruction set definitions */
+/* This generates an empty opcode table... */
+#define PPC_INSNS_TODO (PPC_NONE)
+#define PPC_FLAGS_TODO (0x00000000)
+
+/* PowerPC 40x instruction set */
+#define PPC_INSNS_4xx (PPC_INSNS_BASE | PPC_MEM_TLBSYNC | PPC_4xx_COMMON)
+/* PowerPC 401 */
+#define PPC_INSNS_401 (PPC_INSNS_TODO)
+#define PPC_FLAGS_401 (PPC_FLAGS_TODO)
+/* PowerPC 403 */
+#define PPC_INSNS_403 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_MEM_TLBIA | \
+ PPC_40x_EXCP | PPC_40x_SPEC)
+#define PPC_FLAGS_403 (PPC_FLAGS_MMU_403 | PPC_FLAGS_EXCP_40x)
+/* PowerPC 405 */
+#define PPC_INSNS_405 (PPC_INSNS_4xx | PPC_MEM_SYNC | PPC_CACHE_OPT | \
+ PPC_MEM_TLBIA | PPC_TB | PPC_40x_SPEC | PPC_40x_EXCP | \
+ PPC_405_MAC)
+#define PPC_FLAGS_405 (PPC_FLAGS_MMU_SOFT_4xx | PPC_FLAGS_EXCP_40x)
+/* PowerPC 440 */
+#define PPC_INSNS_440 (PPC_INSNS_4xx | PPC_CACHE_OPT | PPC_405_MAC | \
+ PPC_440_SPEC)
+#define PPC_FLAGS_440 (PPC_FLAGS_TODO)
+/* Non-embedded PowerPC */
+#define PPC_INSNS_COMMON (PPC_INSNS_BASE | PPC_FLOAT | PPC_MEM_SYNC | \
+ PPC_SEGMENT | PPC_MEM_TLBIE)
+/* PowerPC 601 */
+#define PPC_INSNS_601 (PPC_INSNS_COMMON | PPC_EXTERN | PPC_POWER_BR)
+#define PPC_FLAGS_601 (PPC_FLAGS_MMU_601 | PPC_FLAGS_EXCP_601)
+/* PowerPC 602 */
+#define PPC_INSNS_602 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_602 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_602)
+/* PowerPC 603 */
+#define PPC_INSNS_603 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB)
+#define PPC_FLAGS_603 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603)
+/* PowerPC G2 */
+#define PPC_INSNS_G2 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_6xx_TLB | \
+ PPC_MEM_TLBSYNC | PPC_EXTERN | PPC_TB)
+#define PPC_FLAGS_G2 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_603)
+/* PowerPC 604 */
+#define PPC_INSNS_604 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_604 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_604)
+/* PowerPC 740/750 (aka G3) */
+#define PPC_INSNS_7x0 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_7x0 (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_7x0)
+/* PowerPC 745/755 */
+#define PPC_INSNS_7x5 (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_EXTERN | \
+ PPC_MEM_TLBSYNC | PPC_TB | PPC_6xx_TLB)
+#define PPC_FLAGS_7x5 (PPC_FLAGS_MMU_SOFT_6xx | PPC_FLAGS_EXCP_7x5)
+/* PowerPC 74xx (aka G4) */
+#define PPC_INSNS_74xx (PPC_INSNS_COMMON | PPC_FLOAT_EXT | PPC_ALTIVEC | \
+ PPC_MEM_TLBSYNC | PPC_TB)
+#define PPC_FLAGS_74xx (PPC_FLAGS_MMU_32B | PPC_FLAGS_EXCP_74xx)
+
+/* Default PowerPC will be 604/970 */
+#define PPC_INSNS_PPC32 PPC_INSNS_604
+#define PPC_FLAGS_PPC32 PPC_FLAGS_604
+#if 0
+#define PPC_INSNS_PPC64 PPC_INSNS_970
+#define PPC_FLAGS_PPC64 PPC_FLAGS_970
+#endif
+#define PPC_INSNS_DEFAULT PPC_INSNS_604
+#define PPC_FLAGS_DEFAULT PPC_FLAGS_604
+typedef struct ppc_def_t ppc_def_t;
+
+/*****************************************************************************/
+/* Types used to describe some PowerPC registers */
+typedef struct CPUPPCState CPUPPCState;
+typedef struct opc_handler_t opc_handler_t;
+typedef struct ppc_tb_t ppc_tb_t;
+typedef struct ppc_spr_t ppc_spr_t;
+typedef struct ppc_dcr_t ppc_dcr_t;
+typedef struct ppc_avr_t ppc_avr_t;
+
+/* SPR access micro-ops generations callbacks */
+struct ppc_spr_t {
+ void (*uea_read)(void *opaque, int spr_num);
+ void (*uea_write)(void *opaque, int spr_num);
+ void (*oea_read)(void *opaque, int spr_num);
+ void (*oea_write)(void *opaque, int spr_num);
+ const unsigned char *name;
+};
+
+/* Altivec registers (128 bits) */
+struct ppc_avr_t {
+ uint32_t u[4];
+};
+
+/* Software TLB cache */
+typedef struct ppc_tlb_t ppc_tlb_t;
+struct ppc_tlb_t {
+ /* Physical page number */
+ target_phys_addr_t RPN;
+ /* Virtual page number */
+ target_ulong VPN;
+ /* Page size */
+ target_ulong size;
+ /* Protection bits */
+ int prot;
+ int is_user;
+ uint32_t private;
+ uint32_t flags;
+};
+
+/*****************************************************************************/
+/* Machine state register bits definition */
+#define MSR_SF 63 /* Sixty-four-bit mode */
+#define MSR_ISF 61 /* Sixty-four-bit interrupt mode on 630 */
+#define MSR_HV 60 /* hypervisor state */
+#define MSR_VR 25 /* altivec available */
+#define MSR_AP 23 /* Access privilege state on 602 */
+#define MSR_SA 22 /* Supervisor access mode on 602 */
+#define MSR_KEY 19 /* key bit on 603e */
+#define MSR_POW 18 /* Power management */
+#define MSR_WE 18 /* Wait state enable on embedded PowerPC */
+#define MSR_TGPR 17 /* TGPR usage on 602/603 */
+#define MSR_TLB 17 /* TLB on ? */
+#define MSR_CE 17 /* Critical interrupt enable on embedded PowerPC */
+#define MSR_ILE 16 /* Interrupt little-endian mode */
+#define MSR_EE 15 /* External interrupt enable */
+#define MSR_PR 14 /* Problem state */
+#define MSR_FP 13 /* Floating point available */
+#define MSR_ME 12 /* Machine check interrupt enable */
+#define MSR_FE0 11 /* Floating point exception mode 0 */
+#define MSR_SE 10 /* Single-step trace enable */
+#define MSR_DWE 10 /* Debug wait enable on 405 */
+#define MSR_BE 9 /* Branch trace enable */
+#define MSR_DE 9 /* Debug interrupts enable on embedded PowerPC */
+#define MSR_FE1 8 /* Floating point exception mode 1 */
+#define MSR_AL 7 /* AL bit on POWER */
+#define MSR_IP 6 /* Interrupt prefix */
+#define MSR_IR 5 /* Instruction relocate */
+#define MSR_IS 5 /* Instruction address space on embedded PowerPC */
+#define MSR_DR 4 /* Data relocate */
+#define MSR_DS 4 /* Data address space on embedded PowerPC */
+#define MSR_PE 3 /* Protection enable on 403 */
+#define MSR_EP 3 /* Exception prefix on 601 */
+#define MSR_PX 2 /* Protection exclusive on 403 */
+#define MSR_PMM 2 /* Performance monitor mark on POWER */
+#define MSR_RI 1 /* Recoverable interrupt */
+#define MSR_LE 0 /* Little-endian mode */
+#define msr_sf env->msr[MSR_SF]
+#define msr_isf env->msr[MSR_ISF]
+#define msr_hv env->msr[MSR_HV]
+#define msr_vr env->msr[MSR_VR]
+#define msr_ap env->msr[MSR_AP]
+#define msr_sa env->msr[MSR_SA]
+#define msr_key env->msr[MSR_KEY]
+#define msr_pow env->msr[MSR_POW]
+#define msr_we env->msr[MSR_WE]
+#define msr_tgpr env->msr[MSR_TGPR]
+#define msr_tlb env->msr[MSR_TLB]
+#define msr_ce env->msr[MSR_CE]
+#define msr_ile env->msr[MSR_ILE]
+#define msr_ee env->msr[MSR_EE]
+#define msr_pr env->msr[MSR_PR]
+#define msr_fp env->msr[MSR_FP]
+#define msr_me env->msr[MSR_ME]
+#define msr_fe0 env->msr[MSR_FE0]
+#define msr_se env->msr[MSR_SE]
+#define msr_dwe env->msr[MSR_DWE]
+#define msr_be env->msr[MSR_BE]
+#define msr_de env->msr[MSR_DE]
+#define msr_fe1 env->msr[MSR_FE1]
+#define msr_al env->msr[MSR_AL]
+#define msr_ip env->msr[MSR_IP]
+#define msr_ir env->msr[MSR_IR]
+#define msr_is env->msr[MSR_IS]
+#define msr_dr env->msr[MSR_DR]
+#define msr_ds env->msr[MSR_DS]
+#define msr_pe env->msr[MSR_PE]
+#define msr_ep env->msr[MSR_EP]
+#define msr_px env->msr[MSR_PX]
+#define msr_pmm env->msr[MSR_PMM]
+#define msr_ri env->msr[MSR_RI]
+#define msr_le env->msr[MSR_LE]
+
+/*****************************************************************************/
+/* The whole PowerPC CPU context */
+struct CPUPPCState {
+ /* First are the most commonly used resources
+ * during translated code execution
+ */
+#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, t2;
+#endif
+ /* general purpose registers */
+ target_ulong gpr[32];
+ /* LR */
+ target_ulong lr;
+ /* CTR */
+ target_ulong ctr;
+ /* condition register */
+ uint8_t crf[8];
+ /* XER */
+ /* XXX: We use only 5 fields, but we want to keep the structure aligned */
+ uint8_t xer[8];
+ /* Reservation address */
+ target_ulong reserve;
+
+ /* Those ones are used in supervisor mode only */
+ /* machine state register */
+ uint8_t msr[64];
+ /* temporary general purpose registers */
+ target_ulong tgpr[4]; /* Used to speed-up TLB assist handlers */
+
+ /* Floating point execution context */
+ /* temporary float registers */
+ float64 ft0;
+ float64 ft1;
+ float64 ft2;
+ float_status fp_status;
+ /* floating point registers */
+ float64 fpr[32];
+ /* floating point status and control register */
+ uint8_t fpscr[8];
+
+ CPU_COMMON
+
+ int halted; /* TRUE if the CPU is in suspend state */
+
+ int access_type; /* when a memory exception occurs, the access
+ type is stored here */
+
+ /* MMU context */
+ /* Address space register */
+ target_ulong asr;
+ /* segment registers */
+ target_ulong sdr1;
+ target_ulong sr[16];
+ /* BATs */
+ int nb_BATs;
+ target_ulong DBAT[2][8];
+ target_ulong IBAT[2][8];
+
+ /* Other registers */
+ /* Special purpose registers */
+ target_ulong spr[1024];
+ /* Altivec registers */
+ ppc_avr_t avr[32];
+ uint32_t vscr;
+
+ /* Internal devices resources */
+ /* Time base and decrementer */
+ ppc_tb_t *tb_env;
+ /* Device control registers */
+ int (*dcr_read)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong *val);
+ int (*dcr_write)(ppc_dcr_t *dcr_env, int dcr_num, target_ulong val);
+ ppc_dcr_t *dcr_env;
+
+ /* PowerPC TLB registers (for 4xx and 60x software driven TLBs) */
+ int nb_tlb;
+ int nb_ways, last_way;
+ ppc_tlb_t tlb[128];
+ /* Callbacks for specific checks on some implementations */
+ int (*tlb_check_more)(CPUPPCState *env, struct ppc_tlb_t *tlb, int *prot,
+ target_ulong vaddr, int rw, int acc_type,
+ int is_user);
+ /* 403 dedicated access protection registers */
+ target_ulong pb[4];
+
+ /* Those resources are used during exception processing */
+ /* CPU model definition */
+ uint64_t msr_mask;
+ uint32_t flags;
+
+ int exception_index;
+ int error_code;
+ int interrupt_request;
+
+ /* Those resources are used only during code translation */
+ /* Next instruction pointer */
+ target_ulong nip;
+ /* SPR translation callbacks */
+ ppc_spr_t spr_cb[1024];
+ /* opcode handlers */
+ opc_handler_t *opcodes[0x40];
+
+ /* Those resources are used only in Qemu core */
+ jmp_buf jmp_env;
+ int user_mode_only; /* user mode only simulation */
+ uint32_t hflags;
+
+ /* Power management */
+ int power_mode;
+
+ /* temporary hack to handle OSI calls (only used if non NULL) */
+ int (*osi_call)(struct CPUPPCState *env);
+};
+
+/*****************************************************************************/
+CPUPPCState *cpu_ppc_init(void);
+int cpu_ppc_exec(CPUPPCState *s);
+void cpu_ppc_close(CPUPPCState *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. */
+struct siginfo;
+int cpu_ppc_signal_handler(int host_signum, struct siginfo *info,
+ void *puc);
+
+void do_interrupt (CPUPPCState *env);
+void cpu_loop_exit(void);
+
+void dump_stack (CPUPPCState *env);
+
+target_ulong do_load_ibatu (CPUPPCState *env, int nr);
+target_ulong do_load_ibatl (CPUPPCState *env, int nr);
+void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value);
+void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value);
+target_ulong do_load_dbatu (CPUPPCState *env, int nr);
+target_ulong do_load_dbatl (CPUPPCState *env, int nr);
+void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value);
+void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value);
+
+target_ulong do_load_nip (CPUPPCState *env);
+void do_store_nip (CPUPPCState *env, target_ulong value);
+target_ulong do_load_sdr1 (CPUPPCState *env);
+void do_store_sdr1 (CPUPPCState *env, target_ulong value);
+target_ulong do_load_asr (CPUPPCState *env);
+void do_store_asr (CPUPPCState *env, target_ulong value);
+target_ulong do_load_sr (CPUPPCState *env, int srnum);
+void do_store_sr (CPUPPCState *env, int srnum, target_ulong value);
+uint32_t do_load_cr (CPUPPCState *env);
+void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask);
+uint32_t do_load_xer (CPUPPCState *env);
+void do_store_xer (CPUPPCState *env, uint32_t value);
+target_ulong do_load_msr (CPUPPCState *env);
+void do_store_msr (CPUPPCState *env, target_ulong value);
+float64 do_load_fpscr (CPUPPCState *env);
+void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask);
+
+void do_compute_hflags (CPUPPCState *env);
+
+int ppc_find_by_name (const unsigned char *name, ppc_def_t **def);
+int ppc_find_by_pvr (uint32_t apvr, ppc_def_t **def);
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...));
+int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def);
+
+/* Time-base and decrementer management */
+#ifndef NO_CPU_IO_DEFS
+uint32_t cpu_ppc_load_tbl (CPUPPCState *env);
+uint32_t cpu_ppc_load_tbu (CPUPPCState *env);
+void cpu_ppc_store_tbu (CPUPPCState *env, uint32_t value);
+void cpu_ppc_store_tbl (CPUPPCState *env, uint32_t value);
+uint32_t cpu_ppc_load_decr (CPUPPCState *env);
+void cpu_ppc_store_decr (CPUPPCState *env, uint32_t value);
+#endif
+
+#define TARGET_PAGE_BITS 12
+#include "cpu-all.h"
+
+/*****************************************************************************/
+/* Registers definitions */
+#define ugpr(n) (env->gpr[n])
+
+#define XER_SO 31
+#define XER_OV 30
+#define XER_CA 29
+#define XER_CMP 8
+#define XER_BC 0
+#define xer_so env->xer[4]
+#define xer_ov env->xer[6]
+#define xer_ca env->xer[2]
+#define xer_cmp env->xer[1]
+#define xer_bc env->xer[0]
+
+/* SPR definitions */
+#define SPR_MQ (0x000)
+#define SPR_XER (0x001)
+#define SPR_601_VRTCU (0x004)
+#define SPR_601_VRTCL (0x005)
+#define SPR_601_UDECR (0x006)
+#define SPR_LR (0x008)
+#define SPR_CTR (0x009)
+#define SPR_DSISR (0x012)
+#define SPR_DAR (0x013)
+#define SPR_601_RTCU (0x014)
+#define SPR_601_RTCL (0x015)
+#define SPR_DECR (0x016)
+#define SPR_SDR1 (0x019)
+#define SPR_SRR0 (0x01A)
+#define SPR_SRR1 (0x01B)
+#define SPR_440_PID (0x030)
+#define SPR_440_DECAR (0x036)
+#define SPR_CSRR0 (0x03A)
+#define SPR_CSRR1 (0x03B)
+#define SPR_440_DEAR (0x03D)
+#define SPR_440_ESR (0x03E)
+#define SPR_440_IVPR (0x03F)
+#define SPR_8xx_EIE (0x050)
+#define SPR_8xx_EID (0x051)
+#define SPR_8xx_NRE (0x052)
+#define SPR_58x_CMPA (0x090)
+#define SPR_58x_CMPB (0x091)
+#define SPR_58x_CMPC (0x092)
+#define SPR_58x_CMPD (0x093)
+#define SPR_58x_ICR (0x094)
+#define SPR_58x_DER (0x094)
+#define SPR_58x_COUNTA (0x096)
+#define SPR_58x_COUNTB (0x097)
+#define SPR_58x_CMPE (0x098)
+#define SPR_58x_CMPF (0x099)
+#define SPR_58x_CMPG (0x09A)
+#define SPR_58x_CMPH (0x09B)
+#define SPR_58x_LCTRL1 (0x09C)
+#define SPR_58x_LCTRL2 (0x09D)
+#define SPR_58x_ICTRL (0x09E)
+#define SPR_58x_BAR (0x09F)
+#define SPR_VRSAVE (0x100)
+#define SPR_USPRG0 (0x100)
+#define SPR_USPRG4 (0x104)
+#define SPR_USPRG5 (0x105)
+#define SPR_USPRG6 (0x106)
+#define SPR_USPRG7 (0x107)
+#define SPR_VTBL (0x10C)
+#define SPR_VTBU (0x10D)
+#define SPR_SPRG0 (0x110)
+#define SPR_SPRG1 (0x111)
+#define SPR_SPRG2 (0x112)
+#define SPR_SPRG3 (0x113)
+#define SPR_SPRG4 (0x114)
+#define SPR_SCOMC (0x114)
+#define SPR_SPRG5 (0x115)
+#define SPR_SCOMD (0x115)
+#define SPR_SPRG6 (0x116)
+#define SPR_SPRG7 (0x117)
+#define SPR_ASR (0x118)
+#define SPR_EAR (0x11A)
+#define SPR_TBL (0x11C)
+#define SPR_TBU (0x11D)
+#define SPR_SVR (0x11E)
+#define SPR_440_PIR (0x11E)
+#define SPR_PVR (0x11F)
+#define SPR_HSPRG0 (0x130)
+#define SPR_440_DBSR (0x130)
+#define SPR_HSPRG1 (0x131)
+#define SPR_440_DBCR0 (0x134)
+#define SPR_IBCR (0x135)
+#define SPR_440_DBCR1 (0x135)
+#define SPR_DBCR (0x136)
+#define SPR_HDEC (0x136)
+#define SPR_440_DBCR2 (0x136)
+#define SPR_HIOR (0x137)
+#define SPR_MBAR (0x137)
+#define SPR_RMOR (0x138)
+#define SPR_440_IAC1 (0x138)
+#define SPR_HRMOR (0x139)
+#define SPR_440_IAC2 (0x139)
+#define SPR_HSSR0 (0x13A)
+#define SPR_440_IAC3 (0x13A)
+#define SPR_HSSR1 (0x13B)
+#define SPR_440_IAC4 (0x13B)
+#define SPR_LPCR (0x13C)
+#define SPR_440_DAC1 (0x13C)
+#define SPR_LPIDR (0x13D)
+#define SPR_DABR2 (0x13D)
+#define SPR_440_DAC2 (0x13D)
+#define SPR_440_DVC1 (0x13E)
+#define SPR_440_DVC2 (0x13F)
+#define SPR_440_TSR (0x150)
+#define SPR_440_TCR (0x154)
+#define SPR_440_IVOR0 (0x190)
+#define SPR_440_IVOR1 (0x191)
+#define SPR_440_IVOR2 (0x192)
+#define SPR_440_IVOR3 (0x193)
+#define SPR_440_IVOR4 (0x194)
+#define SPR_440_IVOR5 (0x195)
+#define SPR_440_IVOR6 (0x196)
+#define SPR_440_IVOR7 (0x197)
+#define SPR_440_IVOR8 (0x198)
+#define SPR_440_IVOR9 (0x199)
+#define SPR_440_IVOR10 (0x19A)
+#define SPR_440_IVOR11 (0x19B)
+#define SPR_440_IVOR12 (0x19C)
+#define SPR_440_IVOR13 (0x19D)
+#define SPR_440_IVOR14 (0x19E)
+#define SPR_440_IVOR15 (0x19F)
+#define SPR_IBAT0U (0x210)
+#define SPR_IBAT0L (0x211)
+#define SPR_IBAT1U (0x212)
+#define SPR_IBAT1L (0x213)
+#define SPR_IBAT2U (0x214)
+#define SPR_IBAT2L (0x215)
+#define SPR_IBAT3U (0x216)
+#define SPR_IBAT3L (0x217)
+#define SPR_DBAT0U (0x218)
+#define SPR_DBAT0L (0x219)
+#define SPR_DBAT1U (0x21A)
+#define SPR_DBAT1L (0x21B)
+#define SPR_DBAT2U (0x21C)
+#define SPR_DBAT2L (0x21D)
+#define SPR_DBAT3U (0x21E)
+#define SPR_DBAT3L (0x21F)
+#define SPR_IBAT4U (0x230)
+#define SPR_IBAT4L (0x231)
+#define SPR_IBAT5U (0x232)
+#define SPR_IBAT5L (0x233)
+#define SPR_IBAT6U (0x234)
+#define SPR_IBAT6L (0x235)
+#define SPR_IBAT7U (0x236)
+#define SPR_IBAT7L (0x237)
+#define SPR_DBAT4U (0x238)
+#define SPR_DBAT4L (0x239)
+#define SPR_DBAT5U (0x23A)
+#define SPR_DBAT5L (0x23B)
+#define SPR_DBAT6U (0x23C)
+#define SPR_DBAT6L (0x23D)
+#define SPR_DBAT7U (0x23E)
+#define SPR_DBAT7L (0x23F)
+#define SPR_440_INV0 (0x370)
+#define SPR_440_INV1 (0x371)
+#define SPR_440_INV2 (0x372)
+#define SPR_440_INV3 (0x373)
+#define SPR_440_IVT0 (0x374)
+#define SPR_440_IVT1 (0x375)
+#define SPR_440_IVT2 (0x376)
+#define SPR_440_IVT3 (0x377)
+#define SPR_440_DNV0 (0x390)
+#define SPR_440_DNV1 (0x391)
+#define SPR_440_DNV2 (0x392)
+#define SPR_440_DNV3 (0x393)
+#define SPR_440_DVT0 (0x394)
+#define SPR_440_DVT1 (0x395)
+#define SPR_440_DVT2 (0x396)
+#define SPR_440_DVT3 (0x397)
+#define SPR_440_DVLIM (0x398)
+#define SPR_440_IVLIM (0x399)
+#define SPR_440_RSTCFG (0x39B)
+#define SPR_440_DCBTRL (0x39C)
+#define SPR_440_DCBTRH (0x39D)
+#define SPR_440_ICBTRL (0x39E)
+#define SPR_440_ICBTRH (0x39F)
+#define SPR_UMMCR0 (0x3A8)
+#define SPR_UPMC1 (0x3A9)
+#define SPR_UPMC2 (0x3AA)
+#define SPR_USIA (0x3AB)
+#define SPR_UMMCR1 (0x3AC)
+#define SPR_UPMC3 (0x3AD)
+#define SPR_UPMC4 (0x3AE)
+#define SPR_USDA (0x3AF)
+#define SPR_40x_ZPR (0x3B0)
+#define SPR_40x_PID (0x3B1)
+#define SPR_440_MMUCR (0x3B2)
+#define SPR_4xx_CCR0 (0x3B3)
+#define SPR_405_IAC3 (0x3B4)
+#define SPR_405_IAC4 (0x3B5)
+#define SPR_405_DVC1 (0x3B6)
+#define SPR_405_DVC2 (0x3B7)
+#define SPR_MMCR0 (0x3B8)
+#define SPR_PMC1 (0x3B9)
+#define SPR_40x_SGR (0x3B9)
+#define SPR_PMC2 (0x3BA)
+#define SPR_40x_DCWR (0x3BA)
+#define SPR_SIA (0x3BB)
+#define SPR_405_SLER (0x3BB)
+#define SPR_MMCR1 (0x3BC)
+#define SPR_405_SU0R (0x3BC)
+#define SPR_PMC3 (0x3BD)
+#define SPR_405_DBCR1 (0x3BD)
+#define SPR_PMC4 (0x3BE)
+#define SPR_SDA (0x3BF)
+#define SPR_403_VTBL (0x3CC)
+#define SPR_403_VTBU (0x3CD)
+#define SPR_DMISS (0x3D0)
+#define SPR_DCMP (0x3D1)
+#define SPR_DHASH1 (0x3D2)
+#define SPR_DHASH2 (0x3D3)
+#define SPR_4xx_ICDBDR (0x3D3)
+#define SPR_IMISS (0x3D4)
+#define SPR_40x_ESR (0x3D4)
+#define SPR_ICMP (0x3D5)
+#define SPR_40x_DEAR (0x3D5)
+#define SPR_RPA (0x3D6)
+#define SPR_40x_EVPR (0x3D6)
+#define SPR_403_CDBCR (0x3D7)
+#define SPR_TCR (0x3D8)
+#define SPR_40x_TSR (0x3D8)
+#define SPR_IBR (0x3DA)
+#define SPR_40x_TCR (0x3DA)
+#define SPR_ESASR (0x3DB)
+#define SPR_40x_PIT (0x3DB)
+#define SPR_403_TBL (0x3DC)
+#define SPR_403_TBU (0x3DD)
+#define SPR_SEBR (0x3DE)
+#define SPR_40x_SRR2 (0x3DE)
+#define SPR_SER (0x3DF)
+#define SPR_40x_SRR3 (0x3DF)
+#define SPR_HID0 (0x3F0)
+#define SPR_40x_DBSR (0x3F0)
+#define SPR_HID1 (0x3F1)
+#define SPR_IABR (0x3F2)
+#define SPR_40x_DBCR0 (0x3F2)
+#define SPR_601_HID2 (0x3F2)
+#define SPR_HID2 (0x3F3)
+#define SPR_440_DBDR (0x3F3)
+#define SPR_40x_IAC1 (0x3F4)
+#define SPR_DABR (0x3F5)
+#define DABR_MASK (~(target_ulong)0x7)
+#define SPR_40x_IAC2 (0x3F5)
+#define SPR_601_HID5 (0x3F5)
+#define SPR_40x_DAC1 (0x3F6)
+#define SPR_40x_DAC2 (0x3F7)
+#define SPR_L2PM (0x3F8)
+#define SPR_750_HID2 (0x3F8)
+#define SPR_L2CR (0x3F9)
+#define SPR_IABR2 (0x3FA)
+#define SPR_40x_DCCR (0x3FA)
+#define SPR_ICTC (0x3FB)
+#define SPR_40x_ICCR (0x3FB)
+#define SPR_THRM1 (0x3FC)
+#define SPR_403_PBL1 (0x3FC)
+#define SPR_SP (0x3FD)
+#define SPR_THRM2 (0x3FD)
+#define SPR_403_PBU1 (0x3FD)
+#define SPR_LT (0x3FE)
+#define SPR_THRM3 (0x3FE)
+#define SPR_FPECR (0x3FE)
+#define SPR_403_PBL2 (0x3FE)
+#define SPR_PIR (0x3FF)
+#define SPR_403_PBU2 (0x3FF)
+#define SPR_601_HID15 (0x3FF)
+
+/* Memory access type :
+ * may be needed for precise access rights control and precise exceptions.
+ */
+enum {
+ /* 1 bit to define user level / supervisor access */
+ ACCESS_USER = 0x00,
+ ACCESS_SUPER = 0x01,
+ /* Type of instruction that generated the access */
+ ACCESS_CODE = 0x10, /* Code fetch access */
+ ACCESS_INT = 0x20, /* Integer load/store access */
+ ACCESS_FLOAT = 0x30, /* floating point load/store access */
+ ACCESS_RES = 0x40, /* load/store with reservation */
+ ACCESS_EXT = 0x50, /* external access */
+ ACCESS_CACHE = 0x60, /* Cache manipulation */
+};
+
+/*****************************************************************************/
+/* Exceptions */
+#define EXCP_NONE -1
+/* PowerPC hardware exceptions : exception vectors defined in PowerPC book 3 */
+#define EXCP_RESET 0x0100 /* System reset */
+#define EXCP_MACHINE_CHECK 0x0200 /* Machine check exception */
+#define EXCP_DSI 0x0300 /* Data storage exception */
+#define EXCP_DSEG 0x0380 /* Data segment exception */
+#define EXCP_ISI 0x0400 /* Instruction storage exception */
+#define EXCP_ISEG 0x0480 /* Instruction segment exception */
+#define EXCP_EXTERNAL 0x0500 /* External interruption */
+#define EXCP_ALIGN 0x0600 /* Alignment exception */
+#define EXCP_PROGRAM 0x0700 /* Program exception */
+#define EXCP_NO_FP 0x0800 /* Floating point unavailable exception */
+#define EXCP_DECR 0x0900 /* Decrementer exception */
+#define EXCP_HDECR 0x0980 /* Hypervisor decrementer exception */
+#define EXCP_SYSCALL 0x0C00 /* System call */
+#define EXCP_TRACE 0x0D00 /* Trace exception */
+#define EXCP_PERF 0x0F00 /* Performance monitor exception */
+/* Exceptions defined in PowerPC 32 bits programming environment manual */
+#define EXCP_FP_ASSIST 0x0E00 /* Floating-point assist */
+/* Implementation specific exceptions */
+/* 40x exceptions */
+#define EXCP_40x_PIT 0x1000 /* Programmable interval timer interrupt */
+#define EXCP_40x_FIT 0x1010 /* Fixed interval timer interrupt */
+#define EXCP_40x_WATCHDOG 0x1020 /* Watchdog timer exception */
+#define EXCP_40x_DTLBMISS 0x1100 /* Data TLB miss exception */
+#define EXCP_40x_ITLBMISS 0x1200 /* Instruction TLB miss exception */
+#define EXCP_40x_DEBUG 0x2000 /* Debug exception */
+/* 405 specific exceptions */
+#define EXCP_405_APU 0x0F20 /* APU unavailable exception */
+/* TLB assist exceptions (602/603) */
+#define EXCP_I_TLBMISS 0x1000 /* Instruction TLB miss */
+#define EXCP_DL_TLBMISS 0x1100 /* Data load TLB miss */
+#define EXCP_DS_TLBMISS 0x1200 /* Data store TLB miss */
+/* Breakpoint exceptions (602/603/604/620/740/745/750/755...) */
+#define EXCP_IABR 0x1300 /* Instruction address breakpoint */
+#define EXCP_SMI 0x1400 /* System management interrupt */
+/* Altivec related exceptions */
+#define EXCP_VPU 0x0F20 /* VPU unavailable exception */
+/* 601 specific exceptions */
+#define EXCP_601_IO 0x0600 /* IO error exception */
+#define EXCP_601_RUNM 0x2000 /* Run mode exception */
+/* 602 specific exceptions */
+#define EXCP_602_WATCHDOG 0x1500 /* Watchdog exception */
+#define EXCP_602_EMUL 0x1600 /* Emulation trap exception */
+/* G2 specific exceptions */
+#define EXCP_G2_CRIT 0x0A00 /* Critical interrupt */
+/* MPC740/745/750 & IBM 750 specific exceptions */
+#define EXCP_THRM 0x1700 /* Thermal management interrupt */
+/* 74xx specific exceptions */
+#define EXCP_74xx_VPUA 0x1600 /* VPU assist exception */
+/* 970FX specific exceptions */
+#define EXCP_970_SOFTP 0x1500 /* Soft patch exception */
+#define EXCP_970_MAINT 0x1600 /* Maintenance exception */
+#define EXCP_970_THRM 0x1800 /* Thermal exception */
+#define EXCP_970_VPUA 0x1700 /* VPU assist exception */
+/* End of exception vectors area */
+#define EXCP_PPC_MAX 0x4000
+/* Qemu exceptions: special cases we want to stop translation */
+#define EXCP_MTMSR 0x11000 /* mtmsr instruction: */
+ /* may change privilege level */
+#define EXCP_BRANCH 0x11001 /* branch instruction */
+#define EXCP_SYSCALL_USER 0x12000 /* System call in user mode only */
+#define EXCP_INTERRUPT_CRITICAL 0x13000 /* critical IRQ */
+
+/* Error codes */
+enum {
+ /* Exception subtypes for EXCP_ALIGN */
+ EXCP_ALIGN_FP = 0x01, /* FP alignment exception */
+ EXCP_ALIGN_LST = 0x02, /* Unaligned mult/extern load/store */
+ EXCP_ALIGN_LE = 0x03, /* Multiple little-endian access */
+ EXCP_ALIGN_PROT = 0x04, /* Access cross protection boundary */
+ EXCP_ALIGN_BAT = 0x05, /* Access cross a BAT/seg boundary */
+ EXCP_ALIGN_CACHE = 0x06, /* Impossible dcbz access */
+ /* Exception subtypes for EXCP_PROGRAM */
+ /* FP exceptions */
+ EXCP_FP = 0x10,
+ EXCP_FP_OX = 0x01, /* FP overflow */
+ EXCP_FP_UX = 0x02, /* FP underflow */
+ EXCP_FP_ZX = 0x03, /* FP divide by zero */
+ EXCP_FP_XX = 0x04, /* FP inexact */
+ EXCP_FP_VXNAN = 0x05, /* FP invalid SNaN op */
+ EXCP_FP_VXISI = 0x06, /* FP invalid infinite substraction */
+ EXCP_FP_VXIDI = 0x07, /* FP invalid infinite divide */
+ EXCP_FP_VXZDZ = 0x08, /* FP invalid zero divide */
+ EXCP_FP_VXIMZ = 0x09, /* FP invalid infinite * zero */
+ EXCP_FP_VXVC = 0x0A, /* FP invalid compare */
+ EXCP_FP_VXSOFT = 0x0B, /* FP invalid operation */
+ EXCP_FP_VXSQRT = 0x0C, /* FP invalid square root */
+ EXCP_FP_VXCVI = 0x0D, /* FP invalid integer conversion */
+ /* Invalid instruction */
+ EXCP_INVAL = 0x20,
+ EXCP_INVAL_INVAL = 0x01, /* Invalid instruction */
+ EXCP_INVAL_LSWX = 0x02, /* Invalid lswx instruction */
+ EXCP_INVAL_SPR = 0x03, /* Invalid SPR access */
+ EXCP_INVAL_FP = 0x04, /* Unimplemented mandatory fp instr */
+ /* Privileged instruction */
+ EXCP_PRIV = 0x30,
+ EXCP_PRIV_OPC = 0x01,
+ EXCP_PRIV_REG = 0x02,
+ /* Trap */
+ EXCP_TRAP = 0x40,
+};
+
+/*****************************************************************************/
+
+#endif /* !defined (__CPU_PPC_H__) */
diff --git a/target-ppc/exec.h b/target-ppc/exec.h
new file mode 100644
index 0000000..3ef0968
--- /dev/null
+++ b/target-ppc/exec.h
@@ -0,0 +1,90 @@
+/*
+ * PowerPC emulation definitions for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#if !defined (__PPC_H__)
+#define __PPC_H__
+
+#include "config.h"
+
+#include "dyngen-exec.h"
+
+#define TARGET_LONG_BITS 32
+
+register struct CPUPPCState *env asm(AREG0);
+register uint32_t T0 asm(AREG1);
+register uint32_t T1 asm(AREG2);
+register uint32_t T2 asm(AREG3);
+
+#define PARAM(n) ((uint32_t)PARAM##n)
+#define SPARAM(n) ((int32_t)PARAM##n)
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define FT2 (env->ft2)
+
+#if defined (DEBUG_OP)
+#define RETURN() __asm__ __volatile__("nop");
+#else
+#define RETURN() __asm__ __volatile__("");
+#endif
+
+#include "cpu.h"
+#include "exec-all.h"
+
+static inline uint32_t rotl (uint32_t i, int n)
+{
+ return ((i << n) | (i >> (32 - n)));
+}
+
+#if !defined(CONFIG_USER_ONLY)
+#include "softmmu_exec.h"
+#endif /* !defined(CONFIG_USER_ONLY) */
+
+void do_raise_exception_err (uint32_t exception, int error_code);
+void do_raise_exception (uint32_t exception);
+
+void do_sraw(void);
+
+void do_fctiw (void);
+void do_fctiwz (void);
+void do_fnmadd (void);
+void do_fnmsub (void);
+void do_fsqrt (void);
+void do_fres (void);
+void do_frsqrte (void);
+void do_fsel (void);
+void do_fcmpu (void);
+void do_fcmpo (void);
+
+void do_check_reservation (void);
+void do_icbi (void);
+void do_tlbia (void);
+void do_tlbie (void);
+
+static inline void env_to_regs(void)
+{
+}
+
+static inline void regs_to_env(void)
+{
+}
+
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu);
+
+#endif /* !defined (__PPC_H__) */
diff --git a/target-ppc/helper.c b/target-ppc/helper.c
new file mode 100644
index 0000000..3f7a708
--- /dev/null
+++ b/target-ppc/helper.c
@@ -0,0 +1,1458 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+#include <signal.h>
+#include <assert.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+
+//#define DEBUG_MMU
+//#define DEBUG_BATS
+//#define DEBUG_EXCEPTIONS
+//#define FLUSH_ALL_TLBS
+
+/*****************************************************************************/
+/* PowerPC MMU emulation */
+
+#if defined(CONFIG_USER_ONLY)
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu)
+{
+ int exception, error_code;
+
+ if (rw == 2) {
+ exception = EXCP_ISI;
+ error_code = 0;
+ } else {
+ exception = EXCP_DSI;
+ error_code = 0;
+ if (rw)
+ error_code |= 0x02000000;
+ env->spr[SPR_DAR] = address;
+ env->spr[SPR_DSISR] = error_code;
+ }
+ env->exception_index = exception;
+ env->error_code = error_code;
+ return 1;
+}
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ return addr;
+}
+#else
+/* Perform BAT hit & translation */
+static int get_bat (CPUState *env, uint32_t *real, int *prot,
+ uint32_t virtual, int rw, int type)
+{
+ uint32_t *BATlt, *BATut, *BATu, *BATl;
+ uint32_t base, BEPIl, BEPIu, bl;
+ int i;
+ int ret = -1;
+
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: %cBAT v 0x%08x\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', virtual);
+ }
+#endif
+ switch (type) {
+ case ACCESS_CODE:
+ BATlt = env->IBAT[1];
+ BATut = env->IBAT[0];
+ break;
+ default:
+ BATlt = env->DBAT[1];
+ BATut = env->DBAT[0];
+ break;
+ }
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s...: %cBAT v 0x%08x\n", __func__,
+ type == ACCESS_CODE ? 'I' : 'D', virtual);
+ }
+#endif
+ base = virtual & 0xFFFC0000;
+ for (i = 0; i < 4; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ bl = (*BATu & 0x00001FFC) << 15;
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x\n",
+ __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
+ *BATu, *BATl);
+ }
+#endif
+ if ((virtual & 0xF0000000) == BEPIu &&
+ ((virtual & 0x0FFE0000) & ~bl) == BEPIl) {
+ /* BAT matches */
+ if ((msr_pr == 0 && (*BATu & 0x00000002)) ||
+ (msr_pr == 1 && (*BATu & 0x00000001))) {
+ /* Get physical address */
+ *real = (*BATl & 0xF0000000) |
+ ((virtual & 0x0FFE0000 & bl) | (*BATl & 0x0FFE0000)) |
+ (virtual & 0x0001F000);
+ if (*BATl & 0x00000001)
+ *prot = PAGE_READ;
+ if (*BATl & 0x00000002)
+ *prot = PAGE_WRITE | PAGE_READ;
+#if defined (DEBUG_BATS)
+ if (loglevel > 0) {
+ fprintf(logfile, "BAT %d match: r 0x%08x prot=%c%c\n",
+ i, *real, *prot & PAGE_READ ? 'R' : '-',
+ *prot & PAGE_WRITE ? 'W' : '-');
+ }
+#endif
+ ret = 0;
+ break;
+ }
+ }
+ }
+ if (ret < 0) {
+#if defined (DEBUG_BATS)
+ printf("no BAT match for 0x%08x:\n", virtual);
+ for (i = 0; i < 4; i++) {
+ BATu = &BATut[i];
+ BATl = &BATlt[i];
+ BEPIu = *BATu & 0xF0000000;
+ BEPIl = *BATu & 0x0FFE0000;
+ bl = (*BATu & 0x00001FFC) << 15;
+ printf("%s: %cBAT%d v 0x%08x BATu 0x%08x BATl 0x%08x \n\t"
+ "0x%08x 0x%08x 0x%08x\n",
+ __func__, type == ACCESS_CODE ? 'I' : 'D', i, virtual,
+ *BATu, *BATl, BEPIu, BEPIl, bl);
+ }
+#endif
+ }
+ /* No hit */
+ return ret;
+}
+
+/* PTE table lookup */
+static int find_pte (uint32_t *RPN, int *prot, uint32_t base, uint32_t va,
+ int h, int key, int rw)
+{
+ uint32_t pte0, pte1, keep = 0, access = 0;
+ int i, good = -1, store = 0;
+ int ret = -1; /* No entry found */
+
+ for (i = 0; i < 8; i++) {
+ pte0 = ldl_phys(base + (i * 8));
+ pte1 = ldl_phys(base + (i * 8) + 4);
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "Load pte from 0x%08x => 0x%08x 0x%08x "
+ "%d %d %d 0x%08x\n", base + (i * 8), pte0, pte1,
+ pte0 >> 31, h, (pte0 >> 6) & 1, va);
+ }
+#endif
+ /* Check validity and table match */
+ if (pte0 & 0x80000000 && (h == ((pte0 >> 6) & 1))) {
+ /* Check vsid & api */
+ if ((pte0 & 0x7FFFFFBF) == va) {
+ if (good == -1) {
+ good = i;
+ keep = pte1;
+ } else {
+ /* All matches should have equal RPN, WIMG & PP */
+ if ((keep & 0xFFFFF07B) != (pte1 & 0xFFFFF07B)) {
+ if (loglevel > 0)
+ fprintf(logfile, "Bad RPN/WIMG/PP\n");
+ return -1;
+ }
+ }
+ /* Check access rights */
+ if (key == 0) {
+ access = PAGE_READ;
+ if ((pte1 & 0x00000003) != 0x3)
+ access |= PAGE_WRITE;
+ } else {
+ switch (pte1 & 0x00000003) {
+ case 0x0:
+ access = 0;
+ break;
+ case 0x1:
+ case 0x3:
+ access = PAGE_READ;
+ break;
+ case 0x2:
+ access = PAGE_READ | PAGE_WRITE;
+ break;
+ }
+ }
+ if (ret < 0) {
+ if ((rw == 0 && (access & PAGE_READ)) ||
+ (rw == 1 && (access & PAGE_WRITE))) {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "PTE access granted !\n");
+#endif
+ good = i;
+ keep = pte1;
+ ret = 0;
+ } else {
+ /* Access right violation */
+ ret = -2;
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "PTE access rejected\n");
+#endif
+ }
+ *prot = access;
+ }
+ }
+ }
+ }
+ if (good != -1) {
+ *RPN = keep & 0xFFFFF000;
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "found PTE at addr 0x%08x prot=0x%01x ret=%d\n",
+ *RPN, *prot, ret);
+ }
+#endif
+ /* Update page flags */
+ if (!(keep & 0x00000100)) {
+ /* Access flag */
+ keep |= 0x00000100;
+ store = 1;
+ }
+ if (!(keep & 0x00000080)) {
+ if (rw && ret == 0) {
+ /* Change flag */
+ keep |= 0x00000080;
+ store = 1;
+ } else {
+ /* Force page fault for first write access */
+ *prot &= ~PAGE_WRITE;
+ }
+ }
+ if (store) {
+ stl_phys_notdirty(base + (good * 8) + 4, keep);
+ }
+ }
+
+ return ret;
+}
+
+static inline uint32_t get_pgaddr (uint32_t sdr1, uint32_t hash, uint32_t mask)
+{
+ return (sdr1 & 0xFFFF0000) | (hash & mask);
+}
+
+/* Perform segment based translation */
+static int get_segment (CPUState *env, uint32_t *real, int *prot,
+ uint32_t virtual, int rw, int type)
+{
+ uint32_t pg_addr, sdr, ptem, vsid, pgidx;
+ uint32_t hash, mask;
+ uint32_t sr;
+ int key;
+ int ret = -1, ret2;
+
+ sr = env->sr[virtual >> 28];
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "Check segment v=0x%08x %d 0x%08x nip=0x%08x "
+ "lr=0x%08x ir=%d dr=%d pr=%d %d t=%d\n",
+ virtual, virtual >> 28, sr, env->nip,
+ env->lr, msr_ir, msr_dr, msr_pr, rw, type);
+ }
+#endif
+ key = (((sr & 0x20000000) && msr_pr == 1) ||
+ ((sr & 0x40000000) && msr_pr == 0)) ? 1 : 0;
+ if ((sr & 0x80000000) == 0) {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "pte segment: key=%d n=0x%08x\n",
+ key, sr & 0x10000000);
+#endif
+ /* Check if instruction fetch is allowed, if needed */
+ if (type != ACCESS_CODE || (sr & 0x10000000) == 0) {
+ /* Page address translation */
+ vsid = sr & 0x00FFFFFF;
+ pgidx = (virtual >> 12) & 0xFFFF;
+ sdr = env->sdr1;
+ hash = ((vsid ^ pgidx) & 0x0007FFFF) << 6;
+ mask = ((sdr & 0x000001FF) << 16) | 0xFFC0;
+ pg_addr = get_pgaddr(sdr, hash, mask);
+ ptem = (vsid << 7) | (pgidx >> 10);
+#if defined (DEBUG_MMU)
+ if (loglevel > 0) {
+ fprintf(logfile, "0 sdr1=0x%08x vsid=0x%06x api=0x%04x "
+ "hash=0x%07x pg_addr=0x%08x\n", sdr, vsid, pgidx, hash,
+ pg_addr);
+ }
+#endif
+ /* Primary table lookup */
+ ret = find_pte(real, prot, pg_addr, ptem, 0, key, rw);
+ if (ret < 0) {
+ /* Secondary table lookup */
+ hash = (~hash) & 0x01FFFFC0;
+ pg_addr = get_pgaddr(sdr, hash, mask);
+#if defined (DEBUG_MMU)
+ if (virtual != 0xEFFFFFFF && loglevel > 0) {
+ fprintf(logfile, "1 sdr1=0x%08x vsid=0x%06x api=0x%04x "
+ "hash=0x%05x pg_addr=0x%08x\n", sdr, vsid, pgidx,
+ hash, pg_addr);
+ }
+#endif
+ ret2 = find_pte(real, prot, pg_addr, ptem, 1, key, rw);
+ if (ret2 != -1)
+ ret = ret2;
+ }
+ } else {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "No access allowed\n");
+#endif
+ ret = -3;
+ }
+ } else {
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ fprintf(logfile, "direct store...\n");
+#endif
+ /* Direct-store segment : absolutely *BUGGY* for now */
+ switch (type) {
+ case ACCESS_INT:
+ /* Integer load/store : only access allowed */
+ break;
+ case ACCESS_CODE:
+ /* No code fetch is allowed in direct-store areas */
+ return -4;
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ return -4;
+ case ACCESS_RES:
+ /* lwarx, ldarx or srwcx. */
+ return -4;
+ case ACCESS_CACHE:
+ /* dcba, dcbt, dcbtst, dcbf, dcbi, dcbst, dcbz, or icbi */
+ /* Should make the instruction do no-op.
+ * As it already do no-op, it's quite easy :-)
+ */
+ *real = virtual;
+ return 0;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ return -4;
+ default:
+ if (logfile) {
+ fprintf(logfile, "ERROR: instruction should not need "
+ "address translation\n");
+ }
+ printf("ERROR: instruction should not need "
+ "address translation\n");
+ return -4;
+ }
+ if ((rw == 1 || key != 1) && (rw == 0 || key != 0)) {
+ *real = virtual;
+ ret = 2;
+ } else {
+ ret = -2;
+ }
+ }
+
+ return ret;
+}
+
+static int get_physical_address (CPUState *env, uint32_t *physical, int *prot,
+ uint32_t address, int rw, int access_type)
+{
+ int ret;
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s\n", __func__);
+ }
+#endif
+ if ((access_type == ACCESS_CODE && msr_ir == 0) ||
+ (access_type != ACCESS_CODE && msr_dr == 0)) {
+ /* No address translation */
+ *physical = address & ~0xFFF;
+ *prot = PAGE_READ | PAGE_WRITE;
+ ret = 0;
+ } else {
+ /* Try to find a BAT */
+ ret = get_bat(env, physical, prot, address, rw, access_type);
+ if (ret < 0) {
+ /* We didn't match any BAT entry */
+ ret = get_segment(env, physical, prot, address, rw, access_type);
+ }
+ }
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s address %08x => %08x\n",
+ __func__, address, *physical);
+ }
+#endif
+ return ret;
+}
+
+target_ulong cpu_get_phys_page_debug(CPUState *env, target_ulong addr)
+{
+ uint32_t phys_addr;
+ int prot;
+
+ if (get_physical_address(env, &phys_addr, &prot, addr, 0, ACCESS_INT) != 0)
+ return -1;
+ return phys_addr;
+}
+
+/* Perform address translation */
+int cpu_ppc_handle_mmu_fault (CPUState *env, uint32_t address, int rw,
+ int is_user, int is_softmmu)
+{
+ uint32_t physical;
+ int prot;
+ int exception = 0, error_code = 0;
+ int access_type;
+ int ret = 0;
+
+ if (rw == 2) {
+ /* code access */
+ rw = 0;
+ access_type = ACCESS_CODE;
+ } else {
+ /* data access */
+ /* XXX: put correct access by using cpu_restore_state()
+ correctly */
+ access_type = ACCESS_INT;
+ // access_type = env->access_type;
+ }
+ if (env->user_mode_only) {
+ /* user mode only emulation */
+ ret = -2;
+ goto do_fault;
+ }
+ ret = get_physical_address(env, &physical, &prot,
+ address, rw, access_type);
+ if (ret == 0) {
+ ret = tlb_set_page(env, address & ~0xFFF, physical, prot,
+ is_user, is_softmmu);
+ } else if (ret < 0) {
+ do_fault:
+#if defined (DEBUG_MMU)
+ if (loglevel > 0)
+ cpu_dump_state(env, logfile, fprintf, 0);
+#endif
+ if (access_type == ACCESS_CODE) {
+ exception = EXCP_ISI;
+ switch (ret) {
+ case -1:
+ /* No matches in page tables */
+ error_code = 0x40000000;
+ break;
+ case -2:
+ /* Access rights violation */
+ error_code = 0x08000000;
+ break;
+ case -3:
+ /* No execute protection violation */
+ error_code = 0x10000000;
+ break;
+ case -4:
+ /* Direct store exception */
+ /* No code fetch is allowed in direct-store areas */
+ error_code = 0x10000000;
+ break;
+ case -5:
+ /* No match in segment table */
+ exception = EXCP_ISEG;
+ error_code = 0;
+ break;
+ }
+ } else {
+ exception = EXCP_DSI;
+ switch (ret) {
+ case -1:
+ /* No matches in page tables */
+ error_code = 0x40000000;
+ break;
+ case -2:
+ /* Access rights violation */
+ error_code = 0x08000000;
+ break;
+ case -4:
+ /* Direct store exception */
+ switch (access_type) {
+ case ACCESS_FLOAT:
+ /* Floating point load/store */
+ exception = EXCP_ALIGN;
+ error_code = EXCP_ALIGN_FP;
+ break;
+ case ACCESS_RES:
+ /* lwarx, ldarx or srwcx. */
+ error_code = 0x04000000;
+ break;
+ case ACCESS_EXT:
+ /* eciwx or ecowx */
+ error_code = 0x04100000;
+ break;
+ default:
+ printf("DSI: invalid exception (%d)\n", ret);
+ exception = EXCP_PROGRAM;
+ error_code = EXCP_INVAL | EXCP_INVAL_INVAL;
+ break;
+ }
+ break;
+ case -5:
+ /* No match in segment table */
+ exception = EXCP_DSEG;
+ error_code = 0;
+ break;
+ }
+ if (exception == EXCP_DSI && rw == 1)
+ error_code |= 0x02000000;
+ /* Store fault address */
+ env->spr[SPR_DAR] = address;
+ env->spr[SPR_DSISR] = error_code;
+ }
+#if 0
+ printf("%s: set exception to %d %02x\n",
+ __func__, exception, error_code);
+#endif
+ env->exception_index = exception;
+ env->error_code = error_code;
+ ret = 1;
+ }
+ return ret;
+}
+#endif
+
+/*****************************************************************************/
+/* BATs management */
+#if !defined(FLUSH_ALL_TLBS)
+static inline void do_invalidate_BAT (CPUPPCState *env,
+ target_ulong BATu, target_ulong mask)
+{
+ target_ulong base, end, page;
+ base = BATu & ~0x0001FFFF;
+ end = base + mask + 0x00020000;
+#if defined (DEBUG_BATS)
+ if (loglevel != 0)
+ fprintf(logfile, "Flush BAT from %08x to %08x (%08x)\n", base, end, mask);
+#endif
+ for (page = base; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+#if defined (DEBUG_BATS)
+ if (loglevel != 0)
+ fprintf(logfile, "Flush done\n");
+#endif
+}
+#endif
+
+static inline void dump_store_bat (CPUPPCState *env, char ID, int ul, int nr,
+ target_ulong value)
+{
+#if defined (DEBUG_BATS)
+ if (loglevel != 0) {
+ fprintf(logfile, "Set %cBAT%d%c to 0x%08lx (0x%08lx)\n",
+ ID, nr, ul == 0 ? 'u' : 'l', (unsigned long)value,
+ (unsigned long)env->nip);
+ }
+#endif
+}
+
+target_ulong do_load_ibatu (CPUPPCState *env, int nr)
+{
+ return env->IBAT[0][nr];
+}
+
+target_ulong do_load_ibatl (CPUPPCState *env, int nr)
+{
+ return env->IBAT[1][nr];
+}
+
+void do_store_ibatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'I', 0, nr, value);
+ if (env->IBAT[0][nr] != value) {
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#endif
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->IBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->IBAT[1][nr] = (env->IBAT[1][nr] & 0x0000007B) |
+ (env->IBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->IBAT[0][nr], mask);
+#endif
+#if defined(FLUSH_ALL_TLBS)
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void do_store_ibatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'I', 1, nr, value);
+ env->IBAT[1][nr] = value;
+}
+
+target_ulong do_load_dbatu (CPUPPCState *env, int nr)
+{
+ return env->DBAT[0][nr];
+}
+
+target_ulong do_load_dbatl (CPUPPCState *env, int nr)
+{
+ return env->DBAT[1][nr];
+}
+
+void do_store_dbatu (CPUPPCState *env, int nr, target_ulong value)
+{
+ target_ulong mask;
+
+ dump_store_bat(env, 'D', 0, nr, value);
+ if (env->DBAT[0][nr] != value) {
+ /* When storing valid upper BAT, mask BEPI and BRPN
+ * and invalidate all TLBs covered by this BAT
+ */
+ mask = (value << 15) & 0x0FFE0000UL;
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#endif
+ mask = (value << 15) & 0x0FFE0000UL;
+ env->DBAT[0][nr] = (value & 0x00001FFFUL) |
+ (value & ~0x0001FFFFUL & ~mask);
+ env->DBAT[1][nr] = (env->DBAT[1][nr] & 0x0000007B) |
+ (env->DBAT[1][nr] & ~0x0001FFFF & ~mask);
+#if !defined(FLUSH_ALL_TLBS)
+ do_invalidate_BAT(env, env->DBAT[0][nr], mask);
+#else
+ tlb_flush(env, 1);
+#endif
+ }
+}
+
+void do_store_dbatl (CPUPPCState *env, int nr, target_ulong value)
+{
+ dump_store_bat(env, 'D', 1, nr, value);
+ env->DBAT[1][nr] = value;
+}
+
+static inline void invalidate_all_tlbs (CPUPPCState *env)
+{
+ /* XXX: this needs to be completed for sotware driven TLB support */
+ tlb_flush(env, 1);
+}
+
+/*****************************************************************************/
+/* Special registers manipulation */
+target_ulong do_load_nip (CPUPPCState *env)
+{
+ return env->nip;
+}
+
+void do_store_nip (CPUPPCState *env, target_ulong value)
+{
+ env->nip = value;
+}
+
+target_ulong do_load_sdr1 (CPUPPCState *env)
+{
+ return env->sdr1;
+}
+
+void do_store_sdr1 (CPUPPCState *env, target_ulong value)
+{
+#if defined (DEBUG_MMU)
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: 0x%08lx\n", __func__, (unsigned long)value);
+ }
+#endif
+ if (env->sdr1 != value) {
+ env->sdr1 = value;
+ invalidate_all_tlbs(env);
+ }
+}
+
+target_ulong do_load_sr (CPUPPCState *env, int srnum)
+{
+ return env->sr[srnum];
+}
+
+void do_store_sr (CPUPPCState *env, int srnum, target_ulong value)
+{
+#if defined (DEBUG_MMU)
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: reg=%d 0x%08lx %08lx\n",
+ __func__, srnum, (unsigned long)value, env->sr[srnum]);
+ }
+#endif
+ if (env->sr[srnum] != value) {
+ env->sr[srnum] = value;
+#if !defined(FLUSH_ALL_TLBS) && 0
+ {
+ target_ulong page, end;
+ /* Invalidate 256 MB of virtual memory */
+ page = (16 << 20) * srnum;
+ end = page + (16 << 20);
+ for (; page != end; page += TARGET_PAGE_SIZE)
+ tlb_flush_page(env, page);
+ }
+#else
+ invalidate_all_tlbs(env);
+#endif
+ }
+}
+
+uint32_t do_load_cr (CPUPPCState *env)
+{
+ return (env->crf[0] << 28) |
+ (env->crf[1] << 24) |
+ (env->crf[2] << 20) |
+ (env->crf[3] << 16) |
+ (env->crf[4] << 12) |
+ (env->crf[5] << 8) |
+ (env->crf[6] << 4) |
+ (env->crf[7] << 0);
+}
+
+void do_store_cr (CPUPPCState *env, uint32_t value, uint32_t mask)
+{
+ int i, sh;
+
+ for (i = 0, sh = 7; i < 8; i++, sh --) {
+ if (mask & (1 << sh))
+ env->crf[i] = (value >> (sh * 4)) & 0xFUL;
+ }
+}
+
+uint32_t do_load_xer (CPUPPCState *env)
+{
+ return (xer_so << XER_SO) |
+ (xer_ov << XER_OV) |
+ (xer_ca << XER_CA) |
+ (xer_bc << XER_BC) |
+ (xer_cmp << XER_CMP);
+}
+
+void do_store_xer (CPUPPCState *env, uint32_t value)
+{
+ xer_so = (value >> XER_SO) & 0x01;
+ xer_ov = (value >> XER_OV) & 0x01;
+ xer_ca = (value >> XER_CA) & 0x01;
+ xer_cmp = (value >> XER_CMP) & 0xFF;
+ xer_bc = (value >> XER_BC) & 0x3F;
+}
+
+target_ulong do_load_msr (CPUPPCState *env)
+{
+ return (msr_vr << MSR_VR) |
+ (msr_ap << MSR_AP) |
+ (msr_sa << MSR_SA) |
+ (msr_key << MSR_KEY) |
+ (msr_pow << MSR_POW) |
+ (msr_tlb << MSR_TLB) |
+ (msr_ile << MSR_ILE) |
+ (msr_ee << MSR_EE) |
+ (msr_pr << MSR_PR) |
+ (msr_fp << MSR_FP) |
+ (msr_me << MSR_ME) |
+ (msr_fe0 << MSR_FE0) |
+ (msr_se << MSR_SE) |
+ (msr_be << MSR_BE) |
+ (msr_fe1 << MSR_FE1) |
+ (msr_al << MSR_AL) |
+ (msr_ip << MSR_IP) |
+ (msr_ir << MSR_IR) |
+ (msr_dr << MSR_DR) |
+ (msr_pe << MSR_PE) |
+ (msr_px << MSR_PX) |
+ (msr_ri << MSR_RI) |
+ (msr_le << MSR_LE);
+}
+
+void do_compute_hflags (CPUPPCState *env)
+{
+ /* Compute current hflags */
+ env->hflags = (msr_pr << MSR_PR) | (msr_le << MSR_LE) |
+ (msr_fp << MSR_FP) | (msr_fe0 << MSR_FE0) | (msr_fe1 << MSR_FE1) |
+ (msr_vr << MSR_VR) | (msr_ap << MSR_AP) | (msr_sa << MSR_SA) |
+ (msr_se << MSR_SE) | (msr_be << MSR_BE);
+}
+
+void do_store_msr (CPUPPCState *env, target_ulong value)
+{
+ int enter_pm;
+
+ value &= env->msr_mask;
+ if (((value >> MSR_IR) & 1) != msr_ir ||
+ ((value >> MSR_DR) & 1) != msr_dr) {
+ /* Flush all tlb when changing translation mode
+ * When using software driven TLB, we may also need to reload
+ * all defined TLBs
+ */
+ tlb_flush(env, 1);
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+ }
+#if 0
+ if (loglevel != 0) {
+ fprintf(logfile, "%s: T0 %08lx\n", __func__, value);
+ }
+#endif
+ msr_vr = (value >> MSR_VR) & 1;
+ msr_ap = (value >> MSR_AP) & 1;
+ msr_sa = (value >> MSR_SA) & 1;
+ msr_key = (value >> MSR_KEY) & 1;
+ msr_pow = (value >> MSR_POW) & 1;
+ msr_tlb = (value >> MSR_TLB) & 1;
+ msr_ile = (value >> MSR_ILE) & 1;
+ msr_ee = (value >> MSR_EE) & 1;
+ msr_pr = (value >> MSR_PR) & 1;
+ msr_fp = (value >> MSR_FP) & 1;
+ msr_me = (value >> MSR_ME) & 1;
+ msr_fe0 = (value >> MSR_FE0) & 1;
+ msr_se = (value >> MSR_SE) & 1;
+ msr_be = (value >> MSR_BE) & 1;
+ msr_fe1 = (value >> MSR_FE1) & 1;
+ msr_al = (value >> MSR_AL) & 1;
+ msr_ip = (value >> MSR_IP) & 1;
+ msr_ir = (value >> MSR_IR) & 1;
+ msr_dr = (value >> MSR_DR) & 1;
+ msr_pe = (value >> MSR_PE) & 1;
+ msr_px = (value >> MSR_PX) & 1;
+ msr_ri = (value >> MSR_RI) & 1;
+ msr_le = (value >> MSR_LE) & 1;
+ do_compute_hflags(env);
+
+ enter_pm = 0;
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_7x0:
+ if (msr_pow == 1 && (env->spr[SPR_HID0] & 0x00E00000) != 0)
+ enter_pm = 1;
+ break;
+ default:
+ break;
+ }
+ if (enter_pm) {
+ /* power save: exit cpu loop */
+ env->halted = 1;
+ env->exception_index = EXCP_HLT;
+ cpu_loop_exit();
+ }
+}
+
+float64 do_load_fpscr (CPUPPCState *env)
+{
+ /* The 32 MSB of the target fpr are undefined.
+ * They'll be zero...
+ */
+ union {
+ float64 d;
+ struct {
+ uint32_t u[2];
+ } s;
+ } u;
+ int i;
+
+#ifdef WORDS_BIGENDIAN
+#define WORD0 0
+#define WORD1 1
+#else
+#define WORD0 1
+#define WORD1 0
+#endif
+ u.s.u[WORD0] = 0;
+ u.s.u[WORD1] = 0;
+ for (i = 0; i < 8; i++)
+ u.s.u[WORD1] |= env->fpscr[i] << (4 * i);
+ return u.d;
+}
+
+void do_store_fpscr (CPUPPCState *env, float64 f, uint32_t mask)
+{
+ /*
+ * We use only the 32 LSB of the incoming fpr
+ */
+ union {
+ double d;
+ struct {
+ uint32_t u[2];
+ } s;
+ } u;
+ int i, rnd_type;
+
+ u.d = f;
+ if (mask & 0x80)
+ env->fpscr[0] = (env->fpscr[0] & 0x9) | ((u.s.u[WORD1] >> 28) & ~0x9);
+ for (i = 1; i < 7; i++) {
+ if (mask & (1 << (7 - i)))
+ env->fpscr[i] = (u.s.u[WORD1] >> (4 * (7 - i))) & 0xF;
+ }
+ /* TODO: update FEX & VX */
+ /* Set rounding mode */
+ switch (env->fpscr[0] & 0x3) {
+ case 0:
+ /* Best approximation (round to nearest) */
+ rnd_type = float_round_nearest_even;
+ break;
+ case 1:
+ /* Smaller magnitude (round toward zero) */
+ rnd_type = float_round_to_zero;
+ break;
+ case 2:
+ /* Round toward +infinite */
+ rnd_type = float_round_up;
+ break;
+ default:
+ case 3:
+ /* Round toward -infinite */
+ rnd_type = float_round_down;
+ break;
+ }
+ set_float_rounding_mode(rnd_type, &env->fp_status);
+}
+
+/*****************************************************************************/
+/* Exception processing */
+#if defined (CONFIG_USER_ONLY)
+void do_interrupt (CPUState *env)
+{
+ env->exception_index = -1;
+}
+#else
+static void dump_syscall(CPUState *env)
+{
+ fprintf(logfile, "syscall r0=0x%08x r3=0x%08x r4=0x%08x r5=0x%08x r6=0x%08x nip=0x%08x\n",
+ env->gpr[0], env->gpr[3], env->gpr[4],
+ env->gpr[5], env->gpr[6], env->nip);
+}
+
+void do_interrupt (CPUState *env)
+{
+ target_ulong msr, *srr_0, *srr_1, tmp;
+ int excp;
+
+ excp = env->exception_index;
+ msr = do_load_msr(env);
+ /* The default is to use SRR0 & SRR1 to save the exception context */
+ srr_0 = &env->spr[SPR_SRR0];
+ srr_1 = &env->spr[SPR_SRR1];
+#if defined (DEBUG_EXCEPTIONS)
+ if ((excp == EXCP_PROGRAM || excp == EXCP_DSI) && msr_pr == 1) {
+ if (loglevel != 0) {
+ fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n",
+ (unsigned long)env->nip, excp, env->error_code);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+ }
+#endif
+ if (loglevel & CPU_LOG_INT) {
+ fprintf(logfile, "Raise exception at 0x%08lx => 0x%08x (%02x)\n",
+ (unsigned long)env->nip, excp, env->error_code);
+ }
+ msr_pow = 0;
+ /* Generate informations in save/restore registers */
+ switch (excp) {
+ /* Generic PowerPC exceptions */
+ case EXCP_RESET: /* 0x0100 */
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_40x) {
+ if (msr_ip)
+ excp += 0xFFC00;
+ excp |= 0xFFC00000;
+ } else {
+ srr_0 = &env->spr[SPR_40x_SRR2];
+ srr_1 = &env->spr[SPR_40x_SRR3];
+ }
+ goto store_next;
+ case EXCP_MACHINE_CHECK: /* 0x0200 */
+ if (msr_me == 0) {
+ cpu_abort(env, "Machine check exception while not allowed\n");
+ }
+ if (PPC_EXCP(env) == PPC_FLAGS_EXCP_40x) {
+ srr_0 = &env->spr[SPR_40x_SRR2];
+ srr_1 = &env->spr[SPR_40x_SRR3];
+ }
+ msr_me = 0;
+ break;
+ case EXCP_DSI: /* 0x0300 */
+ /* Store exception cause */
+ /* data location address has been stored
+ * when the fault has been detected
+ */
+ msr &= ~0xFFFF0000;
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel) {
+ fprintf(logfile, "DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
+ env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ } else {
+ printf("DSI exception: DSISR=0x%08x, DAR=0x%08x\n",
+ env->spr[SPR_DSISR], env->spr[SPR_DAR]);
+ }
+#endif
+ goto store_next;
+ case EXCP_ISI: /* 0x0400 */
+ /* Store exception cause */
+ msr &= ~0xFFFF0000;
+ msr |= env->error_code;
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel != 0) {
+ fprintf(logfile, "ISI exception: msr=0x%08x, nip=0x%08x\n",
+ msr, env->nip);
+ }
+#endif
+ goto store_next;
+ case EXCP_EXTERNAL: /* 0x0500 */
+ if (msr_ee == 0) {
+#if defined (DEBUG_EXCEPTIONS)
+ if (loglevel > 0) {
+ fprintf(logfile, "Skipping hardware interrupt\n");
+ }
+#endif
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_HARD;
+ return;
+ }
+ goto store_next;
+ case EXCP_ALIGN: /* 0x0600 */
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_601) {
+ /* Store exception cause */
+ /* Get rS/rD and rA from faulting opcode */
+ env->spr[SPR_DSISR] |=
+ (ldl_code((env->nip - 4)) & 0x03FF0000) >> 16;
+ /* data location address has been stored
+ * when the fault has been detected
+ */
+ } else {
+ /* IO error exception on PowerPC 601 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "601 IO error exception is not implemented yet !\n");
+ }
+ goto store_current;
+ case EXCP_PROGRAM: /* 0x0700 */
+ msr &= ~0xFFFF0000;
+ switch (env->error_code & ~0xF) {
+ case EXCP_FP:
+ if (msr_fe0 == 0 && msr_fe1 == 0) {
+#if defined (DEBUG_EXCEPTIONS)
+ printf("Ignore floating point exception\n");
+#endif
+ return;
+ }
+ msr |= 0x00100000;
+ /* Set FX */
+ env->fpscr[7] |= 0x8;
+ /* Finally, update FEX */
+ if ((((env->fpscr[7] & 0x3) << 3) | (env->fpscr[6] >> 1)) &
+ ((env->fpscr[1] << 1) | (env->fpscr[0] >> 3)))
+ env->fpscr[7] |= 0x4;
+ break;
+ case EXCP_INVAL:
+ // printf("Invalid instruction at 0x%08x\n", env->nip);
+ msr |= 0x00080000;
+ break;
+ case EXCP_PRIV:
+ msr |= 0x00040000;
+ break;
+ case EXCP_TRAP:
+ msr |= 0x00020000;
+ break;
+ default:
+ /* Should never occur */
+ break;
+ }
+ msr |= 0x00010000;
+ goto store_current;
+ case EXCP_NO_FP: /* 0x0800 */
+ msr &= ~0xFFFF0000;
+ goto store_current;
+ case EXCP_DECR:
+ if (msr_ee == 0) {
+#if 1
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_TIMER;
+#endif
+ return;
+ }
+ goto store_next;
+ case EXCP_SYSCALL: /* 0x0C00 */
+ /* NOTE: this is a temporary hack to support graphics OSI
+ calls from the MOL driver */
+ if (env->gpr[3] == 0x113724fa && env->gpr[4] == 0x77810f9b &&
+ env->osi_call) {
+ if (env->osi_call(env) != 0)
+ return;
+ }
+ if (loglevel & CPU_LOG_INT) {
+ dump_syscall(env);
+ }
+ goto store_next;
+ case EXCP_TRACE: /* 0x0D00 */
+ /* XXX: TODO */
+ cpu_abort(env, "Trace exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_PERF: /* 0x0F00 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Performance counter exception is not implemented yet !\n");
+ goto store_next;
+ /* 32 bits PowerPC specific exceptions */
+ case EXCP_FP_ASSIST: /* 0x0E00 */
+ /* XXX: TODO */
+ cpu_abort(env, "Floating point assist exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ /* 64 bits PowerPC exceptions */
+ case EXCP_DSEG: /* 0x0380 */
+ /* XXX: TODO */
+ cpu_abort(env, "Data segment exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_ISEG: /* 0x0480 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "Instruction segment exception is not implemented yet !\n");
+ goto store_next;
+ case EXCP_HDECR: /* 0x0980 */
+ if (msr_ee == 0) {
+#if 1
+ /* Requeue it */
+ env->interrupt_request |= CPU_INTERRUPT_TIMER;
+#endif
+ return;
+ }
+ cpu_abort(env,
+ "Hypervisor decrementer exception is not implemented yet !\n");
+ goto store_next;
+ /* Implementation specific exceptions */
+ case 0x0A00:
+ if (PPC_EXCP(env) != PPC_FLAGS_EXCP_602) {
+ /* Critical interrupt on G2 */
+ /* XXX: TODO */
+ cpu_abort(env, "G2 critical interrupt is not implemented yet !\n");
+ goto store_next;
+ } else {
+ cpu_abort(env, "Invalid exception 0x0A00 !\n");
+ }
+ return;
+ case 0x0F20:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* APU unavailable on 405 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "APU unavailable exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_74xx:
+ /* Altivec unavailable */
+ /* XXX: TODO */
+ cpu_abort(env, "Altivec unavailable exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x0F20 !\n");
+ break;
+ }
+ return;
+ case 0x1000:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* PIT on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env, "40x PIT exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* ITLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ goto store_gprs;
+ default:
+ cpu_abort(env, "Invalid exception 0x1000 !\n");
+ break;
+ }
+ return;
+ case 0x1010:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* FIT on 4xx */
+ cpu_abort(env, "40x FIT exception is not implemented yet !\n");
+ /* XXX: TODO */
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1010 !\n");
+ break;
+ }
+ return;
+ case 0x1020:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* Watchdog on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x watchdog exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1020 !\n");
+ break;
+ }
+ return;
+ case 0x1100:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* DTLBMISS on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x DTLBMISS exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* DLTLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ goto store_gprs;
+ default:
+ cpu_abort(env, "Invalid exception 0x1100 !\n");
+ break;
+ }
+ return;
+ case 0x1200:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* ITLBMISS on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "40x ITLBMISS exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ /* DSTLBMISS on 602/603 */
+ msr &= ~0xF00F0000;
+ msr_tgpr = 1;
+ store_gprs:
+#if defined (DEBUG_SOFTWARE_TLB)
+ if (loglevel != 0) {
+ fprintf(logfile, "6xx %sTLB miss: IM %08x DM %08x IC %08x "
+ "DC %08x H1 %08x H2 %08x %08x\n",
+ excp == 0x1000 ? "I" : excp == 0x1100 ? "DL" : "DS",
+ env->spr[SPR_IMISS], env->spr[SPR_DMISS],
+ env->spr[SPR_ICMP], env->spr[SPR_DCMP],
+ env->spr[SPR_DHASH1], env->spr[SPR_DHASH2],
+ env->error_code);
+ }
+#endif
+ /* Swap temporary saved registers with GPRs */
+ tmp = env->gpr[0];
+ env->gpr[0] = env->tgpr[0];
+ env->tgpr[0] = tmp;
+ tmp = env->gpr[1];
+ env->gpr[1] = env->tgpr[1];
+ env->tgpr[1] = tmp;
+ tmp = env->gpr[2];
+ env->gpr[2] = env->tgpr[2];
+ env->tgpr[2] = tmp;
+ tmp = env->gpr[3];
+ env->gpr[3] = env->tgpr[3];
+ env->tgpr[3] = tmp;
+ msr |= env->crf[0] << 28;
+ msr |= env->error_code; /* key, D/I, S/L bits */
+ /* Set way using a LRU mechanism */
+ msr |= (env->last_way ^ 1) << 17;
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1200 !\n");
+ break;
+ }
+ return;
+ case 0x1300:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_601:
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ case PPC_FLAGS_EXCP_604:
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* IABR on 6xx/7xx */
+ /* XXX: TODO */
+ cpu_abort(env, "IABR exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1300 !\n");
+ break;
+ }
+ return;
+ case 0x1400:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_601:
+ case PPC_FLAGS_EXCP_602:
+ case PPC_FLAGS_EXCP_603:
+ case PPC_FLAGS_EXCP_604:
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* SMI on 6xx/7xx */
+ /* XXX: TODO */
+ cpu_abort(env, "SMI exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1400 !\n");
+ break;
+ }
+ return;
+ case 0x1500:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_602:
+ /* Watchdog on 602 */
+ cpu_abort(env,
+ "602 watchdog exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* Soft patch exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 soft-patch exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_74xx:
+ /* VPU assist on 74xx */
+ /* XXX: TODO */
+ cpu_abort(env, "VPU assist exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1500 !\n");
+ break;
+ }
+ return;
+ case 0x1600:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_602:
+ /* Emulation trap on 602 */
+ /* XXX: TODO */
+ cpu_abort(env, "602 emulation trap exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* Maintenance exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 maintenance exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1600 !\n");
+ break;
+ }
+ return;
+ case 0x1700:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_7x0:
+ case PPC_FLAGS_EXCP_7x5:
+ /* Thermal management interrupt on G3 */
+ /* XXX: TODO */
+ cpu_abort(env, "G3 thermal management exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_970:
+ /* VPU assist on 970 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "970 VPU assist exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1700 !\n");
+ break;
+ }
+ return;
+ case 0x1800:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_970:
+ /* Thermal exception on 970 */
+ /* XXX: TODO */
+ cpu_abort(env, "970 thermal management exception "
+ "is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1800 !\n");
+ break;
+ }
+ return;
+ case 0x2000:
+ switch (PPC_EXCP(env)) {
+ case PPC_FLAGS_EXCP_40x:
+ /* DEBUG on 4xx */
+ /* XXX: TODO */
+ cpu_abort(env, "40x debug exception is not implemented yet !\n");
+ goto store_next;
+ case PPC_FLAGS_EXCP_601:
+ /* Run mode exception on 601 */
+ /* XXX: TODO */
+ cpu_abort(env,
+ "601 run mode exception is not implemented yet !\n");
+ goto store_next;
+ default:
+ cpu_abort(env, "Invalid exception 0x1800 !\n");
+ break;
+ }
+ return;
+ /* Other exceptions */
+ /* Qemu internal exceptions:
+ * we should never come here with those values: abort execution
+ */
+ default:
+ cpu_abort(env, "Invalid exception: code %d (%04x)\n", excp, excp);
+ return;
+ store_current:
+ /* save current instruction location */
+ *srr_0 = (env->nip - 4) & 0xFFFFFFFFULL;
+ break;
+ store_next:
+ /* save next instruction location */
+ *srr_0 = env->nip & 0xFFFFFFFFULL;
+ break;
+ }
+ /* Save msr */
+ *srr_1 = msr;
+ /* If we disactivated any translation, flush TLBs */
+ if (msr_ir || msr_dr) {
+ tlb_flush(env, 1);
+ }
+ /* reload MSR with correct bits */
+ msr_ee = 0;
+ msr_pr = 0;
+ msr_fp = 0;
+ msr_fe0 = 0;
+ msr_se = 0;
+ msr_be = 0;
+ msr_fe1 = 0;
+ msr_ir = 0;
+ msr_dr = 0;
+ msr_ri = 0;
+ msr_le = msr_ile;
+ msr_sf = msr_isf;
+ do_compute_hflags(env);
+ /* Jump to handler */
+ env->nip = excp;
+ env->exception_index = EXCP_NONE;
+}
+#endif /* !CONFIG_USER_ONLY */
diff --git a/target-ppc/op.c b/target-ppc/op.c
new file mode 100644
index 0000000..ca1dbc5
--- /dev/null
+++ b/target-ppc/op.c
@@ -0,0 +1,1296 @@
+/*
+ * PowerPC emulation micro-operations for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+//#define DEBUG_OP
+
+#include "config.h"
+#include "exec.h"
+
+#define regs (env)
+#define Ts0 (int32_t)T0
+#define Ts1 (int32_t)T1
+#define Ts2 (int32_t)T2
+
+#define FT0 (env->ft0)
+#define FT1 (env->ft1)
+#define FT2 (env->ft2)
+
+#define PPC_OP(name) void glue(op_, name)(void)
+
+#define REG 0
+#include "op_template.h"
+
+#define REG 1
+#include "op_template.h"
+
+#define REG 2
+#include "op_template.h"
+
+#define REG 3
+#include "op_template.h"
+
+#define REG 4
+#include "op_template.h"
+
+#define REG 5
+#include "op_template.h"
+
+#define REG 6
+#include "op_template.h"
+
+#define REG 7
+#include "op_template.h"
+
+#define REG 8
+#include "op_template.h"
+
+#define REG 9
+#include "op_template.h"
+
+#define REG 10
+#include "op_template.h"
+
+#define REG 11
+#include "op_template.h"
+
+#define REG 12
+#include "op_template.h"
+
+#define REG 13
+#include "op_template.h"
+
+#define REG 14
+#include "op_template.h"
+
+#define REG 15
+#include "op_template.h"
+
+#define REG 16
+#include "op_template.h"
+
+#define REG 17
+#include "op_template.h"
+
+#define REG 18
+#include "op_template.h"
+
+#define REG 19
+#include "op_template.h"
+
+#define REG 20
+#include "op_template.h"
+
+#define REG 21
+#include "op_template.h"
+
+#define REG 22
+#include "op_template.h"
+
+#define REG 23
+#include "op_template.h"
+
+#define REG 24
+#include "op_template.h"
+
+#define REG 25
+#include "op_template.h"
+
+#define REG 26
+#include "op_template.h"
+
+#define REG 27
+#include "op_template.h"
+
+#define REG 28
+#include "op_template.h"
+
+#define REG 29
+#include "op_template.h"
+
+#define REG 30
+#include "op_template.h"
+
+#define REG 31
+#include "op_template.h"
+
+/* PowerPC state maintenance operations */
+/* set_Rc0 */
+PPC_OP(set_Rc0)
+{
+ uint32_t tmp;
+
+ if (Ts0 < 0) {
+ tmp = 0x08;
+ } else if (Ts0 > 0) {
+ tmp = 0x04;
+ } else {
+ tmp = 0x02;
+ }
+ tmp |= xer_ov;
+ env->crf[0] = tmp;
+ RETURN();
+}
+
+/* reset_Rc0 */
+PPC_OP(reset_Rc0)
+{
+ env->crf[0] = 0x02 | xer_ov;
+ RETURN();
+}
+
+/* set_Rc0_1 */
+PPC_OP(set_Rc0_1)
+{
+ env->crf[0] = 0x04 | xer_ov;
+ RETURN();
+}
+
+/* Set Rc1 (for floating point arithmetic) */
+PPC_OP(set_Rc1)
+{
+ env->crf[1] = regs->fpscr[7];
+ RETURN();
+}
+
+/* Constants load */
+PPC_OP(set_T0)
+{
+ T0 = PARAM(1);
+ RETURN();
+}
+
+PPC_OP(set_T1)
+{
+ T1 = PARAM(1);
+ RETURN();
+}
+
+PPC_OP(set_T2)
+{
+ T2 = PARAM(1);
+ RETURN();
+}
+
+/* Generate exceptions */
+PPC_OP(raise_exception_err)
+{
+ do_raise_exception_err(PARAM(1), PARAM(2));
+}
+
+PPC_OP(raise_exception)
+{
+ do_raise_exception(PARAM(1));
+}
+
+PPC_OP(update_nip)
+{
+ env->nip = PARAM(1);
+}
+
+PPC_OP(debug)
+{
+ do_raise_exception(EXCP_DEBUG);
+}
+
+/* Segment registers load and store with immediate index */
+PPC_OP(load_srin)
+{
+ T0 = regs->sr[T1 >> 28];
+ RETURN();
+}
+
+PPC_OP(store_srin)
+{
+ do_store_sr(env, ((uint32_t)T1 >> 28), T0);
+ RETURN();
+}
+
+PPC_OP(load_sdr1)
+{
+ T0 = regs->sdr1;
+ RETURN();
+}
+
+PPC_OP(store_sdr1)
+{
+ do_store_sdr1(env, T0);
+ RETURN();
+}
+
+PPC_OP(exit_tb)
+{
+ EXIT_TB();
+}
+
+/* Load/store special registers */
+PPC_OP(load_cr)
+{
+ T0 = do_load_cr(env);
+ RETURN();
+}
+
+PPC_OP(store_cr)
+{
+ do_store_cr(env, T0, PARAM(1));
+ RETURN();
+}
+
+PPC_OP(load_xer_cr)
+{
+ T0 = (xer_so << 3) | (xer_ov << 2) | (xer_ca << 1);
+ RETURN();
+}
+
+PPC_OP(clear_xer_cr)
+{
+ xer_so = 0;
+ xer_ov = 0;
+ xer_ca = 0;
+ RETURN();
+}
+
+PPC_OP(load_xer_bc)
+{
+ T1 = xer_bc;
+ RETURN();
+}
+
+PPC_OP(load_xer)
+{
+ T0 = do_load_xer(env);
+ RETURN();
+}
+
+PPC_OP(store_xer)
+{
+ do_store_xer(env, T0);
+ RETURN();
+}
+
+PPC_OP(load_msr)
+{
+ T0 = do_load_msr(env);
+ RETURN();
+}
+
+PPC_OP(store_msr)
+{
+ do_store_msr(env, T0);
+ RETURN();
+}
+
+/* SPR */
+PPC_OP(load_spr)
+{
+ T0 = regs->spr[PARAM(1)];
+ RETURN();
+}
+
+PPC_OP(store_spr)
+{
+ regs->spr[PARAM(1)] = T0;
+ RETURN();
+}
+
+PPC_OP(load_lr)
+{
+ T0 = regs->lr;
+ RETURN();
+}
+
+PPC_OP(store_lr)
+{
+ regs->lr = T0;
+ RETURN();
+}
+
+PPC_OP(load_ctr)
+{
+ T0 = regs->ctr;
+ RETURN();
+}
+
+PPC_OP(store_ctr)
+{
+ regs->ctr = T0;
+ RETURN();
+}
+
+PPC_OP(load_tbl)
+{
+ T0 = cpu_ppc_load_tbl(regs);
+ RETURN();
+}
+
+PPC_OP(load_tbu)
+{
+ T0 = cpu_ppc_load_tbu(regs);
+ RETURN();
+}
+
+PPC_OP(store_tbl)
+{
+ cpu_ppc_store_tbl(regs, T0);
+ RETURN();
+}
+
+PPC_OP(store_tbu)
+{
+ cpu_ppc_store_tbu(regs, T0);
+ RETURN();
+}
+
+PPC_OP(load_decr)
+{
+ T0 = cpu_ppc_load_decr(regs);
+ }
+
+PPC_OP(store_decr)
+{
+ cpu_ppc_store_decr(regs, T0);
+ RETURN();
+}
+
+PPC_OP(load_ibat)
+{
+ T0 = regs->IBAT[PARAM(1)][PARAM(2)];
+}
+
+void op_store_ibatu (void)
+{
+ do_store_ibatu(env, PARAM1, T0);
+ RETURN();
+}
+
+void op_store_ibatl (void)
+{
+#if 1
+ env->IBAT[1][PARAM1] = T0;
+#else
+ do_store_ibatl(env, PARAM1, T0);
+#endif
+ RETURN();
+}
+
+PPC_OP(load_dbat)
+{
+ T0 = regs->DBAT[PARAM(1)][PARAM(2)];
+}
+
+void op_store_dbatu (void)
+{
+ do_store_dbatu(env, PARAM1, T0);
+ RETURN();
+}
+
+void op_store_dbatl (void)
+{
+#if 1
+ env->DBAT[1][PARAM1] = T0;
+#else
+ do_store_dbatl(env, PARAM1, T0);
+#endif
+ RETURN();
+}
+
+/* FPSCR */
+PPC_OP(load_fpscr)
+{
+ FT0 = do_load_fpscr(env);
+ RETURN();
+}
+
+PPC_OP(store_fpscr)
+{
+ do_store_fpscr(env, FT0, PARAM1);
+ RETURN();
+}
+
+PPC_OP(reset_scrfx)
+{
+ regs->fpscr[7] &= ~0x8;
+ RETURN();
+}
+
+/* crf operations */
+PPC_OP(getbit_T0)
+{
+ T0 = (T0 >> PARAM(1)) & 1;
+ RETURN();
+}
+
+PPC_OP(getbit_T1)
+{
+ T1 = (T1 >> PARAM(1)) & 1;
+ RETURN();
+}
+
+PPC_OP(setcrfbit)
+{
+ T1 = (T1 & PARAM(1)) | (T0 << PARAM(2));
+ RETURN();
+}
+
+/* Branch */
+#define EIP regs->nip
+
+PPC_OP(setlr)
+{
+ regs->lr = PARAM1;
+}
+
+PPC_OP(goto_tb0)
+{
+ GOTO_TB(op_goto_tb0, PARAM1, 0);
+}
+
+PPC_OP(goto_tb1)
+{
+ GOTO_TB(op_goto_tb1, PARAM1, 1);
+}
+
+PPC_OP(b_T1)
+{
+ regs->nip = T1 & ~3;
+}
+
+PPC_OP(jz_T0)
+{
+ if (!T0)
+ GOTO_LABEL_PARAM(1);
+ RETURN();
+}
+
+PPC_OP(btest_T1)
+{
+ if (T0) {
+ regs->nip = T1 & ~3;
+ } else {
+ regs->nip = PARAM1;
+ }
+ RETURN();
+}
+
+PPC_OP(movl_T1_ctr)
+{
+ T1 = regs->ctr;
+}
+
+PPC_OP(movl_T1_lr)
+{
+ T1 = regs->lr;
+}
+
+/* tests with result in T0 */
+
+PPC_OP(test_ctr)
+{
+ T0 = regs->ctr;
+}
+
+PPC_OP(test_ctr_true)
+{
+ T0 = (regs->ctr != 0 && (T0 & PARAM(1)) != 0);
+}
+
+PPC_OP(test_ctr_false)
+{
+ T0 = (regs->ctr != 0 && (T0 & PARAM(1)) == 0);
+}
+
+PPC_OP(test_ctrz)
+{
+ T0 = (regs->ctr == 0);
+}
+
+PPC_OP(test_ctrz_true)
+{
+ T0 = (regs->ctr == 0 && (T0 & PARAM(1)) != 0);
+}
+
+PPC_OP(test_ctrz_false)
+{
+ T0 = (regs->ctr == 0 && (T0 & PARAM(1)) == 0);
+}
+
+PPC_OP(test_true)
+{
+ T0 = (T0 & PARAM(1));
+}
+
+PPC_OP(test_false)
+{
+ T0 = ((T0 & PARAM(1)) == 0);
+}
+
+/* CTR maintenance */
+PPC_OP(dec_ctr)
+{
+ regs->ctr--;
+ RETURN();
+}
+
+/*** Integer arithmetic ***/
+/* add */
+PPC_OP(add)
+{
+ T0 += T1;
+ RETURN();
+}
+
+void do_addo (void);
+void op_addo (void)
+{
+ do_addo();
+ RETURN();
+}
+
+/* add carrying */
+PPC_OP(addc)
+{
+ T2 = T0;
+ T0 += T1;
+ if (T0 < T2) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_addco (void);
+void op_addco (void)
+{
+ do_addco();
+ RETURN();
+}
+
+/* add extended */
+void do_adde (void);
+void op_adde (void)
+{
+ do_adde();
+}
+
+void do_addeo (void);
+PPC_OP(addeo)
+{
+ do_addeo();
+ RETURN();
+}
+
+/* add immediate */
+PPC_OP(addi)
+{
+ T0 += PARAM(1);
+ RETURN();
+}
+
+/* add immediate carrying */
+PPC_OP(addic)
+{
+ T1 = T0;
+ T0 += PARAM(1);
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* add to minus one extended */
+PPC_OP(addme)
+{
+ T1 = T0;
+ T0 += xer_ca + (-1);
+ if (T1 != 0)
+ xer_ca = 1;
+ RETURN();
+}
+
+void do_addmeo (void);
+void op_addmeo (void)
+{
+ do_addmeo();
+ RETURN();
+}
+
+/* add to zero extended */
+PPC_OP(addze)
+{
+ T1 = T0;
+ T0 += xer_ca;
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_addzeo (void);
+void op_addzeo (void)
+{
+ do_addzeo();
+ RETURN();
+}
+
+/* divide word */
+PPC_OP(divw)
+{
+ if ((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0) {
+ T0 = (int32_t)((-1) * (T0 >> 31));
+ } else {
+ T0 = (Ts0 / Ts1);
+ }
+ RETURN();
+}
+
+void do_divwo (void);
+void op_divwo (void)
+{
+ do_divwo();
+ RETURN();
+}
+
+/* divide word unsigned */
+PPC_OP(divwu)
+{
+ if (T1 == 0) {
+ T0 = 0;
+ } else {
+ T0 /= T1;
+ }
+ RETURN();
+}
+
+void do_divwuo (void);
+void op_divwuo (void)
+{
+ do_divwuo();
+ RETURN();
+}
+
+/* multiply high word */
+PPC_OP(mulhw)
+{
+ T0 = ((int64_t)Ts0 * (int64_t)Ts1) >> 32;
+ RETURN();
+}
+
+/* multiply high word unsigned */
+PPC_OP(mulhwu)
+{
+ T0 = ((uint64_t)T0 * (uint64_t)T1) >> 32;
+ RETURN();
+}
+
+/* multiply low immediate */
+PPC_OP(mulli)
+{
+ T0 = (Ts0 * SPARAM(1));
+ RETURN();
+}
+
+/* multiply low word */
+PPC_OP(mullw)
+{
+ T0 *= T1;
+ RETURN();
+}
+
+void do_mullwo (void);
+void op_mullwo (void)
+{
+ do_mullwo();
+ RETURN();
+}
+
+/* negate */
+PPC_OP(neg)
+{
+ if (T0 != 0x80000000) {
+ T0 = -Ts0;
+ }
+ RETURN();
+}
+
+void do_nego (void);
+void op_nego (void)
+{
+ do_nego();
+ RETURN();
+}
+
+/* substract from */
+PPC_OP(subf)
+{
+ T0 = T1 - T0;
+ RETURN();
+}
+
+void do_subfo (void);
+void op_subfo (void)
+{
+ do_subfo();
+ RETURN();
+}
+
+/* substract from carrying */
+PPC_OP(subfc)
+{
+ T0 = T1 - T0;
+ if (T0 <= T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_subfco (void);
+void op_subfco (void)
+{
+ do_subfco();
+ RETURN();
+}
+
+/* substract from extended */
+void do_subfe (void);
+void op_subfe (void)
+{
+ do_subfe();
+ RETURN();
+}
+
+void do_subfeo (void);
+PPC_OP(subfeo)
+{
+ do_subfeo();
+ RETURN();
+}
+
+/* substract from immediate carrying */
+PPC_OP(subfic)
+{
+ T0 = PARAM(1) + ~T0 + 1;
+ if (T0 <= PARAM(1)) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* substract from minus one extended */
+PPC_OP(subfme)
+{
+ T0 = ~T0 + xer_ca - 1;
+
+ if (T0 != -1)
+ xer_ca = 1;
+ RETURN();
+}
+
+void do_subfmeo (void);
+void op_subfmeo (void)
+{
+ do_subfmeo();
+ RETURN();
+}
+
+/* substract from zero extended */
+PPC_OP(subfze)
+{
+ T1 = ~T0;
+ T0 = T1 + xer_ca;
+ if (T0 < T1) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+void do_subfzeo (void);
+void op_subfzeo (void)
+{
+ do_subfzeo();
+ RETURN();
+}
+
+/*** Integer comparison ***/
+/* compare */
+PPC_OP(cmp)
+{
+ if (Ts0 < Ts1) {
+ T0 = 0x08;
+ } else if (Ts0 > Ts1) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare immediate */
+PPC_OP(cmpi)
+{
+ if (Ts0 < SPARAM(1)) {
+ T0 = 0x08;
+ } else if (Ts0 > SPARAM(1)) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare logical */
+PPC_OP(cmpl)
+{
+ if (T0 < T1) {
+ T0 = 0x08;
+ } else if (T0 > T1) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/* compare logical immediate */
+PPC_OP(cmpli)
+{
+ if (T0 < PARAM(1)) {
+ T0 = 0x08;
+ } else if (T0 > PARAM(1)) {
+ T0 = 0x04;
+ } else {
+ T0 = 0x02;
+ }
+ RETURN();
+}
+
+/*** Integer logical ***/
+/* and */
+PPC_OP(and)
+{
+ T0 &= T1;
+ RETURN();
+}
+
+/* andc */
+PPC_OP(andc)
+{
+ T0 &= ~T1;
+ RETURN();
+}
+
+/* andi. */
+PPC_OP(andi_)
+{
+ T0 &= PARAM(1);
+ RETURN();
+}
+
+/* count leading zero */
+PPC_OP(cntlzw)
+{
+ T1 = T0;
+ for (T0 = 32; T1 > 0; T0--)
+ T1 = T1 >> 1;
+ RETURN();
+}
+
+/* eqv */
+PPC_OP(eqv)
+{
+ T0 = ~(T0 ^ T1);
+ RETURN();
+}
+
+/* extend sign byte */
+PPC_OP(extsb)
+{
+ T0 = (int32_t)((int8_t)(Ts0));
+ RETURN();
+}
+
+/* extend sign half word */
+PPC_OP(extsh)
+{
+ T0 = (int32_t)((int16_t)(Ts0));
+ RETURN();
+}
+
+/* nand */
+PPC_OP(nand)
+{
+ T0 = ~(T0 & T1);
+ RETURN();
+}
+
+/* nor */
+PPC_OP(nor)
+{
+ T0 = ~(T0 | T1);
+ RETURN();
+}
+
+/* or */
+PPC_OP(or)
+{
+ T0 |= T1;
+ RETURN();
+}
+
+/* orc */
+PPC_OP(orc)
+{
+ T0 |= ~T1;
+ RETURN();
+}
+
+/* ori */
+PPC_OP(ori)
+{
+ T0 |= PARAM(1);
+ RETURN();
+}
+
+/* xor */
+PPC_OP(xor)
+{
+ T0 ^= T1;
+ RETURN();
+}
+
+/* xori */
+PPC_OP(xori)
+{
+ T0 ^= PARAM(1);
+ RETURN();
+}
+
+/*** Integer rotate ***/
+/* rotate left word immediate then mask insert */
+PPC_OP(rlwimi)
+{
+ T0 = (rotl(T0, PARAM(1)) & PARAM(2)) | (T1 & PARAM(3));
+ RETURN();
+}
+
+/* rotate left immediate then and with mask insert */
+PPC_OP(rotlwi)
+{
+ T0 = rotl(T0, PARAM(1));
+ RETURN();
+}
+
+PPC_OP(slwi)
+{
+ T0 = T0 << PARAM(1);
+ RETURN();
+}
+
+PPC_OP(srwi)
+{
+ T0 = T0 >> PARAM(1);
+ RETURN();
+}
+
+/* rotate left word then and with mask insert */
+PPC_OP(rlwinm)
+{
+ T0 = rotl(T0, PARAM(1)) & PARAM(2);
+ RETURN();
+}
+
+PPC_OP(rotl)
+{
+ T0 = rotl(T0, T1);
+ RETURN();
+}
+
+PPC_OP(rlwnm)
+{
+ T0 = rotl(T0, T1) & PARAM(1);
+ RETURN();
+}
+
+/*** Integer shift ***/
+/* shift left word */
+PPC_OP(slw)
+{
+ if (T1 & 0x20) {
+ T0 = 0;
+ } else {
+ T0 = T0 << T1;
+ }
+ RETURN();
+}
+
+/* shift right algebraic word */
+void op_sraw (void)
+{
+ do_sraw();
+ RETURN();
+}
+
+/* shift right algebraic word immediate */
+PPC_OP(srawi)
+{
+ T1 = T0;
+ T0 = (Ts0 >> PARAM(1));
+ if (Ts1 < 0 && (Ts1 & PARAM(2)) != 0) {
+ xer_ca = 1;
+ } else {
+ xer_ca = 0;
+ }
+ RETURN();
+}
+
+/* shift right word */
+PPC_OP(srw)
+{
+ if (T1 & 0x20) {
+ T0 = 0;
+ } else {
+ T0 = T0 >> T1;
+ }
+ RETURN();
+}
+
+/*** Floating-Point arithmetic ***/
+/* fadd - fadd. */
+PPC_OP(fadd)
+{
+ FT0 += FT1;
+ RETURN();
+}
+
+/* fsub - fsub. */
+PPC_OP(fsub)
+{
+ FT0 -= FT1;
+ RETURN();
+}
+
+/* fmul - fmul. */
+PPC_OP(fmul)
+{
+ FT0 *= FT1;
+ RETURN();
+}
+
+/* fdiv - fdiv. */
+PPC_OP(fdiv)
+{
+ FT0 = float64_div(FT0, FT1, &env->fp_status);
+ RETURN();
+}
+
+/* fsqrt - fsqrt. */
+PPC_OP(fsqrt)
+{
+ do_fsqrt();
+ RETURN();
+}
+
+/* fres - fres. */
+PPC_OP(fres)
+{
+ do_fres();
+ RETURN();
+}
+
+/* frsqrte - frsqrte. */
+PPC_OP(frsqrte)
+{
+ do_frsqrte();
+ RETURN();
+}
+
+/* fsel - fsel. */
+PPC_OP(fsel)
+{
+ do_fsel();
+ RETURN();
+}
+
+/*** Floating-Point multiply-and-add ***/
+/* fmadd - fmadd. */
+PPC_OP(fmadd)
+{
+ FT0 = (FT0 * FT1) + FT2;
+ RETURN();
+}
+
+/* fmsub - fmsub. */
+PPC_OP(fmsub)
+{
+ FT0 = (FT0 * FT1) - FT2;
+ RETURN();
+}
+
+/* fnmadd - fnmadd. - fnmadds - fnmadds. */
+PPC_OP(fnmadd)
+{
+ do_fnmadd();
+ RETURN();
+}
+
+/* fnmsub - fnmsub. */
+PPC_OP(fnmsub)
+{
+ do_fnmsub();
+ RETURN();
+}
+
+/*** Floating-Point round & convert ***/
+/* frsp - frsp. */
+PPC_OP(frsp)
+{
+ FT0 = (float)FT0;
+ RETURN();
+}
+
+/* fctiw - fctiw. */
+PPC_OP(fctiw)
+{
+ do_fctiw();
+ RETURN();
+}
+
+/* fctiwz - fctiwz. */
+PPC_OP(fctiwz)
+{
+ do_fctiwz();
+ RETURN();
+}
+
+
+/*** Floating-Point compare ***/
+/* fcmpu */
+PPC_OP(fcmpu)
+{
+ do_fcmpu();
+ RETURN();
+}
+
+/* fcmpo */
+PPC_OP(fcmpo)
+{
+ do_fcmpo();
+ RETURN();
+}
+
+/*** Floating-point move ***/
+/* fabs */
+PPC_OP(fabs)
+{
+ FT0 = float64_abs(FT0);
+ RETURN();
+}
+
+/* fnabs */
+PPC_OP(fnabs)
+{
+ FT0 = float64_abs(FT0);
+ FT0 = float64_chs(FT0);
+ RETURN();
+}
+
+/* fneg */
+PPC_OP(fneg)
+{
+ FT0 = float64_chs(FT0);
+ RETURN();
+}
+
+/* Load and store */
+#define MEMSUFFIX _raw
+#include "op_mem.h"
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_mem.h"
+
+#define MEMSUFFIX _kernel
+#include "op_mem.h"
+#endif
+
+/* Special op to check and maybe clear reservation */
+PPC_OP(check_reservation)
+{
+ if ((uint32_t)env->reserve == (uint32_t)(T0 & ~0x00000003))
+ env->reserve = -1;
+ RETURN();
+}
+
+/* Return from interrupt */
+void do_rfi (void);
+void op_rfi (void)
+{
+ do_rfi();
+ RETURN();
+}
+
+/* Trap word */
+void do_tw (uint32_t cmp, int flags);
+void op_tw (void)
+{
+ do_tw(T1, PARAM(1));
+ RETURN();
+}
+
+void op_twi (void)
+{
+ do_tw(PARAM(1), PARAM(2));
+ RETURN();
+}
+
+/* Instruction cache block invalidate */
+PPC_OP(icbi)
+{
+ do_icbi();
+ RETURN();
+}
+
+/* tlbia */
+PPC_OP(tlbia)
+{
+ do_tlbia();
+ RETURN();
+}
+
+/* tlbie */
+PPC_OP(tlbie)
+{
+ do_tlbie();
+ RETURN();
+}
+
+void op_store_pir (void)
+{
+ env->spr[SPR_PIR] = T0 & 0x0000000FUL;
+ RETURN();
+}
diff --git a/target-ppc/op_helper.c b/target-ppc/op_helper.c
new file mode 100644
index 0000000..e949eb4
--- /dev/null
+++ b/target-ppc/op_helper.c
@@ -0,0 +1,589 @@
+/*
+ * PowerPC emulation helpers for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include "exec.h"
+
+#define MEMSUFFIX _raw
+#include "op_helper_mem.h"
+#if !defined(CONFIG_USER_ONLY)
+#define MEMSUFFIX _user
+#include "op_helper_mem.h"
+#define MEMSUFFIX _kernel
+#include "op_helper_mem.h"
+#endif
+
+//#define DEBUG_OP
+//#define DEBUG_EXCEPTIONS
+//#define FLUSH_ALL_TLBS
+
+#define Ts0 (long)((target_long)T0)
+#define Ts1 (long)((target_long)T1)
+#define Ts2 (long)((target_long)T2)
+
+/*****************************************************************************/
+/* Exceptions processing helpers */
+void cpu_loop_exit(void)
+{
+ longjmp(env->jmp_env, 1);
+}
+
+void do_raise_exception_err (uint32_t exception, int error_code)
+{
+#if 0
+ printf("Raise exception %3x code : %d\n", exception, error_code);
+#endif
+ switch (exception) {
+ case EXCP_PROGRAM:
+ if (error_code == EXCP_FP && msr_fe0 == 0 && msr_fe1 == 0)
+ return;
+ break;
+ default:
+ break;
+}
+ env->exception_index = exception;
+ env->error_code = error_code;
+ cpu_loop_exit();
+ }
+
+void do_raise_exception (uint32_t exception)
+{
+ do_raise_exception_err(exception, 0);
+}
+
+/*****************************************************************************/
+/* Fixed point operations helpers */
+void do_addo (void)
+{
+ T2 = T0;
+ T0 += T1;
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_addco (void)
+{
+ T2 = T0;
+ T0 += T1;
+ if (likely(T0 >= T2)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_adde (void)
+{
+ T2 = T0;
+ T0 += T1 + xer_ca;
+ if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_addeo (void)
+{
+ T2 = T0;
+ T0 += T1 + xer_ca;
+ if (likely(!(T0 < T2 || (xer_ca == 1 && T0 == T2)))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!((T2 ^ T1 ^ (-1)) & (T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_addmeo (void)
+{
+ T1 = T0;
+ T0 += xer_ca + (-1);
+ if (likely(!(T1 & (T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T1 != 0))
+ xer_ca = 1;
+}
+
+void do_addzeo (void)
+{
+ T1 = T0;
+ T0 += xer_ca;
+ if (likely(!((T1 ^ (-1)) & (T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T0 >= T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_divwo (void)
+{
+ if (likely(!((Ts0 == INT32_MIN && Ts1 == -1) || Ts1 == 0))) {
+ xer_ov = 0;
+ T0 = (Ts0 / Ts1);
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ T0 = (-1) * ((uint32_t)T0 >> 31);
+ }
+}
+
+void do_divwuo (void)
+{
+ if (likely((uint32_t)T1 != 0)) {
+ xer_ov = 0;
+ T0 = (uint32_t)T0 / (uint32_t)T1;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ T0 = 0;
+ }
+}
+
+void do_mullwo (void)
+{
+ int64_t res = (int64_t)Ts0 * (int64_t)Ts1;
+
+ if (likely((int32_t)res == res)) {
+ xer_ov = 0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+ T0 = (int32_t)res;
+}
+
+void do_nego (void)
+{
+ if (likely(T0 != INT32_MIN)) {
+ xer_ov = 0;
+ T0 = -Ts0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+}
+
+void do_subfo (void)
+{
+ T2 = T0;
+ T0 = T1 - T0;
+ if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ RETURN();
+}
+
+void do_subfco (void)
+{
+ T2 = T0;
+ T0 = T1 - T0;
+ if (likely(T0 > T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ if (likely(!(((~T2) ^ T1 ^ (-1)) & ((~T2) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+}
+
+void do_subfe (void)
+{
+ T0 = T1 + ~T0 + xer_ca;
+ if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_subfeo (void)
+{
+ T2 = T0;
+ T0 = T1 + ~T0 + xer_ca;
+ if (likely(!((~T2 ^ T1 ^ (-1)) & (~T2 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T0 >= T1 && (xer_ca == 0 || T0 != T1))) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+void do_subfmeo (void)
+{
+ T1 = T0;
+ T0 = ~T0 + xer_ca - 1;
+ if (likely(!(~T1 & (~T1 ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_so = 1;
+ xer_ov = 1;
+ }
+ if (likely(T1 != -1))
+ xer_ca = 1;
+}
+
+void do_subfzeo (void)
+{
+ T1 = T0;
+ T0 = ~T0 + xer_ca;
+ if (likely(!((~T1 ^ (-1)) & ((~T1) ^ T0) & (1 << 31)))) {
+ xer_ov = 0;
+ } else {
+ xer_ov = 1;
+ xer_so = 1;
+ }
+ if (likely(T0 >= ~T1)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+}
+
+/* shift right arithmetic helper */
+void do_sraw (void)
+{
+ int32_t ret;
+
+ if (likely(!(T1 & 0x20UL))) {
+ if (likely(T1 != 0)) {
+ ret = (int32_t)T0 >> (T1 & 0x1fUL);
+ if (likely(ret >= 0 || ((int32_t)T0 & ((1 << T1) - 1)) == 0)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ } else {
+ ret = T0;
+ xer_ca = 0;
+ }
+ } else {
+ ret = (-1) * ((uint32_t)T0 >> 31);
+ if (likely(ret >= 0 || ((uint32_t)T0 & ~0x80000000UL) == 0)) {
+ xer_ca = 0;
+ } else {
+ xer_ca = 1;
+ }
+ }
+ T0 = ret;
+}
+
+/*****************************************************************************/
+/* Floating point operations helpers */
+void do_fctiw (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750 (aka G3)
+ */
+ p.i = float64_to_int32(FT0, &env->fp_status);
+ p.i |= 0xFFF80000ULL << 32;
+ FT0 = p.d;
+}
+
+void do_fctiwz (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ /* XXX: higher bits are not supposed to be significant.
+ * to make tests easier, return the same as a real PowerPC 750 (aka G3)
+ */
+ p.i = float64_to_int32_round_to_zero(FT0, &env->fp_status);
+ p.i |= 0xFFF80000ULL << 32;
+ FT0 = p.d;
+}
+
+void do_fnmadd (void)
+{
+ FT0 = float64_mul(FT0, FT1, &env->fp_status);
+ FT0 = float64_add(FT0, FT2, &env->fp_status);
+ if (likely(!isnan(FT0)))
+ FT0 = float64_chs(FT0);
+}
+
+void do_fnmsub (void)
+{
+ FT0 = float64_mul(FT0, FT1, &env->fp_status);
+ FT0 = float64_sub(FT0, FT2, &env->fp_status);
+ if (likely(!isnan(FT0)))
+ FT0 = float64_chs(FT0);
+}
+
+void do_fsqrt (void)
+{
+ FT0 = float64_sqrt(FT0, &env->fp_status);
+}
+
+void do_fres (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ if (likely(isnormal(FT0))) {
+ FT0 = (float)(1.0 / FT0);
+ } else {
+ p.d = FT0;
+ if (p.i == 0x8000000000000000ULL) {
+ p.i = 0xFFF0000000000000ULL;
+ } else if (p.i == 0x0000000000000000ULL) {
+ p.i = 0x7FF0000000000000ULL;
+ } else if (isnan(FT0)) {
+ p.i = 0x7FF8000000000000ULL;
+ } else if (FT0 < 0.0) {
+ p.i = 0x8000000000000000ULL;
+ } else {
+ p.i = 0x0000000000000000ULL;
+ }
+ FT0 = p.d;
+ }
+}
+
+void do_frsqrte (void)
+{
+ union {
+ double d;
+ uint64_t i;
+ } p;
+
+ if (likely(isnormal(FT0) && FT0 > 0.0)) {
+ FT0 = float64_sqrt(FT0, &env->fp_status);
+ FT0 = float32_div(1.0, FT0, &env->fp_status);
+ } else {
+ p.d = FT0;
+ if (p.i == 0x8000000000000000ULL) {
+ p.i = 0xFFF0000000000000ULL;
+ } else if (p.i == 0x0000000000000000ULL) {
+ p.i = 0x7FF0000000000000ULL;
+ } else if (isnan(FT0)) {
+ if (!(p.i & 0x0008000000000000ULL))
+ p.i |= 0x000FFFFFFFFFFFFFULL;
+ } else if (FT0 < 0) {
+ p.i = 0x7FF8000000000000ULL;
+ } else {
+ p.i = 0x0000000000000000ULL;
+ }
+ FT0 = p.d;
+ }
+}
+
+void do_fsel (void)
+{
+ if (FT0 >= 0)
+ FT0 = FT1;
+ else
+ FT0 = FT2;
+}
+
+void do_fcmpu (void)
+{
+ if (likely(!isnan(FT0) && !isnan(FT1))) {
+ if (float64_lt(FT0, FT1, &env->fp_status)) {
+ T0 = 0x08UL;
+ } else if (!float64_le(FT0, FT1, &env->fp_status)) {
+ T0 = 0x04UL;
+ } else {
+ T0 = 0x02UL;
+ }
+ } else {
+ T0 = 0x01UL;
+ env->fpscr[4] |= 0x1;
+ env->fpscr[6] |= 0x1;
+ }
+ env->fpscr[3] = T0;
+}
+
+void do_fcmpo (void)
+{
+ env->fpscr[4] &= ~0x1;
+ if (likely(!isnan(FT0) && !isnan(FT1))) {
+ if (float64_lt(FT0, FT1, &env->fp_status)) {
+ T0 = 0x08UL;
+ } else if (!float64_le(FT0, FT1, &env->fp_status)) {
+ T0 = 0x04UL;
+ } else {
+ T0 = 0x02UL;
+ }
+ } else {
+ T0 = 0x01UL;
+ env->fpscr[4] |= 0x1;
+ /* I don't know how to test "quiet" nan... */
+ if (0 /* || ! quiet_nan(...) */) {
+ env->fpscr[6] |= 0x1;
+ if (!(env->fpscr[1] & 0x8))
+ env->fpscr[4] |= 0x8;
+ } else {
+ env->fpscr[4] |= 0x8;
+ }
+ }
+ env->fpscr[3] = T0;
+}
+
+void do_rfi (void)
+{
+ env->nip = env->spr[SPR_SRR0] & ~0x00000003;
+ T0 = env->spr[SPR_SRR1] & ~0xFFFF0000UL;
+ do_store_msr(env, T0);
+#if defined (DEBUG_OP)
+ dump_rfi();
+#endif
+ env->interrupt_request |= CPU_INTERRUPT_EXITTB;
+}
+
+void do_tw (uint32_t cmp, int flags)
+{
+ if (!likely(!((Ts0 < (int32_t)cmp && (flags & 0x10)) ||
+ (Ts0 > (int32_t)cmp && (flags & 0x08)) ||
+ (Ts0 == (int32_t)cmp && (flags & 0x04)) ||
+ (T0 < cmp && (flags & 0x02)) ||
+ (T0 > cmp && (flags & 0x01)))))
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_TRAP);
+}
+
+/* Instruction cache invalidation helper */
+void do_icbi (void)
+{
+ uint32_t tmp;
+ /* Invalidate one cache line :
+ * PowerPC specification says this is to be treated like a load
+ * (not a fetch) by the MMU. To be sure it will be so,
+ * do the load "by hand".
+ */
+#if defined(TARGET_PPC64)
+ if (!msr_sf)
+ T0 &= 0xFFFFFFFFULL;
+#endif
+ tmp = ldl_kernel(T0);
+ T0 &= ~(ICACHE_LINE_SIZE - 1);
+ tb_invalidate_page_range(T0, T0 + ICACHE_LINE_SIZE);
+}
+
+/*****************************************************************************/
+/* MMU related helpers */
+/* TLB invalidation helpers */
+void do_tlbia (void)
+{
+ tlb_flush(env, 1);
+}
+
+void do_tlbie (void)
+{
+#if !defined(FLUSH_ALL_TLBS)
+ tlb_flush_page(env, T0);
+#else
+ do_tlbia();
+#endif
+}
+
+/*****************************************************************************/
+/* Softmmu support */
+#if !defined (CONFIG_USER_ONLY)
+
+#define MMUSUFFIX _mmu
+#define GETPC() (__builtin_return_address(0))
+
+#define SHIFT 0
+#include "softmmu_template.h"
+
+#define SHIFT 1
+#include "softmmu_template.h"
+
+#define SHIFT 2
+#include "softmmu_template.h"
+
+#define SHIFT 3
+#include "softmmu_template.h"
+
+/* try to fill the TLB and return an exception if error. If retaddr is
+ NULL, it means that the function was called in C code (i.e. not
+ from generated code or from helper.c) */
+/* XXX: fix it to restore all registers */
+void tlb_fill (target_ulong addr, int is_write, int is_user, void *retaddr)
+{
+ TranslationBlock *tb;
+ CPUState *saved_env;
+ target_phys_addr_t pc;
+ int ret;
+
+ /* XXX: hack to restore env in all cases, even if not called from
+ generated code */
+ saved_env = env;
+ env = cpu_single_env;
+ ret = cpu_ppc_handle_mmu_fault(env, addr, is_write, is_user, 1);
+ if (!likely(ret == 0)) {
+ if (likely(retaddr)) {
+ /* now we have a real cpu fault */
+ pc = (target_phys_addr_t)retaddr;
+ tb = tb_find_pc(pc);
+ if (likely(tb)) {
+ /* the PC is inside the translated code. It means that we have
+ a virtual CPU fault */
+ cpu_restore_state(tb, env, pc, NULL);
+}
+ }
+ do_raise_exception_err(env->exception_index, env->error_code);
+ }
+ env = saved_env;
+}
+#endif /* !CONFIG_USER_ONLY */
+
diff --git a/target-ppc/op_helper_mem.h b/target-ppc/op_helper_mem.h
new file mode 100644
index 0000000..fb90691
--- /dev/null
+++ b/target-ppc/op_helper_mem.h
@@ -0,0 +1,100 @@
+void glue(do_lsw, MEMSUFFIX) (int dst)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, dst);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ ugpr(dst++) = glue(ldl, MEMSUFFIX)(T0);
+ if (dst == 32)
+ dst = 0;
+ }
+ if (T1 > 0) {
+ tmp = 0;
+ for (sh = 24; T1 > 0; T1--, T0++, sh -= 8) {
+ tmp |= glue(ldub, MEMSUFFIX)(T0) << sh;
+ }
+ ugpr(dst) = tmp;
+ }
+}
+
+void glue(do_stsw, MEMSUFFIX) (int src)
+{
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, src);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ glue(stl, MEMSUFFIX)(T0, ugpr(src++));
+ if (src == 32)
+ src = 0;
+ }
+ if (T1 > 0) {
+ for (sh = 24; T1 > 0; T1--, T0++, sh -= 8)
+ glue(stb, MEMSUFFIX)(T0, (ugpr(src) >> sh) & 0xFF);
+ }
+}
+
+void glue(do_lsw_le, MEMSUFFIX) (int dst)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, dst);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ tmp = glue(ldl, MEMSUFFIX)(T0);
+ ugpr(dst++) = ((tmp & 0xFF000000) >> 24) | ((tmp & 0x00FF0000) >> 8) |
+ ((tmp & 0x0000FF00) << 8) | ((tmp & 0x000000FF) << 24);
+ if (dst == 32)
+ dst = 0;
+ }
+ if (T1 > 0) {
+ tmp = 0;
+ for (sh = 0; T1 > 0; T1--, T0++, sh += 8) {
+ tmp |= glue(ldub, MEMSUFFIX)(T0) << sh;
+ }
+ ugpr(dst) = tmp;
+ }
+}
+
+void glue(do_stsw_le, MEMSUFFIX) (int src)
+{
+ uint32_t tmp;
+ int sh;
+
+#if 0
+ if (loglevel > 0) {
+ fprintf(logfile, "%s: addr=0x%08x count=%d reg=%d\n",
+ __func__, T0, T1, src);
+ }
+#endif
+ for (; T1 > 3; T1 -= 4, T0 += 4) {
+ tmp = ((ugpr(src++) & 0xFF000000) >> 24);
+ tmp |= ((ugpr(src++) & 0x00FF0000) >> 8);
+ tmp |= ((ugpr(src++) & 0x0000FF00) << 8);
+ tmp |= ((ugpr(src++) & 0x000000FF) << 24);
+ glue(stl, MEMSUFFIX)(T0, tmp);
+ if (src == 32)
+ src = 0;
+ }
+ if (T1 > 0) {
+ for (sh = 0; T1 > 0; T1--, T0++, sh += 8)
+ glue(stb, MEMSUFFIX)(T0, (ugpr(src) >> sh) & 0xFF);
+ }
+}
+
+#undef MEMSUFFIX
diff --git a/target-ppc/op_mem.h b/target-ppc/op_mem.h
new file mode 100644
index 0000000..9b3f721
--- /dev/null
+++ b/target-ppc/op_mem.h
@@ -0,0 +1,371 @@
+/* External helpers */
+void glue(do_lsw, MEMSUFFIX) (int dst);
+void glue(do_stsw, MEMSUFFIX) (int src);
+
+static inline uint16_t glue(ld16r, MEMSUFFIX) (target_ulong EA)
+{
+ uint16_t tmp = glue(lduw, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF00) >> 8) | ((tmp & 0x00FF) << 8);
+}
+
+static inline int32_t glue(ld16rs, MEMSUFFIX) (target_ulong EA)
+{
+ int16_t tmp = glue(lduw, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF00) >> 8) | ((tmp & 0x00FF) << 8);
+}
+
+static inline uint32_t glue(ld32r, MEMSUFFIX) (target_ulong EA)
+{
+ uint32_t tmp = glue(ldl, MEMSUFFIX)(EA);
+ return ((tmp & 0xFF000000) >> 24) | ((tmp & 0x00FF0000) >> 8) |
+ ((tmp & 0x0000FF00) << 8) | ((tmp & 0x000000FF) << 24);
+}
+
+static inline void glue(st16r, MEMSUFFIX) (target_ulong EA, uint16_t data)
+{
+ uint16_t tmp = ((data & 0xFF00) >> 8) | ((data & 0x00FF) << 8);
+ glue(stw, MEMSUFFIX)(EA, tmp);
+}
+
+static inline void glue(st32r, MEMSUFFIX) (target_ulong EA, uint32_t data)
+{
+ uint32_t tmp = ((data & 0xFF000000) >> 24) | ((data & 0x00FF0000) >> 8) |
+ ((data & 0x0000FF00) << 8) | ((data & 0x000000FF) << 24);
+ glue(stl, MEMSUFFIX)(EA, tmp);
+}
+
+/*** Integer load ***/
+#define PPC_LD_OP(name, op) \
+PPC_OP(glue(glue(l, name), MEMSUFFIX)) \
+{ \
+ T1 = glue(op, MEMSUFFIX)(T0); \
+ RETURN(); \
+}
+
+#define PPC_ST_OP(name, op) \
+PPC_OP(glue(glue(st, name), MEMSUFFIX)) \
+{ \
+ glue(op, MEMSUFFIX)(T0, T1); \
+ RETURN(); \
+}
+
+PPC_LD_OP(bz, ldub);
+PPC_LD_OP(ha, ldsw);
+PPC_LD_OP(hz, lduw);
+PPC_LD_OP(wz, ldl);
+
+PPC_LD_OP(ha_le, ld16rs);
+PPC_LD_OP(hz_le, ld16r);
+PPC_LD_OP(wz_le, ld32r);
+
+/*** Integer store ***/
+PPC_ST_OP(b, stb);
+PPC_ST_OP(h, stw);
+PPC_ST_OP(w, stl);
+
+PPC_ST_OP(h_le, st16r);
+PPC_ST_OP(w_le, st32r);
+
+/*** Integer load and store with byte reverse ***/
+PPC_LD_OP(hbr, ld16r);
+PPC_LD_OP(wbr, ld32r);
+PPC_ST_OP(hbr, st16r);
+PPC_ST_OP(wbr, st32r);
+
+PPC_LD_OP(hbr_le, lduw);
+PPC_LD_OP(wbr_le, ldl);
+PPC_ST_OP(hbr_le, stw);
+PPC_ST_OP(wbr_le, stl);
+
+/*** Integer load and store multiple ***/
+PPC_OP(glue(lmw, MEMSUFFIX))
+{
+ int dst = PARAM(1);
+
+ for (; dst < 32; dst++, T0 += 4) {
+ ugpr(dst) = glue(ldl, MEMSUFFIX)(T0);
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stmw, MEMSUFFIX))
+{
+ int src = PARAM(1);
+
+ for (; src < 32; src++, T0 += 4) {
+ glue(stl, MEMSUFFIX)(T0, ugpr(src));
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lmw_le, MEMSUFFIX))
+{
+ int dst = PARAM(1);
+
+ for (; dst < 32; dst++, T0 += 4) {
+ ugpr(dst) = glue(ld32r, MEMSUFFIX)(T0);
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stmw_le, MEMSUFFIX))
+{
+ int src = PARAM(1);
+
+ for (; src < 32; src++, T0 += 4) {
+ glue(st32r, MEMSUFFIX)(T0, ugpr(src));
+ }
+ RETURN();
+}
+
+/*** Integer load and store strings ***/
+PPC_OP(glue(lswi, MEMSUFFIX))
+{
+ glue(do_lsw, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+void glue(do_lsw_le, MEMSUFFIX) (int dst);
+PPC_OP(glue(lswi_le, MEMSUFFIX))
+{
+ glue(do_lsw_le, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+/* PPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+PPC_OP(glue(lswx, MEMSUFFIX))
+{
+ if (T1 > 0) {
+ if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) ||
+ (PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) {
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ } else {
+ glue(do_lsw, MEMSUFFIX)(PARAM(1));
+ }
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lswx_le, MEMSUFFIX))
+{
+ if (T1 > 0) {
+ if ((PARAM(1) < PARAM(2) && (PARAM(1) + T1) > PARAM(2)) ||
+ (PARAM(1) < PARAM(3) && (PARAM(1) + T1) > PARAM(3))) {
+ do_raise_exception_err(EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ } else {
+ glue(do_lsw_le, MEMSUFFIX)(PARAM(1));
+ }
+ }
+ RETURN();
+}
+
+PPC_OP(glue(stsw, MEMSUFFIX))
+{
+ glue(do_stsw, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+void glue(do_stsw_le, MEMSUFFIX) (int src);
+PPC_OP(glue(stsw_le, MEMSUFFIX))
+{
+ glue(do_stsw_le, MEMSUFFIX)(PARAM(1));
+ RETURN();
+}
+
+/*** Floating-point store ***/
+#define PPC_STF_OP(name, op) \
+PPC_OP(glue(glue(st, name), MEMSUFFIX)) \
+{ \
+ glue(op, MEMSUFFIX)(T0, FT1); \
+ RETURN(); \
+}
+
+PPC_STF_OP(fd, stfq);
+PPC_STF_OP(fs, stfl);
+
+static inline void glue(stfqr, MEMSUFFIX) (target_ulong EA, double d)
+{
+ union {
+ double d;
+ uint64_t u;
+ } u;
+
+ u.d = d;
+ u.u = ((u.u & 0xFF00000000000000ULL) >> 56) |
+ ((u.u & 0x00FF000000000000ULL) >> 40) |
+ ((u.u & 0x0000FF0000000000ULL) >> 24) |
+ ((u.u & 0x000000FF00000000ULL) >> 8) |
+ ((u.u & 0x00000000FF000000ULL) << 8) |
+ ((u.u & 0x0000000000FF0000ULL) << 24) |
+ ((u.u & 0x000000000000FF00ULL) << 40) |
+ ((u.u & 0x00000000000000FFULL) << 56);
+ glue(stfq, MEMSUFFIX)(EA, u.d);
+}
+
+static inline void glue(stflr, MEMSUFFIX) (target_ulong EA, float f)
+{
+ union {
+ float f;
+ uint32_t u;
+ } u;
+
+ u.f = f;
+ u.u = ((u.u & 0xFF000000UL) >> 24) |
+ ((u.u & 0x00FF0000ULL) >> 8) |
+ ((u.u & 0x0000FF00UL) << 8) |
+ ((u.u & 0x000000FFULL) << 24);
+ glue(stfl, MEMSUFFIX)(EA, u.f);
+}
+
+PPC_STF_OP(fd_le, stfqr);
+PPC_STF_OP(fs_le, stflr);
+
+/*** Floating-point load ***/
+#define PPC_LDF_OP(name, op) \
+PPC_OP(glue(glue(l, name), MEMSUFFIX)) \
+{ \
+ FT1 = glue(op, MEMSUFFIX)(T0); \
+ RETURN(); \
+}
+
+PPC_LDF_OP(fd, ldfq);
+PPC_LDF_OP(fs, ldfl);
+
+static inline double glue(ldfqr, MEMSUFFIX) (target_ulong EA)
+{
+ union {
+ double d;
+ uint64_t u;
+ } u;
+
+ u.d = glue(ldfq, MEMSUFFIX)(EA);
+ u.u = ((u.u & 0xFF00000000000000ULL) >> 56) |
+ ((u.u & 0x00FF000000000000ULL) >> 40) |
+ ((u.u & 0x0000FF0000000000ULL) >> 24) |
+ ((u.u & 0x000000FF00000000ULL) >> 8) |
+ ((u.u & 0x00000000FF000000ULL) << 8) |
+ ((u.u & 0x0000000000FF0000ULL) << 24) |
+ ((u.u & 0x000000000000FF00ULL) << 40) |
+ ((u.u & 0x00000000000000FFULL) << 56);
+
+ return u.d;
+}
+
+static inline float glue(ldflr, MEMSUFFIX) (target_ulong EA)
+{
+ union {
+ float f;
+ uint32_t u;
+ } u;
+
+ u.f = glue(ldfl, MEMSUFFIX)(EA);
+ u.u = ((u.u & 0xFF000000UL) >> 24) |
+ ((u.u & 0x00FF0000ULL) >> 8) |
+ ((u.u & 0x0000FF00UL) << 8) |
+ ((u.u & 0x000000FFULL) << 24);
+
+ return u.f;
+}
+
+PPC_LDF_OP(fd_le, ldfqr);
+PPC_LDF_OP(fs_le, ldflr);
+
+/* Load and set reservation */
+PPC_OP(glue(lwarx, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ T1 = glue(ldl, MEMSUFFIX)(T0);
+ regs->reserve = T0;
+ }
+ RETURN();
+}
+
+PPC_OP(glue(lwarx_le, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ T1 = glue(ld32r, MEMSUFFIX)(T0);
+ regs->reserve = T0;
+ }
+ RETURN();
+}
+
+/* Store with reservation */
+PPC_OP(glue(stwcx, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ if (regs->reserve != T0) {
+ env->crf[0] = xer_ov;
+ } else {
+ glue(stl, MEMSUFFIX)(T0, T1);
+ env->crf[0] = xer_ov | 0x02;
+ }
+ }
+ regs->reserve = 0;
+ RETURN();
+}
+
+PPC_OP(glue(stwcx_le, MEMSUFFIX))
+{
+ if (T0 & 0x03) {
+ do_raise_exception(EXCP_ALIGN);
+ } else {
+ if (regs->reserve != T0) {
+ env->crf[0] = xer_ov;
+ } else {
+ glue(st32r, MEMSUFFIX)(T0, T1);
+ env->crf[0] = xer_ov | 0x02;
+ }
+ }
+ regs->reserve = 0;
+ RETURN();
+}
+
+PPC_OP(glue(dcbz, MEMSUFFIX))
+{
+ glue(stl, MEMSUFFIX)(T0 + 0x00, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x04, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x08, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x0C, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x10, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x14, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x18, 0);
+ glue(stl, MEMSUFFIX)(T0 + 0x1C, 0);
+ RETURN();
+}
+
+/* External access */
+PPC_OP(glue(eciwx, MEMSUFFIX))
+{
+ T1 = glue(ldl, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+PPC_OP(glue(ecowx, MEMSUFFIX))
+{
+ glue(stl, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+PPC_OP(glue(eciwx_le, MEMSUFFIX))
+{
+ T1 = glue(ld32r, MEMSUFFIX)(T0);
+ RETURN();
+}
+
+PPC_OP(glue(ecowx_le, MEMSUFFIX))
+{
+ glue(st32r, MEMSUFFIX)(T0, T1);
+ RETURN();
+}
+
+#undef MEMSUFFIX
diff --git a/target-ppc/op_template.h b/target-ppc/op_template.h
new file mode 100644
index 0000000..1be640d
--- /dev/null
+++ b/target-ppc/op_template.h
@@ -0,0 +1,183 @@
+/*
+ * PowerPC emulation micro-operations for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* General purpose registers moves */
+void OPPROTO glue(op_load_gpr_T0_gpr, REG)(void)
+{
+ T0 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_gpr_T1_gpr, REG)(void)
+{
+ T1 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_gpr_T2_gpr, REG)(void)
+{
+ T2 = regs->gpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T1_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T1;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T2_gpr_gpr, REG)(void)
+{
+ regs->gpr[REG] = T2;
+ RETURN();
+}
+
+#if REG <= 7
+/* Condition register moves */
+void OPPROTO glue(op_load_crf_T0_crf, REG)(void)
+{
+ T0 = regs->crf[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_load_crf_T1_crf, REG)(void)
+{
+ T1 = regs->crf[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_crf_crf, REG)(void)
+{
+ regs->crf[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T1_crf_crf, REG)(void)
+{
+ regs->crf[REG] = T1;
+ RETURN();
+}
+
+/* Floating point condition and status register moves */
+void OPPROTO glue(op_load_fpscr_T0_fpscr, REG)(void)
+{
+ T0 = regs->fpscr[REG];
+ RETURN();
+}
+
+#if REG == 0
+void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & 0x9) | (T0 & ~0x9);
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & ~0x9) | (PARAM(1) & 0x9);
+ RETURN();
+}
+
+void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = (regs->fpscr[REG] & 0x9);
+ RETURN();
+}
+#else
+void OPPROTO glue(op_store_T0_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = T0;
+ RETURN();
+}
+
+void OPPROTO glue(op_store_T0_fpscri_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = PARAM(1);
+ RETURN();
+}
+
+void OPPROTO glue(op_clear_fpscr_fpscr, REG)(void)
+{
+ regs->fpscr[REG] = 0x0;
+ RETURN();
+}
+#endif
+
+#endif /* REG <= 7 */
+
+/* floating point registers moves */
+void OPPROTO glue(op_load_fpr_FT0_fpr, REG)(void)
+{
+ FT0 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT0_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT0;
+ RETURN();
+}
+
+void OPPROTO glue(op_load_fpr_FT1_fpr, REG)(void)
+{
+ FT1 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT1_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT1;
+ RETURN();
+}
+
+void OPPROTO glue(op_load_fpr_FT2_fpr, REG)(void)
+{
+ FT2 = env->fpr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_FT2_fpr_fpr, REG)(void)
+{
+ env->fpr[REG] = FT2;
+ RETURN();
+}
+
+#if REG <= 15
+/* Segment register moves */
+void OPPROTO glue(op_load_sr, REG)(void)
+{
+ T0 = env->sr[REG];
+ RETURN();
+}
+
+void OPPROTO glue(op_store_sr, REG)(void)
+{
+ do_store_sr(env, REG, T0);
+ RETURN();
+}
+#endif
+
+#undef REG
diff --git a/target-ppc/translate.c b/target-ppc/translate.c
new file mode 100644
index 0000000..046f168
--- /dev/null
+++ b/target-ppc/translate.c
@@ -0,0 +1,2701 @@
+/*
+ * PowerPC emulation for qemu: main translation routines.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+#include <stdarg.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <inttypes.h>
+
+#include "cpu.h"
+#include "exec-all.h"
+#include "disas.h"
+
+//#define DO_SINGLE_STEP
+//#define PPC_DEBUG_DISAS
+
+#ifdef USE_DIRECT_JUMP
+#define TBPARAM(x)
+#else
+#define TBPARAM(x) (long)(x)
+#endif
+
+enum {
+#define DEF(s, n, copy_size) INDEX_op_ ## s,
+#include "opc.h"
+#undef DEF
+ NB_OPS,
+};
+
+static uint16_t *gen_opc_ptr;
+static uint32_t *gen_opparam_ptr;
+
+#include "gen-op.h"
+
+#define GEN8(func, NAME) \
+static GenOpFunc *NAME ## _table [8] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+#define GEN16(func, NAME) \
+static GenOpFunc *NAME ## _table [16] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+#define GEN32(func, NAME) \
+static GenOpFunc *NAME ## _table [32] = { \
+NAME ## 0, NAME ## 1, NAME ## 2, NAME ## 3, \
+NAME ## 4, NAME ## 5, NAME ## 6, NAME ## 7, \
+NAME ## 8, NAME ## 9, NAME ## 10, NAME ## 11, \
+NAME ## 12, NAME ## 13, NAME ## 14, NAME ## 15, \
+NAME ## 16, NAME ## 17, NAME ## 18, NAME ## 19, \
+NAME ## 20, NAME ## 21, NAME ## 22, NAME ## 23, \
+NAME ## 24, NAME ## 25, NAME ## 26, NAME ## 27, \
+NAME ## 28, NAME ## 29, NAME ## 30, NAME ## 31, \
+}; \
+static inline void func(int n) \
+{ \
+ NAME ## _table[n](); \
+}
+
+/* Condition register moves */
+GEN8(gen_op_load_crf_T0, gen_op_load_crf_T0_crf);
+GEN8(gen_op_load_crf_T1, gen_op_load_crf_T1_crf);
+GEN8(gen_op_store_T0_crf, gen_op_store_T0_crf_crf);
+GEN8(gen_op_store_T1_crf, gen_op_store_T1_crf_crf);
+
+/* Floating point condition and status register moves */
+GEN8(gen_op_load_fpscr_T0, gen_op_load_fpscr_T0_fpscr);
+GEN8(gen_op_store_T0_fpscr, gen_op_store_T0_fpscr_fpscr);
+GEN8(gen_op_clear_fpscr, gen_op_clear_fpscr_fpscr);
+static GenOpFunc1 *gen_op_store_T0_fpscri_fpscr_table[8] = {
+ &gen_op_store_T0_fpscri_fpscr0,
+ &gen_op_store_T0_fpscri_fpscr1,
+ &gen_op_store_T0_fpscri_fpscr2,
+ &gen_op_store_T0_fpscri_fpscr3,
+ &gen_op_store_T0_fpscri_fpscr4,
+ &gen_op_store_T0_fpscri_fpscr5,
+ &gen_op_store_T0_fpscri_fpscr6,
+ &gen_op_store_T0_fpscri_fpscr7,
+};
+static inline void gen_op_store_T0_fpscri(int n, uint8_t param)
+{
+ (*gen_op_store_T0_fpscri_fpscr_table[n])(param);
+}
+
+/* Segment register moves */
+GEN16(gen_op_load_sr, gen_op_load_sr);
+GEN16(gen_op_store_sr, gen_op_store_sr);
+
+/* General purpose registers moves */
+GEN32(gen_op_load_gpr_T0, gen_op_load_gpr_T0_gpr);
+GEN32(gen_op_load_gpr_T1, gen_op_load_gpr_T1_gpr);
+GEN32(gen_op_load_gpr_T2, gen_op_load_gpr_T2_gpr);
+
+GEN32(gen_op_store_T0_gpr, gen_op_store_T0_gpr_gpr);
+GEN32(gen_op_store_T1_gpr, gen_op_store_T1_gpr_gpr);
+GEN32(gen_op_store_T2_gpr, gen_op_store_T2_gpr_gpr);
+
+/* floating point registers moves */
+GEN32(gen_op_load_fpr_FT0, gen_op_load_fpr_FT0_fpr);
+GEN32(gen_op_load_fpr_FT1, gen_op_load_fpr_FT1_fpr);
+GEN32(gen_op_load_fpr_FT2, gen_op_load_fpr_FT2_fpr);
+GEN32(gen_op_store_FT0_fpr, gen_op_store_FT0_fpr_fpr);
+GEN32(gen_op_store_FT1_fpr, gen_op_store_FT1_fpr_fpr);
+GEN32(gen_op_store_FT2_fpr, gen_op_store_FT2_fpr_fpr);
+
+static uint8_t spr_access[1024 / 2];
+
+/* internal defines */
+typedef struct DisasContext {
+ struct TranslationBlock *tb;
+ target_ulong nip;
+ uint32_t opcode;
+ uint32_t exception;
+ /* Routine used to access memory */
+ int mem_idx;
+ /* Translation flags */
+#if !defined(CONFIG_USER_ONLY)
+ int supervisor;
+#endif
+ int fpu_enabled;
+ ppc_spr_t *spr_cb; /* Needed to check rights for mfspr/mtspr */
+ int singlestep_enabled;
+} DisasContext;
+
+struct opc_handler_t {
+ /* invalid bits */
+ uint32_t inval;
+ /* instruction type */
+ uint32_t type;
+ /* handler */
+ void (*handler)(DisasContext *ctx);
+};
+
+#define RET_EXCP(ctx, excp, error) \
+do { \
+ if ((ctx)->exception == EXCP_NONE) { \
+ gen_op_update_nip((ctx)->nip); \
+ } \
+ gen_op_raise_exception_err((excp), (error)); \
+ ctx->exception = (excp); \
+} while (0)
+
+#define RET_INVAL(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_INVAL)
+
+#define RET_PRIVOPC(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_OPC)
+
+#define RET_PRIVREG(ctx) \
+RET_EXCP((ctx), EXCP_PROGRAM, EXCP_INVAL | EXCP_PRIV_REG)
+
+/* Stop translation */
+static inline void RET_STOP (DisasContext *ctx)
+{
+ gen_op_update_nip((ctx)->nip);
+ ctx->exception = EXCP_MTMSR;
+}
+
+/* No need to update nip here, as execution flow will change */
+static inline void RET_CHG_FLOW (DisasContext *ctx)
+{
+ ctx->exception = EXCP_MTMSR;
+}
+
+#define GEN_HANDLER(name, opc1, opc2, opc3, inval, type) \
+static void gen_##name (DisasContext *ctx); \
+GEN_OPCODE(name, opc1, opc2, opc3, inval, type); \
+static void gen_##name (DisasContext *ctx)
+
+typedef struct opcode_t {
+ unsigned char opc1, opc2, opc3;
+#if HOST_LONG_BITS == 64 /* Explicitely align to 64 bits */
+ unsigned char pad[5];
+#else
+ unsigned char pad[1];
+#endif
+ opc_handler_t handler;
+ const unsigned char *oname;
+} opcode_t;
+
+/*** Instruction decoding ***/
+#define EXTRACT_HELPER(name, shift, nb) \
+static inline uint32_t name (uint32_t opcode) \
+{ \
+ return (opcode >> (shift)) & ((1 << (nb)) - 1); \
+}
+
+#define EXTRACT_SHELPER(name, shift, nb) \
+static inline int32_t name (uint32_t opcode) \
+{ \
+ return (int16_t)((opcode >> (shift)) & ((1 << (nb)) - 1)); \
+}
+
+/* Opcode part 1 */
+EXTRACT_HELPER(opc1, 26, 6);
+/* Opcode part 2 */
+EXTRACT_HELPER(opc2, 1, 5);
+/* Opcode part 3 */
+EXTRACT_HELPER(opc3, 6, 5);
+/* Update Cr0 flags */
+EXTRACT_HELPER(Rc, 0, 1);
+/* Destination */
+EXTRACT_HELPER(rD, 21, 5);
+/* Source */
+EXTRACT_HELPER(rS, 21, 5);
+/* First operand */
+EXTRACT_HELPER(rA, 16, 5);
+/* Second operand */
+EXTRACT_HELPER(rB, 11, 5);
+/* Third operand */
+EXTRACT_HELPER(rC, 6, 5);
+/*** Get CRn ***/
+EXTRACT_HELPER(crfD, 23, 3);
+EXTRACT_HELPER(crfS, 18, 3);
+EXTRACT_HELPER(crbD, 21, 5);
+EXTRACT_HELPER(crbA, 16, 5);
+EXTRACT_HELPER(crbB, 11, 5);
+/* SPR / TBL */
+EXTRACT_HELPER(_SPR, 11, 10);
+static inline uint32_t SPR (uint32_t opcode)
+{
+ uint32_t sprn = _SPR(opcode);
+
+ return ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+}
+/*** Get constants ***/
+EXTRACT_HELPER(IMM, 12, 8);
+/* 16 bits signed immediate value */
+EXTRACT_SHELPER(SIMM, 0, 16);
+/* 16 bits unsigned immediate value */
+EXTRACT_HELPER(UIMM, 0, 16);
+/* Bit count */
+EXTRACT_HELPER(NB, 11, 5);
+/* Shift count */
+EXTRACT_HELPER(SH, 11, 5);
+/* Mask start */
+EXTRACT_HELPER(MB, 6, 5);
+/* Mask end */
+EXTRACT_HELPER(ME, 1, 5);
+/* Trap operand */
+EXTRACT_HELPER(TO, 21, 5);
+
+EXTRACT_HELPER(CRM, 12, 8);
+EXTRACT_HELPER(FM, 17, 8);
+EXTRACT_HELPER(SR, 16, 4);
+EXTRACT_HELPER(FPIMM, 20, 4);
+
+/*** Jump target decoding ***/
+/* Displacement */
+EXTRACT_SHELPER(d, 0, 16);
+/* Immediate address */
+static inline uint32_t LI (uint32_t opcode)
+{
+ return (opcode >> 0) & 0x03FFFFFC;
+}
+
+static inline uint32_t BD (uint32_t opcode)
+{
+ return (opcode >> 0) & 0xFFFC;
+}
+
+EXTRACT_HELPER(BO, 21, 5);
+EXTRACT_HELPER(BI, 16, 5);
+/* Absolute/relative address */
+EXTRACT_HELPER(AA, 1, 1);
+/* Link */
+EXTRACT_HELPER(LK, 0, 1);
+
+/* Create a mask between <start> and <end> bits */
+static inline uint32_t MASK (uint32_t start, uint32_t end)
+{
+ uint32_t ret;
+
+ ret = (((uint32_t)(-1)) >> (start)) ^ (((uint32_t)(-1) >> (end)) >> 1);
+ if (start > end)
+ return ~ret;
+
+ return ret;
+}
+
+#if HOST_LONG_BITS == 64
+#define OPC_ALIGN 8
+#else
+#define OPC_ALIGN 4
+#endif
+#if defined(__APPLE__)
+#define OPCODES_SECTION \
+ __attribute__ ((section("__TEXT,__opcodes"), unused, aligned (OPC_ALIGN) ))
+#else
+#define OPCODES_SECTION \
+ __attribute__ ((section(".opcodes"), unused, aligned (OPC_ALIGN) ))
+#endif
+
+#define GEN_OPCODE(name, op1, op2, op3, invl, _typ) \
+OPCODES_SECTION opcode_t opc_##name = { \
+ .opc1 = op1, \
+ .opc2 = op2, \
+ .opc3 = op3, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = invl, \
+ .type = _typ, \
+ .handler = &gen_##name, \
+ }, \
+ .oname = stringify(name), \
+}
+
+#define GEN_OPCODE_MARK(name) \
+OPCODES_SECTION opcode_t opc_##name = { \
+ .opc1 = 0xFF, \
+ .opc2 = 0xFF, \
+ .opc3 = 0xFF, \
+ .pad = { 0, }, \
+ .handler = { \
+ .inval = 0x00000000, \
+ .type = 0x00, \
+ .handler = NULL, \
+ }, \
+ .oname = stringify(name), \
+}
+
+/* Start opcode list */
+GEN_OPCODE_MARK(start);
+
+/* Invalid instruction */
+GEN_HANDLER(invalid, 0x00, 0x00, 0x00, 0xFFFFFFFF, PPC_NONE)
+{
+ RET_INVAL(ctx);
+}
+
+static opc_handler_t invalid_handler = {
+ .inval = 0xFFFFFFFF,
+ .type = PPC_NONE,
+ .handler = gen_invalid,
+};
+
+/*** Integer arithmetic ***/
+#define __GEN_INT_ARITH2(name, opc1, opc2, opc3, inval) \
+GEN_HANDLER(name, opc1, opc2, opc3, inval, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+#define __GEN_INT_ARITH2_O(name, opc1, opc2, opc3, inval) \
+GEN_HANDLER(name, opc1, opc2, opc3, inval, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+#define __GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+GEN_HANDLER(name, opc1, opc2, opc3, 0x0000F800, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+#define __GEN_INT_ARITH1_O(name, opc1, opc2, opc3) \
+GEN_HANDLER(name, opc1, opc2, opc3, 0x0000F800, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rD(ctx->opcode)); \
+}
+
+/* Two operands arithmetic functions */
+#define GEN_INT_ARITH2(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH2(name, opc1, opc2, opc3, 0x00000000) \
+__GEN_INT_ARITH2_O(name##o, opc1, opc2, opc3 | 0x10, 0x00000000)
+
+/* Two operands arithmetic functions with no overflow allowed */
+#define GEN_INT_ARITHN(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH2(name, opc1, opc2, opc3, 0x00000400)
+
+/* One operand arithmetic functions */
+#define GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH1(name, opc1, opc2, opc3) \
+__GEN_INT_ARITH1_O(name##o, opc1, opc2, opc3 | 0x10)
+
+/* add add. addo addo. */
+GEN_INT_ARITH2 (add, 0x1F, 0x0A, 0x08);
+/* addc addc. addco addco. */
+GEN_INT_ARITH2 (addc, 0x1F, 0x0A, 0x00);
+/* adde adde. addeo addeo. */
+GEN_INT_ARITH2 (adde, 0x1F, 0x0A, 0x04);
+/* addme addme. addmeo addmeo. */
+GEN_INT_ARITH1 (addme, 0x1F, 0x0A, 0x07);
+/* addze addze. addzeo addzeo. */
+GEN_INT_ARITH1 (addze, 0x1F, 0x0A, 0x06);
+/* divw divw. divwo divwo. */
+GEN_INT_ARITH2 (divw, 0x1F, 0x0B, 0x0F);
+/* divwu divwu. divwuo divwuo. */
+GEN_INT_ARITH2 (divwu, 0x1F, 0x0B, 0x0E);
+/* mulhw mulhw. */
+GEN_INT_ARITHN (mulhw, 0x1F, 0x0B, 0x02);
+/* mulhwu mulhwu. */
+GEN_INT_ARITHN (mulhwu, 0x1F, 0x0B, 0x00);
+/* mullw mullw. mullwo mullwo. */
+GEN_INT_ARITH2 (mullw, 0x1F, 0x0B, 0x07);
+/* neg neg. nego nego. */
+GEN_INT_ARITH1 (neg, 0x1F, 0x08, 0x03);
+/* subf subf. subfo subfo. */
+GEN_INT_ARITH2 (subf, 0x1F, 0x08, 0x01);
+/* subfc subfc. subfco subfco. */
+GEN_INT_ARITH2 (subfc, 0x1F, 0x08, 0x00);
+/* subfe subfe. subfeo subfeo. */
+GEN_INT_ARITH2 (subfe, 0x1F, 0x08, 0x04);
+/* subfme subfme. subfmeo subfmeo. */
+GEN_INT_ARITH1 (subfme, 0x1F, 0x08, 0x07);
+/* subfze subfze. subfzeo subfzeo. */
+GEN_INT_ARITH1 (subfze, 0x1F, 0x08, 0x06);
+/* addi */
+GEN_HANDLER(addi, 0x0E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int32_t simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addi(simm);
+ }
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addic */
+GEN_HANDLER(addic, 0x0C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addic(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addic. */
+GEN_HANDLER(addic_, 0x0D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addic(SIMM(ctx->opcode));
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* addis */
+GEN_HANDLER(addis, 0x0F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int32_t simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm << 16);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_addi(simm << 16);
+ }
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* mulli */
+GEN_HANDLER(mulli, 0x07, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_mulli(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+/* subfic */
+GEN_HANDLER(subfic, 0x08, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_subfic(SIMM(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/*** Integer comparison ***/
+#define GEN_CMP(name, opc) \
+GEN_HANDLER(name, 0x1F, 0x00, opc, 0x00400000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ gen_op_store_T0_crf(crfD(ctx->opcode)); \
+}
+
+/* cmp */
+GEN_CMP(cmp, 0x00);
+/* cmpi */
+GEN_HANDLER(cmpi, 0x0B, 0xFF, 0xFF, 0x00400000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_cmpi(SIMM(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+/* cmpl */
+GEN_CMP(cmpl, 0x01);
+/* cmpli */
+GEN_HANDLER(cmpli, 0x0A, 0xFF, 0xFF, 0x00400000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_cmpli(UIMM(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** Integer logical ***/
+#define __GEN_LOGICAL2(name, opc2, opc3) \
+GEN_HANDLER(name, 0x1F, opc2, opc3, 0x00000000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rS(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+#define GEN_LOGICAL2(name, opc) \
+__GEN_LOGICAL2(name, 0x1C, opc)
+
+#define GEN_LOGICAL1(name, opc) \
+GEN_HANDLER(name, 0x1F, 0x1A, opc, 0x00000000, PPC_INTEGER) \
+{ \
+ gen_op_load_gpr_T0(rS(ctx->opcode)); \
+ gen_op_##name(); \
+ if (Rc(ctx->opcode) != 0) \
+ gen_op_set_Rc0(); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+/* and & and. */
+GEN_LOGICAL2(and, 0x00);
+/* andc & andc. */
+GEN_LOGICAL2(andc, 0x01);
+/* andi. */
+GEN_HANDLER(andi_, 0x1C, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_andi_(UIMM(ctx->opcode));
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* andis. */
+GEN_HANDLER(andis_, 0x1D, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_andi_(UIMM(ctx->opcode) << 16);
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* cntlzw */
+GEN_LOGICAL1(cntlzw, 0x00);
+/* eqv & eqv. */
+GEN_LOGICAL2(eqv, 0x08);
+/* extsb & extsb. */
+GEN_LOGICAL1(extsb, 0x1D);
+/* extsh & extsh. */
+GEN_LOGICAL1(extsh, 0x1C);
+/* nand & nand. */
+GEN_LOGICAL2(nand, 0x0E);
+/* nor & nor. */
+GEN_LOGICAL2(nor, 0x03);
+
+/* or & or. */
+GEN_HANDLER(or, 0x1F, 0x1C, 0x0D, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ /* Optimisation for mr case */
+ if (rS(ctx->opcode) != rB(ctx->opcode)) {
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_or();
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* orc & orc. */
+GEN_LOGICAL2(orc, 0x0C);
+/* xor & xor. */
+GEN_HANDLER(xor, 0x1F, 0x1C, 0x09, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ /* Optimisation for "set to zero" case */
+ if (rS(ctx->opcode) != rB(ctx->opcode)) {
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_xor();
+ } else {
+ gen_op_set_T0(0);
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* ori */
+GEN_HANDLER(ori, 0x18, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_ori(uimm);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* oris */
+GEN_HANDLER(oris, 0x19, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_ori(uimm << 16);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* xori */
+GEN_HANDLER(xori, 0x1A, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_xori(uimm);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/* xoris */
+GEN_HANDLER(xoris, 0x1B, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t uimm = UIMM(ctx->opcode);
+
+ if (rS(ctx->opcode) == rA(ctx->opcode) && uimm == 0) {
+ /* NOP */
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (uimm != 0)
+ gen_op_xori(uimm << 16);
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/*** Integer rotate ***/
+/* rlwimi & rlwimi. */
+GEN_HANDLER(rlwimi, 0x14, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rA(ctx->opcode));
+ gen_op_rlwimi(SH(ctx->opcode), MASK(mb, me), ~MASK(mb, me));
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* rlwinm & rlwinm. */
+GEN_HANDLER(rlwinm, 0x15, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me, sh;
+
+ sh = SH(ctx->opcode);
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+#if 1 // TRY
+ if (sh == 0) {
+ gen_op_andi_(MASK(mb, me));
+ goto store;
+ }
+#endif
+ if (mb == 0) {
+ if (me == 31) {
+ gen_op_rotlwi(sh);
+ goto store;
+#if 0
+ } else if (me == (31 - sh)) {
+ gen_op_slwi(sh);
+ goto store;
+#endif
+ }
+ } else if (me == 31) {
+#if 0
+ if (sh == (32 - mb)) {
+ gen_op_srwi(mb);
+ goto store;
+ }
+#endif
+ }
+ gen_op_rlwinm(sh, MASK(mb, me));
+store:
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* rlwnm & rlwnm. */
+GEN_HANDLER(rlwnm, 0x17, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ uint32_t mb, me;
+
+ mb = MB(ctx->opcode);
+ me = ME(ctx->opcode);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ if (mb == 0 && me == 31) {
+ gen_op_rotl();
+ } else
+ {
+ gen_op_rlwnm(MASK(mb, me));
+ }
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+
+/*** Integer shift ***/
+/* slw & slw. */
+__GEN_LOGICAL2(slw, 0x18, 0x00);
+/* sraw & sraw. */
+__GEN_LOGICAL2(sraw, 0x18, 0x18);
+/* srawi & srawi. */
+GEN_HANDLER(srawi, 0x1F, 0x18, 0x19, 0x00000000, PPC_INTEGER)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ if (SH(ctx->opcode) != 0)
+ gen_op_srawi(SH(ctx->opcode), MASK(32 - SH(ctx->opcode), 31));
+ if (Rc(ctx->opcode) != 0)
+ gen_op_set_Rc0();
+ gen_op_store_T0_gpr(rA(ctx->opcode));
+}
+/* srw & srw. */
+__GEN_LOGICAL2(srw, 0x18, 0x10);
+
+/*** Floating-Point arithmetic ***/
+#define _GEN_FLOAT_ACB(name, op, op1, op2, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rC(ctx->opcode)); \
+ gen_op_load_fpr_FT2(rB(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+#define GEN_FLOAT_ACB(name, op2) \
+_GEN_FLOAT_ACB(name, name, 0x3F, op2, 0); \
+_GEN_FLOAT_ACB(name##s, name, 0x3B, op2, 1);
+
+#define _GEN_FLOAT_AB(name, op, op1, op2, inval, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rB(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+#define GEN_FLOAT_AB(name, op2, inval) \
+_GEN_FLOAT_AB(name, name, 0x3F, op2, inval, 0); \
+_GEN_FLOAT_AB(name##s, name, 0x3B, op2, inval, 1);
+
+#define _GEN_FLOAT_AC(name, op, op1, op2, inval, isfloat) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, inval, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rA(ctx->opcode)); \
+ gen_op_load_fpr_FT1(rC(ctx->opcode)); \
+ gen_op_f##op(); \
+ if (isfloat) { \
+ gen_op_frsp(); \
+ } \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+#define GEN_FLOAT_AC(name, op2, inval) \
+_GEN_FLOAT_AC(name, name, 0x3F, op2, inval, 0); \
+_GEN_FLOAT_AC(name##s, name, 0x3B, op2, inval, 1);
+
+#define GEN_FLOAT_B(name, op2, op3) \
+GEN_HANDLER(f##name, 0x3F, op2, op3, 0x001F0000, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rB(ctx->opcode)); \
+ gen_op_f##name(); \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+#define GEN_FLOAT_BS(name, op1, op2) \
+GEN_HANDLER(f##name, op1, op2, 0xFF, 0x001F07C0, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ gen_op_reset_scrfx(); \
+ gen_op_load_fpr_FT0(rB(ctx->opcode)); \
+ gen_op_f##name(); \
+ gen_op_store_FT0_fpr(rD(ctx->opcode)); \
+ if (Rc(ctx->opcode)) \
+ gen_op_set_Rc1(); \
+}
+
+/* fadd - fadds */
+GEN_FLOAT_AB(add, 0x15, 0x000007C0);
+/* fdiv - fdivs */
+GEN_FLOAT_AB(div, 0x12, 0x000007C0);
+/* fmul - fmuls */
+GEN_FLOAT_AC(mul, 0x19, 0x0000F800);
+
+/* fres */
+GEN_FLOAT_BS(res, 0x3B, 0x18);
+
+/* frsqrte */
+GEN_FLOAT_BS(rsqrte, 0x3F, 0x1A);
+
+/* fsel */
+_GEN_FLOAT_ACB(sel, sel, 0x3F, 0x17, 0);
+/* fsub - fsubs */
+GEN_FLOAT_AB(sub, 0x14, 0x000007C0);
+/* Optional: */
+/* fsqrt */
+GEN_HANDLER(fsqrt, 0x3F, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_OPT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_fsqrt();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+GEN_HANDLER(fsqrts, 0x3B, 0x16, 0xFF, 0x001F07C0, PPC_FLOAT_OPT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_fsqrt();
+ gen_op_frsp();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/*** Floating-Point multiply-and-add ***/
+/* fmadd - fmadds */
+GEN_FLOAT_ACB(madd, 0x1D);
+/* fmsub - fmsubs */
+GEN_FLOAT_ACB(msub, 0x1C);
+/* fnmadd - fnmadds */
+GEN_FLOAT_ACB(nmadd, 0x1F);
+/* fnmsub - fnmsubs */
+GEN_FLOAT_ACB(nmsub, 0x1E);
+
+/*** Floating-Point round & convert ***/
+/* fctiw */
+GEN_FLOAT_B(ctiw, 0x0E, 0x00);
+/* fctiwz */
+GEN_FLOAT_B(ctiwz, 0x0F, 0x00);
+/* frsp */
+GEN_FLOAT_B(rsp, 0x0C, 0x00);
+
+/*** Floating-Point compare ***/
+/* fcmpo */
+GEN_HANDLER(fcmpo, 0x3F, 0x00, 0x00, 0x00600001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rA(ctx->opcode));
+ gen_op_load_fpr_FT1(rB(ctx->opcode));
+ gen_op_fcmpo();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/* fcmpu */
+GEN_HANDLER(fcmpu, 0x3F, 0x00, 0x01, 0x00600001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rA(ctx->opcode));
+ gen_op_load_fpr_FT1(rB(ctx->opcode));
+ gen_op_fcmpu();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** Floating-point move ***/
+/* fabs */
+GEN_FLOAT_B(abs, 0x08, 0x08);
+
+/* fmr - fmr. */
+GEN_HANDLER(fmr, 0x3F, 0x08, 0x02, 0x001F0000, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_reset_scrfx();
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* fnabs */
+GEN_FLOAT_B(nabs, 0x08, 0x04);
+/* fneg */
+GEN_FLOAT_B(neg, 0x08, 0x01);
+
+/*** Floating-Point status & ctrl register ***/
+/* mcrfs */
+GEN_HANDLER(mcrfs, 0x3F, 0x00, 0x02, 0x0063F801, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpscr_T0(crfS(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+ gen_op_clear_fpscr(crfS(ctx->opcode));
+}
+
+/* mffs */
+GEN_HANDLER(mffs, 0x3F, 0x07, 0x12, 0x001FF800, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpscr();
+ gen_op_store_FT0_fpr(rD(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsb0 */
+GEN_HANDLER(mtfsb0, 0x3F, 0x06, 0x02, 0x001FF800, PPC_FLOAT)
+{
+ uint8_t crb;
+
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ crb = crbD(ctx->opcode) >> 2;
+ gen_op_load_fpscr_T0(crb);
+ gen_op_andi_(~(1 << (crbD(ctx->opcode) & 0x03)));
+ gen_op_store_T0_fpscr(crb);
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsb1 */
+GEN_HANDLER(mtfsb1, 0x3F, 0x06, 0x01, 0x001FF800, PPC_FLOAT)
+{
+ uint8_t crb;
+
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ crb = crbD(ctx->opcode) >> 2;
+ gen_op_load_fpscr_T0(crb);
+ gen_op_ori(1 << (crbD(ctx->opcode) & 0x03));
+ gen_op_store_T0_fpscr(crb);
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsf */
+GEN_HANDLER(mtfsf, 0x3F, 0x07, 0x16, 0x02010000, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_load_fpr_FT0(rB(ctx->opcode));
+ gen_op_store_fpscr(FM(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/* mtfsfi */
+GEN_HANDLER(mtfsfi, 0x3F, 0x06, 0x04, 0x006f0800, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ gen_op_store_T0_fpscri(crbD(ctx->opcode) >> 2, FPIMM(ctx->opcode));
+ if (Rc(ctx->opcode))
+ gen_op_set_Rc1();
+}
+
+/*** Integer load ***/
+#define op_ldst(name) (*gen_op_##name[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_l##width[] = { \
+ &gen_op_l##width##_raw, \
+ &gen_op_l##width##_le_raw, \
+};
+#define OP_ST_TABLE(width) \
+static GenOpFunc *gen_op_st##width[] = { \
+ &gen_op_st##width##_raw, \
+ &gen_op_st##width##_le_raw, \
+};
+/* Byte access routine are endian safe */
+#define gen_op_stb_le_raw gen_op_stb_raw
+#define gen_op_lbz_le_raw gen_op_lbz_raw
+#else
+#define OP_LD_TABLE(width) \
+static GenOpFunc *gen_op_l##width[] = { \
+ &gen_op_l##width##_user, \
+ &gen_op_l##width##_le_user, \
+ &gen_op_l##width##_kernel, \
+ &gen_op_l##width##_le_kernel, \
+};
+#define OP_ST_TABLE(width) \
+static GenOpFunc *gen_op_st##width[] = { \
+ &gen_op_st##width##_user, \
+ &gen_op_st##width##_le_user, \
+ &gen_op_st##width##_kernel, \
+ &gen_op_st##width##_le_kernel, \
+};
+/* Byte access routine are endian safe */
+#define gen_op_stb_le_user gen_op_stb_user
+#define gen_op_lbz_le_user gen_op_lbz_user
+#define gen_op_stb_le_kernel gen_op_stb_kernel
+#define gen_op_lbz_le_kernel gen_op_lbz_kernel
+#endif
+
+#define GEN_LD(width, opc) \
+GEN_HANDLER(l##width, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDU(width, opc) \
+GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDUX(width, opc) \
+GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDX(width, opc2, opc3) \
+GEN_HANDLER(l##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_T1_gpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDS(width, op) \
+OP_LD_TABLE(width); \
+GEN_LD(width, op | 0x20); \
+GEN_LDU(width, op | 0x21); \
+GEN_LDUX(width, op | 0x01); \
+GEN_LDX(width, 0x17, op | 0x00)
+
+/* lbz lbzu lbzux lbzx */
+GEN_LDS(bz, 0x02);
+/* lha lhau lhaux lhax */
+GEN_LDS(ha, 0x0A);
+/* lhz lhzu lhzux lhzx */
+GEN_LDS(hz, 0x08);
+/* lwz lwzu lwzux lwzx */
+GEN_LDS(wz, 0x00);
+
+/*** Integer store ***/
+#define GEN_ST(width, opc) \
+GEN_HANDLER(st##width, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STU(width, opc) \
+GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_INTEGER) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STUX(width, opc) \
+GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STX(width, opc2, opc3) \
+GEN_HANDLER(st##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_INTEGER) \
+{ \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ gen_op_load_gpr_T1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STS(width, op) \
+OP_ST_TABLE(width); \
+GEN_ST(width, op | 0x20); \
+GEN_STU(width, op | 0x21); \
+GEN_STUX(width, op | 0x01); \
+GEN_STX(width, 0x17, op | 0x00)
+
+/* stb stbu stbux stbx */
+GEN_STS(b, 0x06);
+/* sth sthu sthux sthx */
+GEN_STS(h, 0x0C);
+/* stw stwu stwux stwx */
+GEN_STS(w, 0x04);
+
+/*** Integer load and store with byte reverse ***/
+/* lhbrx */
+OP_LD_TABLE(hbr);
+GEN_LDX(hbr, 0x16, 0x18);
+/* lwbrx */
+OP_LD_TABLE(wbr);
+GEN_LDX(wbr, 0x16, 0x10);
+/* sthbrx */
+OP_ST_TABLE(hbr);
+GEN_STX(hbr, 0x16, 0x1C);
+/* stwbrx */
+OP_ST_TABLE(wbr);
+GEN_STX(wbr, 0x16, 0x14);
+
+/*** Integer load and store multiple ***/
+#define op_ldstm(name, reg) (*gen_op_##name[ctx->mem_idx])(reg)
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc1 *gen_op_lmw[] = {
+ &gen_op_lmw_raw,
+ &gen_op_lmw_le_raw,
+};
+static GenOpFunc1 *gen_op_stmw[] = {
+ &gen_op_stmw_raw,
+ &gen_op_stmw_le_raw,
+};
+#else
+static GenOpFunc1 *gen_op_lmw[] = {
+ &gen_op_lmw_user,
+ &gen_op_lmw_le_user,
+ &gen_op_lmw_kernel,
+ &gen_op_lmw_le_kernel,
+};
+static GenOpFunc1 *gen_op_stmw[] = {
+ &gen_op_stmw_user,
+ &gen_op_stmw_le_user,
+ &gen_op_stmw_kernel,
+ &gen_op_stmw_le_kernel,
+};
+#endif
+
+/* lmw */
+GEN_HANDLER(lmw, 0x2E, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ if (simm != 0)
+ gen_op_addi(simm);
+ }
+ op_ldstm(lmw, rD(ctx->opcode));
+}
+
+/* stmw */
+GEN_HANDLER(stmw, 0x2F, 0xFF, 0xFF, 0x00000000, PPC_INTEGER)
+{
+ int simm = SIMM(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(simm);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ if (simm != 0)
+ gen_op_addi(simm);
+ }
+ op_ldstm(stmw, rS(ctx->opcode));
+}
+
+/*** Integer load and store strings ***/
+#define op_ldsts(name, start) (*gen_op_##name[ctx->mem_idx])(start)
+#define op_ldstsx(name, rd, ra, rb) (*gen_op_##name[ctx->mem_idx])(rd, ra, rb)
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc1 *gen_op_lswi[] = {
+ &gen_op_lswi_raw,
+ &gen_op_lswi_le_raw,
+};
+static GenOpFunc3 *gen_op_lswx[] = {
+ &gen_op_lswx_raw,
+ &gen_op_lswx_le_raw,
+};
+static GenOpFunc1 *gen_op_stsw[] = {
+ &gen_op_stsw_raw,
+ &gen_op_stsw_le_raw,
+};
+#else
+static GenOpFunc1 *gen_op_lswi[] = {
+ &gen_op_lswi_user,
+ &gen_op_lswi_le_user,
+ &gen_op_lswi_kernel,
+ &gen_op_lswi_le_kernel,
+};
+static GenOpFunc3 *gen_op_lswx[] = {
+ &gen_op_lswx_user,
+ &gen_op_lswx_le_user,
+ &gen_op_lswx_kernel,
+ &gen_op_lswx_le_kernel,
+};
+static GenOpFunc1 *gen_op_stsw[] = {
+ &gen_op_stsw_user,
+ &gen_op_stsw_le_user,
+ &gen_op_stsw_kernel,
+ &gen_op_stsw_le_kernel,
+};
+#endif
+
+/* lswi */
+/* PowerPC32 specification says we must generate an exception if
+ * rA is in the range of registers to be loaded.
+ * In an other hand, IBM says this is valid, but rA won't be loaded.
+ * For now, I'll follow the spec...
+ */
+GEN_HANDLER(lswi, 0x1F, 0x15, 0x12, 0x00000001, PPC_INTEGER)
+{
+ int nb = NB(ctx->opcode);
+ int start = rD(ctx->opcode);
+ int ra = rA(ctx->opcode);
+ int nr;
+
+ if (nb == 0)
+ nb = 32;
+ nr = nb / 4;
+ if (((start + nr) > 32 && start <= ra && (start + nr - 32) > ra) ||
+ ((start + nr) <= 32 && start <= ra && (start + nr) > ra)) {
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_LSWX);
+ return;
+ }
+ if (ra == 0) {
+ gen_op_set_T0(0);
+ } else {
+ gen_op_load_gpr_T0(ra);
+ }
+ gen_op_set_T1(nb);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(lswi, start);
+}
+
+/* lswx */
+GEN_HANDLER(lswx, 0x1F, 0x15, 0x10, 0x00000001, PPC_INTEGER)
+{
+ int ra = rA(ctx->opcode);
+ int rb = rB(ctx->opcode);
+
+ if (ra == 0) {
+ gen_op_load_gpr_T0(rb);
+ ra = rb;
+ } else {
+ gen_op_load_gpr_T0(ra);
+ gen_op_load_gpr_T1(rb);
+ gen_op_add();
+ }
+ gen_op_load_xer_bc();
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldstsx(lswx, rD(ctx->opcode), ra, rb);
+}
+
+/* stswi */
+GEN_HANDLER(stswi, 0x1F, 0x15, 0x16, 0x00000001, PPC_INTEGER)
+{
+ int nb = NB(ctx->opcode);
+
+ if (rA(ctx->opcode) == 0) {
+ gen_op_set_T0(0);
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ }
+ if (nb == 0)
+ nb = 32;
+ gen_op_set_T1(nb);
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(stsw, rS(ctx->opcode));
+}
+
+/* stswx */
+GEN_HANDLER(stswx, 0x1F, 0x15, 0x14, 0x00000001, PPC_INTEGER)
+{
+ int ra = rA(ctx->opcode);
+
+ if (ra == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ ra = rB(ctx->opcode);
+ } else {
+ gen_op_load_gpr_T0(ra);
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_xer_bc();
+ /* NIP cannot be restored if the memory exception comes from an helper */
+ gen_op_update_nip((ctx)->nip - 4);
+ op_ldsts(stsw, rS(ctx->opcode));
+}
+
+/*** Memory synchronisation ***/
+/* eieio */
+GEN_HANDLER(eieio, 0x1F, 0x16, 0x1A, 0x03FF0801, PPC_MEM)
+{
+}
+
+/* isync */
+GEN_HANDLER(isync, 0x13, 0x16, 0xFF, 0x03FF0801, PPC_MEM)
+{
+}
+
+#define op_lwarx() (*gen_op_lwarx[ctx->mem_idx])()
+#define op_stwcx() (*gen_op_stwcx[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc *gen_op_lwarx[] = {
+ &gen_op_lwarx_raw,
+ &gen_op_lwarx_le_raw,
+};
+static GenOpFunc *gen_op_stwcx[] = {
+ &gen_op_stwcx_raw,
+ &gen_op_stwcx_le_raw,
+};
+#else
+static GenOpFunc *gen_op_lwarx[] = {
+ &gen_op_lwarx_user,
+ &gen_op_lwarx_le_user,
+ &gen_op_lwarx_kernel,
+ &gen_op_lwarx_le_kernel,
+};
+static GenOpFunc *gen_op_stwcx[] = {
+ &gen_op_stwcx_user,
+ &gen_op_stwcx_le_user,
+ &gen_op_stwcx_kernel,
+ &gen_op_stwcx_le_kernel,
+};
+#endif
+
+/* lwarx */
+GEN_HANDLER(lwarx, 0x1F, 0x14, 0xFF, 0x00000001, PPC_RES)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_lwarx();
+ gen_op_store_T1_gpr(rD(ctx->opcode));
+}
+
+/* stwcx. */
+GEN_HANDLER(stwcx_, 0x1F, 0x16, 0x04, 0x00000000, PPC_RES)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_gpr_T1(rS(ctx->opcode));
+ op_stwcx();
+}
+
+/* sync */
+GEN_HANDLER(sync, 0x1F, 0x16, 0x12, 0x03FF0801, PPC_MEM)
+{
+}
+
+/*** Floating-point load ***/
+#define GEN_LDF(width, opc) \
+GEN_HANDLER(l##width, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDUF(width, opc) \
+GEN_HANDLER(l##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDUXF(width, opc) \
+GEN_HANDLER(l##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0 || \
+ rA(ctx->opcode) == rD(ctx->opcode)) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_LDXF(width, opc2, opc3) \
+GEN_HANDLER(l##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ op_ldst(l##width); \
+ gen_op_store_FT1_fpr(rD(ctx->opcode)); \
+}
+
+#define GEN_LDFS(width, op) \
+OP_LD_TABLE(width); \
+GEN_LDF(width, op | 0x20); \
+GEN_LDUF(width, op | 0x21); \
+GEN_LDUXF(width, op | 0x01); \
+GEN_LDXF(width, 0x17, op | 0x00)
+
+/* lfd lfdu lfdux lfdx */
+GEN_LDFS(fd, 0x12);
+/* lfs lfsu lfsux lfsx */
+GEN_LDFS(fs, 0x10);
+
+/*** Floating-point store ***/
+#define GEN_STF(width, opc) \
+GEN_HANDLER(st##width, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_set_T0(simm); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ } \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STUF(width, opc) \
+GEN_HANDLER(st##width##u, opc, 0xFF, 0xFF, 0x00000000, PPC_FLOAT) \
+{ \
+ uint32_t simm = SIMM(ctx->opcode); \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ if (simm != 0) \
+ gen_op_addi(simm); \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STUXF(width, opc) \
+GEN_HANDLER(st##width##ux, 0x1F, 0x17, opc, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ RET_INVAL(ctx); \
+ return; \
+ } \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+ gen_op_store_T0_gpr(rA(ctx->opcode)); \
+}
+
+#define GEN_STXF(width, opc2, opc3) \
+GEN_HANDLER(st##width##x, 0x1F, opc2, opc3, 0x00000001, PPC_FLOAT) \
+{ \
+ if (!ctx->fpu_enabled) { \
+ RET_EXCP(ctx, EXCP_NO_FP, 0); \
+ return; \
+ } \
+ if (rA(ctx->opcode) == 0) { \
+ gen_op_load_gpr_T0(rB(ctx->opcode)); \
+ } else { \
+ gen_op_load_gpr_T0(rA(ctx->opcode)); \
+ gen_op_load_gpr_T1(rB(ctx->opcode)); \
+ gen_op_add(); \
+ } \
+ gen_op_load_fpr_FT1(rS(ctx->opcode)); \
+ op_ldst(st##width); \
+}
+
+#define GEN_STFS(width, op) \
+OP_ST_TABLE(width); \
+GEN_STF(width, op | 0x20); \
+GEN_STUF(width, op | 0x21); \
+GEN_STUXF(width, op | 0x01); \
+GEN_STXF(width, 0x17, op | 0x00)
+
+/* stfd stfdu stfdux stfdx */
+GEN_STFS(fd, 0x16);
+/* stfs stfsu stfsux stfsx */
+GEN_STFS(fs, 0x14);
+
+/* Optional: */
+/* stfiwx */
+GEN_HANDLER(stfiwx, 0x1F, 0x17, 0x1E, 0x00000001, PPC_FLOAT)
+{
+ if (!ctx->fpu_enabled) {
+ RET_EXCP(ctx, EXCP_NO_FP, 0);
+ return;
+ }
+ RET_INVAL(ctx);
+}
+
+/*** Branch ***/
+
+static inline void gen_goto_tb(DisasContext *ctx, int n, target_ulong dest)
+{
+ TranslationBlock *tb;
+ tb = ctx->tb;
+ if ((tb->pc & TARGET_PAGE_MASK) == (dest & TARGET_PAGE_MASK)) {
+ if (n == 0)
+ gen_op_goto_tb0(TBPARAM(tb));
+ else
+ gen_op_goto_tb1(TBPARAM(tb));
+ gen_op_set_T1(dest);
+ gen_op_b_T1();
+ gen_op_set_T0((long)tb + n);
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_exit_tb();
+ } else {
+ gen_op_set_T1(dest);
+ gen_op_b_T1();
+ if (ctx->singlestep_enabled)
+ gen_op_debug();
+ gen_op_set_T0(0);
+ gen_op_exit_tb();
+ }
+}
+
+/* b ba bl bla */
+GEN_HANDLER(b, 0x12, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ uint32_t li, target;
+
+ /* sign extend LI */
+ li = ((int32_t)LI(ctx->opcode) << 6) >> 6;
+
+ if (AA(ctx->opcode) == 0)
+ target = ctx->nip + li - 4;
+ else
+ target = li;
+ if (LK(ctx->opcode)) {
+ gen_op_setlr(ctx->nip);
+ }
+ gen_goto_tb(ctx, 0, target);
+ ctx->exception = EXCP_BRANCH;
+}
+
+#define BCOND_IM 0
+#define BCOND_LR 1
+#define BCOND_CTR 2
+
+static inline void gen_bcond(DisasContext *ctx, int type)
+{
+ uint32_t target = 0;
+ uint32_t bo = BO(ctx->opcode);
+ uint32_t bi = BI(ctx->opcode);
+ uint32_t mask;
+ uint32_t li;
+
+ if ((bo & 0x4) == 0)
+ gen_op_dec_ctr();
+ switch(type) {
+ case BCOND_IM:
+ li = (int32_t)((int16_t)(BD(ctx->opcode)));
+ if (AA(ctx->opcode) == 0) {
+ target = ctx->nip + li - 4;
+ } else {
+ target = li;
+ }
+ break;
+ case BCOND_CTR:
+ gen_op_movl_T1_ctr();
+ break;
+ default:
+ case BCOND_LR:
+ gen_op_movl_T1_lr();
+ break;
+ }
+ if (LK(ctx->opcode)) {
+ gen_op_setlr(ctx->nip);
+ }
+ if (bo & 0x10) {
+ /* No CR condition */
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr();
+ break;
+ case 2:
+ gen_op_test_ctrz();
+ break;
+ default:
+ case 4:
+ case 6:
+ if (type == BCOND_IM) {
+ gen_goto_tb(ctx, 0, target);
+ } else {
+ gen_op_b_T1();
+ }
+ goto no_test;
+ }
+ } else {
+ mask = 1 << (3 - (bi & 0x03));
+ gen_op_load_crf_T0(bi >> 2);
+ if (bo & 0x8) {
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr_true(mask);
+ break;
+ case 2:
+ gen_op_test_ctrz_true(mask);
+ break;
+ default:
+ case 4:
+ case 6:
+ gen_op_test_true(mask);
+ break;
+ }
+ } else {
+ switch (bo & 0x6) {
+ case 0:
+ gen_op_test_ctr_false(mask);
+ break;
+ case 2:
+ gen_op_test_ctrz_false(mask);
+ break;
+ default:
+ case 4:
+ case 6:
+ gen_op_test_false(mask);
+ break;
+ }
+ }
+ }
+ if (type == BCOND_IM) {
+ int l1 = gen_new_label();
+ gen_op_jz_T0(l1);
+ gen_goto_tb(ctx, 0, target);
+ gen_set_label(l1);
+ gen_goto_tb(ctx, 1, ctx->nip);
+ } else {
+ gen_op_btest_T1(ctx->nip);
+ }
+ no_test:
+ ctx->exception = EXCP_BRANCH;
+}
+
+GEN_HANDLER(bc, 0x10, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_IM);
+}
+
+GEN_HANDLER(bcctr, 0x13, 0x10, 0x10, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_CTR);
+}
+
+GEN_HANDLER(bclr, 0x13, 0x10, 0x00, 0x00000000, PPC_FLOW)
+{
+ gen_bcond(ctx, BCOND_LR);
+}
+
+/*** Condition register logical ***/
+#define GEN_CRLOGIC(op, opc) \
+GEN_HANDLER(cr##op, 0x13, 0x01, opc, 0x00000001, PPC_INTEGER) \
+{ \
+ gen_op_load_crf_T0(crbA(ctx->opcode) >> 2); \
+ gen_op_getbit_T0(3 - (crbA(ctx->opcode) & 0x03)); \
+ gen_op_load_crf_T1(crbB(ctx->opcode) >> 2); \
+ gen_op_getbit_T1(3 - (crbB(ctx->opcode) & 0x03)); \
+ gen_op_##op(); \
+ gen_op_load_crf_T1(crbD(ctx->opcode) >> 2); \
+ gen_op_setcrfbit(~(1 << (3 - (crbD(ctx->opcode) & 0x03))), \
+ 3 - (crbD(ctx->opcode) & 0x03)); \
+ gen_op_store_T1_crf(crbD(ctx->opcode) >> 2); \
+}
+
+/* crand */
+GEN_CRLOGIC(and, 0x08)
+/* crandc */
+GEN_CRLOGIC(andc, 0x04)
+/* creqv */
+GEN_CRLOGIC(eqv, 0x09)
+/* crnand */
+GEN_CRLOGIC(nand, 0x07)
+/* crnor */
+GEN_CRLOGIC(nor, 0x01)
+/* cror */
+GEN_CRLOGIC(or, 0x0E)
+/* crorc */
+GEN_CRLOGIC(orc, 0x0D)
+/* crxor */
+GEN_CRLOGIC(xor, 0x06)
+/* mcrf */
+GEN_HANDLER(mcrf, 0x13, 0x00, 0xFF, 0x00000001, PPC_INTEGER)
+{
+ gen_op_load_crf_T0(crfS(ctx->opcode));
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+}
+
+/*** System linkage ***/
+/* rfi (supervisor only) */
+GEN_HANDLER(rfi, 0x13, 0x12, 0xFF, 0x03FF8001, PPC_FLOW)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ /* Restore CPU state */
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_rfi();
+ RET_CHG_FLOW(ctx);
+#endif
+}
+
+/* sc */
+GEN_HANDLER(sc, 0x11, 0xFF, 0xFF, 0x03FFFFFD, PPC_FLOW)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_EXCP(ctx, EXCP_SYSCALL_USER, 0);
+#else
+ RET_EXCP(ctx, EXCP_SYSCALL, 0);
+#endif
+}
+
+/*** Trap ***/
+/* tw */
+GEN_HANDLER(tw, 0x1F, 0x04, 0xFF, 0x00000001, PPC_FLOW)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_tw(TO(ctx->opcode));
+}
+
+/* twi */
+GEN_HANDLER(twi, 0x03, 0xFF, 0xFF, 0x00000000, PPC_FLOW)
+{
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+#if 0
+ printf("%s: param=0x%04x T0=0x%04x\n", __func__,
+ SIMM(ctx->opcode), TO(ctx->opcode));
+#endif
+ gen_op_twi(SIMM(ctx->opcode), TO(ctx->opcode));
+}
+
+/*** Processor control ***/
+static inline int check_spr_access (int spr, int rw, int supervisor)
+{
+ uint32_t rights = spr_access[spr >> 1] >> (4 * (spr & 1));
+
+#if 0
+ if (spr != LR && spr != CTR) {
+ if (loglevel > 0) {
+ fprintf(logfile, "%s reg=%d s=%d rw=%d r=0x%02x 0x%02x\n", __func__,
+ SPR_ENCODE(spr), supervisor, rw, rights,
+ (rights >> ((2 * supervisor) + rw)) & 1);
+ } else {
+ printf("%s reg=%d s=%d rw=%d r=0x%02x 0x%02x\n", __func__,
+ SPR_ENCODE(spr), supervisor, rw, rights,
+ (rights >> ((2 * supervisor) + rw)) & 1);
+ }
+ }
+#endif
+ if (rights == 0)
+ return -1;
+ rights = rights >> (2 * supervisor);
+ rights = rights >> rw;
+
+ return rights & 1;
+}
+
+/* mcrxr */
+GEN_HANDLER(mcrxr, 0x1F, 0x00, 0x10, 0x007FF801, PPC_MISC)
+{
+ gen_op_load_xer_cr();
+ gen_op_store_T0_crf(crfD(ctx->opcode));
+ gen_op_clear_xer_cr();
+}
+
+/* mfcr */
+GEN_HANDLER(mfcr, 0x1F, 0x13, 0x00, 0x001FF801, PPC_MISC)
+{
+ gen_op_load_cr();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/* mfmsr */
+GEN_HANDLER(mfmsr, 0x1F, 0x13, 0x02, 0x001FF801, PPC_MISC)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_msr();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+#if 0
+#define SPR_NOACCESS ((void *)(-1))
+#else
+static void spr_noaccess (void *opaque, int sprn)
+{
+ sprn = ((sprn >> 5) & 0x1F) | ((sprn & 0x1F) << 5);
+ printf("ERROR: try to access SPR %d !\n", sprn);
+}
+#define SPR_NOACCESS (&spr_noaccess)
+#endif
+
+/* mfspr */
+static inline void gen_op_mfspr (DisasContext *ctx)
+{
+ void (*read_cb)(void *opaque, int sprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->supervisor)
+ read_cb = ctx->spr_cb[sprn].oea_read;
+ else
+#endif
+ read_cb = ctx->spr_cb[sprn].uea_read;
+ if (read_cb != NULL) {
+ if (read_cb != SPR_NOACCESS) {
+ (*read_cb)(ctx, sprn);
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+ } else {
+ /* Privilege exception */
+ if (loglevel) {
+ fprintf(logfile, "Trying to read priviledged spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to read priviledged spr %d %03x\n", sprn, sprn);
+ RET_PRIVREG(ctx);
+ }
+ } else {
+ /* Not defined */
+ if (loglevel) {
+ fprintf(logfile, "Trying to read invalid spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to read invalid spr %d %03x\n", sprn, sprn);
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
+ }
+}
+
+GEN_HANDLER(mfspr, 0x1F, 0x13, 0x0A, 0x00000001, PPC_MISC)
+{
+ gen_op_mfspr(ctx);
+ }
+
+/* mftb */
+GEN_HANDLER(mftb, 0x1F, 0x13, 0x0B, 0x00000001, PPC_TB)
+{
+ gen_op_mfspr(ctx);
+}
+
+/* mtcrf */
+/* The mask should be 0x00100801, but Mac OS X 10.4 use an alternate form */
+GEN_HANDLER(mtcrf, 0x1F, 0x10, 0x04, 0x00000801, PPC_MISC)
+{
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_cr(CRM(ctx->opcode));
+}
+
+/* mtmsr */
+GEN_HANDLER(mtmsr, 0x1F, 0x12, 0x04, 0x001FF801, PPC_MISC)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_update_nip((ctx)->nip);
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_msr();
+ /* Must stop the translation as machine state (may have) changed */
+ RET_CHG_FLOW(ctx);
+#endif
+}
+
+/* mtspr */
+GEN_HANDLER(mtspr, 0x1F, 0x13, 0x0E, 0x00000001, PPC_MISC)
+{
+ void (*write_cb)(void *opaque, int sprn);
+ uint32_t sprn = SPR(ctx->opcode);
+
+#if !defined(CONFIG_USER_ONLY)
+ if (ctx->supervisor)
+ write_cb = ctx->spr_cb[sprn].oea_write;
+ else
+#endif
+ write_cb = ctx->spr_cb[sprn].uea_write;
+ if (write_cb != NULL) {
+ if (write_cb != SPR_NOACCESS) {
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ (*write_cb)(ctx, sprn);
+ } else {
+ /* Privilege exception */
+ if (loglevel) {
+ fprintf(logfile, "Trying to write priviledged spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to write priviledged spr %d %03x\n", sprn, sprn);
+ RET_PRIVREG(ctx);
+ }
+ } else {
+ /* Not defined */
+ if (loglevel) {
+ fprintf(logfile, "Trying to write invalid spr %d %03x\n",
+ sprn, sprn);
+ }
+ printf("Trying to write invalid spr %d %03x\n", sprn, sprn);
+ RET_EXCP(ctx, EXCP_PROGRAM, EXCP_INVAL | EXCP_INVAL_SPR);
+ }
+}
+
+/*** Cache management ***/
+/* For now, all those will be implemented as nop:
+ * this is valid, regarding the PowerPC specs...
+ * We just have to flush tb while invalidating instruction cache lines...
+ */
+/* dcbf */
+GEN_HANDLER(dcbf, 0x1F, 0x16, 0x02, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+}
+
+/* dcbi (Supervisor only) */
+GEN_HANDLER(dcbi, 0x1F, 0x16, 0x0E, 0x03E00001, PPC_CACHE)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+ op_ldst(stb);
+#endif
+}
+
+/* dcdst */
+GEN_HANDLER(dcbst, 0x1F, 0x16, 0x01, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_ldst(lbz);
+}
+
+/* dcbt */
+GEN_HANDLER(dcbt, 0x1F, 0x16, 0x08, 0x03E00001, PPC_CACHE)
+{
+}
+
+/* dcbtst */
+GEN_HANDLER(dcbtst, 0x1F, 0x16, 0x07, 0x03E00001, PPC_CACHE)
+{
+}
+
+/* dcbz */
+#if defined(CONFIG_USER_ONLY)
+#define op_dcbz() gen_op_dcbz_raw()
+#else
+#define op_dcbz() (*gen_op_dcbz[ctx->mem_idx])()
+static GenOpFunc *gen_op_dcbz[] = {
+ &gen_op_dcbz_user,
+ &gen_op_dcbz_user,
+ &gen_op_dcbz_kernel,
+ &gen_op_dcbz_kernel,
+};
+#endif
+
+GEN_HANDLER(dcbz, 0x1F, 0x16, 0x1F, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_dcbz();
+ gen_op_check_reservation();
+}
+
+/* icbi */
+GEN_HANDLER(icbi, 0x1F, 0x16, 0x1E, 0x03E00001, PPC_CACHE)
+{
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_icbi();
+}
+
+/* Optional: */
+/* dcba */
+GEN_HANDLER(dcba, 0x1F, 0x16, 0x17, 0x03E00001, PPC_CACHE_OPT)
+{
+}
+
+/*** Segment register manipulation ***/
+/* Supervisor only: */
+/* mfsr */
+GEN_HANDLER(mfsr, 0x1F, 0x13, 0x12, 0x0010F801, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_sr(SR(ctx->opcode));
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+/* mfsrin */
+GEN_HANDLER(mfsrin, 0x1F, 0x13, 0x14, 0x001F0001, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_load_srin();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+#endif
+}
+
+/* mtsr */
+GEN_HANDLER(mtsr, 0x1F, 0x12, 0x06, 0x0010F801, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_store_sr(SR(ctx->opcode));
+ RET_STOP(ctx);
+#endif
+}
+
+/* mtsrin */
+GEN_HANDLER(mtsrin, 0x1F, 0x12, 0x07, 0x001F0001, PPC_SEGMENT)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVREG(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVREG(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rS(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_store_srin();
+ RET_STOP(ctx);
+#endif
+}
+
+/*** Lookaside buffer management ***/
+/* Optional & supervisor only: */
+/* tlbia */
+GEN_HANDLER(tlbia, 0x1F, 0x12, 0x0B, 0x03FFFC01, PPC_MEM_TLBIA)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ if (loglevel)
+ fprintf(logfile, "%s: ! supervisor\n", __func__);
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_tlbia();
+ RET_STOP(ctx);
+#endif
+}
+
+/* tlbie */
+GEN_HANDLER(tlbie, 0x1F, 0x12, 0x09, 0x03FF0001, PPC_MEM)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ gen_op_tlbie();
+ RET_STOP(ctx);
+#endif
+}
+
+/* tlbsync */
+GEN_HANDLER(tlbsync, 0x1F, 0x16, 0x11, 0x03FFF801, PPC_MEM)
+{
+#if defined(CONFIG_USER_ONLY)
+ RET_PRIVOPC(ctx);
+#else
+ if (!ctx->supervisor) {
+ RET_PRIVOPC(ctx);
+ return;
+ }
+ /* This has no effect: it should ensure that all previous
+ * tlbie have completed
+ */
+ RET_STOP(ctx);
+#endif
+}
+
+/*** External control ***/
+/* Optional: */
+#define op_eciwx() (*gen_op_eciwx[ctx->mem_idx])()
+#define op_ecowx() (*gen_op_ecowx[ctx->mem_idx])()
+#if defined(CONFIG_USER_ONLY)
+static GenOpFunc *gen_op_eciwx[] = {
+ &gen_op_eciwx_raw,
+ &gen_op_eciwx_le_raw,
+};
+static GenOpFunc *gen_op_ecowx[] = {
+ &gen_op_ecowx_raw,
+ &gen_op_ecowx_le_raw,
+};
+#else
+static GenOpFunc *gen_op_eciwx[] = {
+ &gen_op_eciwx_user,
+ &gen_op_eciwx_le_user,
+ &gen_op_eciwx_kernel,
+ &gen_op_eciwx_le_kernel,
+};
+static GenOpFunc *gen_op_ecowx[] = {
+ &gen_op_ecowx_user,
+ &gen_op_ecowx_le_user,
+ &gen_op_ecowx_kernel,
+ &gen_op_ecowx_le_kernel,
+};
+#endif
+
+/* eciwx */
+GEN_HANDLER(eciwx, 0x1F, 0x16, 0x0D, 0x00000001, PPC_EXTERN)
+{
+ /* Should check EAR[E] & alignment ! */
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ op_eciwx();
+ gen_op_store_T0_gpr(rD(ctx->opcode));
+}
+
+/* ecowx */
+GEN_HANDLER(ecowx, 0x1F, 0x16, 0x09, 0x00000001, PPC_EXTERN)
+{
+ /* Should check EAR[E] & alignment ! */
+ if (rA(ctx->opcode) == 0) {
+ gen_op_load_gpr_T0(rB(ctx->opcode));
+ } else {
+ gen_op_load_gpr_T0(rA(ctx->opcode));
+ gen_op_load_gpr_T1(rB(ctx->opcode));
+ gen_op_add();
+ }
+ gen_op_load_gpr_T2(rS(ctx->opcode));
+ op_ecowx();
+}
+
+/* End opcode list */
+GEN_OPCODE_MARK(end);
+
+#include "translate_init.c"
+
+/*****************************************************************************/
+/* Misc PowerPC helpers */
+void cpu_dump_state(CPUState *env, FILE *f,
+ int (*cpu_fprintf)(FILE *f, const char *fmt, ...),
+ int flags)
+{
+#if defined(TARGET_PPC64) || 1
+#define FILL ""
+#define REGX "%016" PRIx64
+#define RGPL 4
+#define RFPL 4
+#else
+#define FILL " "
+#define REGX "%08" PRIx64
+#define RGPL 8
+#define RFPL 4
+#endif
+
+ int i;
+
+ cpu_fprintf(f, "NIP " REGX " LR " REGX " CTR " REGX "\n",
+ env->nip, env->lr, env->ctr);
+ cpu_fprintf(f, "MSR " REGX FILL " XER %08x TB %08x %08x DECR %08x\n",
+ do_load_msr(env), do_load_xer(env), cpu_ppc_load_tbu(env),
+ cpu_ppc_load_tbl(env), cpu_ppc_load_decr(env));
+ for (i = 0; i < 32; i++) {
+ if ((i & (RGPL - 1)) == 0)
+ cpu_fprintf(f, "GPR%02d", i);
+ cpu_fprintf(f, " " REGX, env->gpr[i]);
+ if ((i & (RGPL - 1)) == (RGPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "CR ");
+ for (i = 0; i < 8; i++)
+ cpu_fprintf(f, "%01x", env->crf[i]);
+ cpu_fprintf(f, " [");
+ for (i = 0; i < 8; i++) {
+ char a = '-';
+ if (env->crf[i] & 0x08)
+ a = 'L';
+ else if (env->crf[i] & 0x04)
+ a = 'G';
+ else if (env->crf[i] & 0x02)
+ a = 'E';
+ cpu_fprintf(f, " %c%c", a, env->crf[i] & 0x01 ? 'O' : ' ');
+ }
+ cpu_fprintf(f, " ] " FILL "RES " REGX "\n", env->reserve);
+ for (i = 0; i < 32; i++) {
+ if ((i & (RFPL - 1)) == 0)
+ cpu_fprintf(f, "FPR%02d", i);
+ cpu_fprintf(f, " %016" PRIx64, *((uint64_t *)&env->fpr[i]));
+ if ((i & (RFPL - 1)) == (RFPL - 1))
+ cpu_fprintf(f, "\n");
+ }
+ cpu_fprintf(f, "SRR0 " REGX " SRR1 " REGX " " FILL FILL FILL
+ "SDR1 " REGX "\n",
+ env->spr[SPR_SRR0], env->spr[SPR_SRR1], env->sdr1);
+
+#undef REGX
+#undef RGPL
+#undef RFPL
+#undef FILL
+}
+
+/*****************************************************************************/
+int gen_intermediate_code_internal (CPUState *env, TranslationBlock *tb,
+ int search_pc)
+{
+ DisasContext ctx, *ctxp = &ctx;
+ opc_handler_t **table, *handler;
+ target_ulong pc_start;
+ uint16_t *gen_opc_end;
+ int j, lj = -1;
+
+ pc_start = tb->pc;
+ gen_opc_ptr = gen_opc_buf;
+ gen_opc_end = gen_opc_buf + OPC_MAX_SIZE;
+ gen_opparam_ptr = gen_opparam_buf;
+ nb_gen_labels = 0;
+ ctx.nip = pc_start;
+ ctx.tb = tb;
+ ctx.exception = EXCP_NONE;
+ ctx.spr_cb = env->spr_cb;
+#if defined(CONFIG_USER_ONLY)
+ ctx.mem_idx = msr_le;
+#else
+ ctx.supervisor = 1 - msr_pr;
+ ctx.mem_idx = ((1 - msr_pr) << 1) | msr_le;
+#endif
+ ctx.fpu_enabled = msr_fp;
+ ctx.singlestep_enabled = env->singlestep_enabled;
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr_se = 1;
+#endif
+ /* Set env in case of segfault during code fetch */
+ while (ctx.exception == EXCP_NONE && gen_opc_ptr < gen_opc_end) {
+ if (env->nb_breakpoints > 0) {
+ for(j = 0; j < env->nb_breakpoints; j++) {
+ if (env->breakpoints[j] == ctx.nip) {
+ gen_op_update_nip(ctx.nip);
+ gen_op_debug();
+ 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.nip;
+ gen_opc_instr_start[lj] = 1;
+ }
+ }
+#if defined PPC_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "----------------\n");
+ fprintf(logfile, "nip=%08x super=%d ir=%d\n",
+ ctx.nip, 1 - msr_pr, msr_ir);
+ }
+#endif
+ ctx.opcode = ldl_code(ctx.nip);
+ if (msr_le) {
+ ctx.opcode = ((ctx.opcode & 0xFF000000) >> 24) |
+ ((ctx.opcode & 0x00FF0000) >> 8) |
+ ((ctx.opcode & 0x0000FF00) << 8) |
+ ((ctx.opcode & 0x000000FF) << 24);
+ }
+#if defined PPC_DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "translate opcode %08x (%02x %02x %02x) (%s)\n",
+ ctx.opcode, opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), msr_le ? "little" : "big");
+ }
+#endif
+ ctx.nip += 4;
+ table = env->opcodes;
+ handler = table[opc1(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc2(ctx.opcode)];
+ if (is_indirect_opcode(handler)) {
+ table = ind_table(handler);
+ handler = table[opc3(ctx.opcode)];
+ }
+ }
+ /* Is opcode *REALLY* valid ? */
+ if (handler->handler == &gen_invalid) {
+ if (loglevel > 0) {
+ fprintf(logfile, "invalid/unsupported opcode: "
+ "%02x - %02x - %02x (%08x) 0x%08x %d\n",
+ opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, msr_ir);
+ } else {
+ printf("invalid/unsupported opcode: "
+ "%02x - %02x - %02x (%08x) 0x%08x %d\n",
+ opc1(ctx.opcode), opc2(ctx.opcode),
+ opc3(ctx.opcode), ctx.opcode, ctx.nip - 4, msr_ir);
+ }
+ } else {
+ if ((ctx.opcode & handler->inval) != 0) {
+ if (loglevel > 0) {
+ fprintf(logfile, "invalid bits: %08x for opcode: "
+ "%02x -%02x - %02x (0x%08x) (0x%08x)\n",
+ ctx.opcode & handler->inval, opc1(ctx.opcode),
+ opc2(ctx.opcode), opc3(ctx.opcode),
+ ctx.opcode, ctx.nip - 4);
+ } else {
+ printf("invalid bits: %08x for opcode: "
+ "%02x -%02x - %02x (0x%08x) (0x%08x)\n",
+ ctx.opcode & handler->inval, opc1(ctx.opcode),
+ opc2(ctx.opcode), opc3(ctx.opcode),
+ ctx.opcode, ctx.nip - 4);
+ }
+ RET_INVAL(ctxp);
+ break;
+ }
+ }
+ (*(handler->handler))(&ctx);
+ /* Check trace mode exceptions */
+ if ((msr_be && ctx.exception == EXCP_BRANCH) ||
+ /* Check in single step trace mode
+ * we need to stop except if:
+ * - rfi, trap or syscall
+ * - first instruction of an exception handler
+ */
+ (msr_se && (ctx.nip < 0x100 ||
+ ctx.nip > 0xF00 ||
+ (ctx.nip & 0xFC) != 0x04) &&
+ ctx.exception != EXCP_SYSCALL &&
+ ctx.exception != EXCP_SYSCALL_USER &&
+ ctx.exception != EXCP_TRAP)) {
+ RET_EXCP(ctxp, EXCP_TRACE, 0);
+ }
+
+ /* if we reach a page boundary or are single stepping, stop
+ * generation
+ */
+ if (((ctx.nip & (TARGET_PAGE_SIZE - 1)) == 0) ||
+ (env->singlestep_enabled)) {
+ break;
+ }
+#if defined (DO_SINGLE_STEP)
+ break;
+#endif
+ }
+ if (ctx.exception == EXCP_NONE) {
+ gen_goto_tb(&ctx, 0, ctx.nip);
+ } else if (ctx.exception != EXCP_BRANCH) {
+ gen_op_set_T0(0);
+ }
+#if 1
+ /* TO BE FIXED: T0 hasn't got a proper value, which makes tb_add_jump
+ * do bad business and then qemu crashes !
+ */
+ gen_op_set_T0(0);
+#endif
+ /* Generate the return instruction */
+ gen_op_exit_tb();
+ *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;
+ tb->size = 0;
+#if 0
+ if (loglevel > 0) {
+ page_dump(logfile);
+ }
+#endif
+ } else {
+ tb->size = ctx.nip - pc_start;
+ }
+#ifdef DEBUG_DISAS
+ if (loglevel & CPU_LOG_TB_CPU) {
+ fprintf(logfile, "---------------- excp: %04x\n", ctx.exception);
+ cpu_dump_state(env, logfile, fprintf, 0);
+ }
+ if (loglevel & CPU_LOG_TB_IN_ASM) {
+ fprintf(logfile, "IN: %s\n", lookup_symbol(pc_start));
+ target_disas(logfile, pc_start, ctx.nip - pc_start, msr_le);
+ fprintf(logfile, "\n");
+ }
+ if (loglevel & CPU_LOG_TB_OP) {
+ fprintf(logfile, "OP:\n");
+ dump_ops(gen_opc_buf, gen_opparam_buf);
+ fprintf(logfile, "\n");
+ }
+#endif
+ return 0;
+}
+
+int gen_intermediate_code (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 0);
+}
+
+int gen_intermediate_code_pc (CPUState *env, struct TranslationBlock *tb)
+{
+ return gen_intermediate_code_internal(env, tb, 1);
+}
diff --git a/target-ppc/translate_init.c b/target-ppc/translate_init.c
new file mode 100644
index 0000000..ddf0c91
--- /dev/null
+++ b/target-ppc/translate_init.c
@@ -0,0 +1,2067 @@
+/*
+ * PowerPC CPU initialization for qemu.
+ *
+ * Copyright (c) 2003-2005 Jocelyn Mayer
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 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, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
+ */
+
+/* A lot of PowerPC definition have been included here.
+ * Most of them are not usable for now but have been kept
+ * inside "#if defined(TODO) ... #endif" statements to make tests easier.
+ */
+
+//#define PPC_DUMP_CPU
+//#define PPC_DEBUG_SPR
+
+struct ppc_def_t {
+ const unsigned char *name;
+ uint32_t pvr;
+ uint32_t pvr_mask;
+ uint32_t insns_flags;
+ uint32_t flags;
+ uint64_t msr_mask;
+};
+
+/* Generic callbacks:
+ * do nothing but store/retrieve spr value
+ */
+static void spr_read_generic (void *opaque, int sprn)
+{
+ gen_op_load_spr(sprn);
+}
+
+static void spr_write_generic (void *opaque, int sprn)
+{
+ gen_op_store_spr(sprn);
+}
+
+/* SPR common to all PPC */
+/* XER */
+static void spr_read_xer (void *opaque, int sprn)
+{
+ gen_op_load_xer();
+}
+
+static void spr_write_xer (void *opaque, int sprn)
+{
+ gen_op_store_xer();
+}
+
+/* LR */
+static void spr_read_lr (void *opaque, int sprn)
+{
+ gen_op_load_lr();
+}
+
+static void spr_write_lr (void *opaque, int sprn)
+{
+ gen_op_store_lr();
+}
+
+/* CTR */
+static void spr_read_ctr (void *opaque, int sprn)
+{
+ gen_op_load_ctr();
+}
+
+static void spr_write_ctr (void *opaque, int sprn)
+{
+ gen_op_store_ctr();
+}
+
+/* User read access to SPR */
+/* USPRx */
+/* UMMCRx */
+/* UPMCx */
+/* USIA */
+/* UDECR */
+static void spr_read_ureg (void *opaque, int sprn)
+{
+ gen_op_load_spr(sprn + 0x10);
+}
+
+/* SPR common to all non-embedded PPC (ie not 4xx) */
+/* DECR */
+static void spr_read_decr (void *opaque, int sprn)
+{
+ gen_op_load_decr();
+}
+
+static void spr_write_decr (void *opaque, int sprn)
+{
+ gen_op_store_decr();
+}
+
+/* SPR common to all non-embedded PPC, except 601 */
+/* Time base */
+static void spr_read_tbl (void *opaque, int sprn)
+{
+ gen_op_load_tbl();
+}
+
+static void spr_write_tbl (void *opaque, int sprn)
+{
+ gen_op_store_tbl();
+}
+
+static void spr_read_tbu (void *opaque, int sprn)
+{
+ gen_op_load_tbu();
+}
+
+static void spr_write_tbu (void *opaque, int sprn)
+{
+ gen_op_store_tbu();
+}
+
+/* IBAT0U...IBAT0U */
+/* IBAT0L...IBAT7L */
+static void spr_read_ibat (void *opaque, int sprn)
+{
+ gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT0U) / 2);
+}
+
+static void spr_read_ibat_h (void *opaque, int sprn)
+{
+ gen_op_load_ibat(sprn & 1, (sprn - SPR_IBAT4U) / 2);
+}
+
+static void spr_write_ibatu (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatu((sprn - SPR_IBAT0U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatu_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatu((sprn - SPR_IBAT4U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatl (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatl((sprn - SPR_IBAT0L) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_ibatl_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_ibatl((sprn - SPR_IBAT4L) / 2);
+ RET_STOP(ctx);
+}
+
+/* DBAT0U...DBAT7U */
+/* DBAT0L...DBAT7L */
+static void spr_read_dbat (void *opaque, int sprn)
+{
+ gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT0U) / 2);
+}
+
+static void spr_read_dbat_h (void *opaque, int sprn)
+{
+ gen_op_load_dbat(sprn & 1, (sprn - SPR_DBAT4U) / 2);
+}
+
+static void spr_write_dbatu (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatu((sprn - SPR_DBAT0U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatu_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatu((sprn - SPR_DBAT4U) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatl (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatl((sprn - SPR_DBAT0L) / 2);
+ RET_STOP(ctx);
+}
+
+static void spr_write_dbatl_h (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_dbatl((sprn - SPR_DBAT4L) / 2);
+ RET_STOP(ctx);
+}
+
+/* SDR1 */
+static void spr_read_sdr1 (void *opaque, int sprn)
+{
+ gen_op_load_sdr1();
+}
+
+static void spr_write_sdr1 (void *opaque, int sprn)
+{
+ DisasContext *ctx = opaque;
+
+ gen_op_store_sdr1();
+ RET_STOP(ctx);
+}
+
+static void spr_write_pir (void *opaque, int sprn)
+{
+ gen_op_store_pir();
+}
+
+static inline void spr_register (CPUPPCState *env, int num,
+ const unsigned char *name,
+ void (*uea_read)(void *opaque, int sprn),
+ void (*uea_write)(void *opaque, int sprn),
+ void (*oea_read)(void *opaque, int sprn),
+ void (*oea_write)(void *opaque, int sprn),
+ target_ulong initial_value)
+{
+ ppc_spr_t *spr;
+
+ spr = &env->spr_cb[num];
+ if (spr->name != NULL ||env-> spr[num] != 0x00000000 ||
+ spr->uea_read != NULL || spr->uea_write != NULL ||
+ spr->oea_read != NULL || spr->oea_write != NULL) {
+ printf("Error: Trying to register SPR %d (%03x) twice !\n", num, num);
+ exit(1);
+ }
+#if defined(PPC_DEBUG_SPR)
+ printf("*** register spr %d (%03x) %s val %08" PRIx64 "\n", num, num, name,
+ (unsigned long long)initial_value);
+#endif
+ spr->name = name;
+ spr->uea_read = uea_read;
+ spr->uea_write = uea_write;
+ spr->oea_read = oea_read;
+ spr->oea_write = oea_write;
+ env->spr[num] = initial_value;
+}
+
+/* Generic PowerPC SPRs */
+static void gen_spr_generic (CPUPPCState *env)
+{
+ /* Integer processing */
+ spr_register(env, SPR_XER, "XER",
+ &spr_read_xer, &spr_write_xer,
+ &spr_read_xer, &spr_write_xer,
+ 0x00000000);
+ /* Branch contol */
+ spr_register(env, SPR_LR, "LR",
+ &spr_read_lr, &spr_write_lr,
+ &spr_read_lr, &spr_write_lr,
+ 0x00000000);
+ spr_register(env, SPR_CTR, "CTR",
+ &spr_read_ctr, &spr_write_ctr,
+ &spr_read_ctr, &spr_write_ctr,
+ 0x00000000);
+ /* Interrupt processing */
+ spr_register(env, SPR_SRR0, "SRR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SRR1, "SRR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Processor control */
+ spr_register(env, SPR_SPRG0, "SPRG0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG1, "SPRG1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG2, "SPRG2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_SPRG3, "SPRG3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR common to all non-embedded PowerPC, including 601 */
+static void gen_spr_ne_601 (CPUPPCState *env)
+{
+ /* Exception processing */
+ spr_register(env, SPR_DSISR, "DSISR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ spr_register(env, SPR_DAR, "DAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Timer */
+ spr_register(env, SPR_DECR, "DECR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_decr, &spr_write_decr,
+ 0x00000000);
+ /* Memory management */
+ spr_register(env, SPR_SDR1, "SDR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_sdr1, &spr_write_sdr1,
+ 0x00000000);
+}
+
+/* BATs 0-3 */
+static void gen_low_BATs (CPUPPCState *env)
+{
+ spr_register(env, SPR_IBAT0U, "IBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT0L, "IBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1U, "IBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT1L, "IBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2U, "IBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT2L, "IBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3U, "IBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatu,
+ 0x00000000);
+ spr_register(env, SPR_IBAT3L, "IBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat, &spr_write_ibatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0U, "DBAT0U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT0L, "DBAT0L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1U, "DBAT1U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT1L, "DBAT1L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2U, "DBAT2U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT2L, "DBAT2L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3U, "DBAT3U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatu,
+ 0x00000000);
+ spr_register(env, SPR_DBAT3L, "DBAT3L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat, &spr_write_dbatl,
+ 0x00000000);
+ env->nb_BATs = 4;
+}
+
+/* BATs 4-7 */
+static void gen_high_BATs (CPUPPCState *env)
+{
+ spr_register(env, SPR_IBAT4U, "IBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT4L, "IBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5U, "IBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT5L, "IBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6U, "IBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT6L, "IBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7U, "IBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatu_h,
+ 0x00000000);
+ spr_register(env, SPR_IBAT7L, "IBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_ibat_h, &spr_write_ibatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4U, "DBAT4U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT4L, "DBAT4L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5U, "DBAT5U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT5L, "DBAT5L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6U, "DBAT6U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT6L, "DBAT6L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7U, "DBAT7U",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatu_h,
+ 0x00000000);
+ spr_register(env, SPR_DBAT7L, "DBAT7L",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_dbat_h, &spr_write_dbatl_h,
+ 0x00000000);
+ env->nb_BATs = 8;
+}
+
+/* Generic PowerPC time base */
+static void gen_tbl (CPUPPCState *env)
+{
+ spr_register(env, SPR_VTBL, "TBL",
+ &spr_read_tbl, SPR_NOACCESS,
+ &spr_read_tbl, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBL, "TBL",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbl,
+ 0x00000000);
+ spr_register(env, SPR_VTBU, "TBU",
+ &spr_read_tbu, SPR_NOACCESS,
+ &spr_read_tbu, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_TBU, "TBU",
+ SPR_NOACCESS, SPR_NOACCESS,
+ SPR_NOACCESS, &spr_write_tbu,
+ 0x00000000);
+}
+
+/* SPR common to all 7xx PowerPC implementations */
+static void gen_spr_7xx (CPUPPCState *env)
+{
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Cache management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_ICTC, "ICTC",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance monitors */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIA, "SIA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UMMCR0, "UMMCR0",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UMMCR1, "UMMCR1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC1, "UPMC1",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC2, "UPMC2",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC3, "UPMC3",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_UPMC4, "UPMC4",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ spr_register(env, SPR_USIA, "USIA",
+ &spr_read_ureg, SPR_NOACCESS,
+ &spr_read_ureg, SPR_NOACCESS,
+ 0x00000000);
+ /* Thermal management */
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM1, "THRM1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM2, "THRM2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_THRM3, "THRM3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+/* SPR specific to PowerPC 604 implementation */
+static void gen_spr_604 (CPUPPCState *env)
+{
+ /* Processor identification */
+ spr_register(env, SPR_PIR, "PIR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_pir,
+ 0x00000000);
+ /* Breakpoints */
+ /* XXX : not implemented */
+ spr_register(env, SPR_IABR, "IABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_DABR, "DABR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Performance counters */
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR0, "MMCR0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_MMCR1, "MMCR1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC1, "PMC1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC2, "PMC2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC3, "PMC3",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_PMC4, "PMC4",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SIA, "SIA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_SDA, "SDA",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ 0x00000000);
+ /* External access control */
+ /* XXX : not implemented */
+ spr_register(env, SPR_EAR, "EAR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+}
+
+// XXX: TODO (64 bits PPC sprs)
+/*
+ * ASR => SPR 280 (64 bits)
+ * FPECR => SPR 1022 (?)
+ * VRSAVE => SPR 256 (Altivec)
+ * SCOMC => SPR 276 (64 bits ?)
+ * SCOMD => SPR 277 (64 bits ?)
+ * HSPRG0 => SPR 304 (hypervisor)
+ * HSPRG1 => SPR 305 (hypervisor)
+ * HDEC => SPR 310 (hypervisor)
+ * HIOR => SPR 311 (hypervisor)
+ * RMOR => SPR 312 (970)
+ * HRMOR => SPR 313 (hypervisor)
+ * HSRR0 => SPR 314 (hypervisor)
+ * HSRR1 => SPR 315 (hypervisor)
+ * LPCR => SPR 316 (970)
+ * LPIDR => SPR 317 (970)
+ * ... and more (thermal management, performance counters, ...)
+ */
+
+static void init_ppc_proc (CPUPPCState *env, ppc_def_t *def)
+{
+ /* Default MMU definitions */
+ env->nb_BATs = -1;
+ env->nb_tlb = 0;
+ env->nb_ways = 0;
+ /* XXX: missing:
+ * 32 bits PPC:
+ * - MPC5xx(x)
+ * - MPC8xx(x)
+ * - RCPU (MPC5xx)
+ */
+ spr_register(env, SPR_PVR, "PVR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, SPR_NOACCESS,
+ def->pvr);
+ switch (def->pvr & def->pvr_mask) {
+ case CPU_PPC_604: /* PPC 604 */
+ case CPU_PPC_604E: /* PPC 604e */
+ case CPU_PPC_604R: /* PPC 604r */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_604(env);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ case CPU_PPC_74x: /* PPC 740 / 750 */
+ case CPU_PPC_74xP: /* PPC 740P / 750P */
+ case CPU_PPC_750CXE: /* IBM PPC 750cxe */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ case CPU_PPC_750FX: /* IBM PPC 750 FX */
+ case CPU_PPC_750GX: /* IBM PPC 750 GX */
+ gen_spr_generic(env);
+ gen_spr_ne_601(env);
+ /* Memory management */
+ gen_low_BATs(env);
+ /* PowerPC 750fx & 750gx has 8 DBATs and 8 IBATs */
+ gen_high_BATs(env);
+ /* Time base */
+ gen_tbl(env);
+ gen_spr_7xx(env);
+ /* XXX : not implemented */
+ spr_register(env, SPR_L2CR, "L2CR",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* Hardware implementation registers */
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID0, "HID0",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_HID1, "HID1",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ /* XXX : not implemented */
+ spr_register(env, SPR_750_HID2, "HID2",
+ SPR_NOACCESS, SPR_NOACCESS,
+ &spr_read_generic, &spr_write_generic,
+ 0x00000000);
+ break;
+
+ default:
+ gen_spr_generic(env);
+ break;
+ }
+ if (env->nb_BATs == -1)
+ env->nb_BATs = 4;
+}
+
+#if defined(PPC_DUMP_CPU)
+static void dump_sprs (CPUPPCState *env)
+{
+ ppc_spr_t *spr;
+ uint32_t pvr = env->spr[SPR_PVR];
+ uint32_t sr, sw, ur, uw;
+ int i, j, n;
+
+ printf("* SPRs for PVR=%08x\n", pvr);
+ for (i = 0; i < 32; i++) {
+ for (j = 0; j < 32; j++) {
+ n = (i << 5) | j;
+ spr = &env->spr_cb[n];
+ sw = spr->oea_write != NULL && spr->oea_write != SPR_NOACCESS;
+ sr = spr->oea_read != NULL && spr->oea_read != SPR_NOACCESS;
+ uw = spr->uea_write != NULL && spr->uea_write != SPR_NOACCESS;
+ ur = spr->uea_read != NULL && spr->uea_read != SPR_NOACCESS;
+ if (sw || sr || uw || ur) {
+ printf("%4d (%03x) %8s s%c%c u%c%c\n",
+ (i << 5) | j, (i << 5) | j, spr->name,
+ sw ? 'w' : '-', sr ? 'r' : '-',
+ uw ? 'w' : '-', ur ? 'r' : '-');
+ }
+ }
+ }
+ fflush(stdout);
+ fflush(stderr);
+}
+#endif
+
+/*****************************************************************************/
+#include <stdlib.h>
+#include <string.h>
+
+int fflush (FILE *stream);
+
+/* Opcode types */
+enum {
+ PPC_DIRECT = 0, /* Opcode routine */
+ PPC_INDIRECT = 1, /* Indirect opcode table */
+};
+
+static inline int is_indirect_opcode (void *handler)
+{
+ return ((unsigned long)handler & 0x03) == PPC_INDIRECT;
+}
+
+static inline opc_handler_t **ind_table(void *handler)
+{
+ return (opc_handler_t **)((unsigned long)handler & ~3);
+}
+
+/* Instruction table creation */
+/* Opcodes tables creation */
+static void fill_new_table (opc_handler_t **table, int len)
+{
+ int i;
+
+ for (i = 0; i < len; i++)
+ table[i] = &invalid_handler;
+}
+
+static int create_new_table (opc_handler_t **table, unsigned char idx)
+{
+ opc_handler_t **tmp;
+
+ tmp = malloc(0x20 * sizeof(opc_handler_t));
+ if (tmp == NULL)
+ return -1;
+ fill_new_table(tmp, 0x20);
+ table[idx] = (opc_handler_t *)((unsigned long)tmp | PPC_INDIRECT);
+
+ return 0;
+}
+
+static int insert_in_table (opc_handler_t **table, unsigned char idx,
+ opc_handler_t *handler)
+{
+ if (table[idx] != &invalid_handler)
+ return -1;
+ table[idx] = handler;
+
+ return 0;
+}
+
+static int register_direct_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx, opc_handler_t *handler)
+{
+ if (insert_in_table(ppc_opcodes, idx, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in main "
+ "opcode table\n", idx);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_in_table (opc_handler_t **table,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ if (table[idx1] == &invalid_handler) {
+ if (create_new_table(table, idx1) < 0) {
+ printf("*** ERROR: unable to create indirect table "
+ "idx=%02x\n", idx1);
+ return -1;
+ }
+ } else {
+ if (!is_indirect_opcode(table[idx1])) {
+ printf("*** ERROR: idx %02x already assigned to a direct "
+ "opcode\n", idx1);
+ return -1;
+ }
+ }
+ if (handler != NULL &&
+ insert_in_table(ind_table(table[idx1]), idx2, handler) < 0) {
+ printf("*** ERROR: opcode %02x already assigned in "
+ "opcode table %02x\n", idx2, idx1);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_ind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ opc_handler_t *handler)
+{
+ int ret;
+
+ ret = register_ind_in_table(ppc_opcodes, idx1, idx2, handler);
+
+ return ret;
+}
+
+static int register_dblind_insn (opc_handler_t **ppc_opcodes,
+ unsigned char idx1, unsigned char idx2,
+ unsigned char idx3, opc_handler_t *handler)
+{
+ if (register_ind_in_table(ppc_opcodes, idx1, idx2, NULL) < 0) {
+ printf("*** ERROR: unable to join indirect table idx "
+ "[%02x-%02x]\n", idx1, idx2);
+ return -1;
+ }
+ if (register_ind_in_table(ind_table(ppc_opcodes[idx1]), idx2, idx3,
+ handler) < 0) {
+ printf("*** ERROR: unable to insert opcode "
+ "[%02x-%02x-%02x]\n", idx1, idx2, idx3);
+ return -1;
+ }
+
+ return 0;
+}
+
+static int register_insn (opc_handler_t **ppc_opcodes, opcode_t *insn)
+{
+ if (insn->opc2 != 0xFF) {
+ if (insn->opc3 != 0xFF) {
+ if (register_dblind_insn(ppc_opcodes, insn->opc1, insn->opc2,
+ insn->opc3, &insn->handler) < 0)
+ return -1;
+ } else {
+ if (register_ind_insn(ppc_opcodes, insn->opc1,
+ insn->opc2, &insn->handler) < 0)
+ return -1;
+ }
+ } else {
+ if (register_direct_insn(ppc_opcodes, insn->opc1, &insn->handler) < 0)
+ return -1;
+ }
+
+ return 0;
+}
+
+static int test_opcode_table (opc_handler_t **table, int len)
+{
+ int i, count, tmp;
+
+ for (i = 0, count = 0; i < len; i++) {
+ /* Consistency fixup */
+ if (table[i] == NULL)
+ table[i] = &invalid_handler;
+ if (table[i] != &invalid_handler) {
+ if (is_indirect_opcode(table[i])) {
+ tmp = test_opcode_table(ind_table(table[i]), 0x20);
+ if (tmp == 0) {
+ free(table[i]);
+ table[i] = &invalid_handler;
+ } else {
+ count++;
+ }
+ } else {
+ count++;
+ }
+ }
+ }
+
+ return count;
+}
+
+static void fix_opcode_tables (opc_handler_t **ppc_opcodes)
+{
+ if (test_opcode_table(ppc_opcodes, 0x40) == 0)
+ printf("*** WARNING: no opcode defined !\n");
+}
+
+/*****************************************************************************/
+static int create_ppc_opcodes (CPUPPCState *env, ppc_def_t *def)
+{
+ opcode_t *opc, *start, *end;
+
+ fill_new_table(env->opcodes, 0x40);
+#if defined(PPC_DUMP_CPU)
+ printf("* PPC instructions for PVR %08x: %s\n", def->pvr, def->name);
+#endif
+ if (&opc_start < &opc_end) {
+ start = &opc_start;
+ end = &opc_end;
+ } else {
+ start = &opc_end;
+ end = &opc_start;
+ }
+ for (opc = start + 1; opc != end; opc++) {
+ if ((opc->handler.type & def->insns_flags) != 0) {
+ if (register_insn(env->opcodes, opc) < 0) {
+ printf("*** ERROR initializing PPC instruction "
+ "0x%02x 0x%02x 0x%02x\n", opc->opc1, opc->opc2,
+ opc->opc3);
+ return -1;
+ }
+#if defined(PPC_DUMP_CPU)
+ if (opc1 != 0x00) {
+ if (opc->opc3 == 0xFF) {
+ if (opc->opc2 == 0xFF) {
+ printf(" %02x -- -- (%2d ----) : %s\n",
+ opc->opc1, opc->opc1, opc->oname);
+ } else {
+ printf(" %02x %02x -- (%2d %4d) : %s\n",
+ opc->opc1, opc->opc2, opc->opc1, opc->opc2,
+ opc->oname);
+ }
+ } else {
+ printf(" %02x %02x %02x (%2d %4d) : %s\n",
+ opc->opc1, opc->opc2, opc->opc3,
+ opc->opc1, (opc->opc3 << 5) | opc->opc2,
+ opc->oname);
+ }
+ }
+#endif
+ }
+ }
+ fix_opcode_tables(env->opcodes);
+ fflush(stdout);
+ fflush(stderr);
+
+ return 0;
+}
+
+int cpu_ppc_register (CPUPPCState *env, ppc_def_t *def)
+{
+ env->msr_mask = def->msr_mask;
+ env->flags = def->flags;
+ if (create_ppc_opcodes(env, def) < 0) {
+ printf("Error creating opcodes table\n");
+ fflush(stdout);
+ fflush(stderr);
+ return -1;
+ }
+ init_ppc_proc(env, def);
+#if defined(PPC_DUMP_CPU)
+ dump_sprs(env);
+#endif
+ fflush(stdout);
+ fflush(stderr);
+
+ return 0;
+}
+
+CPUPPCState *cpu_ppc_init(void)
+{
+ CPUPPCState *env;
+
+ env = qemu_mallocz(sizeof(CPUPPCState));
+ if (!env)
+ return NULL;
+ cpu_exec_init(env);
+ tlb_flush(env, 1);
+#if defined (DO_SINGLE_STEP) && 0
+ /* Single step trace mode */
+ msr_se = 1;
+ msr_be = 1;
+#endif
+ msr_fp = 1; /* Allow floating point exceptions */
+ msr_me = 1; /* Allow machine check exceptions */
+#if defined(CONFIG_USER_ONLY)
+ msr_pr = 1;
+#else
+ env->nip = 0xFFFFFFFC;
+#endif
+ do_compute_hflags(env);
+ env->reserve = -1;
+ return env;
+}
+
+void cpu_ppc_close(CPUPPCState *env)
+{
+ /* Should also remove all opcode tables... */
+ free(env);
+}
+
+/*****************************************************************************/
+/* PowerPC CPU definitions */
+static ppc_def_t ppc_defs[] =
+{
+ /* Embedded PPC */
+#if defined (TODO)
+ /* PPC 401 */
+ {
+ .name = "401",
+ .pvr = CPU_PPC_401,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_401,
+ .flags = PPC_FLAGS_401,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* IOP480 (401 microcontroler) */
+ {
+ .name = "iop480",
+ .pvr = CPU_PPC_IOP480,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_401,
+ .flags = PPC_FLAGS_401,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GA */
+ {
+ .name = "403ga",
+ .pvr = CPU_PPC_403GA,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GB */
+ {
+ .name = "403gb",
+ .pvr = CPU_PPC_403GB,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GC */
+ {
+ .name = "403gc",
+ .pvr = CPU_PPC_403GC,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 403 GCX */
+ {
+ .name = "403gcx",
+ .pvr = CPU_PPC_403GCX,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_403,
+ .flags = PPC_FLAGS_403,
+ .msr_mask = 0x000000000007D23D,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 CR */
+ {
+ .name = "405cr",
+ .pvr = CPU_PPC_405,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 GP */
+ {
+ .name = "405gp",
+ .pvr = CPU_PPC_405,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 EP */
+ {
+ .name = "405ep",
+ .pvr = CPU_PPC_405EP,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 GPR */
+ {
+ .name = "405gpr",
+ .pvr = CPU_PPC_405GPR,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 D2 */
+ {
+ .name = "405d2",
+ .pvr = CPU_PPC_405D2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 405 D4 */
+ {
+ .name = "405d4",
+ .pvr = CPU_PPC_405D4,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* Npe405 H */
+ {
+ .name = "Npe405H",
+ .pvr = CPU_PPC_NPE405H,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* Npe405 L */
+ {
+ .name = "Npe405L",
+ .pvr = CPU_PPC_NPE405L,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB03xx */
+ {
+ .name = "STB03",
+ .pvr = CPU_PPC_STB03,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB04xx */
+ {
+ .name = "STB04",
+ .pvr = CPU_PPC_STB04,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* STB25xx */
+ {
+ .name = "STB25",
+ .pvr = CPU_PPC_STB25,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_405,
+ .msr_mask = 0x00000000020EFF30,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 EP */
+ {
+ .name = "440ep",
+ .pvr = CPU_PPC_440EP,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_440,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 GP */
+ {
+ .name = "440gp",
+ .pvr = CPU_PPC_440GP,
+ .pvr_mask = 0xFFFFFF00,
+ .insns_flags = PPC_INSNS_440,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 440 GX */
+ {
+ .name = "440gx",
+ .pvr = CPU_PPC_440GX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_405,
+ .flags = PPC_FLAGS_440,
+ .msr_mask = 0x000000000006D630,
+ },
+#endif
+
+ /* 32 bits "classic" powerpc */
+#if defined (TODO)
+ /* PPC 601 */
+ {
+ .name = "601",
+ .pvr = CPU_PPC_601,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_601,
+ .flags = PPC_FLAGS_601,
+ .msr_mask = 0x000000000000FD70,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 602 */
+ {
+ .name = "602",
+ .pvr = CPU_PPC_602,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_602,
+ .flags = PPC_FLAGS_602,
+ .msr_mask = 0x0000000000C7FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603 */
+ {
+ .name = "603",
+ .pvr = CPU_PPC_603,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603e */
+ {
+ .name = "603e",
+ .pvr = CPU_PPC_603E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+ {
+ .name = "Stretch",
+ .pvr = CPU_PPC_603E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603ev */
+ {
+ .name = "603ev",
+ .pvr = CPU_PPC_603EV,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 603r */
+ {
+ .name = "603r",
+ .pvr = CPU_PPC_603R,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+ {
+ .name = "Goldeneye",
+ .pvr = CPU_PPC_603R,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_603,
+ .flags = PPC_FLAGS_603,
+ .msr_mask = 0x000000000007FF73,
+ },
+#endif
+#if defined (TODO)
+ /* XXX: TODO: according to Motorola UM, this is a derivative to 603e */
+ {
+ .name = "G2",
+ .pvr = CPU_PPC_G2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_G2,
+ .flags = PPC_FLAGS_G2,
+ .msr_mask = 0x000000000006FFF2,
+ },
+ { /* Same as G2, with LE mode support */
+ .name = "G2le",
+ .pvr = CPU_PPC_G2LE,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_G2,
+ .flags = PPC_FLAGS_G2,
+ .msr_mask = 0x000000000007FFF3,
+ },
+#endif
+ /* PPC 604 */
+ {
+ .name = "604",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* PPC 604e */
+ {
+ .name = "604e",
+ .pvr = CPU_PPC_604E,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* PPC 604r */
+ {
+ .name = "604r",
+ .pvr = CPU_PPC_604R,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_604,
+ .flags = PPC_FLAGS_604,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* generic G3 */
+ {
+ .name = "G3",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC740 (G3) */
+ {
+ .name = "740",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Arthur",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC745 (G3) */
+ {
+ .name = "745",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Goldfinger",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* MPC750 (G3) */
+ {
+ .name = "750",
+ .pvr = CPU_PPC_74x,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC755 (G3) */
+ {
+ .name = "755",
+ .pvr = CPU_PPC_755,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC740P (G3) */
+ {
+ .name = "740p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ {
+ .name = "Conan/Doyle",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+#if defined (TODO)
+ /* MPC745P (G3) */
+ {
+ .name = "745p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* MPC750P (G3) */
+ {
+ .name = "750p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* MPC755P (G3) */
+ {
+ .name = "755p",
+ .pvr = CPU_PPC_74xP,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x5,
+ .flags = PPC_FLAGS_7x5,
+ .msr_mask = 0x000000000007FF77,
+ },
+#endif
+ /* IBM 750CXe (G3 embedded) */
+ {
+ .name = "750cxe",
+ .pvr = CPU_PPC_750CXE,
+ .pvr_mask = 0xFFFFF000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ /* IBM 750FX (G3 embedded) */
+ {
+ .name = "750fx",
+ .pvr = CPU_PPC_750FX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+ /* IBM 750GX (G3 embedded) */
+ {
+ .name = "750gx",
+ .pvr = CPU_PPC_750GX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_7x0,
+ .flags = PPC_FLAGS_7x0,
+ .msr_mask = 0x000000000007FF77,
+ },
+#if defined (TODO)
+ /* generic G4 */
+ {
+ .name = "G4",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7400 (G4) */
+ {
+ .name = "7400",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Max",
+ .pvr = CPU_PPC_7400,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7410 (G4) */
+ {
+ .name = "7410",
+ .pvr = CPU_PPC_7410,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Nitro",
+ .pvr = CPU_PPC_7410,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* XXX: 7441 */
+ /* XXX: 7445 */
+ /* XXX: 7447 */
+ /* XXX: 7447A */
+#if defined (TODO)
+ /* PPC 7450 (G4) */
+ {
+ .name = "7450",
+ .pvr = CPU_PPC_7450,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Vger",
+ .pvr = CPU_PPC_7450,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* XXX: 7451 */
+#if defined (TODO)
+ /* PPC 7455 (G4) */
+ {
+ .name = "7455",
+ .pvr = CPU_PPC_7455,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 6",
+ .pvr = CPU_PPC_7455,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7457 (G4) */
+ {
+ .name = "7457",
+ .pvr = CPU_PPC_7457,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 7",
+ .pvr = CPU_PPC_7457,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 7457A (G4) */
+ {
+ .name = "7457A",
+ .pvr = CPU_PPC_7457A,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+ {
+ .name = "Apollo 7 PM",
+ .pvr = CPU_PPC_7457A,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_74xx,
+ .flags = PPC_FLAGS_74xx,
+ .msr_mask = 0x000000000205FF77,
+ },
+#endif
+ /* 64 bits PPC */
+#if defined (TODO)
+ /* PPC 620 */
+ {
+ .name = "620",
+ .pvr = CPU_PPC_620,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_620,
+ .flags = PPC_FLAGS_620,
+ .msr_mask = 0x800000000005FF73,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 630 (POWER3) */
+ {
+ .name = "630",
+ .pvr = CPU_PPC_630,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_630,
+ .flags = PPC_FLAGS_630,
+ .msr_mask = xxx,
+ }
+ {
+ .name = "POWER3",
+ .pvr = CPU_PPC_630,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_630,
+ .flags = PPC_FLAGS_630,
+ .msr_mask = xxx,
+ }
+#endif
+#if defined (TODO)
+ /* PPC 631 (Power 3+)*/
+ {
+ .name = "631",
+ .pvr = CPU_PPC_631,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_631,
+ .flags = PPC_FLAGS_631,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "POWER3+",
+ .pvr = CPU_PPC_631,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_631,
+ .flags = PPC_FLAGS_631,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER4 */
+ {
+ .name = "POWER4",
+ .pvr = CPU_PPC_POWER4,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER4,
+ .flags = PPC_FLAGS_POWER4,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER4p */
+ {
+ .name = "POWER4+",
+ .pvr = CPU_PPC_POWER4P,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER4,
+ .flags = PPC_FLAGS_POWER4,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER5 */
+ {
+ .name = "POWER5",
+ .pvr = CPU_PPC_POWER5,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER5,
+ .flags = PPC_FLAGS_POWER5,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER5+ */
+ {
+ .name = "POWER5+",
+ .pvr = CPU_PPC_POWER5P,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER5,
+ .flags = PPC_FLAGS_POWER5,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 970 */
+ {
+ .name = "970",
+ .pvr = CPU_PPC_970,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_970,
+ .flags = PPC_FLAGS_970,
+ .msr_mask = 0x900000000204FF36,
+ },
+#endif
+#if defined (TODO)
+ /* PPC 970FX (G5) */
+ {
+ .name = "970fx",
+ .pvr = CPU_PPC_970FX,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_970FX,
+ .flags = PPC_FLAGS_970FX,
+ .msr_mask = 0x800000000204FF36,
+ },
+#endif
+#if defined (TODO)
+ /* RS64 (Apache/A35) */
+ /* This one seems to support the whole POWER2 instruction set
+ * and the PowerPC 64 one.
+ */
+ {
+ .name = "RS64",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "Apache",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "A35",
+ .pvr = CPU_PPC_RS64,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-II (NorthStar/A50) */
+ {
+ .name = "RS64-II",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "NortStar",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "A50",
+ .pvr = CPU_PPC_RS64II,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-III (Pulsar) */
+ {
+ .name = "RS64-III",
+ .pvr = CPU_PPC_RS64III,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "Pulsar",
+ .pvr = CPU_PPC_RS64III,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* RS64-IV (IceStar/IStar/SStar) */
+ {
+ .name = "RS64-IV",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "IceStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "IStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+ {
+ .name = "SStar",
+ .pvr = CPU_PPC_RS64IV,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_RS64,
+ .flags = PPC_FLAGS_RS64,
+ .msr_mask = xxx,
+ },
+#endif
+ /* POWER */
+#if defined (TODO)
+ /* Original POWER */
+ {
+ .name = "POWER",
+ .pvr = CPU_POWER,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER,
+ .flags = PPC_FLAGS_POWER,
+ .msr_mask = xxx,
+ },
+#endif
+#if defined (TODO)
+ /* POWER2 */
+ {
+ .name = "POWER2",
+ .pvr = CPU_POWER2,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_POWER,
+ .flags = PPC_FLAGS_POWER,
+ .msr_mask = xxx,
+ },
+#endif
+ /* Generic PowerPCs */
+#if defined (TODO)
+ {
+ .name = "ppc64",
+ .pvr = CPU_PPC_970,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC64,
+ .flags = PPC_FLAGS_PPC64,
+ .msr_mask = 0xA00000000204FF36,
+ },
+#endif
+ {
+ .name = "ppc32",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC32,
+ .flags = PPC_FLAGS_PPC32,
+ .msr_mask = 0x000000000005FF77,
+ },
+ /* Fallback */
+ {
+ .name = "ppc",
+ .pvr = CPU_PPC_604,
+ .pvr_mask = 0xFFFF0000,
+ .insns_flags = PPC_INSNS_PPC32,
+ .flags = PPC_FLAGS_PPC32,
+ .msr_mask = 0x000000000005FF77,
+ },
+};
+
+int ppc_find_by_name (const unsigned char *name, ppc_def_t **def)
+{
+ int i, ret;
+
+ ret = -1;
+ *def = NULL;
+ for (i = 0; strcmp(ppc_defs[i].name, "ppc") != 0; i++) {
+ if (strcasecmp(name, ppc_defs[i].name) == 0) {
+ *def = &ppc_defs[i];
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+int ppc_find_by_pvr (uint32_t pvr, ppc_def_t **def)
+{
+ int i, ret;
+
+ ret = -1;
+ *def = NULL;
+ for (i = 0; ppc_defs[i].name != NULL; i++) {
+ if ((pvr & ppc_defs[i].pvr_mask) ==
+ (ppc_defs[i].pvr & ppc_defs[i].pvr_mask)) {
+ *def = &ppc_defs[i];
+ ret = 0;
+ break;
+ }
+ }
+
+ return ret;
+}
+
+void ppc_cpu_list (FILE *f, int (*cpu_fprintf)(FILE *f, const char *fmt, ...))
+{
+ int i;
+
+ for (i = 0; ; i++) {
+ (*cpu_fprintf)(f, "PowerPC '%s' PVR %08x mask %08x\n",
+ ppc_defs[i].name,
+ ppc_defs[i].pvr, ppc_defs[i].pvr_mask);
+ if (strcmp(ppc_defs[i].name, "ppc") == 0)
+ break;
+ }
+}