From: Toni Wilen Date: Thu, 7 Aug 2014 17:30:19 +0000 (+0300) Subject: PPC core. X-Git-Tag: 3000~90 X-Git-Url: https://git.unchartedbackwaters.co.uk/w/?a=commitdiff_plain;h=42a1ccec89881d1c0f35c4bc03018e2d79b4f5f8;p=francis%2Fwinuae.git PPC core. --- diff --git a/ppc/pearpc/config.h b/ppc/pearpc/config.h new file mode 100644 index 00000000..d8bf6f5c --- /dev/null +++ b/ppc/pearpc/config.h @@ -0,0 +1,8 @@ + +#ifdef WIN64 +#define SYSTEM_ARCH_SPECIFIC_ENDIAN_DIR "system/arch/x86_64/sysendian.h" +#else +#define SYSTEM_ARCH_SPECIFIC_ENDIAN_DIR "system/arch/x86/sysendian.h" +#endif + +#define HOST_ENDIANESS HOST_ENDIANESS_LE diff --git a/ppc/pearpc/cpu/common.h b/ppc/pearpc/cpu/common.h new file mode 100644 index 00000000..54ba3994 --- /dev/null +++ b/ppc/pearpc/cpu/common.h @@ -0,0 +1,389 @@ +/* + * PearPC + * common.h + * + * Copyright (C) 2003-2006 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CPU_COMMON_H__ +#define __CPU_COMMON_H__ + +#include +#include "system/types.h" + +typedef union Vector_t { + uint64 d[2]; + sint64 sd[2]; + float f[4]; + uint32 w[4]; + sint32 sw[4]; + uint16 h[8]; + sint16 sh[8]; + uint8 b[16]; + sint8 sb[16]; +} Vector_t; + +/* +cr: .67 + 0- 3 cr0 + 4- 7 cr1 + 8-11 cr2 +12-15 cr3 +16-19 cr4 +20-23 cr5 +24-27 cr6 +28-31 cr7 +*/ + +#define CR_CR0(v) ((v)>>28) +#define CR_CR1(v) (((v)>>24)&0xf) +#define CR_CRx(v, x) (((v)>>(4*(7-(x))))&0xf) + +/* +cr0 bits: .68 +lt +gt +eq +so +*/ + +#define CR_CR0_LT (1<<31) +#define CR_CR0_GT (1<<30) +#define CR_CR0_EQ (1<<29) +#define CR_CR0_SO (1<<28) + +/* +cr1 bits: .68 +4 Floating-point exception (FX) +5 Floating-point enabled exception (FEX) +6 Floating-point invalid exception (VX) +7 Floating-point overflow exception (OX) +*/ + +#define CR_CR1_FX (1<<27) +#define CR_CR1_FEX (1<<26) +#define CR_CR1_VX (1<<25) +#define CR_CR1_OX (1<<24) + +/* +FPSCR bits: .70 + +*/ + +#define FPSCR_FX (1<<31) +#define FPSCR_FEX (1<<30) +#define FPSCR_VX (1<<29) +#define FPSCR_OX (1<<28) +#define FPSCR_UX (1<<27) +#define FPSCR_ZX (1<<26) +#define FPSCR_XX (1<<25) +#define FPSCR_VXSNAN (1<<24) +#define FPSCR_VXISI (1<<23) +#define FPSCR_VXIDI (1<<22) +#define FPSCR_VXZDZ (1<<21) +#define FPSCR_VXIMZ (1<<20) +#define FPSCR_VXVC (1<<19) +#define FPSCR_FR (1<<18) +#define FPSCR_FI (1<<17) + +#define FPSCR_FPRF(v) (((v)>>12)&0x1f) + +#define FPSCR_res0 (1<<11) +#define FPSCR_VXSOFT (1<<10) +#define FPSCR_VXSQRT (1<<9) +#define FPSCR_VXCVI (1<<8) +#define FPSCR_VXVE (1<<7) +#define FPSCR_VXOE (1<<6) +#define FPSCR_VXUE (1<<5) +#define FPSCR_VXZE (1<<4) +#define FPSCR_VXXE (1<<3) +#define FPSCR_VXNI (1<<2) +#define FPSCR_RN(v) ((v)&3) + +#define FPSCR_RN_NEAR 0 +#define FPSCR_RN_ZERO 1 +#define FPSCR_RN_PINF 2 +#define FPSCR_RN_MINF 3 + +/* +VSCR bits: + sat = summary saturation + nj = non-java floating-point mode +*/ +#define VSCR_SAT 1 +#define VSCR_NJ (1<<16) + +/* +xer bits: +0 so +1 ov +2 carry +3-24 res +25-31 number of bytes for lswx/stswx +*/ + +#define XER_SO (1<<31) +#define XER_OV (1<<30) +#define XER_CA (1<<29) +#define XER_n(v) ((v)&0x7f) + +/* +msr: .83 +0-12 res +13 POW power management enabled +14 res +15 ILE exception little-endian mode +16 EE enable external interrupt +17 PR privilege level (0=sv) +18 FP floating point avail +19 ME maschine check exception enable +20 FE0 floation point exception mode 0 +21 SE single step enable +22 BE branch trace enable +23 FE1 floation point exception mode 1 +24 res +25 IP exception prefix +26 IR intruction address translation +27 DR data address translation +28-29res +30 RI recoverable exception +31 LE little endian mode + +*/ + +#define MSR_UNKNOWN (1<<30) +#define MSR_UNKNOWN2 (1<<27) +#define MSR_VEC (1<<25) +#define MSR_KEY (1<<19) // 603e +#define MSR_POW (1<<18) +#define MSR_TGPR (1<<15) // 603(e) +#define MSR_ILE (1<<16) +#define MSR_EE (1<<15) +#define MSR_PR (1<<14) +#define MSR_FP (1<<13) +#define MSR_ME (1<<12) +#define MSR_FE0 (1<<11) +#define MSR_SE (1<<10) +#define MSR_BE (1<<9) +#define MSR_FE1 (1<<8) +#define MSR_IP (1<<6) +#define MSR_IR (1<<5) +#define MSR_DR (1<<4) +#define MSR_PM (1<<2) +#define MSR_RI (1<<1) +#define MSR_LE (1<<0) + +//#define PPC_CPU_UNSUPPORTED_MSR_BITS (/*MSR_POW|*/MSR_ILE|MSR_BE|MSR_IP|MSR_LE) +#define PPC_CPU_UNSUPPORTED_MSR_BITS (~(MSR_POW | MSR_UNKNOWN | MSR_UNKNOWN2 | MSR_VEC | MSR_EE | MSR_PR | MSR_FP | MSR_ME | MSR_FE0 | MSR_SE | MSR_FE1 | MSR_IR | MSR_DR | MSR_RI | MSR_IP)) + +#define MSR_RFI_SAVE_MASK (0x87c0ff73) // was ff73 + +/* +BAT Register: .88 +upper: +0-14 BEPI Block effective page index. +15-18 res +19-29 BL Block length. +30 Vs Supervisor mode valid bit. +31 Vp User mode valid bit. +lower: +0-14 BRPN This field is used in conjunction with the BL field to generate highorder bits of the physical address of the block. +15-24 res +25-28 WIMG Memory/cache access mode bits +29 res +30-31 PP Protection bits for block. + +BAT Area +Length BL Encoding +128 Kbytes 000 0000 0000 +256 Kbytes 000 0000 0001 +512 Kbytes 000 0000 0011 +1 Mbyte 000 0000 0111 +2 Mbytes 000 0000 1111 +4 Mbytes 000 0001 1111 +8 Mbytes 000 0011 1111 +16 Mbytes 000 0111 1111 +32 Mbytes 000 1111 1111 +64 Mbytes 001 1111 1111 +128 Mbytes 011 1111 1111 +256 Mbytes 111 1111 1111 +*/ + +#define BATU_BEPI(v) ((v)&0xfffe0000) +#define BATU_BL(v) (((v)&0x1ffc)>>2) +#define BATU_Vs (1<<1) +#define BATU_Vp (1) +#define BATL_BRPN(v) ((v)&0xfffe0000) + +#define BAT_EA_OFFSET(v) ((v)&0x1ffff) +#define BAT_EA_11(v) ((v)&0x0ffe0000) +#define BAT_EA_4(v) ((v)&0xf0000000) + +/* +sdr1: .91 +0-15 The high-order 16 bits of the 32-bit physical address of the page table +16-22 res +23-31 Mask for page table address +*/ + +#define SDR1_HTABORG(v) (((v)>>16)&0xffff) +#define SDR1_HTABMASK(v) ((v)&0x1ff) +#define SDR1_PAGETABLE_BASE(v) ((v)&0xffff) + +/* +sr: .94 +0 T=0: +1 Ks sv prot +2 Kp user prot +3 N No execute +4-7 res +8-31 VSID Virtual Segment ID + +0 T=1: +1 Ks +2 Kp +3-11 BUID Bus Unit ID +12-31 CNTRL_SPEC + */ +#define SR_T (1<<31) +#define SR_Ks (1<<30) +#define SR_Kp (1<<29) +#define SR_N (1<<28) +#define SR_VSID(v) ((v)&0xffffff) +#define SR_BUID(v) (((v)>>20)&0x1ff) +#define SR_CNTRL_SPEC(v) ((v)&0xfffff) + +#define EA_SR(v) (((v)>>28)&0xf) +#define EA_PageIndex(v) (((v)>>12)&0xffff) +#define EA_Offset(v) ((v)&0xfff) +#define EA_API(v) (((v)>>22)&0x3f) + +#define PA_RPN(v) (((v)>>12)&0xfffff) +#define PA_Offset(v) ((v)&0xfff) + +/* +PTE: .364 +0 V +1-24 VSID +25 H +26-31 API +*/ + +#define PTE1_V (1<<31) +#define PTE1_VSID(v) (((v)>>7)&0xffffff) +#define PTE1_H (1<<6) +#define PTE1_API(v) ((v)&0x3f) + +#define PTE2_RPN(v) ((v)&0xfffff000) +#define PTE2_R (1<<8) +#define PTE2_C (1<<7) +#define PTE2_WIMG(v) (((v)>>3)&0xf) +#define PTE2_PP(v) ((v)&3) + +#define PPC_L1_CACHE_LINE_SIZE 32 +#define PPC_LG_L1_CACHE_LINE_SIZE 5 +#define PPC_MAX_L1_COPY_PREFETCH 4 + +/* + * special registers + */ +#define HID0 1008 /* Checkstop and misc enables */ +#define HID1 1009 /* Clock configuration */ +#define IABR 1010 /* Instruction address breakpoint register */ +#define ICTRL 1011 /* Instruction Cache Control */ +#define LDSTDB 1012 /* Load/Store Debug */ +#define DABR 1013 /* Data address breakpoint register */ +#define MSSCR0 1014 /* Memory subsystem control */ +#define MSSCR1 1015 /* Memory subsystem debug */ +#define MSSSR0 1015 /* Memory Subsystem Status */ +#define LDSTCR 1016 /* Load/Store Status/Control */ +#define L2CR2 1016 /* L2 Cache control 2 */ +#define L2CR 1017 /* L2 Cache control */ +#define L3CR 1018 /* L3 Cache control */ +#define ICTC 1019 /* I-cache throttling control */ +#define THRM1 1020 /* Thermal management 1 */ +#define THRM2 1021 /* Thermal management 2 */ +#define THRM3 1022 /* Thermal management 3 */ +#define PIR 1023 /* Processor ID Register */ + +//; hid0 bits +#define HID0_emcp 0 +#define HID0_emcpm 0x80000000 +#define HID0_dbp 1 +#define HID0_dbpm 0x40000000 +#define HID0_eba 2 +#define HID0_ebam 0x20000000 +#define HID0_ebd 3 +#define HID0_ebdm 0x10000000 +#define HID0_sbclk 4 +#define HID0_sbclkm 0x08000000 +#define HID0_eclk 6 +#define HID0_eclkm 0x02000000 +#define HID0_par 7 +#define HID0_parm 0x01000000 +#define HID0_sten 7 +#define HID0_stenm 0x01000000 +#define HID0_doze 8 +#define HID0_dozem 0x00800000 +#define HID0_nap 9 +#define HID0_napm 0x00400000 +#define HID0_sleep 10 +#define HID0_sleepm 0x00200000 +#define HID0_dpm 11 +#define HID0_dpmm 0x00100000 +#define HID0_riseg 12 +#define HID0_risegm 0x00080000 +#define HID0_eiec 13 +#define HID0_eiecm 0x00040000 +#define HID0_mum 14 +#define HID0_mumm 0x00020000 +#define HID0_nhr 15 +#define HID0_nhrm 0x00010000 +#define HID0_ice 16 +#define HID0_icem 0x00008000 +#define HID0_dce 17 +#define HID0_dcem 0x00004000 +#define HID0_ilock 18 +#define HID0_ilockm 0x00002000 +#define HID0_dlock 19 +#define HID0_dlockm 0x00001000 +#define HID0_icfi 20 +#define HID0_icfim 0x00000800 +#define HID0_dcfi 21 +#define HID0_dcfim 0x00000400 +#define HID0_spd 22 +#define HID0_spdm 0x00000200 +#define HID0_sge 24 +#define HID0_sgem 0x00000080 +#define HID0_dcfa 25 +#define HID0_dcfam 0x00000040 +#define HID0_btic 26 +#define HID0_bticm 0x00000020 +#define HID0_lrstk 27 +#define HID0_lrstkm 0x00000010 +#define HID0_abe 28 +#define HID0_abem 0x00000008 +#define HID0_fold 28 +#define HID0_foldm 0x00000008 +#define HID0_bht 29 +#define HID0_bhtm 0x00000004 +#define HID0_nopdst 30 +#define HID0_nopdstm 0x00000002 +#define HID0_nopti 31 +#define HID0_noptim 0x00000001 + +#endif + diff --git a/ppc/pearpc/cpu/cpu.h b/ppc/pearpc/cpu/cpu.h new file mode 100644 index 00000000..ba96934a --- /dev/null +++ b/ppc/pearpc/cpu/cpu.h @@ -0,0 +1,53 @@ +/* + * PearPC + * cpu.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __CPU_H__ +#define __CPU_H__ + +#include "system/types.h" + +uint64 ppc_get_clock_frequency(int cpu); +uint64 ppc_get_bus_frequency(int cpu); +uint64 ppc_get_timebase_frequency(int cpu); + +bool ppc_cpu_init(uint32); +void ppc_cpu_init_config(); + +void ppc_cpu_stop(); +void ppc_cpu_wakeup(); + +void ppc_machine_check_exception(); + +void ppc_cpu_raise_ext_exception(); +void ppc_cpu_cancel_ext_exception(); + +/* + * May only be called from within a CPU thread. + */ + +void ppc_cpu_run(); +uint32 ppc_cpu_get_gpr(int cpu, int i); +void ppc_cpu_set_gpr(int cpu, int i, uint32 newvalue); +void ppc_cpu_set_msr(int cpu, uint32 newvalue); +void ppc_cpu_set_pc(int cpu, uint32 newvalue); +uint32 ppc_cpu_get_pc(int cpu); +uint32 ppc_cpu_get_pvr(int cpu); + +#endif diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_alu.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_alu.cpp new file mode 100644 index 00000000..eceeb175 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_alu.cpp @@ -0,0 +1,1374 @@ +/* + * PearPC + * ppc_alu.cc + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "debug/tracers.h" +#include "cpu/debug.h" +#include "ppc_alu.h" +#include "ppc_dec.h" +#include "ppc_exc.h" +#include "ppc_cpu.h" +#include "ppc_opc.h" +#include "ppc_tools.h" + +static inline uint32 ppc_mask(int MB, int ME) +{ + uint32 mask; + if (MB <= ME) { + if (ME-MB == 31) { + mask = 0xffffffff; + } else { + mask = ((1<<(ME-MB+1))-1)<<(31-ME); + } + } else { + mask = ppc_word_rotl((1<<(32-MB+ME+1))-1, 31-ME); + } + return mask; +} + +/* + * addx Add + * .422 + */ +void ppc_opc_addx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + gCPU.gpr[rD] = gCPU.gpr[rA] + gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * addox Add with Overflow + * .422 + */ +void ppc_opc_addox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + gCPU.gpr[rD] = gCPU.gpr[rA] + gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("addox unimplemented\n"); +} +/* + * addcx Add Carrying + * .423 + */ +void ppc_opc_addcx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + gCPU.gpr[rD] = a + gCPU.gpr[rB]; + // update xer + if (gCPU.gpr[rD] < a) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * addcox Add Carrying with Overflow + * .423 + */ +void ppc_opc_addcox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + gCPU.gpr[rD] = a + gCPU.gpr[rB]; + // update xer + if (gCPU.gpr[rD] < a) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("addcox unimplemented\n"); +} +/* + * addex Add Extended + * .424 + */ +void ppc_opc_addex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + b + ca; + // update xer + if (ppc_carry_3(a, b, ca)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * addeox Add Extended with Overflow + * .424 + */ +void ppc_opc_addeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + b + ca; + // update xer + if (ppc_carry_3(a, b, ca)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("addeox unimplemented\n"); +} +/* + * addi Add Immediate + * .425 + */ +void ppc_opc_addi() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + gCPU.gpr[rD] = (rA ? gCPU.gpr[rA] : 0) + imm; +} +/* + * addic Add Immediate Carrying + * .426 + */ +void ppc_opc_addic() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint32 a = gCPU.gpr[rA]; + gCPU.gpr[rD] = a + imm; + // update XER + if (gCPU.gpr[rD] < a) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } +} +/* + * addic. Add Immediate Carrying and Record + * .427 + */ +void ppc_opc_addic_() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint32 a = gCPU.gpr[rA]; + gCPU.gpr[rD] = a + imm; + // update XER + if (gCPU.gpr[rD] < a) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); +} +/* + * addis Add Immediate Shifted + * .428 + */ +void ppc_opc_addis() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_Shift16(gCPU.current_opc, rD, rA, imm); + gCPU.gpr[rD] = (rA ? gCPU.gpr[rA] : 0) + imm; +} +/* + * addmex Add to Minus One Extended + * .429 + */ +void ppc_opc_addmex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + ca + 0xffffffff; + if (a || ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * addmeox Add to Minus One Extended with Overflow + * .429 + */ +void ppc_opc_addmeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + ca + 0xffffffff; + if (a || ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("addmeox unimplemented\n"); +} +/* + * addzex Add to Zero Extended + * .430 + */ +void ppc_opc_addzex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + ca; + if ((a == 0xffffffff) && ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + // update xer + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * addzeox Add to Zero Extended with Overflow + * .430 + */ +void ppc_opc_addzeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = a + ca; + if ((a == 0xffffffff) && ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + // update xer + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("addzeox unimplemented\n"); +} + +/* + * andx AND + * .431 + */ +void ppc_opc_andx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = gCPU.gpr[rS] & gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * andcx AND with Complement + * .432 + */ +void ppc_opc_andcx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = gCPU.gpr[rS] & ~gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * andi. AND Immediate + * .433 + */ +void ppc_opc_andi_() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_UImm(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] & imm; + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); +} +/* + * andis. AND Immediate Shifted + * .434 + */ +void ppc_opc_andis_() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_Shift16(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] & imm; + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); +} + +/* + * cmp Compare + * .442 + */ +static uint32 ppc_cmp_and_mask[8] = { + 0xfffffff0, + 0xffffff0f, + 0xfffff0ff, + 0xffff0fff, + 0xfff0ffff, + 0xff0fffff, + 0xf0ffffff, + 0x0fffffff, +}; + +void ppc_opc_cmp() +{ + uint32 cr; + int rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, cr, rA, rB); + cr >>= 2; + sint32 a = gCPU.gpr[rA]; + sint32 b = gCPU.gpr[rB]; + uint32 c; + if (a < b) { + c = 8; + } else if (a > b) { + c = 4; + } else { + c = 2; + } + if (gCPU.xer & XER_SO) c |= 1; + cr = 7-cr; + gCPU.cr &= ppc_cmp_and_mask[cr]; + gCPU.cr |= c<<(cr*4); +} +/* + * cmpi Compare Immediate + * .443 + */ +void ppc_opc_cmpi() +{ + uint32 cr; + int rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, cr, rA, imm); + cr >>= 2; + sint32 a = gCPU.gpr[rA]; + sint32 b = imm; + uint32 c; +/* if (!VALGRIND_CHECK_READABLE(a, sizeof a)) { + ht_printf("%08x <--i\n", gCPU.pc); +// SINGLESTEP(""); + }*/ + if (a < b) { + c = 8; + } else if (a > b) { + c = 4; + } else { + c = 2; + } + if (gCPU.xer & XER_SO) c |= 1; + cr = 7-cr; + gCPU.cr &= ppc_cmp_and_mask[cr]; + gCPU.cr |= c<<(cr*4); +} +/* + * cmpl Compare Logical + * .444 + */ +void ppc_opc_cmpl() +{ + uint32 cr; + int rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, cr, rA, rB); + cr >>= 2; + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + uint32 c; + if (a < b) { + c = 8; + } else if (a > b) { + c = 4; + } else { + c = 2; + } + if (gCPU.xer & XER_SO) c |= 1; + cr = 7-cr; + gCPU.cr &= ppc_cmp_and_mask[cr]; + gCPU.cr |= c<<(cr*4); +} +/* + * cmpli Compare Logical Immediate + * .445 + */ +void ppc_opc_cmpli() +{ + uint32 cr; + int rA; + uint32 imm; + PPC_OPC_TEMPL_D_UImm(gCPU.current_opc, cr, rA, imm); + cr >>= 2; + uint32 a = gCPU.gpr[rA]; + uint32 b = imm; + uint32 c; + if (a < b) { + c = 8; + } else if (a > b) { + c = 4; + } else { + c = 2; + } + if (gCPU.xer & XER_SO) c |= 1; + cr = 7-cr; + gCPU.cr &= ppc_cmp_and_mask[cr]; + gCPU.cr |= c<<(cr*4); +} + +/* + * cntlzwx Count Leading Zeros Word + * .447 + */ +void ppc_opc_cntlzwx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + PPC_OPC_ASSERT(rB==0); + uint32 n=0; + uint32 x=0x80000000; + uint32 v=gCPU.gpr[rS]; + while (!(v & x)) { + n++; + if (n==32) break; + x>>=1; + } + gCPU.gpr[rA] = n; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * crand Condition Register AND + * .448 + */ +void ppc_opc_crand() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if ((gCPU.cr & (1<<(31-crA))) && (gCPU.cr & (1<<(31-crB)))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * crandc Condition Register AND with Complement + * .449 + */ +void ppc_opc_crandc() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if ((gCPU.cr & (1<<(31-crA))) && !(gCPU.cr & (1<<(31-crB)))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * creqv Condition Register Equivalent + * .450 + */ +void ppc_opc_creqv() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if (((gCPU.cr & (1<<(31-crA))) && (gCPU.cr & (1<<(31-crB)))) + || (!(gCPU.cr & (1<<(31-crA))) && !(gCPU.cr & (1<<(31-crB))))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * crnand Condition Register NAND + * .451 + */ +void ppc_opc_crnand() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if (!((gCPU.cr & (1<<(31-crA))) && (gCPU.cr & (1<<(31-crB))))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * crnor Condition Register NOR + * .452 + */ +void ppc_opc_crnor() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + uint32 t = (1<<(31-crA)) | (1<<(31-crB)); + if (!(gCPU.cr & t)) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * cror Condition Register OR + * .453 + */ +void ppc_opc_cror() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + uint32 t = (1<<(31-crA)) | (1<<(31-crB)); + if (gCPU.cr & t) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * crorc Condition Register OR with Complement + * .454 + */ +void ppc_opc_crorc() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if ((gCPU.cr & (1<<(31-crA))) || !(gCPU.cr & (1<<(31-crB)))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} +/* + * crxor Condition Register XOR + * .448 + */ +void ppc_opc_crxor() +{ + int crD, crA, crB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crA, crB); + if ((!(gCPU.cr & (1<<(31-crA))) && (gCPU.cr & (1<<(31-crB)))) + || ((gCPU.cr & (1<<(31-crA))) && !(gCPU.cr & (1<<(31-crB))))) { + gCPU.cr |= (1<<(31-crD)); + } else { + gCPU.cr &= ~(1<<(31-crD)); + } +} + +/* + * divwx Divide Word + * .470 + */ +void ppc_opc_divwx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + if (!gCPU.gpr[rB]) { + PPC_ALU_WARN("division by zero @%08x\n", gCPU.pc); + SINGLESTEP(""); + } + sint32 a = gCPU.gpr[rA]; + sint32 b = gCPU.gpr[rB]; + gCPU.gpr[rD] = a / b; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * divwox Divide Word with Overflow + * .470 + */ +void ppc_opc_divwox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + if (!gCPU.gpr[rB]) { + PPC_ALU_ERR("division by zero\n"); + } + sint32 a = gCPU.gpr[rA]; + sint32 b = gCPU.gpr[rB]; + gCPU.gpr[rD] = a / b; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("divwox unimplemented\n"); +} +/* + * divwux Divide Word Unsigned + * .472 + */ +void ppc_opc_divwux() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + if (!gCPU.gpr[rB]) { + PPC_ALU_WARN("division by zero @%08x\n", gCPU.pc); + SINGLESTEP(""); + } + gCPU.gpr[rD] = gCPU.gpr[rA] / gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * divwuox Divide Word Unsigned with Overflow + * .472 + */ +void ppc_opc_divwuox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + if (!gCPU.gpr[rB]) { +// PPC_ALU_ERR("division by zero\n"); + } + gCPU.gpr[rD] = gCPU.gpr[rA] / gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("divwuox unimplemented\n"); +} + +/* + * eqvx Equivalent + * .480 + */ +void ppc_opc_eqvx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = ~(gCPU.gpr[rS] ^ gCPU.gpr[rB]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * extsbx Extend Sign Byte + * .481 + */ +void ppc_opc_extsbx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + PPC_OPC_ASSERT(rB==0); + gCPU.gpr[rA] = gCPU.gpr[rS]; + if (gCPU.gpr[rA] & 0x80) { + gCPU.gpr[rA] |= 0xffffff00; + } else { + gCPU.gpr[rA] &= ~0xffffff00; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * extshx Extend Sign Half Word + * .482 + */ +void ppc_opc_extshx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + PPC_OPC_ASSERT(rB==0); + gCPU.gpr[rA] = gCPU.gpr[rS]; + if (gCPU.gpr[rA] & 0x8000) { + gCPU.gpr[rA] |= 0xffff0000; + } else { + gCPU.gpr[rA] &= ~0xffff0000; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * mulhwx Multiply High Word + * .595 + */ +void ppc_opc_mulhwx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + sint64 a = (sint32)gCPU.gpr[rA]; + sint64 b = (sint32)gCPU.gpr[rB]; + sint64 c = a*b; + gCPU.gpr[rD] = ((uint64)c)>>32; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); +// PPC_ALU_WARN("mulhw. correct?\n"); + } +} +/* + * mulhwux Multiply High Word Unsigned + * .596 + */ +void ppc_opc_mulhwux() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint64 a = gCPU.gpr[rA]; + uint64 b = gCPU.gpr[rB]; + uint64 c = a*b; + gCPU.gpr[rD] = c>>32; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * mulli Multiply Low Immediate + * .598 + */ +void ppc_opc_mulli() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + // FIXME: signed / unsigned correct? + gCPU.gpr[rD] = gCPU.gpr[rA] * imm; +} +/* + * mullwx Multiply Low Word + * .599 + */ +void ppc_opc_mullwx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + gCPU.gpr[rD] = gCPU.gpr[rA] * gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + if (gCPU.current_opc & PPC_OPC_OE) { + // update XER flags + PPC_ALU_ERR("mullwx unimplemented\n"); + } +} + +/* + * nandx NAND + * .600 + */ +void ppc_opc_nandx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = ~(gCPU.gpr[rS] & gCPU.gpr[rB]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * negx Negate + * .601 + */ +void ppc_opc_negx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + gCPU.gpr[rD] = -gCPU.gpr[rA]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * negox Negate with Overflow + * .601 + */ +void ppc_opc_negox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + gCPU.gpr[rD] = -gCPU.gpr[rA]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("negox unimplemented\n"); +} +/* + * norx NOR + * .602 + */ +void ppc_opc_norx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = ~(gCPU.gpr[rS] | gCPU.gpr[rB]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * orx OR + * .603 + */ +void ppc_opc_orx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = gCPU.gpr[rS] | gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * orcx OR with Complement + * .604 + */ +void ppc_opc_orcx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = gCPU.gpr[rS] | ~gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * ori OR Immediate + * .605 + */ +void ppc_opc_ori() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_UImm(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] | imm; +} +/* + * oris OR Immediate Shifted + * .606 + */ +void ppc_opc_oris() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_Shift16(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] | imm; +} + +/* + * rlwimix Rotate Left Word Immediate then Mask Insert + * .617 + */ +void ppc_opc_rlwimix() +{ + int rS, rA, SH, MB, ME; + PPC_OPC_TEMPL_M(gCPU.current_opc, rS, rA, SH, MB, ME); + uint32 v = ppc_word_rotl(gCPU.gpr[rS], SH); + uint32 mask = ppc_mask(MB, ME); + gCPU.gpr[rA] = (v & mask) | (gCPU.gpr[rA] & ~mask); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * rlwinmx Rotate Left Word Immediate then AND with Mask + * .618 + */ +void ppc_opc_rlwinmx() +{ + int rS, rA, SH, MB, ME; + PPC_OPC_TEMPL_M(gCPU.current_opc, rS, rA, SH, MB, ME); + uint32 v = ppc_word_rotl(gCPU.gpr[rS], SH); + uint32 mask = ppc_mask(MB, ME); + gCPU.gpr[rA] = v & mask; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * rlwnmx Rotate Left Word then AND with Mask + * .620 + */ +void ppc_opc_rlwnmx() +{ + int rS, rA, rB, MB, ME; + PPC_OPC_TEMPL_M(gCPU.current_opc, rS, rA, rB, MB, ME); + uint32 v = ppc_word_rotl(gCPU.gpr[rS], gCPU.gpr[rB]); + uint32 mask = ppc_mask(MB, ME); + gCPU.gpr[rA] = v & mask; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * slwx Shift Left Word + * .625 + */ +void ppc_opc_slwx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + uint32 s = gCPU.gpr[rB] & 0x3f; + if (s > 31) { + gCPU.gpr[rA] = 0; + } else { + gCPU.gpr[rA] = gCPU.gpr[rS] << s; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * srawx Shift Right Algebraic Word + * .628 + */ +void ppc_opc_srawx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + uint32 SH = gCPU.gpr[rB] & 0x3f; + gCPU.gpr[rA] = gCPU.gpr[rS]; + gCPU.xer &= ~XER_CA; + if (gCPU.gpr[rA] & 0x80000000) { + uint32 ca = 0; + for (uint i=0; i < SH; i++) { + if (gCPU.gpr[rA] & 1) ca = 1; + gCPU.gpr[rA] >>= 1; + gCPU.gpr[rA] |= 0x80000000; + } + if (ca) gCPU.xer |= XER_CA; + } else { + if (SH > 31) { + gCPU.gpr[rA] = 0; + } else { + gCPU.gpr[rA] >>= SH; + } + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * srawix Shift Right Algebraic Word Immediate + * .629 + */ +void ppc_opc_srawix() +{ + int rS, rA; + uint32 SH; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, SH); + gCPU.gpr[rA] = gCPU.gpr[rS]; + gCPU.xer &= ~XER_CA; + if (gCPU.gpr[rA] & 0x80000000) { + uint32 ca = 0; + for (uint i=0; i < SH; i++) { + if (gCPU.gpr[rA] & 1) ca = 1; + gCPU.gpr[rA] >>= 1; + gCPU.gpr[rA] |= 0x80000000; + } + if (ca) gCPU.xer |= XER_CA; + } else { + if (SH > 31) { + gCPU.gpr[rA] = 0; + } else { + gCPU.gpr[rA] >>= SH; + } + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * srwx Shift Right Word + * .631 + */ +void ppc_opc_srwx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + uint32 v = gCPU.gpr[rB] & 0x3f; + if (v > 31) { + gCPU.gpr[rA] = 0; + } else { + gCPU.gpr[rA] = gCPU.gpr[rS] >> v; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} + +/* + * subfx Subtract From + * .666 + */ +void ppc_opc_subfx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + gCPU.gpr[rD] = ~gCPU.gpr[rA] + gCPU.gpr[rB] + 1; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * subfox Subtract From with Overflow + * .666 + */ +void ppc_opc_subfox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + gCPU.gpr[rD] = ~gCPU.gpr[rA] + gCPU.gpr[rB] + 1; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("subfox unimplemented\n"); +} +/* + * subfcx Subtract From Carrying + * .667 + */ +void ppc_opc_subfcx() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + gCPU.gpr[rD] = ~a + b + 1; + // update xer + if (ppc_carry_3(~a, b, 1)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * subfcox Subtract From Carrying with Overflow + * .667 + */ +void ppc_opc_subfcox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + gCPU.gpr[rD] = ~a + b + 1; + // update xer + if (ppc_carry_3(~a, b, 1)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("subfcox unimplemented\n"); +} +/* + * subfex Subtract From Extended + * .668 + */ +void ppc_opc_subfex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + b + ca; + // update xer + if (ppc_carry_3(~a, b, ca)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * subfeox Subtract From Extended with Overflow + * .668 + */ +void ppc_opc_subfeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + b + ca; + // update xer + if (ppc_carry_3(~a, b, ca)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("subfeox unimplemented\n"); +} +/* + * subfic Subtract From Immediate Carrying + * .669 + */ +void ppc_opc_subfic() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint32 a = gCPU.gpr[rA]; + gCPU.gpr[rD] = ~a + imm + 1; + // update XER + if (ppc_carry_3(~a, imm, 1)) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } +} +/* + * subfmex Subtract From Minus One Extended + * .670 + */ +void ppc_opc_subfmex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + ca + 0xffffffff; + // update XER + if ((a!=0xffffffff) || ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * subfmeox Subtract From Minus One Extended with Overflow + * .670 + */ +void ppc_opc_subfmeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + ca + 0xffffffff; + // update XER + if ((a!=0xffffffff) || ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("subfmeox unimplemented\n"); +} +/* + * subfzex Subtract From Zero Extended + * .671 + */ +void ppc_opc_subfzex() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + ca; + if (!a && ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } +} +/* + * subfzeox Subtract From Zero Extended with Overflow + * .671 + */ +void ppc_opc_subfzeox() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rB == 0); + uint32 a = gCPU.gpr[rA]; + uint32 ca = ((gCPU.xer&XER_CA)?1:0); + gCPU.gpr[rD] = ~a + ca; + if (!a && ca) { + gCPU.xer |= XER_CA; + } else { + gCPU.xer &= ~XER_CA; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rD]); + } + // update XER flags + PPC_ALU_ERR("subfzeox unimplemented\n"); +} + +/* + * xorx XOR + * .680 + */ +void ppc_opc_xorx() +{ + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.gpr[rA] = gCPU.gpr[rS] ^ gCPU.gpr[rB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr0 flags + ppc_update_cr0(gCPU.gpr[rA]); + } +} +/* + * xori XOR Immediate + * .681 + */ +void ppc_opc_xori() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_UImm(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] ^ imm; +} +/* + * xoris XOR Immediate Shifted + * .682 + */ +void ppc_opc_xoris() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_Shift16(gCPU.current_opc, rS, rA, imm); + gCPU.gpr[rA] = gCPU.gpr[rS] ^ imm; +} + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_alu.h b/ppc/pearpc/cpu/cpu_generic/ppc_alu.h new file mode 100644 index 00000000..aef4f2b0 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_alu.h @@ -0,0 +1,99 @@ +/* + * PearPC + * ppc_alu.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_ALU_H__ +#define __PPC_ALU_H__ + +void ppc_opc_addx(); +void ppc_opc_addcx(); +void ppc_opc_addex(); +void ppc_opc_addi(); +void ppc_opc_addic(); +void ppc_opc_addic_(); +void ppc_opc_addis(); +void ppc_opc_addmex(); +void ppc_opc_addzex(); + +void ppc_opc_andx(); +void ppc_opc_andcx(); +void ppc_opc_andi_(); +void ppc_opc_andis_(); + +void ppc_opc_cmp(); +void ppc_opc_cmpi(); +void ppc_opc_cmpl(); +void ppc_opc_cmpli(); + +void ppc_opc_cntlzwx(); + +void ppc_opc_crand(); +void ppc_opc_crandc(); +void ppc_opc_creqv(); +void ppc_opc_crnand(); +void ppc_opc_crnor(); +void ppc_opc_cror(); +void ppc_opc_crorc(); +void ppc_opc_crxor(); + +void ppc_opc_divwx(); +void ppc_opc_divwux(); + +void ppc_opc_eqvx(); + +void ppc_opc_extsbx(); +void ppc_opc_extshx(); + +void ppc_opc_mulhwx(); +void ppc_opc_mulhwux(); +void ppc_opc_mulli(); +void ppc_opc_mullwx(); + +void ppc_opc_nandx(); + +void ppc_opc_negx(); +void ppc_opc_norx(); + +void ppc_opc_orx(); +void ppc_opc_orcx(); +void ppc_opc_ori(); +void ppc_opc_oris(); + +void ppc_opc_rlwimix(); +void ppc_opc_rlwinmx(); +void ppc_opc_rlwnmx(); + +void ppc_opc_slwx(); +void ppc_opc_srawx(); +void ppc_opc_srawix(); +void ppc_opc_srwx(); + +void ppc_opc_subfx(); +void ppc_opc_subfcx(); +void ppc_opc_subfex(); +void ppc_opc_subfic(); +void ppc_opc_subfmex(); +void ppc_opc_subfzex(); + +void ppc_opc_xorx(); +void ppc_opc_xori(); +void ppc_opc_xoris(); + +#endif + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_cpu.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_cpu.cpp new file mode 100644 index 00000000..801be36a --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_cpu.cpp @@ -0,0 +1,343 @@ +/* + * PearPC + * ppc_cpu.cc + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * Portions Copyright (C) 2004 Apple Computer, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include +#include + +#include "system/systhread.h" +#include "system/arch/sysendian.h" +//#include "tools/snprintf.h" +#include "debug/tracers.h" +#include "cpu/cpu.h" +#include "cpu/debug.h" +#include "info.h" +//#include "io/pic/pic.h" +//#include "debug/debugger.h" +#include "debug/tracers.h" +#include "ppc_cpu.h" +#include "ppc_dec.h" +#include "ppc_fpu.h" +#include "ppc_exc.h" +#include "ppc_mmu.h" +#include "ppc_tools.h" +#include "ppc.h" + +//#include "io/graphic/gcard.h" + +PPC_CPU_State gCPU; +//Debugger *gDebugger; + +static bool gSinglestep = false; + +bool activate = false; +static inline void ppc_debug_hook() +{ +} + +sys_mutex exception_mutex; + +void ppc_cpu_atomic_raise_ext_exception() +{ + sys_lock_mutex(exception_mutex); + gCPU.ext_exception = true; + gCPU.exception_pending = true; + sys_unlock_mutex(exception_mutex); +} + +void ppc_cpu_atomic_cancel_ext_exception() +{ + sys_lock_mutex(exception_mutex); + gCPU.ext_exception = false; + if (!gCPU.dec_exception) gCPU.exception_pending = false; + sys_unlock_mutex(exception_mutex); +} + +void ppc_cpu_atomic_raise_dec_exception() +{ + sys_lock_mutex(exception_mutex); + gCPU.dec_exception = true; + gCPU.exception_pending = true; + sys_unlock_mutex(exception_mutex); +} + +void ppc_cpu_wakeup() +{ +} + +extern int ppc_cycle_count; +void ppc_hsync_handler(void) +{ +#if 0 + if (!ppc_state) + return; + uint64 oldpdec = gCPU.pdec; + //gCPU.ptb += ppc_cycle_count; + gCPU.pdec -= ppc_cycle_count; + if (gCPU.pdec > oldpdec) { + ppc_wakeup(); + sys_lock_mutex(exception_mutex); + gCPU.exception_pending = true; + gCPU.dec_exception = true; + gCPU.pdec=(uint64)0xffffffff*TB_TO_PTB_FACTOR; + sys_unlock_mutex(exception_mutex); + } +#endif +} + +void ppc_cpu_run() +{ +// gDebugger = new Debugger(); +// gDebugger->mAlwaysShowRegs = true; + PPC_CPU_TRACE("execution started at %08x\n", gCPU.pc); + uint ops=0; + gCPU.effective_code_page = 0xffffffff; +// ppc_fpu_test(); +// return; + while (true) { + gCPU.npc = gCPU.pc+4; + if ((gCPU.pc & ~0xfff) == gCPU.effective_code_page) { + gCPU.current_opc = ppc_word_from_BE(*((uint32*)(&gCPU.physical_code_page[gCPU.pc & 0xfff]))); + ppc_debug_hook(); + } else { + int ret; + if ((ret = ppc_direct_effective_memory_handle_code(gCPU.pc & ~0xfff, gCPU.physical_code_page))) { + if (ret == PPC_MMU_EXC) { + gCPU.pc = gCPU.npc; + continue; + } else { + PPC_CPU_ERR("?\n"); + } + } + gCPU.effective_code_page = gCPU.pc & ~0xfff; + continue; + } + //ht_printf("%08x %04x\n", gCPU.pc, gCPU.current_opc); + ppc_exec_opc(); + ops++; + gCPU.ptb++; +#if 1 + if (gCPU.pdec == 0) { + gCPU.exception_pending = true; + gCPU.dec_exception = true; + gCPU.pdec=(uint64)0xffffffff*TB_TO_PTB_FACTOR; + } else { + gCPU.pdec--; + } +#endif + if ((ops & 0x3ffff)==0) { +/* if (pic_check_interrupt()) { + gCPU.exception_pending = true; + gCPU.ext_exception = true; + }*/ + if ((ops & 0x0fffff)==0) { +// uint32 j=0; +// ppc_read_effective_word(0xc046b2f8, j); + + ht_printf("@%08x (%u ops) pdec: %08x lr: %08x\n", gCPU.pc, ops, gCPU.pdec, gCPU.lr); +#if 0 + extern uint32 PIC_enable_low; + extern uint32 PIC_enable_high; + ht_printf("enable "); + int x = 1; + for (int i=0; i<31; i++) { + if (PIC_enable_low & x) { + ht_printf("%d ", i); + } + x<<=1; + } + x=1; + for (int i=0; i<31; i++) { + if (PIC_enable_high & x) { + ht_printf("%d ", 32+i); + } + x<<=1; + } + ht_printf("\n"); +#endif + } + } + + gCPU.pc = gCPU.npc; + + extern int debugger_active, pause_emulation; + extern void sleep_millis(int); + while (debugger_active || pause_emulation) { + sleep_millis(10); + } + + if (gCPU.exception_pending) { + if (gCPU.stop_exception) { + gCPU.stop_exception = false; + if (!gCPU.dec_exception && !gCPU.ext_exception) gCPU.exception_pending = false; + break; + } + if (gCPU.msr & MSR_EE) { + sys_lock_mutex(exception_mutex); + if (gCPU.ext_exception) { + ppc_exception(PPC_EXC_EXT_INT); + gCPU.ext_exception = false; + gCPU.pc = gCPU.npc; + if (!gCPU.dec_exception) gCPU.exception_pending = false; + sys_unlock_mutex(exception_mutex); + continue; + } + if (gCPU.dec_exception) { + ppc_exception(PPC_EXC_DEC); + gCPU.dec_exception = false; + gCPU.pc = gCPU.npc; + gCPU.exception_pending = false; + sys_unlock_mutex(exception_mutex); + continue; + } + sys_unlock_mutex(exception_mutex); + PPC_CPU_ERR("no interrupt, but signaled?!\n"); + } + } +#ifdef PPC_CPU_ENABLE_SINGLESTEP + if (gCPU.msr & MSR_SE) { + if (gCPU.singlestep_ignore) { + gCPU.singlestep_ignore = false; + } else { + ppc_exception(PPC_EXC_TRACE2); + gCPU.pc = gCPU.npc; + continue; + } + } +#endif + } +} + +void ppc_cpu_stop() +{ + sys_lock_mutex(exception_mutex); + gCPU.stop_exception = true; + gCPU.exception_pending = true; + sys_unlock_mutex(exception_mutex); +} + +uint64 ppc_get_clock_frequency(int cpu) +{ + return PPC_CLOCK_FREQUENCY; +} + +uint64 ppc_get_bus_frequency(int cpu) +{ + return PPC_BUS_FREQUENCY; +} + +uint64 ppc_get_timebase_frequency(int cpu) +{ + return PPC_TIMEBASE_FREQUENCY; +} + + +void ppc_machine_check_exception() +{ + PPC_CPU_ERR("machine check exception\n"); +} + +uint32 ppc_cpu_get_gpr(int cpu, int i) +{ + return gCPU.gpr[i]; +} + +void ppc_cpu_set_gpr(int cpu, int i, uint32 newvalue) +{ + gCPU.gpr[i] = newvalue; +} + +void ppc_cpu_set_msr(int cpu, uint32 newvalue) +{ + gCPU.msr = newvalue; +} + +void ppc_cpu_set_pc(int cpu, uint32 newvalue) +{ + gCPU.pc = newvalue; +} + +uint32 ppc_cpu_get_pc(int cpu) +{ + return gCPU.pc; +} + +uint32 ppc_cpu_get_pvr(int cpu) +{ + return gCPU.pvr; +} + +void ppc_cpu_map_framebuffer(uint32 pa, uint32 ea) +{ + // use BAT for framebuffer + gCPU.dbatu[0] = ea|(7<<2)|0x3; + gCPU.dbat_bl17[0] = ~(BATU_BL(gCPU.dbatu[0])<<17); + gCPU.dbatl[0] = pa; +} + +void ppc_set_singlestep_v(bool v, const char *file, int line, const char *format, ...) +{ + char buffer[200]; + va_list arg; + va_start(arg, format); + ht_printf("singlestep %s from %s:%d, info: ", v ? "set" : "cleared", file, line); + vsprintf(buffer, format, arg); + ht_printf("%s\n", buffer); + va_end(arg); + ppc_crash(); + gSinglestep = v; +} + +void ppc_set_singlestep_nonverbose(bool v) +{ + gSinglestep = v; +} + +#define CPU_KEY_PVR "cpu_pvr" + +//#include "configparser.h" + +bool ppc_cpu_init(uint32 pvr) +{ + memset(&gCPU, 0, sizeof gCPU); + gCPU.pvr = pvr; //gConfig->getConfigInt(CPU_KEY_PVR); + gCPU.hid[1] = 0x80000000; + + ppc_dec_init(); + // initialize srs (mostly for prom) + for (int i=0; i<16; i++) { + gCPU.sr[i] = 0x2aa*i; + } + sys_create_mutex(&exception_mutex); + + PPC_CPU_WARN("You are using the generic CPU!\n"); + PPC_CPU_WARN("This is much slower than the just-in-time compiler and\n"); + PPC_CPU_WARN("should only be used for debugging purposes or if there's\n"); + PPC_CPU_WARN("no just-in-time compiler for your platform.\n"); + + return true; +} + +#if 0 +void ppc_cpu_init_config() +{ + gConfig->acceptConfigEntryIntDef("cpu_pvr", 0x000c0201); +} +#endif diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_cpu.h b/ppc/pearpc/cpu/cpu_generic/ppc_cpu.h new file mode 100644 index 00000000..2f4c5753 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_cpu.h @@ -0,0 +1,119 @@ +/* + * PearPC + * ppc_cpu.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_CPU_H__ +#define __PPC_CPU_H__ + +#include +#include "system/types.h" +#include "cpu/common.h" + +#define PPC_MHz(v) ((v)*1000*1000) + +#define TB_TO_PTB_FACTOR 10 + +#define PPC_MODEL "ppc_model" +#define PPC_CPU_MODEL "ppc_cpu" +#define PPC_CLOCK_FREQUENCY PPC_MHz(10) +#define PPC_BUS_FREQUENCY PPC_MHz(10) +#define PPC_TIMEBASE_FREQUENCY (PPC_CLOCK_FREQUENCY / TB_TO_PTB_FACTOR) + +struct PPC_CPU_State { + // * uisa + uint32 gpr[32]; + uint64 fpr[32]; + uint32 cr; + uint32 fpscr; + uint32 xer; // spr 1 + uint32 xer_ca; // carry from xer + uint32 lr; // spr 8 + uint32 ctr; // spr 9 + // * oea + uint32 msr; + uint32 pvr; // spr 287 + + // * memory managment + uint32 ibatu[4]; // spr 528, 530, 532, 534 + uint32 ibatl[4]; // spr 529, 531, 533, 535 + uint32 ibat_bl17[4]; // for internal use + + uint32 dbatu[4]; // spr 536, 538, 540, 542 + uint32 dbatl[4]; // spr 537, 539, 541, 543 + uint32 dbat_bl17[4]; // for internal use + + uint32 sdr1; // spr 25 (page table base address) + + uint32 sr[16]; + + // * exception handling + uint32 dar; // spr 19 + uint32 dsisr; // spr 18 + uint32 sprg[4]; // spr 272-275 + uint32 srr[2]; // spr 26-27 + + // * misc + uint32 dec; // spr 22 + uint32 ear; // spr 282 .101 + uint32 pir; // spr 1032 + uint64 tb; // .75 spr 284(l)/285(u) + + uint32 hid[16]; + // * internal + + uint32 pc; + uint32 npc; + uint32 current_opc; + bool exception_pending; + bool dec_exception; + bool ext_exception; + bool stop_exception; + bool singlestep_ignore; + + uint32 pagetable_base; + int pagetable_hashmask; + uint32 reserve; + bool have_reservation; + + // for generic cpu core + uint32 effective_code_page; + byte *physical_code_page; + uint64 pdec; // more precise version of dec + uint64 ptb; // more precise version of tb + + // for altivec + uint32 vscr; + uint32 vrsave; // spr 256 + Vector_t vr[36]; // <--- this MUST be 16-byte alligned + uint32 vtemp; +}; + +extern PPC_CPU_State gCPU; + +void ppc_cpu_atomic_raise_ext_exception(); +void ppc_cpu_atomic_cancel_ext_exception(); + +extern uint32 gBreakpoint; +extern uint32 gBreakpoint2; + +void ppc_set_singlestep_v(bool v, const char *file, int line, const char *format, ...); +void ppc_set_singlestep_nonverbose(bool v); + +#endif + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_dec.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_dec.cpp new file mode 100644 index 00000000..4d2ea08a --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_dec.cpp @@ -0,0 +1,611 @@ +/* + * PearPC + * ppc_dec.cc + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * Portions Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nmsu.edu) + * Portions Copyright (C) 2004 Apple Computer, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "cstring" + +#include "system/types.h" +#include "cpu/debug.h" +#include "cpu/cpu.h" +#include "ppc_alu.h" +#include "ppc_cpu.h" +#include "ppc_dec.h" +#include "ppc_exc.h" +#include "ppc_fpu.h" +#include "ppc_vec.h" +#include "ppc_mmu.h" +#include "ppc_opc.h" + +//#include "io/prom/promosi.h" + +static void ppc_opc_invalid() +{ +#if 0 + if (gCPU.pc == gPromOSIEntry && gCPU.current_opc == PROM_MAGIC_OPCODE) { + call_prom_osi(); + return; + } + if (gCPU.current_opc == 0x00333301) { + // memset(r3, r4, r5) + uint32 dest = gCPU.gpr[3]; + uint32 c = gCPU.gpr[4]; + uint32 size = gCPU.gpr[5]; + if (dest & 0xfff) { + byte *dst; + ppc_direct_effective_memory_handle(dest, dst); + uint32 a = 4096 - (dest & 0xfff); + memset(dst, c, a); + size -= a; + dest += a; + } + while (size >= 4096) { + byte *dst; + ppc_direct_effective_memory_handle(dest, dst); + memset(dst, c, 4096); + dest += 4096; + size -= 4096; + } + if (size) { + byte *dst; + ppc_direct_effective_memory_handle(dest, dst); + memset(dst, c, size); + } + gCPU.pc = gCPU.npc; + return; + } + if (gCPU.current_opc == 0x00333302) { + // memcpy + uint32 dest = gCPU.gpr[3]; + uint32 src = gCPU.gpr[4]; + uint32 size = gCPU.gpr[5]; + byte *d, *s; + ppc_direct_effective_memory_handle(dest, d); + ppc_direct_effective_memory_handle(src, s); + while (size--) { + if (!(dest & 0xfff)) ppc_direct_effective_memory_handle(dest, d); + if (!(src & 0xfff)) ppc_direct_effective_memory_handle(src, s); + *d = *s; + src++; dest++; d++; s++; + } + gCPU.pc = gCPU.npc; + return; + } +#endif + ht_printf("[PPC/DEC] Bad opcode: %08x (%u:%u)\n", + gCPU.current_opc, PPC_OPC_MAIN(gCPU.current_opc), + PPC_OPC_EXT(gCPU.current_opc)); + + SINGLESTEP("unknown instruction\n"); +} + +// main opcode 19 +static void ppc_opc_group_1() +{ + uint32 ext = PPC_OPC_EXT(gCPU.current_opc); + if (ext & 1) { + // crxxx + if (ext <= 225) { + switch (ext) { + case 33: ppc_opc_crnor(); return; + case 129: ppc_opc_crandc(); return; + case 193: ppc_opc_crxor(); return; + case 225: ppc_opc_crnand(); return; + } + } else { + switch (ext) { + case 257: ppc_opc_crand(); return; + case 289: ppc_opc_creqv(); return; + case 417: ppc_opc_crorc(); return; + case 449: ppc_opc_cror(); return; + } + } + } else if (ext & (1<<9)) { + // bcctrx + if (ext == 528) { + ppc_opc_bcctrx(); + return; + } + } else { + switch (ext) { + case 16: ppc_opc_bclrx(); return; + case 0: ppc_opc_mcrf(); return; + case 50: ppc_opc_rfi(); return; + case 150: ppc_opc_isync(); return; + } + } + ppc_opc_invalid(); +} + +ppc_opc_function ppc_opc_table_group2[1015]; + +// main opcode 31 +static void ppc_opc_init_group2() +{ + for (uint i=0; i<(sizeof ppc_opc_table_group2 / sizeof ppc_opc_table_group2[0]); i++) { + ppc_opc_table_group2[i] = ppc_opc_invalid; + } + ppc_opc_table_group2[0] = ppc_opc_cmp; + ppc_opc_table_group2[4] = ppc_opc_tw; + ppc_opc_table_group2[8] = ppc_opc_subfcx;//+ + ppc_opc_table_group2[10] = ppc_opc_addcx;//+ + ppc_opc_table_group2[11] = ppc_opc_mulhwux; + ppc_opc_table_group2[19] = ppc_opc_mfcr; + ppc_opc_table_group2[20] = ppc_opc_lwarx; + ppc_opc_table_group2[23] = ppc_opc_lwzx; + ppc_opc_table_group2[24] = ppc_opc_slwx; + ppc_opc_table_group2[26] = ppc_opc_cntlzwx; + ppc_opc_table_group2[28] = ppc_opc_andx; + ppc_opc_table_group2[32] = ppc_opc_cmpl; + ppc_opc_table_group2[40] = ppc_opc_subfx; + ppc_opc_table_group2[54] = ppc_opc_dcbst; + ppc_opc_table_group2[55] = ppc_opc_lwzux; + ppc_opc_table_group2[60] = ppc_opc_andcx; + ppc_opc_table_group2[75] = ppc_opc_mulhwx; + ppc_opc_table_group2[83] = ppc_opc_mfmsr; + ppc_opc_table_group2[86] = ppc_opc_dcbf; + ppc_opc_table_group2[87] = ppc_opc_lbzx; + ppc_opc_table_group2[104] = ppc_opc_negx; + ppc_opc_table_group2[119] = ppc_opc_lbzux; + ppc_opc_table_group2[124] = ppc_opc_norx; + ppc_opc_table_group2[136] = ppc_opc_subfex;//+ + ppc_opc_table_group2[138] = ppc_opc_addex;//+ + ppc_opc_table_group2[144] = ppc_opc_mtcrf; + ppc_opc_table_group2[146] = ppc_opc_mtmsr; + ppc_opc_table_group2[150] = ppc_opc_stwcx_; + ppc_opc_table_group2[151] = ppc_opc_stwx; + ppc_opc_table_group2[183] = ppc_opc_stwux; + ppc_opc_table_group2[200] = ppc_opc_subfzex;//+ + ppc_opc_table_group2[202] = ppc_opc_addzex;//+ + ppc_opc_table_group2[210] = ppc_opc_mtsr; + ppc_opc_table_group2[215] = ppc_opc_stbx; + ppc_opc_table_group2[232] = ppc_opc_subfmex;//+ + ppc_opc_table_group2[234] = ppc_opc_addmex; + ppc_opc_table_group2[235] = ppc_opc_mullwx;//+ + ppc_opc_table_group2[242] = ppc_opc_mtsrin; + ppc_opc_table_group2[246] = ppc_opc_dcbtst; + ppc_opc_table_group2[247] = ppc_opc_stbux; + ppc_opc_table_group2[266] = ppc_opc_addx;//+ + ppc_opc_table_group2[278] = ppc_opc_dcbt; + ppc_opc_table_group2[279] = ppc_opc_lhzx; + ppc_opc_table_group2[284] = ppc_opc_eqvx; + ppc_opc_table_group2[306] = ppc_opc_tlbie; + ppc_opc_table_group2[310] = ppc_opc_eciwx; + ppc_opc_table_group2[311] = ppc_opc_lhzux; + ppc_opc_table_group2[316] = ppc_opc_xorx; + ppc_opc_table_group2[339] = ppc_opc_mfspr; + ppc_opc_table_group2[343] = ppc_opc_lhax; + ppc_opc_table_group2[370] = ppc_opc_tlbia; + ppc_opc_table_group2[371] = ppc_opc_mftb; + ppc_opc_table_group2[375] = ppc_opc_lhaux; + ppc_opc_table_group2[407] = ppc_opc_sthx; + ppc_opc_table_group2[412] = ppc_opc_orcx; + ppc_opc_table_group2[438] = ppc_opc_ecowx; + ppc_opc_table_group2[439] = ppc_opc_sthux; + ppc_opc_table_group2[444] = ppc_opc_orx; + ppc_opc_table_group2[459] = ppc_opc_divwux;//+ + ppc_opc_table_group2[467] = ppc_opc_mtspr; + ppc_opc_table_group2[470] = ppc_opc_dcbi; + ppc_opc_table_group2[476] = ppc_opc_nandx; + ppc_opc_table_group2[491] = ppc_opc_divwx;//+ + ppc_opc_table_group2[512] = ppc_opc_mcrxr; + ppc_opc_table_group2[533] = ppc_opc_lswx; + ppc_opc_table_group2[534] = ppc_opc_lwbrx; + ppc_opc_table_group2[535] = ppc_opc_lfsx; + ppc_opc_table_group2[536] = ppc_opc_srwx; + ppc_opc_table_group2[566] = ppc_opc_tlbsync; + ppc_opc_table_group2[567] = ppc_opc_lfsux; + ppc_opc_table_group2[595] = ppc_opc_mfsr; + ppc_opc_table_group2[597] = ppc_opc_lswi; + ppc_opc_table_group2[598] = ppc_opc_sync; + ppc_opc_table_group2[599] = ppc_opc_lfdx; + ppc_opc_table_group2[631] = ppc_opc_lfdux; + ppc_opc_table_group2[659] = ppc_opc_mfsrin; + ppc_opc_table_group2[661] = ppc_opc_stswx; + ppc_opc_table_group2[662] = ppc_opc_stwbrx; + ppc_opc_table_group2[663] = ppc_opc_stfsx; + ppc_opc_table_group2[695] = ppc_opc_stfsux; + ppc_opc_table_group2[725] = ppc_opc_stswi; + ppc_opc_table_group2[727] = ppc_opc_stfdx; + ppc_opc_table_group2[758] = ppc_opc_dcba; + ppc_opc_table_group2[759] = ppc_opc_stfdux; + ppc_opc_table_group2[790] = ppc_opc_lhbrx; + ppc_opc_table_group2[792] = ppc_opc_srawx; + ppc_opc_table_group2[824] = ppc_opc_srawix; + ppc_opc_table_group2[854] = ppc_opc_eieio; + ppc_opc_table_group2[918] = ppc_opc_sthbrx; + ppc_opc_table_group2[922] = ppc_opc_extshx; + ppc_opc_table_group2[954] = ppc_opc_extsbx; + ppc_opc_table_group2[982] = ppc_opc_icbi; + ppc_opc_table_group2[983] = ppc_opc_stfiwx; + ppc_opc_table_group2[1014] = ppc_opc_dcbz; + + if ((ppc_cpu_get_pvr(0) & 0xffff0000) == 0x000c0000) { + /* Added for Altivec support */ + ppc_opc_table_group2[6] = ppc_opc_lvsl; + ppc_opc_table_group2[7] = ppc_opc_lvebx; + ppc_opc_table_group2[38] = ppc_opc_lvsr; + ppc_opc_table_group2[39] = ppc_opc_lvehx; + ppc_opc_table_group2[71] = ppc_opc_lvewx; + ppc_opc_table_group2[103] = ppc_opc_lvx; + ppc_opc_table_group2[135] = ppc_opc_stvebx; + ppc_opc_table_group2[167] = ppc_opc_stvehx; + ppc_opc_table_group2[199] = ppc_opc_stvewx; + ppc_opc_table_group2[231] = ppc_opc_stvx; + ppc_opc_table_group2[342] = ppc_opc_dst; + ppc_opc_table_group2[359] = ppc_opc_lvxl; + ppc_opc_table_group2[374] = ppc_opc_dstst; + ppc_opc_table_group2[487] = ppc_opc_stvxl; + ppc_opc_table_group2[822] = ppc_opc_dss; + } +} + +// main opcode 31 +inline static void ppc_opc_group_2() +{ + uint32 ext = PPC_OPC_EXT(gCPU.current_opc); + if (ext >= (sizeof ppc_opc_table_group2 / sizeof ppc_opc_table_group2[0])) { + ppc_opc_invalid(); + } + ppc_opc_table_group2[ext](); +} + +// main opcode 59 +static void ppc_opc_group_f1() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + uint32 ext = PPC_OPC_EXT(gCPU.current_opc); + switch (ext & 0x1f) { + case 18: ppc_opc_fdivsx(); return; + case 20: ppc_opc_fsubsx(); return; + case 21: ppc_opc_faddsx(); return; + case 22: ppc_opc_fsqrtsx(); return; + case 24: ppc_opc_fresx(); return; + case 25: ppc_opc_fmulsx(); return; + case 28: ppc_opc_fmsubsx(); return; + case 29: ppc_opc_fmaddsx(); return; + case 30: ppc_opc_fnmsubsx(); return; + case 31: ppc_opc_fnmaddsx(); return; + } + ppc_opc_invalid(); +} + +// main opcode 63 +static void ppc_opc_group_f2() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + uint32 ext = PPC_OPC_EXT(gCPU.current_opc); + if (ext & 16) { + switch (ext & 0x1f) { + case 18: ppc_opc_fdivx(); return; + case 20: ppc_opc_fsubx(); return; + case 21: ppc_opc_faddx(); return; + case 22: ppc_opc_fsqrtx(); return; + case 23: ppc_opc_fselx(); return; + case 25: ppc_opc_fmulx(); return; + case 26: ppc_opc_frsqrtex(); return; + case 28: ppc_opc_fmsubx(); return; + case 29: ppc_opc_fmaddx(); return; + case 30: ppc_opc_fnmsubx(); return; + case 31: ppc_opc_fnmaddx(); return; + } + } else { + switch (ext) { + case 0: ppc_opc_fcmpu(); return; + case 12: ppc_opc_frspx(); return; + case 14: ppc_opc_fctiwx(); return; + case 15: ppc_opc_fctiwzx(); return; + //-- + case 32: ppc_opc_fcmpo(); return; + case 38: ppc_opc_mtfsb1x(); return; + case 40: ppc_opc_fnegx(); return; + case 64: ppc_opc_mcrfs(); return; + case 70: ppc_opc_mtfsb0x(); return; + case 72: ppc_opc_fmrx(); return; + case 134: ppc_opc_mtfsfix(); return; + case 136: ppc_opc_fnabsx(); return; + case 264: ppc_opc_fabsx(); return; + case 583: ppc_opc_mffsx(); return; + case 711: ppc_opc_mtfsfx(); return; + } + } + ppc_opc_invalid(); +} + +ppc_opc_function ppc_opc_table_groupv[965]; + +static void ppc_opc_init_groupv() +{ + for (uint i=0; i<(sizeof ppc_opc_table_groupv / sizeof ppc_opc_table_groupv[0]);i++) { + ppc_opc_table_groupv[i] = ppc_opc_invalid; + } + ppc_opc_table_groupv[0] = ppc_opc_vaddubm; + ppc_opc_table_groupv[1] = ppc_opc_vmaxub; + ppc_opc_table_groupv[2] = ppc_opc_vrlb; + ppc_opc_table_groupv[4] = ppc_opc_vmuloub; + ppc_opc_table_groupv[5] = ppc_opc_vaddfp; + ppc_opc_table_groupv[6] = ppc_opc_vmrghb; + ppc_opc_table_groupv[7] = ppc_opc_vpkuhum; + ppc_opc_table_groupv[32] = ppc_opc_vadduhm; + ppc_opc_table_groupv[33] = ppc_opc_vmaxuh; + ppc_opc_table_groupv[34] = ppc_opc_vrlh; + ppc_opc_table_groupv[36] = ppc_opc_vmulouh; + ppc_opc_table_groupv[37] = ppc_opc_vsubfp; + ppc_opc_table_groupv[38] = ppc_opc_vmrghh; + ppc_opc_table_groupv[39] = ppc_opc_vpkuwum; + ppc_opc_table_groupv[64] = ppc_opc_vadduwm; + ppc_opc_table_groupv[65] = ppc_opc_vmaxuw; + ppc_opc_table_groupv[66] = ppc_opc_vrlw; + ppc_opc_table_groupv[70] = ppc_opc_vmrghw; + ppc_opc_table_groupv[71] = ppc_opc_vpkuhus; + ppc_opc_table_groupv[103] = ppc_opc_vpkuwus; + ppc_opc_table_groupv[129] = ppc_opc_vmaxsb; + ppc_opc_table_groupv[130] = ppc_opc_vslb; + ppc_opc_table_groupv[132] = ppc_opc_vmulosb; + ppc_opc_table_groupv[133] = ppc_opc_vrefp; + ppc_opc_table_groupv[134] = ppc_opc_vmrglb; + ppc_opc_table_groupv[135] = ppc_opc_vpkshus; + ppc_opc_table_groupv[161] = ppc_opc_vmaxsh; + ppc_opc_table_groupv[162] = ppc_opc_vslh; + ppc_opc_table_groupv[164] = ppc_opc_vmulosh; + ppc_opc_table_groupv[165] = ppc_opc_vrsqrtefp; + ppc_opc_table_groupv[166] = ppc_opc_vmrglh; + ppc_opc_table_groupv[167] = ppc_opc_vpkswus; + ppc_opc_table_groupv[192] = ppc_opc_vaddcuw; + ppc_opc_table_groupv[193] = ppc_opc_vmaxsw; + ppc_opc_table_groupv[194] = ppc_opc_vslw; + ppc_opc_table_groupv[197] = ppc_opc_vexptefp; + ppc_opc_table_groupv[198] = ppc_opc_vmrglw; + ppc_opc_table_groupv[199] = ppc_opc_vpkshss; + ppc_opc_table_groupv[226] = ppc_opc_vsl; + ppc_opc_table_groupv[229] = ppc_opc_vlogefp; + ppc_opc_table_groupv[231] = ppc_opc_vpkswss; + ppc_opc_table_groupv[256] = ppc_opc_vaddubs; + ppc_opc_table_groupv[257] = ppc_opc_vminub; + ppc_opc_table_groupv[258] = ppc_opc_vsrb; + ppc_opc_table_groupv[260] = ppc_opc_vmuleub; + ppc_opc_table_groupv[261] = ppc_opc_vrfin; + ppc_opc_table_groupv[262] = ppc_opc_vspltb; + ppc_opc_table_groupv[263] = ppc_opc_vupkhsb; + ppc_opc_table_groupv[288] = ppc_opc_vadduhs; + ppc_opc_table_groupv[289] = ppc_opc_vminuh; + ppc_opc_table_groupv[290] = ppc_opc_vsrh; + ppc_opc_table_groupv[292] = ppc_opc_vmuleuh; + ppc_opc_table_groupv[293] = ppc_opc_vrfiz; + ppc_opc_table_groupv[294] = ppc_opc_vsplth; + ppc_opc_table_groupv[295] = ppc_opc_vupkhsh; + ppc_opc_table_groupv[320] = ppc_opc_vadduws; + ppc_opc_table_groupv[321] = ppc_opc_vminuw; + ppc_opc_table_groupv[322] = ppc_opc_vsrw; + ppc_opc_table_groupv[325] = ppc_opc_vrfip; + ppc_opc_table_groupv[326] = ppc_opc_vspltw; + ppc_opc_table_groupv[327] = ppc_opc_vupklsb; + ppc_opc_table_groupv[354] = ppc_opc_vsr; + ppc_opc_table_groupv[357] = ppc_opc_vrfim; + ppc_opc_table_groupv[359] = ppc_opc_vupklsh; + ppc_opc_table_groupv[384] = ppc_opc_vaddsbs; + ppc_opc_table_groupv[385] = ppc_opc_vminsb; + ppc_opc_table_groupv[386] = ppc_opc_vsrab; + ppc_opc_table_groupv[388] = ppc_opc_vmulesb; + ppc_opc_table_groupv[389] = ppc_opc_vcfux; + ppc_opc_table_groupv[390] = ppc_opc_vspltisb; + ppc_opc_table_groupv[391] = ppc_opc_vpkpx; + ppc_opc_table_groupv[416] = ppc_opc_vaddshs; + ppc_opc_table_groupv[417] = ppc_opc_vminsh; + ppc_opc_table_groupv[418] = ppc_opc_vsrah; + ppc_opc_table_groupv[420] = ppc_opc_vmulesh; + ppc_opc_table_groupv[421] = ppc_opc_vcfsx; + ppc_opc_table_groupv[422] = ppc_opc_vspltish; + ppc_opc_table_groupv[423] = ppc_opc_vupkhpx; + ppc_opc_table_groupv[448] = ppc_opc_vaddsws; + ppc_opc_table_groupv[449] = ppc_opc_vminsw; + ppc_opc_table_groupv[450] = ppc_opc_vsraw; + ppc_opc_table_groupv[453] = ppc_opc_vctuxs; + ppc_opc_table_groupv[454] = ppc_opc_vspltisw; + ppc_opc_table_groupv[485] = ppc_opc_vctsxs; + ppc_opc_table_groupv[487] = ppc_opc_vupklpx; + ppc_opc_table_groupv[512] = ppc_opc_vsububm; + ppc_opc_table_groupv[513] = ppc_opc_vavgub; + ppc_opc_table_groupv[514] = ppc_opc_vand; + ppc_opc_table_groupv[517] = ppc_opc_vmaxfp; + ppc_opc_table_groupv[518] = ppc_opc_vslo; + ppc_opc_table_groupv[544] = ppc_opc_vsubuhm; + ppc_opc_table_groupv[545] = ppc_opc_vavguh; + ppc_opc_table_groupv[546] = ppc_opc_vandc; + ppc_opc_table_groupv[549] = ppc_opc_vminfp; + ppc_opc_table_groupv[550] = ppc_opc_vsro; + ppc_opc_table_groupv[576] = ppc_opc_vsubuwm; + ppc_opc_table_groupv[577] = ppc_opc_vavguw; + ppc_opc_table_groupv[578] = ppc_opc_vor; + ppc_opc_table_groupv[610] = ppc_opc_vxor; + ppc_opc_table_groupv[641] = ppc_opc_vavgsb; + ppc_opc_table_groupv[642] = ppc_opc_vnor; + ppc_opc_table_groupv[673] = ppc_opc_vavgsh; + ppc_opc_table_groupv[704] = ppc_opc_vsubcuw; + ppc_opc_table_groupv[705] = ppc_opc_vavgsw; + ppc_opc_table_groupv[768] = ppc_opc_vsububs; + ppc_opc_table_groupv[770] = ppc_opc_mfvscr; + ppc_opc_table_groupv[772] = ppc_opc_vsum4ubs; + ppc_opc_table_groupv[800] = ppc_opc_vsubuhs; + ppc_opc_table_groupv[802] = ppc_opc_mtvscr; + ppc_opc_table_groupv[804] = ppc_opc_vsum4shs; + ppc_opc_table_groupv[832] = ppc_opc_vsubuws; + ppc_opc_table_groupv[836] = ppc_opc_vsum2sws; + ppc_opc_table_groupv[896] = ppc_opc_vsubsbs; + ppc_opc_table_groupv[900] = ppc_opc_vsum4sbs; + ppc_opc_table_groupv[928] = ppc_opc_vsubshs; + ppc_opc_table_groupv[960] = ppc_opc_vsubsws; + ppc_opc_table_groupv[964] = ppc_opc_vsumsws; +} + +// main opcode 04 +static void ppc_opc_group_v() +{ + uint32 ext = PPC_OPC_EXT(gCPU.current_opc); +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + switch(ext & 0x1f) { + case 16: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vmhraddshs(); + else + return ppc_opc_vmhaddshs(); + case 17: return ppc_opc_vmladduhm(); + case 18: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vmsummbm(); + else + return ppc_opc_vmsumubm(); + case 19: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vmsumuhs(); + else + return ppc_opc_vmsumuhm(); + case 20: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vmsumshs(); + else + return ppc_opc_vmsumshm(); + case 21: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vperm(); + else + return ppc_opc_vsel(); + case 22: return ppc_opc_vsldoi(); + case 23: + if (gCPU.current_opc & PPC_OPC_Rc) + return ppc_opc_vnmsubfp(); + else + return ppc_opc_vmaddfp(); + } + switch(ext & 0x1ff) + { + case 3: return ppc_opc_vcmpequbx(); + case 35: return ppc_opc_vcmpequhx(); + case 67: return ppc_opc_vcmpequwx(); + case 99: return ppc_opc_vcmpeqfpx(); + case 227: return ppc_opc_vcmpgefpx(); + case 259: return ppc_opc_vcmpgtubx(); + case 291: return ppc_opc_vcmpgtuhx(); + case 323: return ppc_opc_vcmpgtuwx(); + case 355: return ppc_opc_vcmpgtfpx(); + case 387: return ppc_opc_vcmpgtsbx(); + case 419: return ppc_opc_vcmpgtshx(); + case 451: return ppc_opc_vcmpgtswx(); + case 483: return ppc_opc_vcmpbfpx(); + } + + if (ext >= (sizeof ppc_opc_table_groupv / sizeof ppc_opc_table_groupv[0])) { + return ppc_opc_invalid(); + } + return ppc_opc_table_groupv[ext](); +} + +static ppc_opc_function ppc_opc_table_main[64] = { + &ppc_opc_invalid, // 0 + &ppc_opc_invalid, // 1 + &ppc_opc_invalid, // 2 (tdi on 64 bit platforms) + &ppc_opc_twi, // 3 + &ppc_opc_invalid, // 4 (altivec group 1) + &ppc_opc_invalid, // 5 + &ppc_opc_invalid, // 6 + &ppc_opc_mulli, // 7 + &ppc_opc_subfic, // 8 + &ppc_opc_invalid, // 9 + &ppc_opc_cmpli, // 10 + &ppc_opc_cmpi, // 11 + &ppc_opc_addic, // 12 + &ppc_opc_addic_, // 13 + &ppc_opc_addi, // 14 + &ppc_opc_addis, // 15 + &ppc_opc_bcx, // 16 + &ppc_opc_sc, // 17 + &ppc_opc_bx, // 18 + &ppc_opc_group_1, // 19 + &ppc_opc_rlwimix, // 20 + &ppc_opc_rlwinmx, // 21 + &ppc_opc_invalid, // 22 + &ppc_opc_rlwnmx, // 23 + &ppc_opc_ori, // 24 + &ppc_opc_oris, // 25 + &ppc_opc_xori, // 26 + &ppc_opc_xoris, // 27 + &ppc_opc_andi_, // 28 + &ppc_opc_andis_, // 29 + &ppc_opc_invalid, // 30 (group_rld on 64 bit platforms) + &ppc_opc_group_2, // 31 + &ppc_opc_lwz, // 32 + &ppc_opc_lwzu, // 33 + &ppc_opc_lbz, // 34 + &ppc_opc_lbzu, // 35 + &ppc_opc_stw, // 36 + &ppc_opc_stwu, // 37 + &ppc_opc_stb, // 38 + &ppc_opc_stbu, // 39 + &ppc_opc_lhz, // 40 + &ppc_opc_lhzu, // 41 + &ppc_opc_lha, // 42 + &ppc_opc_lhau, // 43 + &ppc_opc_sth, // 44 + &ppc_opc_sthu, // 45 + &ppc_opc_lmw, // 46 + &ppc_opc_stmw, // 47 + &ppc_opc_lfs, // 48 + &ppc_opc_lfsu, // 49 + &ppc_opc_lfd, // 50 + &ppc_opc_lfdu, // 51 + &ppc_opc_stfs, // 52 + &ppc_opc_stfsu, // 53 + &ppc_opc_stfd, // 54 + &ppc_opc_stfdu, // 55 + &ppc_opc_invalid, // 56 + &ppc_opc_invalid, // 57 + &ppc_opc_invalid, // 58 (ld on 64 bit platforms) + &ppc_opc_group_f1, // 59 + &ppc_opc_invalid, // 60 + &ppc_opc_invalid, // 61 + &ppc_opc_invalid, // 62 + &ppc_opc_group_f2, // 63 +}; + +void FASTCALL ppc_exec_opc() +{ + uint32 mainopc = PPC_OPC_MAIN(gCPU.current_opc); + ppc_opc_table_main[mainopc](); +} + +void ppc_dec_init() +{ + ppc_opc_init_group2(); + if ((ppc_cpu_get_pvr(0) & 0xffff0000) == 0x000c0000) { + ppc_opc_table_main[4] = ppc_opc_group_v; + ppc_opc_init_groupv(); + } +} diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_dec.h b/ppc/pearpc/cpu/cpu_generic/ppc_dec.h new file mode 100644 index 00000000..c0be5c7b --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_dec.h @@ -0,0 +1,53 @@ +/* + * PearPC + * ppc_dec.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_DEC_H__ +#define __PPC_DEC_H__ + +#include "system/types.h" + +void FASTCALL ppc_exec_opc(); +void ppc_dec_init(); + +typedef void (*ppc_opc_function)(); + +#define PPC_OPC_ASSERT(v) + +#define PPC_OPC_MAIN(opc) (((opc)>>26)&0x3f) +#define PPC_OPC_EXT(opc) (((opc)>>1)&0x3ff) +#define PPC_OPC_Rc 1 +#define PPC_OPC_OE (1<<10) +#define PPC_OPC_LK 1 +#define PPC_OPC_AA (1<<1) + +#define PPC_OPC_TEMPL_A(opc, rD, rA, rB, rC) {rD=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;rB=((opc)>>11)&0x1f;rC=((opc)>>6)&0x1f;} +#define PPC_OPC_TEMPL_B(opc, BO, BI, BD) {BO=((opc)>>21)&0x1f;BI=((opc)>>16)&0x1f;BD=(opc)&0xfffc;if (BD&0x8000) BD |= 0xffff0000;} +#define PPC_OPC_TEMPL_D_SImm(opc, rD, rA, imm) {rD=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;imm=(opc)&0xffff;if (imm & 0x8000) imm |= 0xffff0000;} +#define PPC_OPC_TEMPL_D_UImm(opc, rD, rA, imm) {rD=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;imm=(opc)&0xffff;} +#define PPC_OPC_TEMPL_D_Shift16(opc, rD, rA, imm) {rD=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;imm=(opc)<<16;} +#define PPC_OPC_TEMPL_I(opc, LI) {LI=(opc)&0x3fffffc;if (LI&0x02000000) LI |= 0xfc000000;} +#define PPC_OPC_TEMPL_M(opc, rS, rA, SH, MB, ME) {rS=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;SH=((opc)>>11)&0x1f;MB=((opc)>>6)&0x1f;ME=((opc)>>1)&0x1f;} +#define PPC_OPC_TEMPL_X(opc, rS, rA, rB) {rS=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;rB=((opc)>>11)&0x1f;} +#define PPC_OPC_TEMPL_XFX(opc, rS, CRM) {rS=((opc)>>21)&0x1f;CRM=((opc)>>12)&0xff;} +#define PPC_OPC_TEMPL_XO(opc, rS, rA, rB) {rS=((opc)>>21)&0x1f;rA=((opc)>>16)&0x1f;rB=((opc)>>11)&0x1f;} +#define PPC_OPC_TEMPL_XL(opc, BO, BI, BD) {BO=((opc)>>21)&0x1f;BI=((opc)>>16)&0x1f;BD=((opc)>>11)&0x1f;} +#define PPC_OPC_TEMPL_XFL(opc, rB, FM) {rB=((opc)>>11)&0x1f;FM=((opc)>>17)&0xff;} + +#endif diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_exc.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_exc.cpp new file mode 100644 index 00000000..abf0b8c9 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_exc.cpp @@ -0,0 +1,132 @@ +/* + * PearPC + * ppc_exc.cc + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nmsu.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Pages marked: v.??? + * From: IBM PowerPC MicroProcessor Family: Altivec(tm) Technology... + * Programming Environments Manual + */ + +#include "tools/snprintf.h" +#include "debug/tracers.h" +#include "cpu/debug.h" +#include "info.h" +#include "ppc_cpu.h" +#include "ppc_exc.h" +#include "ppc_mmu.h" + +/* + * .247 + */ +bool FASTCALL ppc_exception(uint32 type, uint32 flags, uint32 a) +{ + if (type != PPC_EXC_DEC) { + PPC_EXC_TRACE("@%08x: type = %08x (%08x, %08x)\n", gCPU.pc, type, flags, a); + } + switch (type) { + case PPC_EXC_DSI: { // .271 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + gCPU.dar = a; + gCPU.dsisr = flags; + break; + } + case PPC_EXC_ISI: { // .274 + if (gCPU.pc == 0) { + PPC_EXC_WARN("pc == 0 in ISI\n"); + SINGLESTEP(""); + } + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = (gCPU.msr & 0x87c0ffff) | flags; + break; + } + case PPC_EXC_DEC: { // .284 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_EXT_INT: { + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_SC: { // .285 + gCPU.srr[0] = gCPU.npc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_NO_FPU: { // .284 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_NO_VEC: { // v.41 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_PROGRAM: { // .283 + if (flags & PPC_EXC_PROGRAM_NEXT) { + gCPU.srr[0] = gCPU.npc; + } else { + gCPU.srr[0] = gCPU.pc; + } + gCPU.srr[1] = (gCPU.msr & 0x87c0ffff) | flags; + break; + } + case PPC_EXC_FLOAT_ASSIST: { // .288 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + case PPC_EXC_MACHINE_CHECK: { // .270 + if (!(gCPU.msr & MSR_ME)) { + PPC_EXC_ERR("machine check exception and MSR[ME]=0.\n"); + } + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = (gCPU.msr & 0x87c0ffff) | MSR_RI; + break; + } + case PPC_EXC_TRACE2: { // .286 + gCPU.srr[0] = gCPU.pc; + gCPU.srr[1] = gCPU.msr & 0x87c0ffff; + break; + } + default: + PPC_EXC_ERR("unknown\n"); + return false; + } + ppc_mmu_tlb_invalidate(); + if (1 || (gCPU.msr & MSR_IP)) + type |= 0xfff00000; + gCPU.msr = 0; + gCPU.npc = type; + return true; +} + +void ppc_cpu_raise_ext_exception() +{ + ppc_cpu_atomic_raise_ext_exception(); +} + +void ppc_cpu_cancel_ext_exception() +{ + ppc_cpu_atomic_cancel_ext_exception(); +} diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_exc.h b/ppc/pearpc/cpu/cpu_generic/ppc_exc.h new file mode 100644 index 00000000..9aa88621 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_exc.h @@ -0,0 +1,74 @@ +/* + * PearPC + * ppc_exc.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_EXC_H__ +#define __PPC_EXC_H__ + +#include "system/types.h" + +/* + * .250 + */ +#define PPC_EXC_UNKNOWN 0 +#define PPC_EXC_SYS_RESET 0x100 +#define PPC_EXC_MACHINE_CHECK 0x00200 +#define PPC_EXC_DSI 0x00300 +#define PPC_EXC_ISI 0x00400 +#define PPC_EXC_EXT_INT 0x00500 +#define PPC_EXC_ALIGNMENT 0x00600 +#define PPC_EXC_PROGRAM 0x00700 +#define PPC_EXC_NO_FPU 0x00800 +#define PPC_EXC_DEC 0x00900 +//Reserved 0x00A00 +//Reserved 0x00B00 +#define PPC_EXC_SC 0x00C00 +#define PPC_EXC_TRACE2 0x00D00 +#define PPC_EXC_FLOAT_ASSIST 0x00E00 +#define PPC_EXC_PERF_MON 0xF00 +#define PPC_EXC_NO_VEC 0xF20 +#define PPC_EXC_ALTIVEC 0xF20 +#define PPC_EXC_ALTIVEC_ASSIST 0x1600 +#define PPC_EXC_TAU 0x1700 + +#define PPC_EXC_DSISR_PAGE (1<<30) +#define PPC_EXC_DSISR_PROT (1<<27) +#define PPC_EXC_DSISR_STORE (1<<25) + +#define PPC_EXC_SRR1_PAGE PPC_EXC_DSISR_PAGE +#define PPC_EXC_SRR1_GUARD (1<<28) +#define PPC_EXC_SRR1_PROT PPC_EXC_DSISR_PROT + +#define PPC_EXC_PROGRAM_FLOAT (1<<20) +#define PPC_EXC_PROGRAM_ILL (1<<19) +#define PPC_EXC_PROGRAM_PRIV (1<<18) +#define PPC_EXC_PROGRAM_TRAP (1<<17) + +/* + * set if srr0 does not not contain the address of the intruction + * causing the exception + */ +#define PPC_EXC_PROGRAM_NEXT (1<<16) + +bool FASTCALL ppc_exception(uint32 type=0, uint32 flags=0, uint32 a=0); +void ppc_cpu_raise_ext_exception(); +void ppc_cpu_cancel_ext_exception(); + +#endif + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_fpu.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_fpu.cpp new file mode 100644 index 00000000..a40a4eb3 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_fpu.cpp @@ -0,0 +1,1359 @@ +/* + * PearPC + * ppc_fpu.cc + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * Copyright (C) 2003 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "debug/tracers.h" +#include "ppc_cpu.h" +#include "ppc_dec.h" +#include "ppc_fpu.h" + +// .121 + + +#define PPC_FPR_TYPE2(a,b) (((a)<<8)|(b)) +inline void ppc_fpu_add(ppc_double &res, ppc_double &a, ppc_double &b) +{ + switch (PPC_FPR_TYPE2(a.type, b.type)) { + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_norm): { + int diff = a.e - b.e; + if (diff<0) { + diff = -diff; + if (diff <= 56) { + a.m >>= diff; + } else if (a.m != 0) { + a.m = 1; + } else { + a.m = 0; + } + res.e = b.e; + } else { + if (diff <= 56) { + b.m >>= diff; + } else if (b.m != 0) { + b.m = 1; + } else { + b.m = 0; + } + res.e = a.e; + } + res.type = ppc_fpr_norm; + if (a.s == b.s) { + res.s = a.s; + res.m = a.m + b.m; + if (res.m & (1ULL<<56)) { + res.m >>= 1; + res.e++; + } + } else { + res.s = a.s; + res.m = a.m - b.m; + if (!res.m) { + if (FPSCR_RN(gCPU.fpscr) == FPSCR_RN_MINF) { + res.s |= b.s; + } else { + res.s &= b.s; + } + res.type = ppc_fpr_zero; + } else { + if ((sint64)res.m < 0) { + res.m = b.m - a.m; + res.s = b.s; + } + diff = ppc_fpu_normalize(res) - 8; + res.e -= diff; + res.m <<= diff; + } + } + break; + } + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_NaN): + res.s = a.s; + res.type = ppc_fpr_NaN; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_zero): + res.e = a.e; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_zero): + res.s = a.s; + res.m = a.m; + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_norm): + res.e = b.e; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_NaN): + res.s = b.s; + res.m = b.m; + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_Inf): + if (a.s != b.s) { + // +oo + -oo == NaN + res.s = a.s ^ b.s; + res.type = ppc_fpr_NaN; + break; + } + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_zero): + res.s = a.s; + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_Inf): + res.s = b.s; + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_zero): + // round bla + res.type = ppc_fpr_zero; + res.s = a.s && b.s; + break; + } +} + +inline void ppc_fpu_quadro_mshr(ppc_quadro &q, int exp) +{ + if (exp >= 64) { + q.m1 = q.m0; + q.m0 = 0; + exp -= 64; + } + uint64 t = q.m0 & ((1ULL<>= exp; + q.m1 >>= exp; + q.m1 |= t<<(64-exp); +} + +inline void ppc_fpu_quadro_mshl(ppc_quadro &q, int exp) +{ + if (exp >= 64) { + q.m0 = q.m1; + q.m1 = 0; + exp -= 64; + } + uint64 t = (q.m1 >> (64-exp)) & ((1ULL< b.m0) { + cmp = +1; + } else { + if (a.m1 < b.m1) { + cmp = -1; + } else if (a.m1 > b.m1) { + cmp = +1; + } else { + cmp = 0; + } + } + if (!cmp) { + if (FPSCR_RN(gCPU.fpscr) == FPSCR_RN_MINF) { + res.s |= b.s; + } else { + res.s &= b.s; + } + res.type = ppc_fpr_zero; + } else { + if (cmp < 0) { + ppc_fpu_sub_quadro_m(res, b, a); + res.s = b.s; + } else { + ppc_fpu_sub_quadro_m(res, a, b); + } + diff = ppc_fpu_normalize_quadro(res) - (128-107); + int X_prime = res.m1 & 1; + res.m1 &= 0xfffffffffffffffeULL; + ppc_fpu_quadro_mshl(res, diff); + res.e -= diff; + res.m1 |= X_prime; + } + // res = [107] + } + break; + } + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_NaN): + res.s = a.s; + res.type = ppc_fpr_NaN; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_zero): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_zero): + res.e = a.e; + res.s = a.s; + res.m0 = a.m0; + res.m1 = a.m1; + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_NaN): + res.e = b.e; + res.s = b.s; + res.m0 = b.m0; + res.m1 = b.m1; + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_Inf): + if (a.s != b.s) { + // +oo + -oo == NaN + res.s = a.s ^ b.s; + res.type = ppc_fpr_NaN; + break; + } + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_zero): + res.s = a.s; + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_Inf): + res.s = b.s; + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_zero): + // round bla + res.type = ppc_fpr_zero; + res.s = a.s && b.s; + break; + } +} + +inline void ppc_fpu_add_uint64_carry(uint64 &a, uint64 b, uint64 &carry) +{ + carry = (a+b < a) ? 1 : 0; + a += b; +} + +// 'res' has 56 significant bits on return, a + b have 56 significant bits each +inline void ppc_fpu_mul(ppc_double &res, const ppc_double &a, const ppc_double &b) +{ + res.s = a.s ^ b.s; + switch (PPC_FPR_TYPE2(a.type, b.type)) { + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_norm): { + res.type = ppc_fpr_norm; + res.e = a.e + b.e; +// printf("new exp: %d\n", res.e); +// ht_printf("MUL:\na.m: %qb\nb.m: %qb\n", a.m, b.m); + uint64 fH, fM1, fM2, fL; + fL = (a.m & 0xffffffff) * (b.m & 0xffffffff); // [32] * [32] = [63,64] + fM1 = (a.m >> 32) * (b.m & 0xffffffff); // [24] * [32] = [55,56] + fM2 = (a.m & 0xffffffff) * (b.m >> 32); // [32] * [24] = [55,56] + fH = (a.m >> 32) * (b.m >> 32); // [24] * [24] = [47,48] +// ht_printf("fH: %qx fM1: %qx fM2: %qx fL: %qx\n", fH, fM1, fM2, fL); + + // calulate fH * 2^64 + (fM1 + fM2) * 2^32 + fL + uint64 rL, rH; + rL = fL; // rL = rH = [63,64] + rH = fH; // rH = fH = [47,48] + uint64 split; + split = fM1 + fM2; + uint64 carry; + ppc_fpu_add_uint64_carry(rL, (split & 0xffffffff) << 32, carry); // rL = [63,64] + rH += carry; // rH = [0 .. 2^48] + rH += split >> 32; // rH = [0:48], where 46, 47 or 48 set + + // res.m = [0 0 .. 0 | rH_48 rH_47 .. rH_0 | rL_63 rL_62 .. rL_55] + // [---------------------------------------------------------] + // bit = [63 62 .. 58 | 57 56 .. 9 | 8 7 0 ] + // [---------------------------------------------------------] + // [15 bits zero | 49 bits rH | 8 most sign.bits rL ] + res.m = rH << 9; + res.m |= rL >> (64-9); + // res.m = [58] + +// ht_printf("fH: %qx fM1: %qx fM2: %qx fL: %qx\n", fH, fM1, fM2, fL); + if (res.m & (1ULL << 57)) { + res.m >>= 2; + res.e += 2; + } else if (res.m & (1ULL << 56)) { + res.m >>= 1; + res.e++; + } + // res.m = [56] + break; + } + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_NaN): + res.type = a.type; + res.e = a.e; + break; + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_zero): + res.s = a.s; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_zero): + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_NaN): + res.s = b.s; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_zero): + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_zero): + res.type = ppc_fpr_NaN; + break; + } +} + +// 'res' has 'prec' significant bits on return, a + b have 56 significant bits each +// for 111 >= prec >= 64 +inline void ppc_fpu_mul_quadro(ppc_quadro &res, ppc_double &a, ppc_double &b, int prec) +{ + res.s = a.s ^ b.s; + switch (PPC_FPR_TYPE2(a.type, b.type)) { + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_norm): { + res.type = ppc_fpr_norm; + res.e = a.e + b.e; +// printf("new exp: %d\n", res.e); +// ht_printf("MUL:\na.m: %016qx\nb.m: %016qx\n", a.m, b.m); + uint64 fH, fM1, fM2, fL; + fL = (a.m & 0xffffffff) * (b.m & 0xffffffff); // [32] * [32] = [63,64] + fM1 = (a.m >> 32) * (b.m & 0xffffffff); // [24] * [32] = [55,56] + fM2 = (a.m & 0xffffffff) * (b.m >> 32); // [32] * [24] = [55,56] + fH = (a.m >> 32) * (b.m >> 32); // [24] * [24] = [47,48] +// ht_printf("fH: %016qx fM1: %016qx fM2: %016qx fL: %016qx\n", fH, fM1, fM2, fL); + + // calulate fH * 2^64 + (fM1 + fM2) * 2^32 + fL + uint64 rL, rH; + rL = fL; // rL = rH = [63,64] + rH = fH; // rH = fH = [47,48] + uint64 split; + split = fM1 + fM2; + uint64 carry; + ppc_fpu_add_uint64_carry(rL, (split & 0xffffffff) << 32, carry); // rL = [63,64] + rH += carry; // rH = [0 .. 2^48] + rH += split >> 32; // rH = [0:48], where 46, 47 or 48 set + + // res.m0 = [0 0 .. 0 | rH_48 rH_47 .. rH_0 | rL_63 rL_62 .. rL_0] + // [-----------------------------------------------------------] + // log.bit= [127 126 .. 113 | 112 64 | 63 62 0 ] + // [-----------------------------------------------------------] + // [ 15 bits zero | 49 bits rH | 64 bits rL ] + res.m0 = rH; + res.m1 = rL; + // res.m0|res.m1 = [111,112,113] + +// ht_printf("res = %016qx%016qx\n", res.m0, res.m1); + if (res.m0 & (1ULL << 48)) { + ppc_fpu_quadro_mshr(res, 2+(111-prec)); + res.e += 2; + } else if (res.m0 & (1ULL << 47)) { + ppc_fpu_quadro_mshr(res, 1+(111-prec)); + res.e += 1; + } else { + ppc_fpu_quadro_mshr(res, 111-prec); + } + // res.m0|res.m1 = [prec] + break; + } + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_NaN): + res.type = a.type; + res.e = a.e; + break; + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_zero): + res.s = a.s; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_zero): + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_NaN): + res.s = b.s; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_zero): + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_zero): + res.type = ppc_fpr_NaN; + break; + } +} + +// calculate one of these: +// + m1 * m2 + s +// + m1 * m2 - s +// - m1 * m2 + s +// - m1 * m2 - s +// using a 106 bit accumulator +// +// .752 +// +// FIXME: There is a bug in this code that shows up in Mac OS X Finder fwd/bwd +// button: the top line is not rendered correctly. This works with the jitc_x86 +// FPU however... +inline void ppc_fpu_mul_add(ppc_double &res, ppc_double &m1, ppc_double &m2, + ppc_double &s) +{ + ppc_quadro p; +/* ht_printf("m1 = %d * %016qx * 2^%d, %s\n", m1.s, m1.m, m1.e, + ppc_fpu_get_fpr_type(m1.type)); + ht_printf("m2 = %d * %016qx * 2^%d, %s\n", m2.s, m2.m, m2.e, + ppc_fpu_get_fpr_type(m2.type));*/ + // create product with 106 significant bits + ppc_fpu_mul_quadro(p, m1, m2, 106); +/* ht_printf("p = %d * %016qx%016qx * 2^%d, %s\n", p.s, p.m0, p.m1, p.e, + ppc_fpu_get_fpr_type(p.type));*/ + // convert s into ppc_quadro +/* ht_printf("s = %d * %016qx * 2^%d %s\n", s.s, s.m, s.e, + ppc_fpu_get_fpr_type(s.type));*/ + ppc_quadro q; + q.e = s.e; + q.s = s.s; + q.type = s.type; + q.m0 = 0; + q.m1 = s.m; + // .. with 106 significant bits + ppc_fpu_quadro_mshl(q, 106-56); +/* ht_printf("q = %d * %016qx%016qx * 2^%d %s\n", q.s, q.m0, q.m1, q.e, + ppc_fpu_get_fpr_type(q.type));*/ + // now we must add p, q. + ppc_quadro x; + ppc_fpu_add_quadro(x, p, q); + // x = [107] +/* ht_printf("x = %d * %016qx%016qx * 2^%d %s\n", x.s, x.m0, x.m1, x.e, + ppc_fpu_get_fpr_type(x.type));*/ + res.type = x.type; + res.s = x.s; + res.e = x.e; + if (x.type == ppc_fpr_norm) { + res.m = x.m0 << 13; // 43 bits from m0 + res.m |= (x.m1 >> (64-12)) << 1; // 12 bits from m1 + res.m |= x.m1 & 1; // X' bit from m1 + } +/* ht_printf("res = %d * %016qx * 2^%d %s\n", res.s, res.m, res.e, + ppc_fpu_get_fpr_type(res.type));*/ +} + +inline void ppc_fpu_div(ppc_double &res, const ppc_double &a, const ppc_double &b) +{ + res.s = a.s ^ b.s; + switch (PPC_FPR_TYPE2(a.type, b.type)) { + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_norm): { + res.type = ppc_fpr_norm; + res.e = a.e - b.e; + res.m = 0; + uint64 am = a.m, bm = b.m; + uint i = 0; + while (am && (i<56)) { + res.m <<= 1; + if (am >= bm) { + res.m |= 1; + am -= bm; + } + am <<= 1; +// printf("am=%llx, bm=%llx, rm=%llx\n", am, bm, res.m); + i++; + } + res.m <<= 57-i; + if (res.m & (1ULL << 56)) { + res.m >>= 1; + } else { + res.e--; + } +// printf("final: am=%llx, bm=%llx, rm=%llx\n", am, bm, res.m); + break; + } + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_NaN): + res.e = a.e; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_NaN, ppc_fpr_zero): + res.s = a.s; + // fall-thru + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_norm): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_norm): + res.type = a.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_NaN): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_NaN): + res.s = b.s; + res.type = b.type; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_Inf): + res.type = ppc_fpr_zero; + break; + case PPC_FPR_TYPE2(ppc_fpr_norm, ppc_fpr_zero): + res.type = ppc_fpr_Inf; + break; + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_Inf, ppc_fpr_zero): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_Inf): + case PPC_FPR_TYPE2(ppc_fpr_zero, ppc_fpr_zero): + res.type = ppc_fpr_NaN; + break; + } +} + +inline void ppc_fpu_sqrt(ppc_double &D, const ppc_double &B) +{ + switch (B.type) { + case ppc_fpr_norm: + if (B.s) { + D.type = ppc_fpr_NaN; + gCPU.fpscr |= FPSCR_VXSQRT; + break; + } + // D := 1/2(D_old + B/D_old) + D = B; + D.e /= 2; + for (int i=0; i<6; i++) { + ppc_double D_old = D; + ppc_double B_div_D_old; + ppc_fpu_div(B_div_D_old, B, D_old); + ppc_fpu_add(D, D_old, B_div_D_old); + D.e--; + +/* uint64 e; + ppc_double E = D; + ppc_fpu_pack_double(E, e); + printf("%.20f\n", *(double *)&e);*/ + } + break; + case ppc_fpr_zero: + D.type = ppc_fpr_zero; + D.s = B.s; + break; + case ppc_fpr_Inf: + if (B.s) { + D.type = ppc_fpr_NaN; + gCPU.fpscr |= FPSCR_VXSQRT; + } else { + D.type = ppc_fpr_Inf; + D.s = 0; + } + break; + case ppc_fpr_NaN: + D.type = ppc_fpr_NaN; + break; + } +} + +void ppc_fpu_test() +{ + ppc_double A, B, C; + double a, b, c; + A.type = B.type = ppc_fpr_norm; + A.s = 1; + A.e = 0; + A.m = 0; + A.m = ((1ULL<<56)-1)-((1ULL<<10)-1); + ht_printf("%qb\n", A.m); + B.s = 1; + B.e = 0; + B.m = 0; + B.m = ((1ULL<<56)-1)-((1ULL<<50)-1); + a = ppc_fpu_get_double(A); + b = ppc_fpu_get_double(B); + printf("%f + %f = \n", a, b); + ppc_fpu_add(C, A, B); + uint64 d; + uint32 s; + ppc_fpu_pack_double_as_single(C, d); + ht_printf("%064qb\n", d); + ppc_fpu_unpack_double(C, d); + ppc_fpu_pack_single(C, s); + ht_printf("single: %032b\n", s); + ppc_single Cs; + ppc_fpu_unpack_single(Cs, s); + ppc_fpu_single_to_double(Cs, C); +// ht_printf("%d\n", ppc_fpu_double_to_int(C)); + c = ppc_fpu_get_double(C); + printf("%f\n", c); +} + +/* + * a and b must not be NaNs + */ +inline uint32 ppc_fpu_compare(ppc_double &a, ppc_double &b) +{ + if (a.type == ppc_fpr_zero) { + if (b.type == ppc_fpr_zero) return 2; + return (b.s) ? 4: 8; + } + if (b.type == ppc_fpr_zero) return (a.s) ? 8: 4; + if (a.s != b.s) return (a.s) ? 8: 4; + if (a.e > b.e) return (a.s) ? 8: 4; + if (a.e < b.e) return (a.s) ? 4: 8; + if (a.m > b.m) return (a.s) ? 8: 4; + if (a.m < b.m) return (a.s) ? 4: 8; + return 2; +} + +double ppc_fpu_get_double(uint64 d) +{ + ppc_double dd; + ppc_fpu_unpack_double(dd, d); + return ppc_fpu_get_double(dd); +} + +double ppc_fpu_get_double(ppc_double &d) +{ + if (d.type == ppc_fpr_norm) { + double r = d.m; + for (int i=0; i<55; i++) { + r = r / 2.0; + } + if (d.e < 0) { + for (int i=0; i>d.e; i--) { + r = r / 2.0; + } + } else if (d.e > 0) { + for (int i=0; i>= 2; + ppc_double A, B; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + uint32 cmp; + if (A.type == ppc_fpr_NaN || B.type == ppc_fpr_NaN) { + gCPU.fpscr |= FPSCR_VXSNAN; + /*if (bla)*/ gCPU.fpscr |= FPSCR_VXVC; + cmp = 1; + } else { + cmp = ppc_fpu_compare(A, B); + } + crfD = 7-crfD; + gCPU.fpscr &= ~0x1f000; + gCPU.fpscr |= (cmp << 12); + gCPU.cr &= ppc_fpu_cmp_and_mask[crfD]; + gCPU.cr |= (cmp << (crfD * 4)); +} +/* + * fcmpu Floating Compare Unordered + * .489 + */ +void ppc_opc_fcmpu() +{ + int crfD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, crfD, frA, frB); + crfD >>= 2; + ppc_double A, B; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + uint32 cmp; + if (A.type == ppc_fpr_NaN || B.type == ppc_fpr_NaN) { + gCPU.fpscr |= FPSCR_VXSNAN; + cmp = 1; + } else { + cmp = ppc_fpu_compare(A, B); + } + crfD = 7-crfD; + gCPU.fpscr &= ~0x1f000; + gCPU.fpscr |= (cmp << 12); + gCPU.cr &= ppc_fpu_cmp_and_mask[crfD]; + gCPU.cr |= (cmp << (crfD * 4)); +} +/* + * fctiwx Floating Convert to Integer Word + * .492 + */ +void ppc_opc_fctiwx() +{ + int frD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, frA, frB); + PPC_OPC_ASSERT(frA==0); + ppc_double B; + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + gCPU.fpr[frD] = ppc_fpu_double_to_int(B); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fctiw.\n"); + } +} +/* + * fctiwzx Floating Convert to Integer Word with Round toward Zero + * .493 + */ +void ppc_opc_fctiwzx() +{ + int frD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, frA, frB); + PPC_OPC_ASSERT(frA==0); + uint32 oldfpscr = gCPU.fpscr; + gCPU.fpscr &= ~3; + gCPU.fpscr |= 1; + ppc_double B; + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + gCPU.fpr[frD] = ppc_fpu_double_to_int(B); + gCPU.fpscr = oldfpscr; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fctiwz.\n"); + } +} +/* + * fdivx Floating Divide (Double-Precision) + * .494 + */ +void ppc_opc_fdivx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frC==0); + ppc_double A, B, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + if (A.type == ppc_fpr_zero && B.type == ppc_fpr_zero) { + gCPU.fpscr |= FPSCR_VXZDZ; + } + if (A.type == ppc_fpr_Inf && B.type == ppc_fpr_Inf) { + gCPU.fpscr |= FPSCR_VXIDI; + } + if (B.type == ppc_fpr_zero && A.type != ppc_fpr_zero) { + // FIXME:: + gCPU.fpscr |= FPSCR_VXIDI; + } + ppc_fpu_div(D, A, B); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fdiv.\n"); + } +} +/* + * fdivsx Floating Divide Single + * .495 + */ +void ppc_opc_fdivsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frC==0); + ppc_double A, B, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + if (A.type == ppc_fpr_zero && B.type == ppc_fpr_zero) { + gCPU.fpscr |= FPSCR_VXZDZ; + } + if (A.type == ppc_fpr_Inf && B.type == ppc_fpr_Inf) { + gCPU.fpscr |= FPSCR_VXIDI; + } + if (B.type == ppc_fpr_zero && A.type != ppc_fpr_zero) { + // FIXME:: + gCPU.fpscr |= FPSCR_VXIDI; + } + ppc_fpu_div(D, A, B); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fdivs.\n"); + } +} +/* + * fmaddx Floating Multiply-Add (Double-Precision) + * .496 + */ +void ppc_opc_fmaddx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + ppc_fpu_mul_add(D, A, C, B); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmadd.\n"); + } +} +/* + * fmaddx Floating Multiply-Add Single + * .497 + */ +void ppc_opc_fmaddsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + ppc_fpu_mul_add(D, A, C, B); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmadds.\n"); + } +} +/* + * fmrx Floating Move Register + * .498 + */ +void ppc_opc_fmrx() +{ + int frD, rA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, frB); + PPC_OPC_ASSERT(rA==0); + gCPU.fpr[frD] = gCPU.fpr[frB]; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmr.\n"); + } +} +/* + * fmsubx Floating Multiply-Subtract (Double-Precision) + * .499 + */ +void ppc_opc_fmsubx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + B.s ^= 1; + ppc_fpu_mul_add(D, A, C, B); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmsub.\n"); + } +} +/* + * fmsubsx Floating Multiply-Subtract Single + * .500 + */ +void ppc_opc_fmsubsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + ppc_fpu_mul_add(D, A, C, B); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmsubs.\n"); + } +} +/* + * fmulx Floating Multipy (Double-Precision) + * .501 + */ +void ppc_opc_fmulx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frB==0); + ppc_double A, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + if ((A.type == ppc_fpr_Inf && C.type == ppc_fpr_zero) + || (A.type == ppc_fpr_zero && C.type == ppc_fpr_Inf)) { + gCPU.fpscr |= FPSCR_VXIMZ; + } + ppc_fpu_mul(D, A, C); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); +// *((double*)&gCPU.fpr[frD]) = *((double*)(&gCPU.fpr[frA]))*(*((double*)(&gCPU.fpr[frC]))); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmul.\n"); + } +} +/* + * fmulsx Floating Multipy Single + * .502 + */ +void ppc_opc_fmulsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frB==0); + ppc_double A, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + if ((A.type == ppc_fpr_Inf && C.type == ppc_fpr_zero) + || (A.type == ppc_fpr_zero && C.type == ppc_fpr_Inf)) { + gCPU.fpscr |= FPSCR_VXIMZ; + } + ppc_fpu_mul(D, A, C); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fmuls.\n"); + } +} +/* + * fnabsx Floating Negative Absolute Value + * .503 + */ +void ppc_opc_fnabsx() +{ + int frD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, frA, frB); + PPC_OPC_ASSERT(frA==0); + gCPU.fpr[frD] = gCPU.fpr[frB] | FPU_SIGN_BIT; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fnabs.\n"); + } +} +/* + * fnegx Floating Negate + * .504 + */ +void ppc_opc_fnegx() +{ + int frD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, frA, frB); + PPC_OPC_ASSERT(frA==0); + gCPU.fpr[frD] = gCPU.fpr[frB] ^ FPU_SIGN_BIT; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fneg.\n"); + } +} +/* + * fnmaddx Floating Negative Multiply-Add (Double-Precision) + * .505 + */ +void ppc_opc_fnmaddx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D/*, E*/; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + ppc_fpu_mul_add(D, A, C, B); + D.s ^= 1; + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fnmadd.\n"); + } +} +/* + * fnmaddsx Floating Negative Multiply-Add Single + * .506 + */ +void ppc_opc_fnmaddsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + ppc_fpu_mul_add(D, A, C, B); + D.s ^= 1; + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fnmadds.\n"); + } +} +/* + * fnmsubx Floating Negative Multiply-Subtract (Double-Precision) + * .507 + */ +void ppc_opc_fnmsubx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + B.s ^= 1; + ppc_fpu_mul_add(D, A, C, B); + D.s ^= 1; + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fnmsub.\n"); + } +} +/* + * fnsubsx Floating Negative Multiply-Subtract Single + * .508 + */ +void ppc_opc_fnmsubsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A, B, C, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_unpack_double(C, gCPU.fpr[frC]); + B.s ^= 1; + ppc_fpu_mul_add(D, A, C, B); + D.s ^= 1; + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fnmsubs.\n"); + } +} +/* + * fresx Floating Reciprocal Estimate Single + * .509 + */ +void ppc_opc_fresx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frA==0 && frC==0); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fres.\n"); + } + PPC_FPU_ERR("fres\n"); +} +/* + * frspx Floating Round to Single + * .511 + */ +void ppc_opc_frspx() +{ + int frD, frA, frB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, frA, frB); + PPC_OPC_ASSERT(frA==0); + ppc_double B; + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(B, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("frsp.\n"); + } +} +/* + * frsqrtex Floating Reciprocal Square Root Estimate + * .512 + */ +void ppc_opc_frsqrtex() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frA==0 && frC==0); + ppc_double B; + ppc_double D; + ppc_double E; + ppc_double Q; + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_sqrt(Q, B); + E.type = ppc_fpr_norm; E.s = 0; E.e = 0; E.m = 0x80000000000000ULL; + ppc_fpu_div(D, E, Q); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("frsqrte.\n"); + } +} +/* + * fselx Floating Select + * .514 + */ +void ppc_opc_fselx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + ppc_double A; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + if (A.type == ppc_fpr_NaN || (A.type != ppc_fpr_zero && A.s)) { + gCPU.fpr[frD] = gCPU.fpr[frB]; + } else { + gCPU.fpr[frD] = gCPU.fpr[frC]; + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fsel.\n"); + } +} +/* + * fsqrtx Floating Square Root (Double-Precision) + * .515 + */ +void ppc_opc_fsqrtx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frA==0 && frC==0); + ppc_double B; + ppc_double D; + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + ppc_fpu_sqrt(D, B); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fsqrt.\n"); + } +} +/* + * fsqrtsx Floating Square Root Single + * .515 + */ +void ppc_opc_fsqrtsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frA==0 && frC==0); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fsqrts.\n"); + } + PPC_FPU_ERR("fsqrts\n"); +} +/* + * fsubx Floating Subtract (Double-Precision) + * .517 + */ +void ppc_opc_fsubx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frC==0); + ppc_double A, B, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + if (B.type != ppc_fpr_NaN) { + B.s ^= 1; + } + if (A.s != B.s && A.type == ppc_fpr_Inf && B.type == ppc_fpr_Inf) { + gCPU.fpscr |= FPSCR_VXISI; + } + ppc_fpu_add(D, A, B); + gCPU.fpscr |= ppc_fpu_pack_double(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fsub.\n"); + } +} +/* + * fsubsx Floating Subtract Single + * .518 + */ +void ppc_opc_fsubsx() +{ + int frD, frA, frB, frC; + PPC_OPC_TEMPL_A(gCPU.current_opc, frD, frA, frB, frC); + PPC_OPC_ASSERT(frC==0); + ppc_double A, B, D; + ppc_fpu_unpack_double(A, gCPU.fpr[frA]); + ppc_fpu_unpack_double(B, gCPU.fpr[frB]); + if (B.type != ppc_fpr_NaN) { + B.s ^= 1; + } + if (A.s != B.s && A.type == ppc_fpr_Inf && B.type == ppc_fpr_Inf) { + gCPU.fpscr |= FPSCR_VXISI; + } + ppc_fpu_add(D, A, B); + gCPU.fpscr |= ppc_fpu_pack_double_as_single(D, gCPU.fpr[frD]); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_FPU_ERR("fsubs.\n"); + } +} + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_fpu.h b/ppc/pearpc/cpu/cpu_generic/ppc_fpu.h new file mode 100644 index 00000000..18bca355 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_fpu.h @@ -0,0 +1,609 @@ +/* + * PearPC + * ppc_fpu.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_FPU_H__ +#define __PPC_FPU_H__ + + +#define FPU_SIGN_BIT (0x8000000000000000ULL) + +#define FPD_SIGN(v) (((v)&FPU_SIGN_BIT)?1:0) +#define FPD_EXP(v) ((v)>>52) +#define FPD_FRAC(v) ((v)&0x000fffffffffffffULL) + +#define FPS_SIGN(v) ((v)&0x80000000) +#define FPS_EXP(v) ((v)>>23) +#define FPS_FRAC(v) ((v)&0x007fffff) + +// m must be uint64 +#define FPD_PACK_VAR(f, s, e, m) (f) = ((s)?FPU_SIGN_BIT:0ULL)|((((uint64)(e))&0x7ff)<<52)|((m)&((1ULL<<52)-1)) +#define FPD_UNPACK_VAR(f, s, e, m) {(s)=FPD_SIGN(f);(e)=FPD_EXP(f)&0x7ff;(m)=FPD_FRAC(f);} + +#define FPS_PACK_VAR(f, s, e, m) (f) = ((s)?0x80000000:0)|((e)<<23)|((m)&0x7fffff) +#define FPS_UNPACK_VAR(f, s, e, m) {(s)=FPS_SIGN(f);(e)=FPS_EXP(f)&0xff;(m)=FPS_FRAC(f);} + +#define FPD_UNPACK(freg, fvar) FPD_UNPACK(freg, fvar.s, fvar.e, fvar.m) + + +void ppc_fpu_test(); + +enum ppc_fpr_type { + ppc_fpr_norm, + ppc_fpr_zero, + ppc_fpr_NaN, + ppc_fpr_Inf, +}; + +struct ppc_quadro { + ppc_fpr_type type; + int s; + int e; + uint64 m0; // most significant + uint64 m1; // least significant +}; + +struct ppc_double { + ppc_fpr_type type; + int s; + int e; + uint64 m; +}; + +struct ppc_single { + ppc_fpr_type type; + int s; + int e; + uint m; +}; + +inline int ppc_count_leading_zeros(uint64 i) +{ + int ret; + uint32 dd = i >> 32; + if (dd) { + ret = 31; + if (dd > 0xffff) { ret -= 16; dd >>= 16; } + if (dd > 0xff) { ret -= 8; dd >>= 8; } + if (dd & 0xf0) { ret -= 4; dd >>= 4; } + if (dd & 0xc) { ret -= 2; dd >>= 2; } + if (dd & 0x2) ret--; + } else { + dd = (uint32)i; + ret = 63; + if (dd > 0xffff) { ret -= 16; dd >>= 16; } + if (dd > 0xff) { ret -= 8; dd >>= 8; } + if (dd & 0xf0) { ret -= 4; dd >>= 4; } + if (dd & 0xc) { ret -= 2; dd >>= 2; } + if (dd & 0x2) ret--; + } + return ret; +} + +inline int ppc_fpu_normalize_quadro(ppc_quadro &d) +{ + int ret = d.m0 ? ppc_count_leading_zeros(d.m0) : 64 + ppc_count_leading_zeros(d.m1); + return ret; +} + +inline int ppc_fpu_normalize(ppc_double &d) +{ + return ppc_count_leading_zeros(d.m); +} + +inline int ppc_fpu_normalize_single(ppc_single &s) +{ + int ret; + uint32 dd = s.m; + ret = 31; + if (dd > 0xffff) { ret -= 16; dd >>= 16; } + if (dd > 0xff) { ret -= 8; dd >>= 8; } + if (dd & 0xf0) { ret -= 4; dd >>= 4; } + if (dd & 0xc) { ret -= 2; dd >>= 2; } + if (dd & 0x2) ret--; + return ret; +} + +#include "tools/snprintf.h" +inline void ppc_fpu_unpack_double(ppc_double &res, uint64 d) +{ + FPD_UNPACK_VAR(d, res.s, res.e, res.m); +// ht_printf("ud: %qx: s:%d e:%d m:%qx\n", d, res.s, res.e, res.m); + // .124 + if (res.e == 2047) { + if (res.m == 0) { + res.type = ppc_fpr_Inf; + } else { + res.type = ppc_fpr_NaN; + } + } else if (res.e == 0) { + if (res.m == 0) { + res.type = ppc_fpr_zero; + } else { + // normalize denormalized exponent + int diff = ppc_fpu_normalize(res) - 8; + res.m <<= diff+3; + res.e -= 1023 - 1 + diff; + res.type = ppc_fpr_norm; + } + } else { + res.e -= 1023; // unbias exponent + res.type = ppc_fpr_norm; + // add implied bit + res.m |= 1ULL<<52; + res.m <<= 3; + } +// ht_printf("ud: %qx: s:%d e:%d m:%qx\n", d, res.s, res.e, res.m); +} + + +inline void ppc_fpu_unpack_single(ppc_single &res, uint32 d) +{ + FPS_UNPACK_VAR(d, res.s, res.e, res.m); + // .124 + if (res.e == 255) { + if (res.m == 0) { + res.type = ppc_fpr_Inf; + } else { + res.type = ppc_fpr_NaN; + } + } else if (res.e == 0) { + if (res.m == 0) { + res.type = ppc_fpr_zero; + } else { + // normalize denormalized exponent + int diff = ppc_fpu_normalize_single(res) - 8; + res.m <<= diff+3; + res.e -= 127 - 1 + diff; + res.type = ppc_fpr_norm; + } + } else { + res.e -= 127; // unbias exponent + res.type = ppc_fpr_norm; + // add implied bit + res.m |= 1<<23; + res.m <<= 3; + } +} + +inline uint32 ppc_fpu_round(ppc_double &d) +{ + // .132 + switch (FPSCR_RN(gCPU.fpscr)) { + case FPSCR_RN_NEAR: + if (d.m & 0x7) { + if ((d.m & 0x7) != 4) { + d.m += 4; + } else if (d.m & 8) { + d.m += 4; + } + return FPSCR_XX; + } + return 0; + case FPSCR_RN_ZERO: + if (d.m & 0x7) { + return FPSCR_XX; + } + return 0; + case FPSCR_RN_PINF: + if (!d.s && (d.m & 0x7)) { + d.m += 8; + return FPSCR_XX; + } + return 0; + case FPSCR_RN_MINF: + if (d.s && (d.m & 0x7)) { + d.m += 8; + return FPSCR_XX; + } + return 0; + } + return 0; +} + +inline uint32 ppc_fpu_round_single(ppc_single &s) +{ + switch (FPSCR_RN(gCPU.fpscr)) { + case FPSCR_RN_NEAR: + if (s.m & 0x7) { + if ((s.m & 0x7) != 4) { + s.m += 4; + } else if (s.m & 8) { + s.m += 4; + } + return FPSCR_XX; + } + return 0; + case FPSCR_RN_ZERO: + if (s.m & 0x7) { + return FPSCR_XX; + } + return 0; + case FPSCR_RN_PINF: + if (!s.s && (s.m & 0x7)) { + s.m += 8; + return FPSCR_XX; + } + return 0; + case FPSCR_RN_MINF: + if (s.s && (s.m & 0x7)) { + s.m += 8; + return FPSCR_XX; + } + return 0; + } + return 0; +} + +inline uint32 ppc_fpu_round_single(ppc_double &s) +{ + switch (FPSCR_RN(gCPU.fpscr)) { + case FPSCR_RN_NEAR: + if (s.m & 0x7) { + if ((s.m & 0x7) != 4) { + s.m += 4; + } else if (s.m & 8) { + s.m += 4; + } + return FPSCR_XX; + } + return 0; + case FPSCR_RN_ZERO: + if (s.m & 0x7) { + return FPSCR_XX; + } + return 0; + case FPSCR_RN_PINF: + if (!s.s && (s.m & 0x7)) { + s.m += 8; + return FPSCR_XX; + } + return 0; + case FPSCR_RN_MINF: + if (s.s && (s.m & 0x7)) { + s.m += 8; + return FPSCR_XX; + } + return 0; + } + return 0; +} + +inline uint32 ppc_fpu_pack_double(ppc_double &d, uint64 &res) +{ + // .124 + uint32 ret = 0; +// ht_printf("pd_type: %d\n", d.type); + switch (d.type) { + case ppc_fpr_norm: +// ht_printf("pd: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + d.e += 1023; // bias exponent +// ht_printf("pd: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + if (d.e > 0) { + ret |= ppc_fpu_round(d); + if (d.m & (1ULL<<56)) { + d.e++; + d.m >>= 4; + } else { + d.m >>= 3; + } + if (d.e >= 2047) { + d.e = 2047; + d.m = 0; + ret |= FPSCR_OX; + } + } else { + // number is denormalized + d.e = -d.e+1; + if (d.e <= 56) { + d.m >>= d.e; + ret |= ppc_fpu_round(d); + d.m <<= 1; + if (d.m & (1ULL<<56)) { + d.e = 1; + d.m = 0; + } else { + d.e = 0; + d.m >>= 4; + ret |= FPSCR_UX; + } + } else { + // underflow to zero + d.e = 0; + d.m = 0; + ret |= FPSCR_UX; + } + } + break; + case ppc_fpr_zero: + d.e = 0; + d.m = 0; + break; + case ppc_fpr_NaN: + d.e = 2047; + d.m = 1; + break; + case ppc_fpr_Inf: + d.e = 2047; + d.m = 0; + break; + } +// ht_printf("pd: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + FPD_PACK_VAR(res, d.s, d.e, d.m); + return ret; +} + +inline uint32 ppc_fpu_pack_single(ppc_double &d, uint32 &res) +{ + // .124 + uint32 ret = 0; + switch (d.type) { + case ppc_fpr_norm: +// ht_printf("ps: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + d.e += 127; // bias exponent + d.m >>= 29; +// ht_printf("ps: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + if (d.e > 0) { + ret |= ppc_fpu_round_single(d); + if (d.m & (1ULL<<27)) { + d.e++; + d.m >>= 4; + } else { + d.m >>= 3; + } + if (d.e >= 255) { + d.e = 255; + d.m = 0; + ret |= FPSCR_OX; + } + } else { + // number is denormalized + d.e = -d.e+1; + if (d.e <= 27) { + d.m >>= d.e; + ret |= ppc_fpu_round_single(d); + d.m <<= 1; + if (d.m & (1ULL<<27)) { + d.e = 1; + d.m = 0; + } else { + d.e = 0; + d.m >>= 4; + ret |= FPSCR_UX; + } + } else { + // underflow to zero + d.e = 0; + d.m = 0; + ret |= FPSCR_UX; + } + } + break; + case ppc_fpr_zero: + d.e = 0; + d.m = 0; + break; + case ppc_fpr_NaN: + d.e = 255; + d.m = 1; + break; + case ppc_fpr_Inf: + d.e = 255; + d.m = 0; + break; + } +// ht_printf("ps: %qx: s:%d e:%d m:%qx\n", d, d.s, d.e, d.m); + FPS_PACK_VAR(res, d.s, d.e, d.m); + return ret; +} + +inline void ppc_fpu_single_to_double(ppc_single &s, ppc_double &d) +{ + d.s = s.s; + d.e = s.e; + d.m = ((uint64)s.m)<<29; + d.type = s.type; +} + +inline uint32 ppc_fpu_pack_double_as_single(ppc_double &d, uint64 &res) +{ + // .757 + ppc_single s; + s.m = d.m >> 29; + s.e = d.e; + s.s = d.s; + s.type = d.type; + uint32 ret = 0; + + switch (s.type) { + case ppc_fpr_norm: + s.e = d.e+127; + if (s.e > 0) { + ret |= ppc_fpu_round_single(s); + if (s.m & (1<<27)) { + s.e++; + s.m >>= 4; + } else { + s.m >>= 3; + } + if (s.e >= 255) { + s.type = ppc_fpr_Inf; + s.e = 255; + s.m = 0; + ret |= FPSCR_OX; + } + d.e = s.e-127; + } else { + // number is denormalized + s.e = -s.e+1; + if (s.e <= 27) { + s.m >>= s.e; + ret |= ppc_fpu_round_single(s); + s.m <<= 1; + if (s.m & (1<<27)) { + s.e = 1; + s.m = 0; + } else { + s.e = 0; + s.m >>= 4; + ret |= FPSCR_UX; + } + } else { + // underflow to zero + s.type = ppc_fpr_zero; + s.e = 0; + s.m = 0; + ret |= FPSCR_UX; + } + } + break; + case ppc_fpr_zero: + s.e = 0; + s.m = 0; + break; + case ppc_fpr_NaN: + s.e = 2047; + s.m = 1; + break; + case ppc_fpr_Inf: + s.e = 2047; + s.m = 0; + break; + } + if (s.type == ppc_fpr_norm) { + d.m = ((uint64)(s.m))<<32; + } else { + d.m = s.m; + } +// ht_printf("dm: %qx\n", d.m); + ret |= ppc_fpu_pack_double(d, res); + return ret; +} + +inline uint32 ppc_fpu_double_to_int(ppc_double &d) +{ + switch (d.type) { + case ppc_fpr_norm: { + if (d.e < 0) { + switch (FPSCR_RN(gCPU.fpscr)) { + case FPSCR_RN_NEAR: + if (d.e < -1) { + return 0; + } else { + return d.s ? (uint32)-1 : 1; + } + case FPSCR_RN_ZERO: + return 0; + case FPSCR_RN_PINF: + if (d.s) { + return 0; + } else { + return 1; + } + case FPSCR_RN_MINF: + if (d.s) { + return (uint32)-1; + } else { + return 0; + } + } + } + if (d.e >= 31) { + if (d.s) { + return 0x80000000; + } else { + return 0x7fffffff; + } + } + int i=0; + uint64 mask = (1ULL<<(56 - d.e - 1))-1; + // we have to round + switch (FPSCR_RN(gCPU.fpscr)) { + case FPSCR_RN_NEAR: + if (d.m & mask) { + if (d.m & (1ULL<<(56 - d.e - 2))) { + i = 1; + } + } + break; + case FPSCR_RN_ZERO: + break; + case FPSCR_RN_PINF: + if (!d.s && (d.m & mask)) { + i = 1; + } + break; + case FPSCR_RN_MINF: + if (d.s && (d.m & mask)) { + i = 1; + } + break; + } + d.m >>= 56 - d.e - 1; + d.m += i; + return d.s ? -d.m : d.m; + } + case ppc_fpr_zero: + return 0; + case ppc_fpr_Inf: + case ppc_fpr_NaN: + if (d.s) { + return 0x80000000; + } else { + return 0x7fffffff; + } + } + return 0; +} + +double ppc_fpu_get_double(uint64 d); +double ppc_fpu_get_double(ppc_double &d); + +void ppc_opc_fabsx(); +void ppc_opc_faddx(); +void ppc_opc_faddsx(); +void ppc_opc_fcmpo(); +void ppc_opc_fcmpu(); +void ppc_opc_fctiwx(); +void ppc_opc_fctiwzx(); +void ppc_opc_fdivx(); +void ppc_opc_fdivsx(); +void ppc_opc_fmaddx(); +void ppc_opc_fmaddsx(); +void ppc_opc_fmrx(); +void ppc_opc_fmsubx(); +void ppc_opc_fmsubsx(); +void ppc_opc_fmulx(); +void ppc_opc_fmulsx(); +void ppc_opc_fnabsx(); +void ppc_opc_fnegx(); +void ppc_opc_fnmaddx(); +void ppc_opc_fnmaddsx(); +void ppc_opc_fnmsubx(); +void ppc_opc_fnmsubsx(); +void ppc_opc_fresx(); +void ppc_opc_frspx(); +void ppc_opc_frsqrtex(); +void ppc_opc_fselx(); +void ppc_opc_fsqrtx(); +void ppc_opc_fsqrtsx(); +void ppc_opc_fsubx(); +void ppc_opc_fsubsx(); + +#endif diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_mmu.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_mmu.cpp new file mode 100644 index 00000000..0891ecb1 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_mmu.cpp @@ -0,0 +1,2149 @@ +/* + * PearPC + * ppc_mmu.cc + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * Portions Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nmsu.edu) + * Portions Copyright (C) 2004 Apple Computer, Inc. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Pages marked: v.??? + * From: IBM PowerPC MicroProcessor Family: Altivec(tm) Technology... + * Programming Environments Manual + */ + +#include +#include +#include "system/arch/sysendian.h" +//#include "tools/snprintf.h" +#include "debug/tracers.h" +//#include "io/prom/prom.h" +#include "io/io.h" +#include "ppc_cpu.h" +#include "ppc_fpu.h" +#include "ppc_vec.h" +#include "ppc_mmu.h" +#include "ppc_exc.h" +#include "ppc_tools.h" + +#if 0 +byte *gMemory = NULL; +uint32 gMemorySize; +#endif + +#undef TLB + +static int ppc_pte_protection[] = { + // read(0)/write(1) key pp + + // read + 1, // r/w + 1, // r/w + 1, // r/w + 1, // r + 0, // - + 1, // r + 1, // r/w + 1, // r + + // write + 1, // r/w + 1, // r/w + 1, // r/w + 0, // r + 0, // - + 0, // r + 1, // r/w + 0, // r +}; + +inline int FASTCALL ppc_effective_to_physical(uint32 addr, int flags, uint32 &result) +{ + if (flags & PPC_MMU_CODE) { + if (!(gCPU.msr & MSR_IR)) { + result = addr; + return PPC_MMU_OK; + } + /* + * BAT translation .329 + */ + + uint32 batu = (gCPU.msr & MSR_PR ? BATU_Vp : BATU_Vs); + + for (int i=0; i<4; i++) { + uint32 bl17 = gCPU.ibat_bl17[i]; + uint32 addr2 = addr & (bl17 | 0xf001ffff); + if (BATU_BEPI(addr2) == BATU_BEPI(gCPU.ibatu[i])) { + // bat applies to this address + if (gCPU.ibatu[i] & batu) { + // bat entry valid + uint32 offset = BAT_EA_OFFSET(addr); + uint32 page = BAT_EA_11(addr); + page &= ~bl17; + page |= BATL_BRPN(gCPU.ibatl[i]); + // fixme: check access rights + result = page | offset; + return PPC_MMU_OK; + } + } + } + } else { + if (!(gCPU.msr & MSR_DR)) { + result = addr; + return PPC_MMU_OK; + } + /* + * BAT translation .329 + */ + + uint32 batu = (gCPU.msr & MSR_PR ? BATU_Vp : BATU_Vs); + + for (int i=0; i<4; i++) { + uint32 bl17 = gCPU.dbat_bl17[i]; + uint32 addr2 = addr & (bl17 | 0xf001ffff); + if (BATU_BEPI(addr2) == BATU_BEPI(gCPU.dbatu[i])) { + // bat applies to this address + if (gCPU.dbatu[i] & batu) { + // bat entry valid + uint32 offset = BAT_EA_OFFSET(addr); + uint32 page = BAT_EA_11(addr); + page &= ~bl17; + page |= BATL_BRPN(gCPU.dbatl[i]); + // fixme: check access rights + result = page | offset; + return PPC_MMU_OK; + } + } + } + } + + /* + * Address translation with segment register + */ + uint32 sr = gCPU.sr[EA_SR(addr)]; + + if (sr & SR_T) { + // woea + // FIXME: implement me + PPC_MMU_ERR("sr & T\n"); + } else { +#ifdef TLB + for (int i=0; i<4; i++) { + if ((addr & ~0xfff) == (gCPU.tlb_va[i])) { + gCPU.tlb_last = i; +// ht_printf("TLB: %d: %08x -> %08x\n", i, addr, gCPU.tlb_pa[i] | (addr & 0xfff)); + result = gCPU.tlb_pa[i] | (addr & 0xfff); + return PPC_MMU_OK; + } + } +#endif + // page address translation + if ((flags & PPC_MMU_CODE) && (sr & SR_N)) { + // segment isnt executable + if (!(flags & PPC_MMU_NO_EXC)) { + ppc_exception(PPC_EXC_ISI, PPC_EXC_SRR1_GUARD); + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; + } + uint32 offset = EA_Offset(addr); // 12 bit + uint32 page_index = EA_PageIndex(addr); // 16 bit + uint32 VSID = SR_VSID(sr); // 24 bit + uint32 api = EA_API(addr); // 6 bit (part of page_index) + // VSID.page_index = Virtual Page Number (VPN) + + // Hashfunction no 1 "xor" .360 + uint32 hash1 = (VSID ^ page_index); + uint32 pteg_addr = ((hash1 & gCPU.pagetable_hashmask)<<6) | gCPU.pagetable_base; + int key; + if (gCPU.msr & MSR_PR) { + key = (sr & SR_Kp) ? 4 : 0; + } else { + key = (sr & SR_Ks) ? 4 : 0; + } + + uint32 pte_protection_offset = ((flags&PPC_MMU_WRITE) ? 8:0) + key; + + for (int i=0; i<8; i++) { + uint32 pte; + if (ppc_read_physical_word(pteg_addr, pte)) { + if (!(flags & PPC_MMU_NO_EXC)) { + PPC_MMU_ERR("read physical in address translate failed\n"); + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; + } + if ((pte & PTE1_V) && (!(pte & PTE1_H))) { + if (VSID == PTE1_VSID(pte) && (api == PTE1_API(pte))) { + // page found + if (ppc_read_physical_word(pteg_addr+4, pte)) { + if (!(flags & PPC_MMU_NO_EXC)) { + PPC_MMU_ERR("read physical in address translate failed\n"); + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; + } + // check accessmode .346 + if (!ppc_pte_protection[pte_protection_offset + PTE2_PP(pte)]) { + if (!(flags & PPC_MMU_NO_EXC)) { + if (flags & PPC_MMU_CODE) { + PPC_MMU_WARN("correct impl? code + read protection\n"); + ppc_exception(PPC_EXC_ISI, PPC_EXC_SRR1_PROT, addr); + return PPC_MMU_EXC; + } else { + if (flags & PPC_MMU_WRITE) { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PROT | PPC_EXC_DSISR_STORE, addr); + } else { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PROT, addr); + } + return PPC_MMU_EXC; + } + } + return PPC_MMU_FATAL; + } + // ok.. + uint32 pap = PTE2_RPN(pte); + result = pap | offset; +#ifdef TLB + gCPU.tlb_last++; + gCPU.tlb_last &= 3; + gCPU.tlb_pa[gCPU.tlb_last] = pap; + gCPU.tlb_va[gCPU.tlb_last] = addr & ~0xfff; +// ht_printf("TLB: STORE %d: %08x -> %08x\n", gCPU.tlb_last, addr, pap); +#endif + // update access bits + if (flags & PPC_MMU_WRITE) { + pte |= PTE2_C | PTE2_R; + } else { + pte |= PTE2_R; + } + ppc_write_physical_word(pteg_addr+4, pte); + return PPC_MMU_OK; + } + } + pteg_addr+=8; + } + + // Hashfunction no 2 "not" .360 + hash1 = ~hash1; + pteg_addr = ((hash1 & gCPU.pagetable_hashmask)<<6) | gCPU.pagetable_base; + for (int i=0; i<8; i++) { + uint32 pte; + if (ppc_read_physical_word(pteg_addr, pte)) { + if (!(flags & PPC_MMU_NO_EXC)) { + PPC_MMU_ERR("read physical in address translate failed\n"); + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; + } + if ((pte & PTE1_V) && (pte & PTE1_H)) { + if (VSID == PTE1_VSID(pte) && (api == PTE1_API(pte))) { + // page found + if (ppc_read_physical_word(pteg_addr+4, pte)) { + if (!(flags & PPC_MMU_NO_EXC)) { + PPC_MMU_ERR("read physical in address translate failed\n"); + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; + } + // check accessmode + int key; + if (gCPU.msr & MSR_PR) { + key = (sr & SR_Kp) ? 4 : 0; + } else { + key = (sr & SR_Ks) ? 4 : 0; + } + if (!ppc_pte_protection[((flags&PPC_MMU_WRITE)?8:0) + key + PTE2_PP(pte)]) { + if (!(flags & PPC_MMU_NO_EXC)) { + if (flags & PPC_MMU_CODE) { + PPC_MMU_WARN("correct impl? code + read protection\n"); + ppc_exception(PPC_EXC_ISI, PPC_EXC_SRR1_PROT, addr); + return PPC_MMU_EXC; + } else { + if (flags & PPC_MMU_WRITE) { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PROT | PPC_EXC_DSISR_STORE, addr); + } else { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PROT, addr); + } + return PPC_MMU_EXC; + } + } + return PPC_MMU_FATAL; + } + // ok.. + result = PTE2_RPN(pte) | offset; + + // update access bits + if (flags & PPC_MMU_WRITE) { + pte |= PTE2_C | PTE2_R; + } else { + pte |= PTE2_R; + } + ppc_write_physical_word(pteg_addr+4, pte); +// PPC_MMU_WARN("hash function 2 used!\n"); +// gSinglestep = true; + return PPC_MMU_OK; + } + } + pteg_addr+=8; + } + } + // page fault + if (!(flags & PPC_MMU_NO_EXC)) { + if (flags & PPC_MMU_CODE) { + ppc_exception(PPC_EXC_ISI, PPC_EXC_SRR1_PAGE); + } else { + if (flags & PPC_MMU_WRITE) { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PAGE | PPC_EXC_DSISR_STORE, addr); + } else { + ppc_exception(PPC_EXC_DSI, PPC_EXC_DSISR_PAGE, addr); + } + } + return PPC_MMU_EXC; + } + return PPC_MMU_FATAL; +} + +void ppc_mmu_tlb_invalidate() +{ + gCPU.effective_code_page = 0xffffffff; +} + +/* +pagetable: +min. 2^10 (64k) PTEGs +PTEG = 64byte +The page table can be any size 2^n where 16 <= n <= 25. + +A PTEG contains eight +PTEs of eight bytes each; therefore, each PTEG is 64 bytes long. +*/ + +bool FASTCALL ppc_mmu_set_sdr1(uint32 newval, bool quiesce) +{ + /* if (newval == gCPU.sdr1)*/ quiesce = false; + PPC_MMU_TRACE("new pagetable: sdr1 = 0x%08x\n", newval); + uint32 htabmask = SDR1_HTABMASK(newval); + uint32 x = 1; + uint32 xx = 0; + int n = 0; + while ((htabmask & x) && (n < 9)) { + n++; + xx|=x; + x<<=1; + } + if (htabmask & ~xx) { + PPC_MMU_TRACE("new pagetable: broken htabmask (%05x)\n", htabmask); + return false; + } + uint32 htaborg = SDR1_HTABORG(newval); + if (htaborg & xx) { + PPC_MMU_TRACE("new pagetable: broken htaborg (%05x)\n", htaborg); + return false; + } + gCPU.pagetable_base = htaborg<<16; + gCPU.sdr1 = newval; + gCPU.pagetable_hashmask = ((xx<<10)|0x3ff); + PPC_MMU_TRACE("new pagetable: sdr1 accepted\n"); + PPC_MMU_TRACE("number of pages: 2^%d pagetable_start: 0x%08x size: 2^%d\n", n+13, gCPU.pagetable_base, n+16); +#if 0 + if (quiesce) { + prom_quiesce(); + } +#endif + return true; +} + +bool FASTCALL ppc_mmu_page_create(uint32 ea, uint32 pa) +{ + uint32 sr = gCPU.sr[EA_SR(ea)]; + uint32 page_index = EA_PageIndex(ea); // 16 bit + uint32 VSID = SR_VSID(sr); // 24 bit + uint32 api = EA_API(ea); // 6 bit (part of page_index) + uint32 hash1 = (VSID ^ page_index); + uint32 pte, pte2; + uint32 h = 0; + for (int j=0; j<2; j++) { + uint32 pteg_addr = ((hash1 & gCPU.pagetable_hashmask)<<6) | gCPU.pagetable_base; + for (int i=0; i<8; i++) { + if (ppc_read_physical_word(pteg_addr, pte)) { + PPC_MMU_ERR("read physical in address translate failed\n"); + return false; + } + if (!(pte & PTE1_V)) { + // free pagetable entry found + pte = PTE1_V | (VSID << 7) | h | api; + pte2 = (PA_RPN(pa) << 12) | 0; + if (ppc_write_physical_word(pteg_addr, pte) + || ppc_write_physical_word(pteg_addr+4, pte2)) { + return false; + } else { + // ok + return true; + } + } + pteg_addr+=8; + } + hash1 = ~hash1; + h = PTE1_H; + } + return false; +} + +inline bool FASTCALL ppc_mmu_page_free(uint32 ea) +{ + return true; +} + +extern bool uae_ppc_direct_physical_memory_handle(uint32, byte *&ptr); + +inline int FASTCALL ppc_direct_physical_memory_handle(uint32 addr, byte *&ptr) +{ + if (uae_ppc_direct_physical_memory_handle(addr, ptr)) + return PPC_MMU_OK; + return PPC_MMU_FATAL; +} + +int FASTCALL ppc_direct_effective_memory_handle(uint32 addr, byte *&ptr) +{ + uint32 ea; + int r; + if (!((r = ppc_effective_to_physical(addr, PPC_MMU_READ, ea)))) { + return ppc_direct_physical_memory_handle(ea, ptr); + } + return r; +} + +int FASTCALL ppc_direct_effective_memory_handle_code(uint32 addr, byte *&ptr) +{ + uint32 ea; + int r; + if (!((r = ppc_effective_to_physical(addr, PPC_MMU_READ | PPC_MMU_CODE, ea)))) { + return ppc_direct_physical_memory_handle(ea, ptr); + } + return r; +} + +inline int FASTCALL ppc_read_physical_qword(uint32 addr, Vector_t &result) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + VECT_D(result,0) = ppc_dword_from_BE(*((uint64*)(gMemory+addr))); + VECT_D(result,1) = ppc_dword_from_BE(*((uint64*)(gMemory+addr+8))); + return PPC_MMU_OK; + } +#endif + return io_mem_read128(addr, (uint128 *)&result); +} + +inline int FASTCALL ppc_read_physical_dword(uint32 addr, uint64 &result) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + result = ppc_dword_from_BE(*((uint64*)(gMemory+addr))); + return PPC_MMU_OK; + } +#endif + int ret = io_mem_read64(addr, result); + //result = ppc_bswap_dword(result); + return ret; +} + +inline int FASTCALL ppc_read_physical_word(uint32 addr, uint32 &result) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + result = ppc_word_from_BE(*((uint32*)(gMemory+addr))); + return PPC_MMU_OK; + } +#endif + int ret = io_mem_read(addr, result, 4); + //result = ppc_bswap_word(result); + return ret; +} + +inline int FASTCALL ppc_read_physical_half(uint32 addr, uint16 &result) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + result = ppc_half_from_BE(*((uint16*)(gMemory+addr))); + return PPC_MMU_OK; + } +#endif + uint32 r; + int ret = io_mem_read(addr, r, 2); + //result = ppc_bswap_half(r); + result = r; + return ret; +} + +inline int FASTCALL ppc_read_physical_byte(uint32 addr, uint8 &result) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + result = gMemory[addr]; + return PPC_MMU_OK; + } +#endif + uint32 r; + int ret = io_mem_read(addr, r, 1); + result = r; + return ret; +} + +inline int FASTCALL ppc_read_effective_code(uint32 addr, uint32 &result) +{ + if (addr & 3) { + // EXC..bla + return PPC_MMU_FATAL; + } + uint32 p; + int r; + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_READ | PPC_MMU_CODE, p)))) { + return ppc_read_physical_word(p, result); + } + return r; +} + +inline int FASTCALL ppc_read_effective_qword(uint32 addr, Vector_t &result) +{ + uint32 p; + int r; + + addr &= ~0x0f; + + if (!(r = ppc_effective_to_physical(addr, PPC_MMU_READ, p))) { + return ppc_read_physical_qword(p, result); + } + + return r; +} + +inline int FASTCALL ppc_read_effective_dword(uint32 addr, uint64 &result) +{ + uint32 p; + int r; + if (!(r = ppc_effective_to_physical(addr, PPC_MMU_READ, p))) { + if (EA_Offset(addr) > 4088) { + // read overlaps two pages.. tricky + byte *r1, *r2; + byte b[14]; + ppc_effective_to_physical((addr & ~0xfff)+4089, PPC_MMU_READ, p); + if ((r = ppc_direct_physical_memory_handle(p, r1))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_READ, p))) return r; + if ((r = ppc_direct_physical_memory_handle(p, r2))) return r; + memmove(&b[0], r1, 7); + memmove(&b[7], r2, 7); + memmove(&result, &b[EA_Offset(addr)-4089], 8); + result = ppc_dword_from_BE(result); + return PPC_MMU_OK; + } else { + return ppc_read_physical_dword(p, result); + } + } + return r; +} + +inline int FASTCALL ppc_read_effective_word(uint32 addr, uint32 &result) +{ + uint32 p; + int r; + if (!(r = ppc_effective_to_physical(addr, PPC_MMU_READ, p))) { + if (EA_Offset(addr) > 4092) { + // read overlaps two pages.. tricky + byte *r1, *r2; + byte b[6]; + ppc_effective_to_physical((addr & ~0xfff)+4093, PPC_MMU_READ, p); + if ((r = ppc_direct_physical_memory_handle(p, r1))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_READ, p))) return r; + if ((r = ppc_direct_physical_memory_handle(p, r2))) return r; + memmove(&b[0], r1, 3); + memmove(&b[3], r2, 3); + memmove(&result, &b[EA_Offset(addr)-4093], 4); + result = ppc_word_from_BE(result); + return PPC_MMU_OK; + } else { + return ppc_read_physical_word(p, result); + } + } + return r; +} + +inline int FASTCALL ppc_read_effective_half(uint32 addr, uint16 &result) +{ + uint32 p; + int r; + if (!((r = ppc_effective_to_physical(addr, PPC_MMU_READ, p)))) { + if (EA_Offset(addr) > 4094) { + // read overlaps two pages.. tricky + byte b1, b2; + ppc_effective_to_physical((addr & ~0xfff)+4095, PPC_MMU_READ, p); + if ((r = ppc_read_physical_byte(p, b1))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_READ, p))) return r; + if ((r = ppc_read_physical_byte(p, b2))) return r; + result = (b1<<8)|b2; + return PPC_MMU_OK; + } else { + return ppc_read_physical_half(p, result); + } + } + return r; +} + +inline int FASTCALL ppc_read_effective_byte(uint32 addr, uint8 &result) +{ + uint32 p; + int r; + if (!((r = ppc_effective_to_physical(addr, PPC_MMU_READ, p)))) { + return ppc_read_physical_byte(p, result); + } + return r; +} + +inline int FASTCALL ppc_write_physical_qword(uint32 addr, Vector_t data) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + *((uint64*)(gMemory+addr)) = ppc_dword_to_BE(VECT_D(data,0)); + *((uint64*)(gMemory+addr+8)) = ppc_dword_to_BE(VECT_D(data,1)); + return PPC_MMU_OK; + } +#endif + if (io_mem_write128(addr, (uint128 *)&data) == IO_MEM_ACCESS_OK) { + return PPC_MMU_OK; + } else { + return PPC_MMU_FATAL; + } +} + +inline int FASTCALL ppc_write_physical_dword(uint32 addr, uint64 data) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + *((uint64*)(gMemory+addr)) = ppc_dword_to_BE(data); + return PPC_MMU_OK; + } +#endif +// if (io_mem_write64(addr, ppc_bswap_dword(data)) == IO_MEM_ACCESS_OK) { + if (io_mem_write64(addr, data) == IO_MEM_ACCESS_OK) { + return PPC_MMU_OK; + } else { + return PPC_MMU_FATAL; + } +} + +inline int FASTCALL ppc_write_physical_word(uint32 addr, uint32 data) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + *((uint32*)(gMemory+addr)) = ppc_word_to_BE(data); + return PPC_MMU_OK; + } +#endif + //return io_mem_write(addr, ppc_bswap_word(data), 4); + return io_mem_write(addr, data, 4); +} + +inline int FASTCALL ppc_write_physical_half(uint32 addr, uint16 data) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + *((uint16*)(gMemory+addr)) = ppc_half_to_BE(data); + return PPC_MMU_OK; + } +#endif + //return io_mem_write(addr, ppc_bswap_half(data), 2); + return io_mem_write(addr, data, 2); +} + +inline int FASTCALL ppc_write_physical_byte(uint32 addr, uint8 data) +{ +#if 0 + if (addr < gMemorySize) { + // big endian + gMemory[addr] = data; + return PPC_MMU_OK; + } +#endif + return io_mem_write(addr, data, 1); +} + +inline int FASTCALL ppc_write_effective_qword(uint32 addr, Vector_t data) +{ + uint32 p; + int r; + + addr &= ~0x0f; + + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_WRITE, p)))) { + return ppc_write_physical_qword(p, data); + } + return r; +} + +inline int FASTCALL ppc_write_effective_dword(uint32 addr, uint64 data) +{ + uint32 p; + int r; + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_WRITE, p)))) { + if (EA_Offset(addr) > 4088) { + // write overlaps two pages.. tricky + byte *r1, *r2; + byte b[14]; + ppc_effective_to_physical((addr & ~0xfff)+4089, PPC_MMU_WRITE, p); + if ((r = ppc_direct_physical_memory_handle(p, r1))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_WRITE, p))) return r; + if ((r = ppc_direct_physical_memory_handle(p, r2))) return r; + data = ppc_dword_to_BE(data); + memmove(&b[0], r1, 7); + memmove(&b[7], r2, 7); + memmove(&b[EA_Offset(addr)-4089], &data, 8); + memmove(r1, &b[0], 7); + memmove(r2, &b[7], 7); + return PPC_MMU_OK; + } else { + return ppc_write_physical_dword(p, data); + } + } + return r; +} + +inline int FASTCALL ppc_write_effective_word(uint32 addr, uint32 data) +{ + uint32 p; + int r; + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_WRITE, p)))) { + if (EA_Offset(addr) > 4092) { + // write overlaps two pages.. tricky + byte *r1, *r2; + byte b[6]; + ppc_effective_to_physical((addr & ~0xfff)+4093, PPC_MMU_WRITE, p); + if ((r = ppc_direct_physical_memory_handle(p, r1))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_WRITE, p))) return r; + if ((r = ppc_direct_physical_memory_handle(p, r2))) return r; + data = ppc_word_to_BE(data); + memmove(&b[0], r1, 3); + memmove(&b[3], r2, 3); + memmove(&b[EA_Offset(addr)-4093], &data, 4); + memmove(r1, &b[0], 3); + memmove(r2, &b[3], 3); + return PPC_MMU_OK; + } else { + return ppc_write_physical_word(p, data); + } + } + return r; +} + +inline int FASTCALL ppc_write_effective_half(uint32 addr, uint16 data) +{ + uint32 p; + int r; + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_WRITE, p)))) { + if (EA_Offset(addr) > 4094) { + // write overlaps two pages.. tricky + ppc_effective_to_physical((addr & ~0xfff)+4095, PPC_MMU_WRITE, p); + if ((r = ppc_write_physical_byte(p, data>>8))) return r; + if ((r = ppc_effective_to_physical((addr & ~0xfff)+4096, PPC_MMU_WRITE, p))) return r; + if ((r = ppc_write_physical_byte(p, data))) return r; + return PPC_MMU_OK; + } else { + return ppc_write_physical_half(p, data); + } + } + return r; +} + +inline int FASTCALL ppc_write_effective_byte(uint32 addr, uint8 data) +{ + uint32 p; + int r; + if (!((r=ppc_effective_to_physical(addr, PPC_MMU_WRITE, p)))) { + return ppc_write_physical_byte(p, data); + } + return r; +} + +bool FASTCALL ppc_init_physical_memory(uint size) +{ +#if 0 + if (size < 64*1024*1024) { + PPC_MMU_ERR("Main memory size must >= 64MB!\n"); + } + gMemory = (byte*)malloc(size); + gMemorySize = size; + return gMemory != NULL; +#else + return NULL; +#endif +} + +uint32 ppc_get_memory_size() +{ + return 0; //return gMemorySize; +} + +/*************************************************************************** + * DMA Interface + */ + +bool ppc_dma_write(uint32 dest, const void *src, uint32 size) +{ +#if 0 + if (dest > gMemorySize || (dest+size) > gMemorySize) return false; +#endif + byte *ptr; + ppc_direct_physical_memory_handle(dest, ptr); + + memcpy(ptr, src, size); + return true; +} + +bool ppc_dma_read(void *dest, uint32 src, uint32 size) +{ +#if 0 + if (src > gMemorySize || (src + size) > gMemorySize) return false; +#endif + byte *ptr; + ppc_direct_physical_memory_handle(src, ptr); + + memcpy(dest, ptr, size); + return true; +} + +bool ppc_dma_set(uint32 dest, int c, uint32 size) +{ +#if 0 + if (dest > gMemorySize || (dest+size) > gMemorySize) return false; +#endif + byte *ptr; + ppc_direct_physical_memory_handle(dest, ptr); + + memset(ptr, c, size); + return true; +} + +/*************************************************************************** + * MMU Opcodes + */ + +#include "ppc_dec.h" + +/* + * dcbz Data Cache Clear to Zero + * .464 + */ +void ppc_opc_dcbz() +{ + //PPC_L1_CACHE_LINE_SIZE + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + // assert rD=0 + uint32 a = (rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]; + // BAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + ppc_write_effective_dword(a, 0) + || ppc_write_effective_dword(a+8, 0) + || ppc_write_effective_dword(a+16, 0) + || ppc_write_effective_dword(a+24, 0); +} + +/* + * lbz Load Byte and Zero + * .521 + */ +void ppc_opc_lbz() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint8 r; + int ret = ppc_read_effective_byte((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} +/* + * lbzu Load Byte and Zero with Update + * .522 + */ +void ppc_opc_lbzu() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + // FIXME: check rA!=0 && rA!=rD + uint8 r; + int ret = ppc_read_effective_byte(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + gCPU.gpr[rD] = r; + } +} +/* + * lbzux Load Byte and Zero with Update Indexed + * .523 + */ +void ppc_opc_lbzux() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + // FIXME: check rA!=0 && rA!=rD + uint8 r; + int ret = ppc_read_effective_byte(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + gCPU.gpr[rD] = r; + } +} +/* + * lbzx Load Byte and Zero Indexed + * .524 + */ +void ppc_opc_lbzx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint8 r; + int ret = ppc_read_effective_byte((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} +/* + * lfd Load Floating-Point Double + * .530 + */ +void ppc_opc_lfd() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frD, rA, imm); + uint64 r; + int ret = ppc_read_effective_dword((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.fpr[frD] = r; + } +} +/* + * lfdu Load Floating-Point Double with Update + * .531 + */ +void ppc_opc_lfdu() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frD, rA, imm); + // FIXME: check rA!=0 + uint64 r; + int ret = ppc_read_effective_dword(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.fpr[frD] = r; + gCPU.gpr[rA] += imm; + } +} +/* + * lfdux Load Floating-Point Double with Update Indexed + * .532 + */ +void ppc_opc_lfdux() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, rB); + // FIXME: check rA!=0 + uint64 r; + int ret = ppc_read_effective_dword(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + gCPU.fpr[frD] = r; + } +} +/* + * lfdx Load Floating-Point Double Indexed + * .533 + */ +void ppc_opc_lfdx() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, rB); + uint64 r; + int ret = ppc_read_effective_dword((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.fpr[frD] = r; + } +} +/* + * lfs Load Floating-Point Single + * .534 + */ +void ppc_opc_lfs() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frD, rA, imm); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + ppc_single s; + ppc_double d; + ppc_fpu_unpack_single(s, r); + ppc_fpu_single_to_double(s, d); + ppc_fpu_pack_double(d, gCPU.fpr[frD]); + } +} +/* + * lfsu Load Floating-Point Single with Update + * .535 + */ +void ppc_opc_lfsu() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frD, rA, imm); + // FIXME: check rA!=0 + uint32 r; + int ret = ppc_read_effective_word(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + ppc_single s; + ppc_double d; + ppc_fpu_unpack_single(s, r); + ppc_fpu_single_to_double(s, d); + ppc_fpu_pack_double(d, gCPU.fpr[frD]); + gCPU.gpr[rA] += imm; + } +} +/* + * lfsux Load Floating-Point Single with Update Indexed + * .536 + */ +void ppc_opc_lfsux() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, rB); + // FIXME: check rA!=0 + uint32 r; + int ret = ppc_read_effective_word(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + ppc_single s; + ppc_double d; + ppc_fpu_unpack_single(s, r); + ppc_fpu_single_to_double(s, d); + ppc_fpu_pack_double(d, gCPU.fpr[frD]); + } +} +/* + * lfsx Load Floating-Point Single Indexed + * .537 + */ +void ppc_opc_lfsx() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, rB); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + ppc_single s; + ppc_double d; + ppc_fpu_unpack_single(s, r); + ppc_fpu_single_to_double(s, d); + ppc_fpu_pack_double(d, gCPU.fpr[frD]); + } +} +/* + * lha Load Half Word Algebraic + * .538 + */ +void ppc_opc_lha() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint16 r; + int ret = ppc_read_effective_half((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = (r&0x8000)?(r|0xffff0000):r; + } +} +/* + * lhau Load Half Word Algebraic with Update + * .539 + */ +void ppc_opc_lhau() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint16 r; + // FIXME: rA != 0 + int ret = ppc_read_effective_half(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + gCPU.gpr[rD] = (r&0x8000)?(r|0xffff0000):r; + } +} +/* + * lhaux Load Half Word Algebraic with Update Indexed + * .540 + */ +void ppc_opc_lhaux() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint16 r; + // FIXME: rA != 0 + int ret = ppc_read_effective_half(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + gCPU.gpr[rD] = (r&0x8000)?(r|0xffff0000):r; + } +} +/* + * lhax Load Half Word Algebraic Indexed + * .541 + */ +void ppc_opc_lhax() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint16 r; + // FIXME: rA != 0 + int ret = ppc_read_effective_half((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = (r&0x8000) ? (r|0xffff0000):r; + } +} +/* + * lhbrx Load Half Word Byte-Reverse Indexed + * .542 + */ +void ppc_opc_lhbrx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint16 r; + int ret = ppc_read_effective_half((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = ppc_bswap_half(r); + } +} +/* + * lhz Load Half Word and Zero + * .543 + */ +void ppc_opc_lhz() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint16 r; + int ret = ppc_read_effective_half((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} +/* + * lhzu Load Half Word and Zero with Update + * .544 + */ +void ppc_opc_lhzu() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint16 r; + // FIXME: rA!=0 + int ret = ppc_read_effective_half(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + gCPU.gpr[rA] += imm; + } +} +/* + * lhzux Load Half Word and Zero with Update Indexed + * .545 + */ +void ppc_opc_lhzux() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint16 r; + // FIXME: rA != 0 + int ret = ppc_read_effective_half(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + gCPU.gpr[rD] = r; + } +} +/* + * lhzx Load Half Word and Zero Indexed + * .546 + */ +void ppc_opc_lhzx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint16 r; + int ret = ppc_read_effective_half((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} +/* + * lmw Load Multiple Word + * .547 + */ +void ppc_opc_lmw() +{ + int rD, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint32 ea = (rA ? gCPU.gpr[rA] : 0) + imm; + while (rD <= 31) { + if (ppc_read_effective_word(ea, gCPU.gpr[rD])) { + return; + } + rD++; + ea += 4; + } +} +/* + * lswi Load String Word Immediate + * .548 + */ +void ppc_opc_lswi() +{ + int rA, rD, NB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, NB); + if (NB==0) NB=32; + uint32 ea = rA ? gCPU.gpr[rA] : 0; + uint32 r = 0; + int i = 4; + uint8 v; + while (NB > 0) { + if (!i) { + i = 4; + gCPU.gpr[rD] = r; + rD++; + rD%=32; + r = 0; + } + if (ppc_read_effective_byte(ea, v)) { + return; + } + r<<=8; + r|=v; + ea++; + i--; + NB--; + } + while (i) { r<<=8; i--; } + gCPU.gpr[rD] = r; +} +/* + * lswx Load String Word Indexed + * .550 + */ +void ppc_opc_lswx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + int NB = XER_n(gCPU.xer); + uint32 ea = gCPU.gpr[rB] + (rA ? gCPU.gpr[rA] : 0); + + uint32 r = 0; + int i = 4; + uint8 v; + while (NB > 0) { + if (!i) { + i = 4; + gCPU.gpr[rD] = r; + rD++; + rD%=32; + r = 0; + } + if (ppc_read_effective_byte(ea, v)) { + return; + } + r<<=8; + r|=v; + ea++; + i--; + NB--; + } + while (i) { r<<=8; i--; } + gCPU.gpr[rD] = r; +} +/* + * lwarx Load Word and Reserve Indexed + * .553 + */ +void ppc_opc_lwarx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + gCPU.reserve = r; + gCPU.have_reservation = 1; + } +} +/* + * lwbrx Load Word Byte-Reverse Indexed + * .556 + */ +void ppc_opc_lwbrx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = ppc_bswap_word(r); + } +} +/* + * lwz Load Word and Zero + * .557 + */ +void ppc_opc_lwz() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} +/* + * lbzu Load Word and Zero with Update + * .558 + */ +void ppc_opc_lwzu() +{ + int rA, rD; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rD, rA, imm); + // FIXME: check rA!=0 && rA!=rD + uint32 r; + int ret = ppc_read_effective_word(gCPU.gpr[rA]+imm, r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + gCPU.gpr[rD] = r; + } +} +/* + * lwzux Load Word and Zero with Update Indexed + * .559 + */ +void ppc_opc_lwzux() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + // FIXME: check rA!=0 && rA!=rD + uint32 r; + int ret = ppc_read_effective_word(gCPU.gpr[rA]+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + gCPU.gpr[rD] = r; + } +} +/* + * lwzx Load Word and Zero Indexed + * .560 + */ +void ppc_opc_lwzx() +{ + int rA, rD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + uint32 r; + int ret = ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], r); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rD] = r; + } +} + +/* lvx Load Vector Indexed + * v.127 + */ +void ppc_opc_lvx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + Vector_t r; + + int ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]); + + int ret = ppc_read_effective_qword(ea, r); + if (ret == PPC_MMU_OK) { + gCPU.vr[vrD] = r; + } +} + +/* lvxl Load Vector Index LRU + * v.128 + */ +void ppc_opc_lvxl() +{ + ppc_opc_lvx(); + /* This instruction should hint to the cache that the value won't be + * needed again in memory anytime soon. We don't emulate the cache, + * so this is effectively exactly the same as lvx. + */ +} + +/* lvebx Load Vector Element Byte Indexed + * v.119 + */ +void ppc_opc_lvebx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + uint32 ea; + uint8 r; + ea = (rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]; + int ret = ppc_read_effective_byte(ea, r); + if (ret == PPC_MMU_OK) { + VECT_B(gCPU.vr[vrD], ea & 0xf) = r; + } +} + +/* lvehx Load Vector Element Half Word Indexed + * v.121 + */ +void ppc_opc_lvehx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + uint32 ea; + uint16 r; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]) & ~1; + int ret = ppc_read_effective_half(ea, r); + if (ret == PPC_MMU_OK) { + VECT_H(gCPU.vr[vrD], (ea & 0xf) >> 1) = r; + } +} + +/* lvewx Load Vector Element Word Indexed + * v.122 + */ +void ppc_opc_lvewx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + uint32 ea; + uint32 r; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]) & ~3; + int ret = ppc_read_effective_word(ea, r); + if (ret == PPC_MMU_OK) { + VECT_W(gCPU.vr[vrD], (ea & 0xf) >> 2) = r; + } +} + +#if HOST_ENDIANESS == HOST_ENDIANESS_LE +static byte lvsl_helper[] = { + 0x1F, 0x1E, 0x1D, 0x1C, 0x1B, 0x1A, 0x19, 0x18, + 0x17, 0x16, 0x15, 0x14, 0x13, 0x12, 0x11, 0x10, + 0x0F, 0x0E, 0x0D, 0x0C, 0x0B, 0x0A, 0x09, 0x08, + 0x07, 0x06, 0x05, 0x04, 0x03, 0x02, 0x01, 0x00 +}; +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE +static byte lvsl_helper[] = { + 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, + 0x08, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, + 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, + 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F +}; +#else +#error Endianess not supported! +#endif + +/* + * lvsl Load Vector for Shift Left + * v.123 + */ +void ppc_opc_lvsl() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + uint32 ea; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]); +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + memmove(&gCPU.vr[vrD], lvsl_helper+0x10-(ea & 0xf), 16); +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE + memmove(&gCPU.vr[vrD], lvsl_helper+(ea & 0xf), 16); +#else +#error Endianess not supported! +#endif +} + +/* + * lvsr Load Vector for Shift Right + * v.125 + */ +void ppc_opc_lvsr() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrD, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, rA, rB); + uint32 ea; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]); +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + memmove(&gCPU.vr[vrD], lvsl_helper+(ea & 0xf), 16); +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE + memmove(&gCPU.vr[vrD], lvsl_helper+0x10-(ea & 0xf), 16); +#else +#error Endianess not supported! +#endif +} + +/* + * dst Data Stream Touch + * v.115 + */ +void ppc_opc_dst() +{ + VECTOR_DEBUG; + /* Since we are not emulating the cache, this is a nop */ +} + +/* + * stb Store Byte + * .632 + */ +void ppc_opc_stb() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + ppc_write_effective_byte((rA?gCPU.gpr[rA]:0)+imm, (uint8)gCPU.gpr[rS]) != PPC_MMU_FATAL; +} +/* + * stbu Store Byte with Update + * .633 + */ +void ppc_opc_stbu() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + // FIXME: check rA!=0 + int ret = ppc_write_effective_byte(gCPU.gpr[rA]+imm, (uint8)gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + } +} +/* + * stbux Store Byte with Update Indexed + * .634 + */ +void ppc_opc_stbux() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rA!=0 + int ret = ppc_write_effective_byte(gCPU.gpr[rA]+gCPU.gpr[rB], (uint8)gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + } +} +/* + * stbx Store Byte Indexed + * .635 + */ +void ppc_opc_stbx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + ppc_write_effective_byte((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], (uint8)gCPU.gpr[rS]) != PPC_MMU_FATAL; +} +/* + * stfd Store Floating-Point Double + * .642 + */ +void ppc_opc_stfd() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frS, rA, imm); + ppc_write_effective_dword((rA?gCPU.gpr[rA]:0)+imm, gCPU.fpr[frS]) != PPC_MMU_FATAL; +} +/* + * stfdu Store Floating-Point Double with Update + * .643 + */ +void ppc_opc_stfdu() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frS, rA, imm); + // FIXME: check rA!=0 + int ret = ppc_write_effective_dword(gCPU.gpr[rA]+imm, gCPU.fpr[frS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + } +} +/* + * stfd Store Floating-Point Double with Update Indexed + * .644 + */ +void ppc_opc_stfdux() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frS, rA, rB); + // FIXME: check rA!=0 + int ret = ppc_write_effective_dword(gCPU.gpr[rA]+gCPU.gpr[rB], gCPU.fpr[frS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + } +} +/* + * stfdx Store Floating-Point Double Indexed + * .645 + */ +void ppc_opc_stfdx() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frS, rA, rB); + ppc_write_effective_dword((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], gCPU.fpr[frS]) != PPC_MMU_FATAL; +} +/* + * stfiwx Store Floating-Point as Integer Word Indexed + * .646 + */ +void ppc_opc_stfiwx() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frS, rA, rB); + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], (uint32)gCPU.fpr[frS]) != PPC_MMU_FATAL; +} +/* + * stfs Store Floating-Point Single + * .647 + */ +void ppc_opc_stfs() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frS, rA, imm); + uint32 s; + ppc_double d; + ppc_fpu_unpack_double(d, gCPU.fpr[frS]); + ppc_fpu_pack_single(d, s); + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+imm, s) != PPC_MMU_FATAL; +} +/* + * stfsu Store Floating-Point Single with Update + * .648 + */ +void ppc_opc_stfsu() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, frS, rA, imm); + // FIXME: check rA!=0 + uint32 s; + ppc_double d; + ppc_fpu_unpack_double(d, gCPU.fpr[frS]); + ppc_fpu_pack_single(d, s); + int ret = ppc_write_effective_word(gCPU.gpr[rA]+imm, s); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + } +} +/* + * stfsux Store Floating-Point Single with Update Indexed + * .649 + */ +void ppc_opc_stfsux() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frS, rA, rB); + // FIXME: check rA!=0 + uint32 s; + ppc_double d; + ppc_fpu_unpack_double(d, gCPU.fpr[frS]); + ppc_fpu_pack_single(d, s); + int ret = ppc_write_effective_word(gCPU.gpr[rA]+gCPU.gpr[rB], s); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + } +} +/* + * stfsx Store Floating-Point Single Indexed + * .650 + */ +void ppc_opc_stfsx() +{ + if ((gCPU.msr & MSR_FP) == 0) { + ppc_exception(PPC_EXC_NO_FPU); + return; + } + int rA, frS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frS, rA, rB); + uint32 s; + ppc_double d; + ppc_fpu_unpack_double(d, gCPU.fpr[frS]); + ppc_fpu_pack_single(d, s); + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], s) != PPC_MMU_FATAL; +} +/* + * sth Store Half Word + * .651 + */ +void ppc_opc_sth() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + ppc_write_effective_half((rA?gCPU.gpr[rA]:0)+imm, (uint16)gCPU.gpr[rS]) != PPC_MMU_FATAL; +} +/* + * sthbrx Store Half Word Byte-Reverse Indexed + * .652 + */ +void ppc_opc_sthbrx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + ppc_write_effective_half((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], ppc_bswap_half(gCPU.gpr[rS])) != PPC_MMU_FATAL; +} +/* + * sthu Store Half Word with Update + * .653 + */ +void ppc_opc_sthu() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + // FIXME: check rA!=0 + int ret = ppc_write_effective_half(gCPU.gpr[rA]+imm, (uint16)gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + } +} +/* + * sthux Store Half Word with Update Indexed + * .654 + */ +void ppc_opc_sthux() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rA!=0 + int ret = ppc_write_effective_half(gCPU.gpr[rA]+gCPU.gpr[rB], (uint16)gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + } +} +/* + * sthx Store Half Word Indexed + * .655 + */ +void ppc_opc_sthx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + ppc_write_effective_half((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], (uint16)gCPU.gpr[rS]) != PPC_MMU_FATAL; +} +/* + * stmw Store Multiple Word + * .656 + */ +void ppc_opc_stmw() +{ + int rS, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + uint32 ea = (rA ? gCPU.gpr[rA] : 0) + imm; + while (rS <= 31) { + if (ppc_write_effective_word(ea, gCPU.gpr[rS])) { + return; + } + rS++; + ea += 4; + } +} +/* + * stswi Store String Word Immediate + * .657 + */ +void ppc_opc_stswi() +{ + int rA, rS, NB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, NB); + if (NB==0) NB=32; + uint32 ea = rA ? gCPU.gpr[rA] : 0; + uint32 r = 0; + int i = 0; + + while (NB > 0) { + if (!i) { + r = gCPU.gpr[rS]; + rS++; + rS%=32; + i = 4; + } + if (ppc_write_effective_byte(ea, (r>>24))) { + return; + } + r<<=8; + ea++; + i--; + NB--; + } +} +/* + * stswx Store String Word Indexed + * .658 + */ +void ppc_opc_stswx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + int NB = XER_n(gCPU.xer); + uint32 ea = gCPU.gpr[rB] + (rA ? gCPU.gpr[rA] : 0); + uint32 r = 0; + int i = 0; + + while (NB > 0) { + if (!i) { + r = gCPU.gpr[rS]; + rS++; + rS%=32; + i = 4; + } + if (ppc_write_effective_byte(ea, (r>>24))) { + return; + } + r<<=8; + ea++; + i--; + NB--; + } +} +/* + * stw Store Word + * .659 + */ +void ppc_opc_stw() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+imm, gCPU.gpr[rS]) != PPC_MMU_FATAL; +} +/* + * stwbrx Store Word Byte-Reverse Indexed + * .660 + */ +void ppc_opc_stwbrx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: doppelt gemoppelt + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], ppc_bswap_word(gCPU.gpr[rS])) != PPC_MMU_FATAL; +} +/* + * stwcx. Store Word Conditional Indexed + * .661 + */ +void ppc_opc_stwcx_() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + gCPU.cr &= 0x0fffffff; + if (gCPU.have_reservation) { + gCPU.have_reservation = false; + uint32 v; + if (ppc_read_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], v)) { + return; + } + if (v==gCPU.reserve) { + if (ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], gCPU.gpr[rS])) { + return; + } + gCPU.cr |= CR_CR0_EQ; + } + if (gCPU.xer & XER_SO) { + gCPU.cr |= CR_CR0_SO; + } + } +} +/* + * stwu Store Word with Update + * .663 + */ +void ppc_opc_stwu() +{ + int rA, rS; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, rS, rA, imm); + // FIXME: check rA!=0 + int ret = ppc_write_effective_word(gCPU.gpr[rA]+imm, gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += imm; + } +} +/* + * stwux Store Word with Update Indexed + * .664 + */ +void ppc_opc_stwux() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rA!=0 + int ret = ppc_write_effective_word(gCPU.gpr[rA]+gCPU.gpr[rB], gCPU.gpr[rS]); + if (ret == PPC_MMU_OK) { + gCPU.gpr[rA] += gCPU.gpr[rB]; + } +} +/* + * stwx Store Word Indexed + * .665 + */ +void ppc_opc_stwx() +{ + int rA, rS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + ppc_write_effective_word((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB], gCPU.gpr[rS]) != PPC_MMU_FATAL; +} + +/* stvx Store Vector Indexed + * v.134 + */ +void ppc_opc_stvx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrS, rA, rB); + + int ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]); + + ppc_write_effective_qword(ea, gCPU.vr[vrS]) != PPC_MMU_FATAL; +} + +/* stvxl Store Vector Indexed LRU + * v.135 + */ +void ppc_opc_stvxl() +{ + ppc_opc_stvx(); + /* This instruction should hint to the cache that the value won't be + * needed again in memory anytime soon. We don't emulate the cache, + * so this is effectively exactly the same as lvx. + */ +} + +/* stvebx Store Vector Element Byte Indexed + * v.131 + */ +void ppc_opc_stvebx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrS, rA, rB); + uint32 ea; + ea = (rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]; + ppc_write_effective_byte(ea, VECT_B(gCPU.vr[vrS], ea & 0xf)); +} + +/* stvehx Store Vector Element Half Word Indexed + * v.132 + */ +void ppc_opc_stvehx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrS, rA, rB); + uint32 ea; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]) & ~1; + ppc_write_effective_half(ea, VECT_H(gCPU.vr[vrS], (ea & 0xf) >> 1)); +} + +/* stvewx Store Vector Element Word Indexed + * v.133 + */ +void ppc_opc_stvewx() +{ +#ifndef __VEC_EXC_OFF__ + if ((gCPU.msr & MSR_VEC) == 0) { + ppc_exception(PPC_EXC_NO_VEC); + return; + } +#endif + VECTOR_DEBUG; + int rA, vrS, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrS, rA, rB); + uint32 ea; + ea = ((rA?gCPU.gpr[rA]:0)+gCPU.gpr[rB]) & ~3; + ppc_write_effective_word(ea, VECT_W(gCPU.vr[vrS], (ea & 0xf) >> 2)); +} + +/* dstst Data Stream Touch for Store + * v.117 + */ +void ppc_opc_dstst() +{ + VECTOR_DEBUG; + /* Since we are not emulating the cache, this is a nop */ +} + +/* dss Data Stream Stop + * v.114 + */ +void ppc_opc_dss() +{ + VECTOR_DEBUG; + /* Since we are not emulating the cache, this is a nop */ +} diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_mmu.h b/ppc/pearpc/cpu/cpu_generic/ppc_mmu.h new file mode 100644 index 00000000..7cf8a731 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_mmu.h @@ -0,0 +1,169 @@ +/* + * PearPC + * ppc_mmu.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nmsu.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_MMU_H__ +#define __PPC_MMU_H__ + +#include "system/types.h" + +extern byte *gMemory; +extern uint32 gMemorySize; + +#define PPC_MMU_READ 1 +#define PPC_MMU_WRITE 2 +#define PPC_MMU_CODE 4 +#define PPC_MMU_SV 8 +#define PPC_MMU_NO_EXC 16 + +#define PPC_MMU_OK 0 +#define PPC_MMU_EXC 1 +#define PPC_MMU_FATAL 2 + +int FASTCALL ppc_effective_to_physical(uint32 addr, int flags, uint32 &result); +bool FASTCALL ppc_mmu_set_sdr1(uint32 newval, bool quiesce); +void ppc_mmu_tlb_invalidate(); + +int FASTCALL ppc_read_physical_dword(uint32 addr, uint64 &result); +int FASTCALL ppc_read_physical_word(uint32 addr, uint32 &result); +int FASTCALL ppc_read_physical_half(uint32 addr, uint16 &result); +int FASTCALL ppc_read_physical_byte(uint32 addr, uint8 &result); + +int FASTCALL ppc_read_effective_code(uint32 addr, uint32 &result); +int FASTCALL ppc_read_effective_dword(uint32 addr, uint64 &result); +int FASTCALL ppc_read_effective_word(uint32 addr, uint32 &result); +int FASTCALL ppc_read_effective_half(uint32 addr, uint16 &result); +int FASTCALL ppc_read_effective_byte(uint32 addr, uint8 &result); + +int FASTCALL ppc_write_physical_dword(uint32 addr, uint64 data); +int FASTCALL ppc_write_physical_word(uint32 addr, uint32 data); +int FASTCALL ppc_write_physical_half(uint32 addr, uint16 data); +int FASTCALL ppc_write_physical_byte(uint32 addr, uint8 data); + +int FASTCALL ppc_write_effective_dword(uint32 addr, uint64 data); +int FASTCALL ppc_write_effective_word(uint32 addr, uint32 data); +int FASTCALL ppc_write_effective_half(uint32 addr, uint16 data); +int FASTCALL ppc_write_effective_byte(uint32 addr, uint8 data); + +int FASTCALL ppc_direct_physical_memory_handle(uint32 addr, byte *&ptr); +int FASTCALL ppc_direct_effective_memory_handle(uint32 addr, byte *&ptr); +int FASTCALL ppc_direct_effective_memory_handle_code(uint32 addr, byte *&ptr); +bool FASTCALL ppc_mmu_page_create(uint32 ea, uint32 pa); +bool FASTCALL ppc_mmu_page_free(uint32 ea); +bool FASTCALL ppc_init_physical_memory(uint size); + +/* +pte: (page table entry) +1st word: +0 V Valid +1-24 VSID Virtual Segment ID +25 H Hash function +26-31 API Abbreviated page index +2nd word: +0-19 RPN Physical page number +20-22 res +23 R Referenced bit +24 C Changed bit +25-28 WIMG Memory/cache control bits +29 res +30-31 PP Page protection bits +*/ + +/* + * MMU Opcodes + */ +void ppc_opc_dcbz(); + +void ppc_opc_lbz(); +void ppc_opc_lbzu(); +void ppc_opc_lbzux(); +void ppc_opc_lbzx(); +void ppc_opc_lfd(); +void ppc_opc_lfdu(); +void ppc_opc_lfdux(); +void ppc_opc_lfdx(); +void ppc_opc_lfs(); +void ppc_opc_lfsu(); +void ppc_opc_lfsux(); +void ppc_opc_lfsx(); +void ppc_opc_lha(); +void ppc_opc_lhau(); +void ppc_opc_lhaux(); +void ppc_opc_lhax(); +void ppc_opc_lhbrx(); +void ppc_opc_lhz(); +void ppc_opc_lhzu(); +void ppc_opc_lhzux(); +void ppc_opc_lhzx(); +void ppc_opc_lmw(); +void ppc_opc_lswi(); +void ppc_opc_lswx(); +void ppc_opc_lwarx(); +void ppc_opc_lwbrx(); +void ppc_opc_lwz(); +void ppc_opc_lwzu(); +void ppc_opc_lwzux(); +void ppc_opc_lwzx(); +void ppc_opc_lvx(); /* for altivec support */ +void ppc_opc_lvxl(); +void ppc_opc_lvebx(); +void ppc_opc_lvehx(); +void ppc_opc_lvewx(); +void ppc_opc_lvsl(); +void ppc_opc_lvsr(); +void ppc_opc_dst(); + +void ppc_opc_stb(); +void ppc_opc_stbu(); +void ppc_opc_stbux(); +void ppc_opc_stbx(); +void ppc_opc_stfd(); +void ppc_opc_stfdu(); +void ppc_opc_stfdux(); +void ppc_opc_stfdx(); +void ppc_opc_stfiwx(); +void ppc_opc_stfs(); +void ppc_opc_stfsu(); +void ppc_opc_stfsux(); +void ppc_opc_stfsx(); +void ppc_opc_sth(); +void ppc_opc_sthbrx(); +void ppc_opc_sthu(); +void ppc_opc_sthux(); +void ppc_opc_sthx(); +void ppc_opc_stmw(); +void ppc_opc_stswi(); +void ppc_opc_stswx(); +void ppc_opc_stw(); +void ppc_opc_stwbrx(); +void ppc_opc_stwcx_(); +void ppc_opc_stwu(); +void ppc_opc_stwux(); +void ppc_opc_stwx(); +void ppc_opc_stvx(); /* for altivec support */ +void ppc_opc_stvxl(); +void ppc_opc_stvebx(); +void ppc_opc_stvehx(); +void ppc_opc_stvewx(); +void ppc_opc_dstst(); +void ppc_opc_dss(); + +#endif + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_opc.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_opc.cpp new file mode 100644 index 00000000..68ce1b36 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_opc.cpp @@ -0,0 +1,959 @@ +/* + * PearPC + * ppc_opc.cc + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * Copyright (C) 2004 Dainel Foesch (dfoesch@cs.nmsu.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "debug/tracers.h" +#include "cpu/debug.h" +//#include "io/pic/pic.h" +#include "info.h" +#include "ppc_cpu.h" +#include "ppc_exc.h" +#include "ppc_mmu.h" +#include "ppc_opc.h" +#include "ppc_dec.h" + +extern void ppc_doze(void); + +void ppc_set_msr(uint32 newmsr) +{ +/* if ((newmsr & MSR_EE) && !(gCPU.msr & MSR_EE)) { + if (pic_check_interrupt()) { + gCPU.exception_pending = true; + gCPU.ext_exception = true; + } + }*/ + ppc_mmu_tlb_invalidate(); +#ifndef PPC_CPU_ENABLE_SINGLESTEP + if (newmsr & MSR_SE) { + SINGLESTEP(""); + PPC_CPU_WARN("MSR[SE] (singlestep enable) set, but compiled w/o SE support.\n"); + } +#else + gCPU.singlestep_ignore = true; +#endif + if (newmsr & PPC_CPU_UNSUPPORTED_MSR_BITS) { + PPC_CPU_ERR("unsupported bits in MSR set: %08x @%08x\n", newmsr & PPC_CPU_UNSUPPORTED_MSR_BITS, gCPU.pc); + } + if (newmsr & MSR_POW) { + ppc_doze(); + // doze(); + newmsr &= ~MSR_POW; + } + gCPU.msr = newmsr; + +} + +/* + * bx Branch + * .435 + */ +void ppc_opc_bx() +{ + uint32 li; + PPC_OPC_TEMPL_I(gCPU.current_opc, li); + if (!(gCPU.current_opc & PPC_OPC_AA)) { + li += gCPU.pc; + } + if (gCPU.current_opc & PPC_OPC_LK) { + gCPU.lr = gCPU.pc + 4; + } + gCPU.npc = li; +} + +/* + * bcx Branch Conditional + * .436 + */ +void ppc_opc_bcx() +{ + uint32 BO, BI, BD; + PPC_OPC_TEMPL_B(gCPU.current_opc, BO, BI, BD); + if (!(BO & 4)) { + gCPU.ctr--; + } + bool bo2 = (BO & 2); + bool bo8 = (BO & 8); // branch condition true + bool cr = (gCPU.cr & (1<<(31-BI))); + if (((BO & 4) || ((gCPU.ctr!=0) ^ bo2)) + && ((BO & 16) || (!(cr ^ bo8)))) { + if (!(gCPU.current_opc & PPC_OPC_AA)) { + BD += gCPU.pc; + } + if (gCPU.current_opc & PPC_OPC_LK) { + gCPU.lr = gCPU.pc + 4; + } + gCPU.npc = BD; + } +} + +/* + * bcctrx Branch Conditional to Count Register + * .438 + */ +void ppc_opc_bcctrx() +{ + uint32 BO, BI, BD; + PPC_OPC_TEMPL_XL(gCPU.current_opc, BO, BI, BD); + PPC_OPC_ASSERT(BD==0); + PPC_OPC_ASSERT(!(BO & 2)); + bool bo8 = (BO & 8); + bool cr = (gCPU.cr & (1<<(31-BI))); + if ((BO & 16) || (!(cr ^ bo8))) { + if (gCPU.current_opc & PPC_OPC_LK) { + gCPU.lr = gCPU.pc + 4; + } + gCPU.npc = gCPU.ctr & 0xfffffffc; + } +} +/* + * bclrx Branch Conditional to Link Register + * .440 + */ +void ppc_opc_bclrx() +{ + uint32 BO, BI, BD; + PPC_OPC_TEMPL_XL(gCPU.current_opc, BO, BI, BD); + PPC_OPC_ASSERT(BD==0); + if (!(BO & 4)) { + gCPU.ctr--; + } + bool bo2 = (BO & 2); + bool bo8 = (BO & 8); + bool cr = (gCPU.cr & (1<<(31-BI))); + if (((BO & 4) || ((gCPU.ctr!=0) ^ bo2)) + && ((BO & 16) || (!(cr ^ bo8)))) { + BD = gCPU.lr & 0xfffffffc; + if (gCPU.current_opc & PPC_OPC_LK) { + gCPU.lr = gCPU.pc + 4; + } + gCPU.npc = BD; + } +} + +/* + * dcbf Data Cache Block Flush + * .458 + */ +void ppc_opc_dcbf() +{ + // NO-OP +} +/* + * dcbi Data Cache Block Invalidate + * .460 + */ +void ppc_opc_dcbi() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + // FIXME: check addr +} +/* + * dcbst Data Cache Block Store + * .461 + */ +void ppc_opc_dcbst() +{ + // NO-OP +} +/* + * dcbt Data Cache Block Touch + * .462 + */ +void ppc_opc_dcbt() +{ + // NO-OP +} +/* + * dcbtst Data Cache Block Touch for Store + * .463 + */ +void ppc_opc_dcbtst() +{ + // NO-OP +} +/* + * eciwx External Control In Word Indexed + * .474 + */ +void ppc_opc_eciwx() +{ + PPC_OPC_ERR("eciwx unimplemented.\n"); +} +/* + * ecowx External Control Out Word Indexed + * .476 + */ +void ppc_opc_ecowx() +{ + PPC_OPC_ERR("ecowx unimplemented.\n"); +} +/* + * eieio Enforce In-Order Execution of I/O + * .478 + */ +void ppc_opc_eieio() +{ + // NO-OP +} + +/* + * icbi Instruction Cache Block Invalidate + * .519 + */ +void ppc_opc_icbi() +{ + // NO-OP +} + +/* + * isync Instruction Synchronize + * .520 + */ +void ppc_opc_isync() +{ + // NO-OP +} + +static uint32 ppc_cmp_and_mask[8] = { + 0xfffffff0, + 0xffffff0f, + 0xfffff0ff, + 0xffff0fff, + 0xfff0ffff, + 0xff0fffff, + 0xf0ffffff, + 0x0fffffff, +}; +/* + * mcrf Move Condition Register Field + * .561 + */ +void ppc_opc_mcrf() +{ + uint32 crD, crS, bla; + PPC_OPC_TEMPL_X(gCPU.current_opc, crD, crS, bla); + // FIXME: bla == 0 + crD>>=2; + crS>>=2; + crD = 7-crD; + crS = 7-crS; + uint32 c = (gCPU.cr>>(crS*4)) & 0xf; + gCPU.cr &= ppc_cmp_and_mask[crD]; + gCPU.cr |= c<<(crD*4); +} +/* + * mcrfs Move to Condition Register from FPSCR + * .562 + */ +void ppc_opc_mcrfs() +{ + PPC_OPC_ERR("mcrfs unimplemented.\n"); +} +/* + * mcrxr Move to Condition Register from XER + * .563 + */ +void ppc_opc_mcrxr() +{ + PPC_OPC_ERR("mcrxr unimplemented.\n"); +} +/* + * mfcr Move from Condition Register + * .564 + */ +void ppc_opc_mfcr() +{ + int rD, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT(rA==0 && rB==0); + gCPU.gpr[rD] = gCPU.cr; +} +/* + * mffs Move from FPSCR + * .565 + */ +void ppc_opc_mffsx() +{ + int frD, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, frD, rA, rB); + PPC_OPC_ASSERT(rA==0 && rB==0); + gCPU.fpr[frD] = gCPU.fpscr; + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_OPC_ERR("mffs. unimplemented.\n"); + } +} +/* + * mfmsr Move from Machine State Register + * .566 + */ +void ppc_opc_mfmsr() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rD, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + PPC_OPC_ASSERT((rA == 0) && (rB == 0)); + gCPU.gpr[rD] = gCPU.msr; +} +/* + * mfspr Move from Special-Purpose Register + * .567 + */ +void ppc_opc_mfspr() +{ + int rD, spr1, spr2; + PPC_OPC_TEMPL_XO(gCPU.current_opc, rD, spr1, spr2); + switch (spr2) { + case 0: + switch (spr1) { + case 1: gCPU.gpr[rD] = gCPU.xer; return; + case 8: gCPU.gpr[rD] = gCPU.lr; return; + case 9: gCPU.gpr[rD] = gCPU.ctr; return; + } + case 8: // altivec made this spr unpriviledged + if (spr1 == 0) { + gCPU.gpr[rD] = gCPU.vrsave; + return; + } + } + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + switch (spr2) { + case 0: + switch (spr1) { + case 18: gCPU.gpr[rD] = gCPU.dsisr; return; + case 19: gCPU.gpr[rD] = gCPU.dar; return; + case 22: { + gCPU.dec = gCPU.pdec / TB_TO_PTB_FACTOR; + gCPU.gpr[rD] = gCPU.dec; + return; + } + case 25: gCPU.gpr[rD] = gCPU.sdr1; return; + case 26: gCPU.gpr[rD] = gCPU.srr[0]; return; + case 27: gCPU.gpr[rD] = gCPU.srr[1]; return; + } + break; + case 8: + switch (spr1) { + case 12: { + gCPU.tb = gCPU.ptb / TB_TO_PTB_FACTOR; + gCPU.gpr[rD] = gCPU.tb; + return; + } + case 13: { + gCPU.tb = gCPU.ptb / TB_TO_PTB_FACTOR; + gCPU.gpr[rD] = gCPU.tb >> 32; + return; + } + case 16: gCPU.gpr[rD] = gCPU.sprg[0]; return; + case 17: gCPU.gpr[rD] = gCPU.sprg[1]; return; + case 18: gCPU.gpr[rD] = gCPU.sprg[2]; return; + case 19: gCPU.gpr[rD] = gCPU.sprg[3]; return; + case 26: gCPU.gpr[rD] = gCPU.ear; return; + case 31: gCPU.gpr[rD] = gCPU.pvr; return; + } + break; + case 16: + switch (spr1) { + case 16: gCPU.gpr[rD] = gCPU.ibatu[0]; return; + case 17: gCPU.gpr[rD] = gCPU.ibatl[0]; return; + case 18: gCPU.gpr[rD] = gCPU.ibatu[1]; return; + case 19: gCPU.gpr[rD] = gCPU.ibatl[1]; return; + case 20: gCPU.gpr[rD] = gCPU.ibatu[2]; return; + case 21: gCPU.gpr[rD] = gCPU.ibatl[2]; return; + case 22: gCPU.gpr[rD] = gCPU.ibatu[3]; return; + case 23: gCPU.gpr[rD] = gCPU.ibatl[3]; return; + case 24: gCPU.gpr[rD] = gCPU.dbatu[0]; return; + case 25: gCPU.gpr[rD] = gCPU.dbatl[0]; return; + case 26: gCPU.gpr[rD] = gCPU.dbatu[1]; return; + case 27: gCPU.gpr[rD] = gCPU.dbatl[1]; return; + case 28: gCPU.gpr[rD] = gCPU.dbatu[2]; return; + case 29: gCPU.gpr[rD] = gCPU.dbatl[2]; return; + case 30: gCPU.gpr[rD] = gCPU.dbatu[3]; return; + case 31: gCPU.gpr[rD] = gCPU.dbatl[3]; return; + } + break; + case 29: + switch (spr1) { + case 16: + gCPU.gpr[rD] = 0; + return; + case 17: + gCPU.gpr[rD] = 0; + return; + case 18: + gCPU.gpr[rD] = 0; + return; + case 24: + gCPU.gpr[rD] = 0; + return; + case 25: + gCPU.gpr[rD] = 0; + return; + case 26: + gCPU.gpr[rD] = 0; + return; + case 28: + gCPU.gpr[rD] = 0; + return; + case 29: + gCPU.gpr[rD] = 0; + return; + case 30: + gCPU.gpr[rD] = 0; + return; + } + case 31: + switch (spr1) { + case 16: +// PPC_OPC_WARN("read from spr %d:%d (HID0) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = gCPU.hid[0]; + return; + case 17: + //PPC_OPC_WARN("read from spr %d:%d (HID1) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = gCPU.hid[1]; + return; + case 18: + gCPU.gpr[rD] = 0; + return; + case 21: + gCPU.gpr[rD] = 0; + return; + case 22: + gCPU.gpr[rD] = 0; + return; + case 23: + gCPU.gpr[rD] = 0; + return; + case 25: + PPC_OPC_WARN("read from spr %d:%d (L2CR) not supported! (from %08x)\n", spr1, spr2, gCPU.pc); + gCPU.gpr[rD] = 0; + return; + case 27: + PPC_OPC_WARN("read from spr %d:%d (ICTC) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = 0; + return; + case 28: +// PPC_OPC_WARN("read from spr %d:%d (THRM1) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = 0; + return; + case 29: +// PPC_OPC_WARN("read from spr %d:%d (THRM2) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = 0; + return; + case 30: +// PPC_OPC_WARN("read from spr %d:%d (THRM3) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = 0; + return; + case 31: +// PPC_OPC_WARN("read from spr %d:%d (???) not supported!\n", spr1, spr2); + gCPU.gpr[rD] = 0; + return; + } + } + ht_printf("unknown mfspr: %i:%i\n", spr1, spr2); + SINGLESTEP("invalid mfspr\n"); +} +/* + * mfsr Move from Segment Register + * .570 + */ +void ppc_opc_mfsr() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rD, SR, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, SR, rB); + // FIXME: check insn + gCPU.gpr[rD] = gCPU.sr[SR & 0xf]; +} +/* + * mfsrin Move from Segment Register Indirect + * .572 + */ +void ppc_opc_mfsrin() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rD, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, rA, rB); + // FIXME: check insn + gCPU.gpr[rD] = gCPU.sr[gCPU.gpr[rB] >> 28]; +} +/* + * mftb Move from Time Base + * .574 + */ +void ppc_opc_mftb() +{ + int rD, spr1, spr2; + PPC_OPC_TEMPL_X(gCPU.current_opc, rD, spr1, spr2); + switch (spr2) { + case 8: + switch (spr1) { + case 12: { + gCPU.tb = gCPU.ptb / TB_TO_PTB_FACTOR; + gCPU.gpr[rD] = gCPU.tb; + return; + } + case 13: { + gCPU.tb = gCPU.ptb / TB_TO_PTB_FACTOR; + gCPU.gpr[rD] = gCPU.tb >> 32; + return; + } + } + break; + } + SINGLESTEP("unknown mftb\n"); +} +/* + * mtcrf Move to Condition Register Fields + * .576 + */ +void ppc_opc_mtcrf() +{ + + int rS; + uint32 crm; + uint32 CRM; + PPC_OPC_TEMPL_XFX(gCPU.current_opc, rS, crm); + CRM = ((crm&0x80)?0xf0000000:0)|((crm&0x40)?0x0f000000:0)|((crm&0x20)?0x00f00000:0)|((crm&0x10)?0x000f0000:0)| + ((crm&0x08)?0x0000f000:0)|((crm&0x04)?0x00000f00:0)|((crm&0x02)?0x000000f0:0)|((crm&0x01)?0x0000000f:0); + gCPU.cr = (gCPU.gpr[rS] & CRM) | (gCPU.cr & ~CRM); +} +/* + * mtfsb0x Move to FPSCR Bit 0 + * .577 + */ +void ppc_opc_mtfsb0x() +{ + int crbD, n1, n2; + PPC_OPC_TEMPL_X(gCPU.current_opc, crbD, n1, n2); + if (crbD != 1 && crbD != 2) { + gCPU.fpscr &= ~(1<<(31-crbD)); + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_OPC_ERR("mtfsb0. unimplemented.\n"); + } +} +/* + * mtfsb1x Move to FPSCR Bit 1 + * .578 + */ +void ppc_opc_mtfsb1x() +{ + int crbD, n1, n2; + PPC_OPC_TEMPL_X(gCPU.current_opc, crbD, n1, n2); + if (crbD != 1 && crbD != 2) { + gCPU.fpscr |= 1<<(31-crbD); + } + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_OPC_ERR("mtfsb1. unimplemented.\n"); + } +} +/* + * mtfsfx Move to FPSCR Fields + * .579 + */ +void ppc_opc_mtfsfx() +{ + int frB; + uint32 fm, FM; + PPC_OPC_TEMPL_XFL(gCPU.current_opc, frB, fm); + FM = ((fm&0x80)?0xf0000000:0)|((fm&0x40)?0x0f000000:0)|((fm&0x20)?0x00f00000:0)|((fm&0x10)?0x000f0000:0)| + ((fm&0x08)?0x0000f000:0)|((fm&0x04)?0x00000f00:0)|((fm&0x02)?0x000000f0:0)|((fm&0x01)?0x0000000f:0); + gCPU.fpscr = (gCPU.fpr[frB] & FM) | (gCPU.fpscr & ~FM); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_OPC_ERR("mtfsf. unimplemented.\n"); + } +} +/* + * mtfsfix Move to FPSCR Field Immediate + * .580 + */ +void ppc_opc_mtfsfix() +{ + int crfD, n1; + uint32 imm; + PPC_OPC_TEMPL_X(gCPU.current_opc, crfD, n1, imm); + crfD >>= 2; + imm >>= 1; + crfD = 7-crfD; + gCPU.fpscr &= ppc_cmp_and_mask[crfD]; + gCPU.fpscr |= imm<<(crfD*4); + if (gCPU.current_opc & PPC_OPC_Rc) { + // update cr1 flags + PPC_OPC_ERR("mtfsfi. unimplemented.\n"); + } +} +/* + * mtmsr Move to Machine State Register + * .581 + */ +void ppc_opc_mtmsr() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + PPC_OPC_ASSERT((rA == 0) && (rB == 0)); + ppc_set_msr(gCPU.gpr[rS]); +} +/* + * mtspr Move to Special-Purpose Register + * .584 + */ +void ppc_opc_mtspr() +{ + int rS, spr1, spr2; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, spr1, spr2); + switch (spr2) { + case 0: + switch (spr1) { + case 1: gCPU.xer = gCPU.gpr[rS]; return; + case 8: gCPU.lr = gCPU.gpr[rS]; return; + case 9: gCPU.ctr = gCPU.gpr[rS]; return; + } + case 8: //altivec makes this register unpriviledged + if (spr1 == 0) { + gCPU.vrsave = gCPU.gpr[rS]; + return; + } + } + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + switch (spr2) { + case 0: + switch (spr1) { +/* case 18: gCPU.gpr[rD] = gCPU.dsisr; return; + case 19: gCPU.gpr[rD] = gCPU.dar; return;*/ + case 22: { + gCPU.dec = gCPU.gpr[rS]; + gCPU.pdec = gCPU.dec; + gCPU.pdec *= TB_TO_PTB_FACTOR; + return; + } + case 25: + if (!ppc_mmu_set_sdr1(gCPU.gpr[rS], true)) { + PPC_OPC_ERR("cannot set sdr1\n"); + } + return; + case 26: gCPU.srr[0] = gCPU.gpr[rS]; return; + case 27: gCPU.srr[1] = gCPU.gpr[rS]; return; + } + break; + case 8: + switch (spr1) { + case 16: gCPU.sprg[0] = gCPU.gpr[rS]; return; + case 17: gCPU.sprg[1] = gCPU.gpr[rS]; return; + case 18: gCPU.sprg[2] = gCPU.gpr[rS]; return; + case 19: gCPU.sprg[3] = gCPU.gpr[rS]; return; + case 26: gCPU.ear = gCPU.gpr[rS]; return; + case 28: gCPU.tb = (gCPU.tb & 0xffffffff00000000) | gCPU.gpr[rS]; return; + case 29: gCPU.tb = (gCPU.tb & 0x00000000ffffffff) | ((uint64)gCPU.gpr[rS] << 32); return; + } + break; + case 16: + switch (spr1) { + case 16: + gCPU.ibatu[0] = gCPU.gpr[rS]; + gCPU.ibat_bl17[0] = ~(BATU_BL(gCPU.ibatu[0])<<17); + return; + case 17: + gCPU.ibatl[0] = gCPU.gpr[rS]; + return; + case 18: + gCPU.ibatu[1] = gCPU.gpr[rS]; + gCPU.ibat_bl17[1] = ~(BATU_BL(gCPU.ibatu[1])<<17); + return; + case 19: + gCPU.ibatl[1] = gCPU.gpr[rS]; + return; + case 20: + gCPU.ibatu[2] = gCPU.gpr[rS]; + gCPU.ibat_bl17[2] = ~(BATU_BL(gCPU.ibatu[2])<<17); + return; + case 21: + gCPU.ibatl[2] = gCPU.gpr[rS]; + return; + case 22: + gCPU.ibatu[3] = gCPU.gpr[rS]; + gCPU.ibat_bl17[3] = ~(BATU_BL(gCPU.ibatu[3])<<17); + return; + case 23: + gCPU.ibatl[3] = gCPU.gpr[rS]; + return; + case 24: + gCPU.dbatu[0] = gCPU.gpr[rS]; + gCPU.dbat_bl17[0] = ~(BATU_BL(gCPU.dbatu[0])<<17); + return; + case 25: + gCPU.dbatl[0] = gCPU.gpr[rS]; + return; + case 26: + gCPU.dbatu[1] = gCPU.gpr[rS]; + gCPU.dbat_bl17[1] = ~(BATU_BL(gCPU.dbatu[1])<<17); + return; + case 27: + gCPU.dbatl[1] = gCPU.gpr[rS]; + return; + case 28: + gCPU.dbatu[2] = gCPU.gpr[rS]; + gCPU.dbat_bl17[2] = ~(BATU_BL(gCPU.dbatu[2])<<17); + return; + case 29: + gCPU.dbatl[2] = gCPU.gpr[rS]; + return; + case 30: + gCPU.dbatu[3] = gCPU.gpr[rS]; + gCPU.dbat_bl17[3] = ~(BATU_BL(gCPU.dbatu[3])<<17); + return; + case 31: + gCPU.dbatl[3] = gCPU.gpr[rS]; + return; + } + break; + case 29: + switch(spr1) { + case 17: return; + case 24: return; + case 25: return; + case 26: return; + } + case 31: + switch (spr1) { + case 16: +// PPC_OPC_WARN("write(%08x) to spr %d:%d (HID0) not supported! @%08x\n", gCPU.gpr[rS], spr1, spr2, gCPU.pc); + gCPU.hid[0] = gCPU.gpr[rS]; + return; + case 17: return; + case 18: + PPC_OPC_ERR("write(%08x) to spr %d:%d (IABR) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 21: + PPC_OPC_ERR("write(%08x) to spr %d:%d (DABR) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 24: + PPC_OPC_WARN("write(%08x) to spr %d:%d (?) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 27: + PPC_OPC_WARN("write(%08x) to spr %d:%d (ICTC) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 28: +// PPC_OPC_WARN("write(%08x) to spr %d:%d (THRM1) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 29: +// PPC_OPC_WARN("write(%08x) to spr %d:%d (THRM2) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 30: +// PPC_OPC_WARN("write(%08x) to spr %d:%d (THRM3) not supported!\n", gCPU.gpr[rS], spr1, spr2); + return; + case 31: return; + } + } + ht_printf("unknown mtspr: %i:%i\n", spr1, spr2); + SINGLESTEP("unknown mtspr\n"); +} +/* + * mtsr Move to Segment Register + * .587 + */ +void ppc_opc_mtsr() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, SR, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, SR, rB); + // FIXME: check insn + gCPU.sr[SR & 0xf] = gCPU.gpr[rS]; +} +/* + * mtsrin Move to Segment Register Indirect + * .591 + */ +void ppc_opc_mtsrin() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check insn + gCPU.sr[gCPU.gpr[rB] >> 28] = gCPU.gpr[rS]; +} + +/* + * rfi Return from Interrupt + * .607 + */ +void ppc_opc_rfi() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + ppc_set_msr(gCPU.srr[1] & MSR_RFI_SAVE_MASK); + gCPU.npc = gCPU.srr[0] & 0xfffffffc; +} + +/* + * sc System Call + * .621 + */ +#if 0 +#include "io/graphic/gcard.h" +#endif +void ppc_opc_sc() +{ +#if 0 + if (gCPU.gpr[3] == 0x113724fa && gCPU.gpr[4] == 0x77810f9b) { + gcard_osi(0); + return; + } +#endif + ppc_exception(PPC_EXC_SC); +} + +extern void uae_ppc_sync(void); +/* + * sync Synchronize + * .672 + */ +void ppc_opc_sync() +{ + // NO-OP + uae_ppc_sync(); +} + +/* + * tlbie Translation Lookaside Buffer Invalidate Entry + * .676 + */ +void ppc_opc_tlbia() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rS.. for 0 + ppc_mmu_tlb_invalidate(); +} + +/* + * tlbie Translation Lookaside Buffer Invalidate All + * .676 + */ +void ppc_opc_tlbie() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rS.. for 0 + ppc_mmu_tlb_invalidate(); +} + +/* + * tlbsync Translation Lookaside Buffer Syncronize + * .677 + */ +void ppc_opc_tlbsync() +{ + if (gCPU.msr & MSR_PR) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_PRIV); + return; + } + int rS, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, rS, rA, rB); + // FIXME: check rS.. for 0 + ppc_mmu_tlb_invalidate(); +} + +/* + * tw Trap Word + * .678 + */ +void ppc_opc_tw() +{ + int TO, rA, rB; + PPC_OPC_TEMPL_X(gCPU.current_opc, TO, rA, rB); + uint32 a = gCPU.gpr[rA]; + uint32 b = gCPU.gpr[rB]; + if (((TO & 16) && ((sint32)a < (sint32)b)) + || ((TO & 8) && ((sint32)a > (sint32)b)) + || ((TO & 4) && (a == b)) + || ((TO & 2) && (a < b)) + || ((TO & 1) && (a > b))) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_TRAP); + } +} + +/* + * twi Trap Word Immediate + * .679 + */ +void ppc_opc_twi() +{ + int TO, rA; + uint32 imm; + PPC_OPC_TEMPL_D_SImm(gCPU.current_opc, TO, rA, imm); + uint32 a = gCPU.gpr[rA]; + if (((TO & 16) && ((sint32)a < (sint32)imm)) + || ((TO & 8) && ((sint32)a > (sint32)imm)) + || ((TO & 4) && (a == imm)) + || ((TO & 2) && (a < imm)) + || ((TO & 1) && (a > imm))) { + ppc_exception(PPC_EXC_PROGRAM, PPC_EXC_PROGRAM_TRAP); + } +} + +/* dcba Data Cache Block Allocate + * .??? + */ +void ppc_opc_dcba() +{ + /* NO-OP */ +} diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_opc.h b/ppc/pearpc/cpu/cpu_generic/ppc_opc.h new file mode 100644 index 00000000..5c261922 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_opc.h @@ -0,0 +1,89 @@ +/* + * PearPC + * ppc_opc.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_OPC_H__ +#define __PPC_OPC_H__ + +#include "system/types.h" + +static inline void ppc_update_cr0(uint32 r) +{ + gCPU.cr &= 0x0fffffff; + if (!r) { + gCPU.cr |= CR_CR0_EQ; + } else if (r & 0x80000000) { + gCPU.cr |= CR_CR0_LT; + } else { + gCPU.cr |= CR_CR0_GT; + } + if (gCPU.xer & XER_SO) gCPU.cr |= CR_CR0_SO; +} + +void ppc_opc_bx(); +void ppc_opc_bcx(); +void ppc_opc_bcctrx(); +void ppc_opc_bclrx(); + +void ppc_opc_dcba(); +void ppc_opc_dcbf(); +void ppc_opc_dcbi(); +void ppc_opc_dcbst(); +void ppc_opc_dcbt(); +void ppc_opc_dcbtst(); + +void ppc_opc_eciwx(); +void ppc_opc_ecowx(); +void ppc_opc_eieio(); + +void ppc_opc_icbi(); +void ppc_opc_isync(); + +void ppc_opc_mcrf(); +void ppc_opc_mcrfs(); +void ppc_opc_mcrxr(); +void ppc_opc_mfcr(); +void ppc_opc_mffsx(); +void ppc_opc_mfmsr(); +void ppc_opc_mfspr(); +void ppc_opc_mfsr(); +void ppc_opc_mfsrin(); +void ppc_opc_mftb(); +void ppc_opc_mtcrf(); +void ppc_opc_mtfsb0x(); +void ppc_opc_mtfsb1x(); +void ppc_opc_mtfsfx(); +void ppc_opc_mtfsfix(); +void ppc_opc_mtmsr(); +void ppc_opc_mtspr(); +void ppc_opc_mtsr(); +void ppc_opc_mtsrin(); + +void ppc_opc_rfi(); +void ppc_opc_sc(); +void ppc_opc_sync(); +void ppc_opc_tlbia(); +void ppc_opc_tlbie(); +void ppc_opc_tlbsync(); +void ppc_opc_tw(); +void ppc_opc_twi(); + + +#endif + diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_tools.h b/ppc/pearpc/cpu/cpu_generic/ppc_tools.h new file mode 100644 index 00000000..c9212d56 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_tools.h @@ -0,0 +1,43 @@ +/* + * PearPC + * ppc_tools.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_TOOLS_H__ +#define __PPC_TOOLS_H__ + +#include "system/types.h" + +static inline FUNCTION_CONST bool ppc_carry_3(uint32 a, uint32 b, uint32 c) +{ + if ((a+b) < a) { + return true; + } + if ((a+b+c) < c) { + return true; + } + return false; +} + +static inline FUNCTION_CONST uint32 ppc_word_rotl(uint32 data, int n) +{ + n &= 0x1f; + return (data << n) | (data >> (32-n)); +} + +#endif diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_vec.cpp b/ppc/pearpc/cpu/cpu_generic/ppc_vec.cpp new file mode 100644 index 00000000..ede83b2e --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_vec.cpp @@ -0,0 +1,3206 @@ +/* + * PearPC + * ppc_vec.cc + * + * Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nsmu.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* Pages marked: v.??? + * From: IBM PowerPC MicroProcessor Family: Altivec(tm) Technology... + * Programming Environments Manual + */ + +#include + +/* + * FIXME: put somewhere appropriate + */ +#ifndef HAS_LOG2 +#define log2(x) log(x)/log((float)2) +#endif /* HAS_LOG2 */ + +#ifndef HAS_EXP2 +#define exp2(x) pow(2, x) +#endif /* HAS_EXP2 */ + +#include "debug/tracers.h" +#include "ppc_cpu.h" +#include "ppc_dec.h" +#include "ppc_fpu.h" +#include "ppc_vec.h" + +#define SIGN32 0x80000000 + +/* PACK_PIXEL Packs a uint32 pixel to uint16 pixel + * v.219 + */ +static inline uint16 PACK_PIXEL(uint32 clr) +{ + return (((clr & 0x000000f8) >> 3) | \ + ((clr & 0x0000f800) >> 6) | \ + ((clr & 0x01f80000) >> 9)); +} + +/* UNPACK_PIXEL Unpacks a uint16 pixel to uint32 pixel + * v.276 & v.279 + */ +static inline uint32 UNPACK_PIXEL(uint16 clr) +{ + return (((uint32)(clr & 0x001f)) | \ + ((uint32)(clr & 0x03E0) << 3) | \ + ((uint32)(clr & 0x7c00) << 6) | \ + (((clr) & 0x8000) ? 0xff000000 : 0)); +} + +static inline uint8 SATURATE_UB(uint16 val) +{ + if (val & 0xff00) { + gCPU.vscr |= VSCR_SAT; + return 0xff; + } + return val; +} +static inline uint8 SATURATE_0B(uint16 val) +{ + if (val & 0xff00) { + gCPU.vscr |= VSCR_SAT; + return 0; + } + return val; +} + +static inline uint16 SATURATE_UH(uint32 val) +{ + if (val & 0xffff0000) { + gCPU.vscr |= VSCR_SAT; + return 0xffff; + } + return val; +} + +static inline uint16 SATURATE_0H(uint32 val) +{ + if (val & 0xffff0000) { + gCPU.vscr |= VSCR_SAT; + return 0; + } + return val; +} + +static inline sint8 SATURATE_SB(sint16 val) +{ + if (val > 127) { // 0x7F + gCPU.vscr |= VSCR_SAT; + return 127; + } else if (val < -128) { // 0x80 + gCPU.vscr |= VSCR_SAT; + return -128; + } + return val; +} + +static inline uint8 SATURATE_USB(sint16 val) +{ + if (val > 0xff) { + gCPU.vscr |= VSCR_SAT; + return 0xff; + } else if (val < 0) { + gCPU.vscr |= VSCR_SAT; + return 0; + } + return (uint8)val; +} + +static inline sint16 SATURATE_SH(sint32 val) +{ + if (val > 32767) { // 0x7fff + gCPU.vscr |= VSCR_SAT; + return 32767; + } else if (val < -32768) { // 0x8000 + gCPU.vscr |= VSCR_SAT; + return -32768; + } + return val; +} + +static inline uint16 SATURATE_USH(sint32 val) +{ + if (val > 0xffff) { + gCPU.vscr |= VSCR_SAT; + return 0xffff; + } else if (val < 0) { + gCPU.vscr |= VSCR_SAT; + return 0; + } + return (uint16)val; +} + +static inline sint32 SATURATE_UW(sint64 val) +{ + if (val > 0xffffffffLL) { + gCPU.vscr |= VSCR_SAT; + return 0xffffffffLL; + } + return val; +} + +static inline sint32 SATURATE_SW(sint64 val) +{ + if (val > 2147483647LL) { // 0x7fffffff + gCPU.vscr |= VSCR_SAT; + return 2147483647LL; + } else if (val < -2147483648LL) { // 0x80000000 + gCPU.vscr |= VSCR_SAT; + return -2147483648LL; + } + return val; +} + +/* vperm Vector Permutation + * v.218 + */ +void ppc_opc_vperm() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrA, vrB, vrC; + int sel; + Vector_t r; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + for (int i=0; i<16; i++) { + sel = gCPU.vr[vrC].b[i]; + if (sel & 0x10) + r.b[i] = VECT_B(gCPU.vr[vrB], sel & 0xf); + else + r.b[i] = VECT_B(gCPU.vr[vrA], sel & 0xf); + } + + gCPU.vr[vrD] = r; +} + +/* vsel Vector Select + * v.238 + */ +void ppc_opc_vsel() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + uint64 mask, val; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + mask = gCPU.vr[vrC].d[0]; + val = gCPU.vr[vrB].d[0] & mask; + val |= gCPU.vr[vrA].d[0] & ~mask; + gCPU.vr[vrD].d[0] = val; + + mask = gCPU.vr[vrC].d[1]; + val = gCPU.vr[vrB].d[1] & mask; + val |= gCPU.vr[vrA].d[1] & ~mask; + gCPU.vr[vrD].d[1] = val; +} + +/* vsrb Vector Shift Right Byte + * v.256 + */ +void ppc_opc_vsrb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<16; i++) { + gCPU.vr[vrD].b[i] = gCPU.vr[vrA].b[i] >> (gCPU.vr[vrB].b[i] & 0x7); + } +} + +/* vsrh Vector Shift Right Half Word + * v.257 + */ +void ppc_opc_vsrh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<8; i++) { + gCPU.vr[vrD].h[i] = gCPU.vr[vrA].h[i] >> (gCPU.vr[vrB].h[i] & 0xf); + } +} + +/* vsrw Vector Shift Right Word + * v.259 + */ +void ppc_opc_vsrw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<4; i++) { + gCPU.vr[vrD].w[i] = gCPU.vr[vrA].w[i] >> (gCPU.vr[vrB].w[i] & 0x1f); + } +} + +/* vsrab Vector Shift Right Arithmetic Byte + * v.253 + */ +void ppc_opc_vsrab() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<16; i++) { + gCPU.vr[vrD].sb[i] = gCPU.vr[vrA].sb[i] >> (gCPU.vr[vrB].b[i] & 0x7); + } +} + +/* vsrah Vector Shift Right Arithmetic Half Word + * v.254 + */ +void ppc_opc_vsrah() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<8; i++) { + gCPU.vr[vrD].sh[i] = gCPU.vr[vrA].sh[i] >> (gCPU.vr[vrB].h[i] & 0xf); + } +} + +/* vsraw Vector Shift Right Arithmetic Word + * v.255 + */ +void ppc_opc_vsraw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<4; i++) { + gCPU.vr[vrD].sw[i] = gCPU.vr[vrA].sw[i] >> (gCPU.vr[vrB].w[i] & 0x1f); + } +} + +/* vslb Vector Shift Left Byte + * v.240 + */ +void ppc_opc_vslb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<16; i++) { + gCPU.vr[vrD].b[i] = gCPU.vr[vrA].b[i] << (gCPU.vr[vrB].b[i] & 0x7); + } +} + +/* vslh Vector Shift Left Half Word + * v.242 + */ +void ppc_opc_vslh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<8; i++) { + gCPU.vr[vrD].h[i] = gCPU.vr[vrA].h[i] << (gCPU.vr[vrB].h[i] & 0xf); + } +} + +/* vslw Vector Shift Left Word + * v.244 + */ +void ppc_opc_vslw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + for (int i=0; i<4; i++) { + gCPU.vr[vrD].w[i] = gCPU.vr[vrA].w[i] << (gCPU.vr[vrB].w[i] & 0x1f); + } +} + +/* vsr Vector Shift Right + * v.251 + */ +void ppc_opc_vsr() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + int shift; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + /* Specs say that the low-order 3 bits of all byte elements in vB + * must be the same, or the result is undefined. So we can just + * use the same low-order 3 bits for all of our shifts. + */ + shift = gCPU.vr[vrB].w[0] & 0x7; + + r.d[0] = gCPU.vr[vrA].d[0] >> shift; + r.d[1] = gCPU.vr[vrA].d[1] >> shift; + + VECT_D(r, 1) |= VECT_D(gCPU.vr[vrA], 0) << (64 - shift); + + gCPU.vr[vrD] = r; +} + +/* vsro Vector Shift Right Octet + * v.258 + */ +void ppc_opc_vsro() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + int shift, i; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + shift = (gCPU.vr[vrB].w[0] >> 3) & 0xf; +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + for (i=0; i<(16-shift); i++) { + r.b[i] = gCPU.vr[vrA].b[i+shift]; + } + + for (; i<16; i++) { + r.b[i] = 0; + } +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE + for (i=0; i> (64 - shift); + + gCPU.vr[vrD] = r; +} + +/* vslo Vector Shift Left Octet + * v.243 + */ +void ppc_opc_vslo() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + int shift, i; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + shift = (gCPU.vr[vrB].w[0] >> 3) & 0xf; +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + for (i=0; i> (8 - shift); + } + + gCPU.vr[vrD] = r; +} + +/* vrlh Vector Rotate Left Half Word + * v.235 + */ +void ppc_opc_vrlh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, shift; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + shift = (gCPU.vr[vrB].h[i] & 0xf); + + r.h[i] = gCPU.vr[vrA].h[i] << shift; + r.h[i] |= gCPU.vr[vrA].h[i] >> (16 - shift); + } + + gCPU.vr[vrD] = r; +} + +/* vrlw Vector Rotate Left Word + * v.236 + */ +void ppc_opc_vrlw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, shift; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + shift = (gCPU.vr[vrB].w[i] & 0x1F); + + r.w[i] = gCPU.vr[vrA].w[i] << shift; + r.w[i] |= gCPU.vr[vrA].w[i] >> (32 - shift); + } + + gCPU.vr[vrD] = r; +} + +/* With the merges, I just don't see any point in risking that a compiler + * might generate actual alu code to calculate anything when it's + * compile-time known. Plus, it's easier to validate it like this. + */ + +/* vmrghb Vector Merge High Byte + * v.195 + */ +void ppc_opc_vmrghb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = VECT_B(gCPU.vr[vrA], 0); + VECT_B(r, 1) = VECT_B(gCPU.vr[vrB], 0); + VECT_B(r, 2) = VECT_B(gCPU.vr[vrA], 1); + VECT_B(r, 3) = VECT_B(gCPU.vr[vrB], 1); + VECT_B(r, 4) = VECT_B(gCPU.vr[vrA], 2); + VECT_B(r, 5) = VECT_B(gCPU.vr[vrB], 2); + VECT_B(r, 6) = VECT_B(gCPU.vr[vrA], 3); + VECT_B(r, 7) = VECT_B(gCPU.vr[vrB], 3); + VECT_B(r, 8) = VECT_B(gCPU.vr[vrA], 4); + VECT_B(r, 9) = VECT_B(gCPU.vr[vrB], 4); + VECT_B(r,10) = VECT_B(gCPU.vr[vrA], 5); + VECT_B(r,11) = VECT_B(gCPU.vr[vrB], 5); + VECT_B(r,12) = VECT_B(gCPU.vr[vrA], 6); + VECT_B(r,13) = VECT_B(gCPU.vr[vrB], 6); + VECT_B(r,14) = VECT_B(gCPU.vr[vrA], 7); + VECT_B(r,15) = VECT_B(gCPU.vr[vrB], 7); + + gCPU.vr[vrD] = r; +} + +/* vmrghh Vector Merge High Half Word + * v.196 + */ +void ppc_opc_vmrghh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = VECT_H(gCPU.vr[vrA], 0); + VECT_H(r, 1) = VECT_H(gCPU.vr[vrB], 0); + VECT_H(r, 2) = VECT_H(gCPU.vr[vrA], 1); + VECT_H(r, 3) = VECT_H(gCPU.vr[vrB], 1); + VECT_H(r, 4) = VECT_H(gCPU.vr[vrA], 2); + VECT_H(r, 5) = VECT_H(gCPU.vr[vrB], 2); + VECT_H(r, 6) = VECT_H(gCPU.vr[vrA], 3); + VECT_H(r, 7) = VECT_H(gCPU.vr[vrB], 3); + + gCPU.vr[vrD] = r; +} + +/* vmrghw Vector Merge High Word + * v.197 + */ +void ppc_opc_vmrghw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_W(r, 0) = VECT_W(gCPU.vr[vrA], 0); + VECT_W(r, 1) = VECT_W(gCPU.vr[vrB], 0); + VECT_W(r, 2) = VECT_W(gCPU.vr[vrA], 1); + VECT_W(r, 3) = VECT_W(gCPU.vr[vrB], 1); + + gCPU.vr[vrD] = r; +} + +/* vmrglb Vector Merge Low Byte + * v.198 + */ +void ppc_opc_vmrglb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = VECT_B(gCPU.vr[vrA], 8); + VECT_B(r, 1) = VECT_B(gCPU.vr[vrB], 8); + VECT_B(r, 2) = VECT_B(gCPU.vr[vrA], 9); + VECT_B(r, 3) = VECT_B(gCPU.vr[vrB], 9); + VECT_B(r, 4) = VECT_B(gCPU.vr[vrA],10); + VECT_B(r, 5) = VECT_B(gCPU.vr[vrB],10); + VECT_B(r, 6) = VECT_B(gCPU.vr[vrA],11); + VECT_B(r, 7) = VECT_B(gCPU.vr[vrB],11); + VECT_B(r, 8) = VECT_B(gCPU.vr[vrA],12); + VECT_B(r, 9) = VECT_B(gCPU.vr[vrB],12); + VECT_B(r,10) = VECT_B(gCPU.vr[vrA],13); + VECT_B(r,11) = VECT_B(gCPU.vr[vrB],13); + VECT_B(r,12) = VECT_B(gCPU.vr[vrA],14); + VECT_B(r,13) = VECT_B(gCPU.vr[vrB],14); + VECT_B(r,14) = VECT_B(gCPU.vr[vrA],15); + VECT_B(r,15) = VECT_B(gCPU.vr[vrB],15); + + gCPU.vr[vrD] = r; +} + +/* vmrglh Vector Merge Low Half Word + * v.199 + */ +void ppc_opc_vmrglh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = VECT_H(gCPU.vr[vrA], 4); + VECT_H(r, 1) = VECT_H(gCPU.vr[vrB], 4); + VECT_H(r, 2) = VECT_H(gCPU.vr[vrA], 5); + VECT_H(r, 3) = VECT_H(gCPU.vr[vrB], 5); + VECT_H(r, 4) = VECT_H(gCPU.vr[vrA], 6); + VECT_H(r, 5) = VECT_H(gCPU.vr[vrB], 6); + VECT_H(r, 6) = VECT_H(gCPU.vr[vrA], 7); + VECT_H(r, 7) = VECT_H(gCPU.vr[vrB], 7); + + gCPU.vr[vrD] = r; +} + +/* vmrglw Vector Merge Low Word + * v.200 + */ +void ppc_opc_vmrglw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_W(r, 0) = VECT_W(gCPU.vr[vrA], 2); + VECT_W(r, 1) = VECT_W(gCPU.vr[vrB], 2); + VECT_W(r, 2) = VECT_W(gCPU.vr[vrA], 3); + VECT_W(r, 3) = VECT_W(gCPU.vr[vrB], 3); + + gCPU.vr[vrD] = r; +} + +/* vspltb Vector Splat Byte + * v.245 + */ +void ppc_opc_vspltb() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + /* The documentation doesn't stipulate what a value higher than 0xf + * will do. Thus, this is by default an undefined value. We + * are thus doing this the fastest way that won't crash us. + */ + val = VECT_B(gCPU.vr[vrB], uimm & 0xf); + val |= (val << 8); + val |= (val << 16); + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* vsplth Vector Splat Half Word + * v.246 + */ +void ppc_opc_vsplth() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + /* The documentation doesn't stipulate what a value higher than 0x7 + * will do. Thus, this is by default an undefined value. We + * are thus doing this the fastest way that won't crash us. + */ + val = VECT_H(gCPU.vr[vrB], uimm & 0x7); + val |= (val << 16); + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* vspltw Vector Splat Word + * v.250 + */ +void ppc_opc_vspltw() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + /* The documentation doesn't stipulate what a value higher than 0x3 + * will do. Thus, this is by default an undefined value. We + * are thus doing this the fastest way that won't crash us. + */ + val = VECT_W(gCPU.vr[vrB], uimm & 0x3); + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* vspltisb Vector Splat Immediate Signed Byte + * v.247 + */ +void ppc_opc_vspltisb() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrB; + uint32 simm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, simm, vrB); + PPC_OPC_ASSERT(vrB==0); + + val = (simm & 0x10) ? (simm | 0xE0) : simm; + val |= (val << 8); + val |= (val << 16); + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* vspltish Vector Splat Immediate Signed Half Word + * v.248 + */ +void ppc_opc_vspltish() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrB; + uint32 simm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, simm, vrB); + PPC_OPC_ASSERT(vrB==0); + + val = (simm & 0x10) ? (simm | 0xFFE0) : simm; + val |= (val << 16); + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* vspltisw Vector Splat Immediate Signed Word + * v.249 + */ +void ppc_opc_vspltisw() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrB; + uint32 simm; + uint64 val; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, simm, vrB); + PPC_OPC_ASSERT(vrB==0); + + val = (simm & 0x10) ? (simm | 0xFFFFFFE0) : simm; + val |= (val << 32); + + gCPU.vr[vrD].d[0] = val; + gCPU.vr[vrD].d[1] = val; +} + +/* mfvscr Move from Vector Status and Control Register + * v.129 + */ +void ppc_opc_mfvscr() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + PPC_OPC_ASSERT(vrB==0); + + VECT_W(gCPU.vr[vrD], 3) = gCPU.vscr; + VECT_W(gCPU.vr[vrD], 2) = 0; + VECT_D(gCPU.vr[vrD], 0) = 0; +} + +/* mtvscr Move to Vector Status and Control Register + * v.130 + */ +void ppc_opc_mtvscr() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + PPC_OPC_ASSERT(vrD==0); + + gCPU.vscr = VECT_W(gCPU.vr[vrB], 3); +} + +/* vpkuhum Vector Pack Unsigned Half Word Unsigned Modulo + * v.224 + */ +void ppc_opc_vpkuhum() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = VECT_B(gCPU.vr[vrA], 1); + VECT_B(r, 1) = VECT_B(gCPU.vr[vrA], 3); + VECT_B(r, 2) = VECT_B(gCPU.vr[vrA], 5); + VECT_B(r, 3) = VECT_B(gCPU.vr[vrA], 7); + VECT_B(r, 4) = VECT_B(gCPU.vr[vrA], 9); + VECT_B(r, 5) = VECT_B(gCPU.vr[vrA],11); + VECT_B(r, 6) = VECT_B(gCPU.vr[vrA],13); + VECT_B(r, 7) = VECT_B(gCPU.vr[vrA],15); + + VECT_B(r, 8) = VECT_B(gCPU.vr[vrB], 1); + VECT_B(r, 9) = VECT_B(gCPU.vr[vrB], 3); + VECT_B(r,10) = VECT_B(gCPU.vr[vrB], 5); + VECT_B(r,11) = VECT_B(gCPU.vr[vrB], 7); + VECT_B(r,12) = VECT_B(gCPU.vr[vrB], 9); + VECT_B(r,13) = VECT_B(gCPU.vr[vrB],11); + VECT_B(r,14) = VECT_B(gCPU.vr[vrB],13); + VECT_B(r,15) = VECT_B(gCPU.vr[vrB],15); + + gCPU.vr[vrD] = r; +} + +/* vpkuwum Vector Pack Unsigned Word Unsigned Modulo + * v.226 + */ +void ppc_opc_vpkuwum() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = VECT_H(gCPU.vr[vrA], 1); + VECT_H(r, 1) = VECT_H(gCPU.vr[vrA], 3); + VECT_H(r, 2) = VECT_H(gCPU.vr[vrA], 5); + VECT_H(r, 3) = VECT_H(gCPU.vr[vrA], 7); + + VECT_H(r, 4) = VECT_H(gCPU.vr[vrB], 1); + VECT_H(r, 5) = VECT_H(gCPU.vr[vrB], 3); + VECT_H(r, 6) = VECT_H(gCPU.vr[vrB], 5); + VECT_H(r, 7) = VECT_H(gCPU.vr[vrB], 7); + + gCPU.vr[vrD] = r; +} + +/* vpkpx Vector Pack Pixel32 + * v.219 + */ +void ppc_opc_vpkpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = PACK_PIXEL(VECT_W(gCPU.vr[vrA], 0)); + VECT_H(r, 1) = PACK_PIXEL(VECT_W(gCPU.vr[vrA], 1)); + VECT_H(r, 2) = PACK_PIXEL(VECT_W(gCPU.vr[vrA], 2)); + VECT_H(r, 3) = PACK_PIXEL(VECT_W(gCPU.vr[vrA], 3)); + + VECT_H(r, 4) = PACK_PIXEL(VECT_W(gCPU.vr[vrB], 0)); + VECT_H(r, 5) = PACK_PIXEL(VECT_W(gCPU.vr[vrB], 1)); + VECT_H(r, 6) = PACK_PIXEL(VECT_W(gCPU.vr[vrB], 2)); + VECT_H(r, 7) = PACK_PIXEL(VECT_W(gCPU.vr[vrB], 3)); + + gCPU.vr[vrD] = r; +} + + +/* vpkuhus Vector Pack Unsigned Half Word Unsigned Saturate + * v.225 + */ +void ppc_opc_vpkuhus() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 0)); + VECT_B(r, 1) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 1)); + VECT_B(r, 2) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 2)); + VECT_B(r, 3) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 3)); + VECT_B(r, 4) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 4)); + VECT_B(r, 5) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 5)); + VECT_B(r, 6) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 6)); + VECT_B(r, 7) = SATURATE_UB(VECT_H(gCPU.vr[vrA], 7)); + + VECT_B(r, 8) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 0)); + VECT_B(r, 9) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 1)); + VECT_B(r,10) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 2)); + VECT_B(r,11) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 3)); + VECT_B(r,12) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 4)); + VECT_B(r,13) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 5)); + VECT_B(r,14) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 6)); + VECT_B(r,15) = SATURATE_UB(VECT_H(gCPU.vr[vrB], 7)); + + gCPU.vr[vrD] = r; +} + +/* vpkshss Vector Pack Signed Half Word Signed Saturate + * v.220 + */ +void ppc_opc_vpkshss() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 0)); + VECT_B(r, 1) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 1)); + VECT_B(r, 2) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 2)); + VECT_B(r, 3) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 3)); + VECT_B(r, 4) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 4)); + VECT_B(r, 5) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 5)); + VECT_B(r, 6) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 6)); + VECT_B(r, 7) = SATURATE_SB(VECT_H(gCPU.vr[vrA], 7)); + + VECT_B(r, 8) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 0)); + VECT_B(r, 9) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 1)); + VECT_B(r,10) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 2)); + VECT_B(r,11) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 3)); + VECT_B(r,12) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 4)); + VECT_B(r,13) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 5)); + VECT_B(r,14) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 6)); + VECT_B(r,15) = SATURATE_SB(VECT_H(gCPU.vr[vrB], 7)); + + gCPU.vr[vrD] = r; +} + +/* vpkuwus Vector Pack Unsigned Word Unsigned Saturate + * v.227 + */ +void ppc_opc_vpkuwus() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = SATURATE_UH(VECT_W(gCPU.vr[vrA], 0)); + VECT_H(r, 1) = SATURATE_UH(VECT_W(gCPU.vr[vrA], 1)); + VECT_H(r, 2) = SATURATE_UH(VECT_W(gCPU.vr[vrA], 2)); + VECT_H(r, 3) = SATURATE_UH(VECT_W(gCPU.vr[vrA], 3)); + + VECT_H(r, 4) = SATURATE_UH(VECT_W(gCPU.vr[vrB], 0)); + VECT_H(r, 5) = SATURATE_UH(VECT_W(gCPU.vr[vrB], 1)); + VECT_H(r, 6) = SATURATE_UH(VECT_W(gCPU.vr[vrB], 2)); + VECT_H(r, 7) = SATURATE_UH(VECT_W(gCPU.vr[vrB], 3)); + + gCPU.vr[vrD] = r; +} + +/* vpkswss Vector Pack Signed Word Signed Saturate + * v.222 + */ +void ppc_opc_vpkswss() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = SATURATE_SH(VECT_W(gCPU.vr[vrA], 0)); + VECT_H(r, 1) = SATURATE_SH(VECT_W(gCPU.vr[vrA], 1)); + VECT_H(r, 2) = SATURATE_SH(VECT_W(gCPU.vr[vrA], 2)); + VECT_H(r, 3) = SATURATE_SH(VECT_W(gCPU.vr[vrA], 3)); + + VECT_H(r, 4) = SATURATE_SH(VECT_W(gCPU.vr[vrB], 0)); + VECT_H(r, 5) = SATURATE_SH(VECT_W(gCPU.vr[vrB], 1)); + VECT_H(r, 6) = SATURATE_SH(VECT_W(gCPU.vr[vrB], 2)); + VECT_H(r, 7) = SATURATE_SH(VECT_W(gCPU.vr[vrB], 3)); + + gCPU.vr[vrD] = r; +} + +/* vpkshus Vector Pack Signed Half Word Unsigned Saturate + * v.221 + */ +void ppc_opc_vpkshus() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_B(r, 0) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 0)); + VECT_B(r, 1) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 1)); + VECT_B(r, 2) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 2)); + VECT_B(r, 3) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 3)); + VECT_B(r, 4) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 4)); + VECT_B(r, 5) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 5)); + VECT_B(r, 6) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 6)); + VECT_B(r, 7) = SATURATE_USB(VECT_H(gCPU.vr[vrA], 7)); + + VECT_B(r, 8) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 0)); + VECT_B(r, 9) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 1)); + VECT_B(r,10) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 2)); + VECT_B(r,11) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 3)); + VECT_B(r,12) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 4)); + VECT_B(r,13) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 5)); + VECT_B(r,14) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 6)); + VECT_B(r,15) = SATURATE_USB(VECT_H(gCPU.vr[vrB], 7)); + + gCPU.vr[vrD] = r; +} + +/* vpkswus Vector Pack Signed Word Unsigned Saturate + * v.223 + */ +void ppc_opc_vpkswus() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + VECT_H(r, 0) = SATURATE_USH(VECT_W(gCPU.vr[vrA], 0)); + VECT_H(r, 1) = SATURATE_USH(VECT_W(gCPU.vr[vrA], 1)); + VECT_H(r, 2) = SATURATE_USH(VECT_W(gCPU.vr[vrA], 2)); + VECT_H(r, 3) = SATURATE_USH(VECT_W(gCPU.vr[vrA], 3)); + + VECT_H(r, 4) = SATURATE_USH(VECT_W(gCPU.vr[vrB], 0)); + VECT_H(r, 5) = SATURATE_USH(VECT_W(gCPU.vr[vrB], 1)); + VECT_H(r, 6) = SATURATE_USH(VECT_W(gCPU.vr[vrB], 2)); + VECT_H(r, 7) = SATURATE_USH(VECT_W(gCPU.vr[vrB], 3)); + + gCPU.vr[vrD] = r; +} + +/* vupkhsb Vector Unpack High Signed Byte + * v.277 + */ +void ppc_opc_vupkhsb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_SH(r, 0) = VECT_SB(gCPU.vr[vrB], 0); + VECT_SH(r, 1) = VECT_SB(gCPU.vr[vrB], 1); + VECT_SH(r, 2) = VECT_SB(gCPU.vr[vrB], 2); + VECT_SH(r, 3) = VECT_SB(gCPU.vr[vrB], 3); + VECT_SH(r, 4) = VECT_SB(gCPU.vr[vrB], 4); + VECT_SH(r, 5) = VECT_SB(gCPU.vr[vrB], 5); + VECT_SH(r, 6) = VECT_SB(gCPU.vr[vrB], 6); + VECT_SH(r, 7) = VECT_SB(gCPU.vr[vrB], 7); + + gCPU.vr[vrD] = r; +} + +/* vupkhpx Vector Unpack High Pixel32 + * v.279 + */ +void ppc_opc_vupkhpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_W(r, 0) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 0)); + VECT_W(r, 1) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 1)); + VECT_W(r, 2) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 2)); + VECT_W(r, 3) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 3)); + + gCPU.vr[vrD] = r; +} + +/* vupkhsh Vector Unpack High Signed Half Word + * v.278 + */ +void ppc_opc_vupkhsh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_SW(r, 0) = VECT_SH(gCPU.vr[vrB], 0); + VECT_SW(r, 1) = VECT_SH(gCPU.vr[vrB], 1); + VECT_SW(r, 2) = VECT_SH(gCPU.vr[vrB], 2); + VECT_SW(r, 3) = VECT_SH(gCPU.vr[vrB], 3); + + gCPU.vr[vrD] = r; +} + +/* vupklsb Vector Unpack Low Signed Byte + * v.280 + */ +void ppc_opc_vupklsb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_SH(r, 0) = VECT_SB(gCPU.vr[vrB], 8); + VECT_SH(r, 1) = VECT_SB(gCPU.vr[vrB], 9); + VECT_SH(r, 2) = VECT_SB(gCPU.vr[vrB],10); + VECT_SH(r, 3) = VECT_SB(gCPU.vr[vrB],11); + VECT_SH(r, 4) = VECT_SB(gCPU.vr[vrB],12); + VECT_SH(r, 5) = VECT_SB(gCPU.vr[vrB],13); + VECT_SH(r, 6) = VECT_SB(gCPU.vr[vrB],14); + VECT_SH(r, 7) = VECT_SB(gCPU.vr[vrB],15); + + gCPU.vr[vrD] = r; +} + +/* vupklpx Vector Unpack Low Pixel32 + * v.279 + */ +void ppc_opc_vupklpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_W(r, 0) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 4)); + VECT_W(r, 1) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 5)); + VECT_W(r, 2) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 6)); + VECT_W(r, 3) = UNPACK_PIXEL(VECT_H(gCPU.vr[vrB], 7)); + + gCPU.vr[vrD] = r; +} + +/* vupklsh Vector Unpack Low Signed Half Word + * v.281 + */ +void ppc_opc_vupklsh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + Vector_t r; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + VECT_SW(r, 0) = VECT_SH(gCPU.vr[vrB], 4); + VECT_SW(r, 1) = VECT_SH(gCPU.vr[vrB], 5); + VECT_SW(r, 2) = VECT_SH(gCPU.vr[vrB], 6); + VECT_SW(r, 3) = VECT_SH(gCPU.vr[vrB], 7); + + gCPU.vr[vrD] = r; +} + +/* vaddubm Vector Add Unsigned Byte Modulo + * v.141 + */ +void ppc_opc_vaddubm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint8 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = gCPU.vr[vrA].b[i] + gCPU.vr[vrB].b[i]; + gCPU.vr[vrD].b[i] = res; + } +} + +/* vadduhm Vector Add Unsigned Half Word Modulo + * v.143 + */ +void ppc_opc_vadduhm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = gCPU.vr[vrA].h[i] + gCPU.vr[vrB].h[i]; + gCPU.vr[vrD].h[i] = res; + } +} + +/* vadduwm Vector Add Unsigned Word Modulo + * v.145 + */ +void ppc_opc_vadduwm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].w[i] + gCPU.vr[vrB].w[i]; + gCPU.vr[vrD].w[i] = res; + } +} + +/* vaddfp Vector Add Float Point + * v.137 + */ +void ppc_opc_vaddfp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + float res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + res = gCPU.vr[vrA].f[i] + gCPU.vr[vrB].f[i]; + gCPU.vr[vrD].f[i] = res; + } +} + +/* vaddcuw Vector Add Carryout Unsigned Word + * v.136 + */ +void ppc_opc_vaddcuw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].w[i] + gCPU.vr[vrB].w[i]; + gCPU.vr[vrD].w[i] = (res < gCPU.vr[vrA].w[i]) ? 1 : 0; + } +} + +/* vaddubs Vector Add Unsigned Byte Saturate + * v.142 + */ +void ppc_opc_vaddubs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = (uint16)gCPU.vr[vrA].b[i] + (uint16)gCPU.vr[vrB].b[i]; + gCPU.vr[vrD].b[i] = SATURATE_UB(res); + } +} + +/* vaddsbs Vector Add Signed Byte Saturate + * v.138 + */ +void ppc_opc_vaddsbs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = (sint16)gCPU.vr[vrA].sb[i] + (sint16)gCPU.vr[vrB].sb[i]; + gCPU.vr[vrD].b[i] = SATURATE_SB(res); + } +} + +/* vadduhs Vector Add Unsigned Half Word Saturate + * v.144 + */ +void ppc_opc_vadduhs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = (uint32)gCPU.vr[vrA].h[i] + (uint32)gCPU.vr[vrB].h[i]; + gCPU.vr[vrD].h[i] = SATURATE_UH(res); + } +} + +/* vaddshs Vector Add Signed Half Word Saturate + * v.139 + */ +void ppc_opc_vaddshs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = (sint32)gCPU.vr[vrA].sh[i] + (sint32)gCPU.vr[vrB].sh[i]; + gCPU.vr[vrD].h[i] = SATURATE_SH(res); + } +} + +/* vadduws Vector Add Unsigned Word Saturate + * v.146 + */ +void ppc_opc_vadduws() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].w[i] + gCPU.vr[vrB].w[i]; + + // We do this to prevent us from having to do 64-bit math + if (res < gCPU.vr[vrA].w[i]) { + res = 0xFFFFFFFF; + gCPU.vscr |= VSCR_SAT; + } + + /* 64-bit math | 32-bit hack + * ------------------------+------------------------------------- + * add, addc (a+b) | add (a+b) + * sub, subb (r>ub) | sub (rub) | xor, and (sign == sign) + * sub, subb (r gCPU.vr[vrA].w[i]) { + res = 0; + gCPU.vscr |= VSCR_SAT; + } + + /* 64-bit math | 32-bit hack + * ------------------------+------------------------------------- + * sub, subb (a+b) | sub (a+b) + * sub, subb (r>ub) | sub (rub) | xor, and (sign == sign) + * sub, subb (r> 15) + (sint32)gCPU.vr[vrC].sh[i]; + + gCPU.vr[vrD].sh[i] = SATURATE_SH(prod); + } +} + +/* vmladduhm Vector Multiply Low and Add Unsigned Half Word Modulo + * v.194 + */ +void ppc_opc_vmladduhm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + uint32 prod; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<8; i++) { + prod = (uint32)gCPU.vr[vrA].h[i] * (uint32)gCPU.vr[vrB].h[i]; + + prod = prod + (uint32)gCPU.vr[vrC].h[i]; + + gCPU.vr[vrD].h[i] = prod; + } +} + +/* vmhraddshs Vector Multiply High Round and Add Signed Half Word Saturate + * v.186 + */ +void ppc_opc_vmhraddshs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + sint32 prod; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<8; i++) { + prod = (sint32)gCPU.vr[vrA].sh[i] * (sint32)gCPU.vr[vrB].sh[i]; + + prod += 0x4000; + prod = (prod >> 15) + (sint32)gCPU.vr[vrC].sh[i]; + + gCPU.vr[vrD].sh[i] = SATURATE_SH(prod); + } +} + +/* vmsumubm Vector Multiply Sum Unsigned Byte Modulo + * v.204 + */ +void ppc_opc_vmsumubm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + uint32 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].w[i]; + + temp += (uint16)gCPU.vr[vrA].b[i<<2] * + (uint16)gCPU.vr[vrB].b[i<<2]; + + temp += (uint16)gCPU.vr[vrA].b[(i<<2)+1] * + (uint16)gCPU.vr[vrB].b[(i<<2)+1]; + + temp += (uint16)gCPU.vr[vrA].b[(i<<2)+2] * + (uint16)gCPU.vr[vrB].b[(i<<2)+2]; + + temp += (uint16)gCPU.vr[vrA].b[(i<<2)+3] * + (uint16)gCPU.vr[vrB].b[(i<<2)+3]; + + gCPU.vr[vrD].w[i] = temp; + } +} + +/* vmsumuhm Vector Multiply Sum Unsigned Half Word Modulo + * v.205 + */ +void ppc_opc_vmsumuhm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + uint32 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].w[i]; + + temp += (uint32)gCPU.vr[vrA].h[i<<1] * + (uint32)gCPU.vr[vrB].h[i<<1]; + temp += (uint32)gCPU.vr[vrA].h[(i<<1)+1] * + (uint32)gCPU.vr[vrB].h[(i<<1)+1]; + + gCPU.vr[vrD].w[i] = temp; + } +} + +/* vmsummbm Vector Multiply Sum Mixed-Sign Byte Modulo + * v.201 + */ +void ppc_opc_vmsummbm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + sint32 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].sw[i]; + + temp += (sint16)gCPU.vr[vrA].sb[i<<2] * + (uint16)gCPU.vr[vrB].b[i<<2]; + temp += (sint16)gCPU.vr[vrA].sb[(i<<2)+1] * + (uint16)gCPU.vr[vrB].b[(i<<2)+1]; + temp += (sint16)gCPU.vr[vrA].sb[(i<<2)+2] * + (uint16)gCPU.vr[vrB].b[(i<<2)+2]; + temp += (sint16)gCPU.vr[vrA].sb[(i<<2)+3] * + (uint16)gCPU.vr[vrB].b[(i<<2)+3]; + + gCPU.vr[vrD].sw[i] = temp; + } +} + +/* vmsumshm Vector Multiply Sum Signed Half Word Modulo + * v.202 + */ +void ppc_opc_vmsumshm() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + sint32 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].sw[i]; + + temp += (sint32)gCPU.vr[vrA].sh[i<<1] * + (sint32)gCPU.vr[vrB].sh[i<<1]; + temp += (sint32)gCPU.vr[vrA].sh[(i<<1)+1] * + (sint32)gCPU.vr[vrB].sh[(i<<1)+1]; + + gCPU.vr[vrD].sw[i] = temp; + } +} + +/* vmsumuhs Vector Multiply Sum Unsigned Half Word Saturate + * v.206 + */ +void ppc_opc_vmsumuhs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + uint64 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + /* For this, there's no way to get around 64-bit math. If we use + * the hacks used before, then we have to do it so often, that + * we'll outpace the 64-bit math in execution time. + */ + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].w[i]; + + temp += (uint32)gCPU.vr[vrA].h[i<<1] * + (uint32)gCPU.vr[vrB].h[i<<1]; + + temp += (uint32)gCPU.vr[vrA].h[(i<<1)+1] * + (uint32)gCPU.vr[vrB].h[(i<<1)+1]; + + gCPU.vr[vrD].w[i] = SATURATE_UW(temp); + } +} + +/* vmsumshs Vector Multiply Sum Signed Half Word Saturate + * v.203 + */ +void ppc_opc_vmsumshs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + sint64 temp; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + /* For this, there's no way to get around 64-bit math. If we use + * the hacks used before, then we have to do it so often, that + * we'll outpace the 64-bit math in execution time. + */ + + for (int i=0; i<4; i++) { + temp = gCPU.vr[vrC].sw[i]; + + temp += (sint32)gCPU.vr[vrA].sh[i<<1] * + (sint32)gCPU.vr[vrB].sh[i<<1]; + temp += (sint32)gCPU.vr[vrA].sh[(i<<1)+1] * + (sint32)gCPU.vr[vrB].sh[(i<<1)+1]; + + gCPU.vr[vrD].sw[i] = SATURATE_SW(temp); + } +} + +/* vsum4ubs Vector Sum Across Partial (1/4) Unsigned Byte Saturate + * v.275 + */ +void ppc_opc_vsum4ubs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + /* For this, there's no way to get around 64-bit math. If we use + * the hacks used before, then we have to do it so often, that + * we'll outpace the 64-bit math in execution time. + */ + + for (int i=0; i<4; i++) { + res = (uint64)gCPU.vr[vrB].w[i]; + + res += (uint64)gCPU.vr[vrA].b[(i<<2)]; + res += (uint64)gCPU.vr[vrA].b[(i<<2)+1]; + res += (uint64)gCPU.vr[vrA].b[(i<<2)+2]; + res += (uint64)gCPU.vr[vrA].b[(i<<2)+3]; + + gCPU.vr[vrD].w[i] = SATURATE_UW(res); + } +} + +/* vsum4sbs Vector Sum Across Partial (1/4) Signed Byte Saturate + * v.273 + */ +void ppc_opc_vsum4sbs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = (sint64)gCPU.vr[vrB].sw[i]; + + res += (sint64)gCPU.vr[vrA].sb[(i<<2)]; + res += (sint64)gCPU.vr[vrA].sb[(i<<2)+1]; + res += (sint64)gCPU.vr[vrA].sb[(i<<2)+2]; + res += (sint64)gCPU.vr[vrA].sb[(i<<2)+3]; + + gCPU.vr[vrD].sw[i] = SATURATE_SW(res); + } +} + +/* vsum4shs Vector Sum Across Partial (1/4) Signed Half Word Saturate + * v.274 + */ +void ppc_opc_vsum4shs() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = (sint64)gCPU.vr[vrB].sw[i]; + + res += (sint64)gCPU.vr[vrA].sh[(i<<1)]; + res += (sint64)gCPU.vr[vrA].sh[(i<<1)+1]; + + gCPU.vr[vrD].sw[i] = SATURATE_SW(res); + } +} + +/* vsum2sws Vector Sum Across Partial (1/2) Signed Word Saturate + * v.272 + */ +void ppc_opc_vsum2sws() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + res = (sint64)gCPU.vr[vrA].sw[0] + (sint64)gCPU.vr[vrA].sw[1]; + res += (sint64)gCPU.vr[vrB].sw[VECT_ODD(0)]; + + gCPU.vr[vrD].w[VECT_ODD(0)] = SATURATE_SW(res); + gCPU.vr[vrD].w[VECT_EVEN(0)] = 0; + + res = (sint64)gCPU.vr[vrA].sw[2] + (sint64)gCPU.vr[vrA].sw[3]; + res += (sint64)gCPU.vr[vrB].sw[VECT_ODD(1)]; + + gCPU.vr[vrD].w[VECT_ODD(1)] = SATURATE_SW(res); + gCPU.vr[vrD].w[VECT_EVEN(1)] = 0; +} + +/* vsumsws Vector Sum Across Signed Word Saturate + * v.271 + */ +void ppc_opc_vsumsws() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + res = (sint64)gCPU.vr[vrA].sw[0] + (sint64)gCPU.vr[vrA].sw[1]; + res += (sint64)gCPU.vr[vrA].sw[2] + (sint64)gCPU.vr[vrA].sw[3]; + + res += (sint64)VECT_W(gCPU.vr[vrB], 3); + + VECT_W(gCPU.vr[vrD], 3) = SATURATE_SW(res); + VECT_W(gCPU.vr[vrD], 2) = 0; + VECT_W(gCPU.vr[vrD], 1) = 0; + VECT_W(gCPU.vr[vrD], 0) = 0; +} + +/* vnmsubfp Vector Negative Multiply-Subtract Floating Point + * v.215 + */ +void ppc_opc_vnmsubfp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB, vrC; + double res; + PPC_OPC_TEMPL_A(gCPU.current_opc, vrD, vrA, vrB, vrC); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + res = (double)gCPU.vr[vrA].f[i] * (double)gCPU.vr[vrC].f[i]; + + res = (double)gCPU.vr[vrB].f[i] - res; + + gCPU.vr[vrD].f[i] = (float)res; + } +} + +/* vavgub Vector Average Unsigned Byte + * v.152 + */ +void ppc_opc_vavgub() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = (uint16)gCPU.vr[vrA].b[i] + + (uint16)gCPU.vr[vrB].b[i] + 1; + + gCPU.vr[vrD].b[i] = (res >> 1); + } +} + +/* vavguh Vector Average Unsigned Half Word + * v.153 + */ +void ppc_opc_vavguh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = (uint32)gCPU.vr[vrA].h[i] + + (uint32)gCPU.vr[vrB].h[i] + 1; + + gCPU.vr[vrD].h[i] = (res >> 1); + } +} + +/* vavguw Vector Average Unsigned Word + * v.154 + */ +void ppc_opc_vavguw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = (uint64)gCPU.vr[vrA].w[i] + + (uint64)gCPU.vr[vrB].w[i] + 1; + + gCPU.vr[vrD].w[i] = (res >> 1); + } +} + +/* vavgsb Vector Average Signed Byte + * v.149 + */ +void ppc_opc_vavgsb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = (sint16)gCPU.vr[vrA].sb[i] + + (sint16)gCPU.vr[vrB].sb[i] + 1; + + gCPU.vr[vrD].sb[i] = (res >> 1); + } +} + +/* vavgsh Vector Average Signed Half Word + * v.150 + */ +void ppc_opc_vavgsh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = (sint32)gCPU.vr[vrA].sh[i] + + (sint32)gCPU.vr[vrB].sh[i] + 1; + + gCPU.vr[vrD].sh[i] = (res >> 1); + } +} + +/* vavgsw Vector Average Signed Word + * v.151 + */ +void ppc_opc_vavgsw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint64 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = (sint64)gCPU.vr[vrA].sw[i] + + (sint64)gCPU.vr[vrB].sw[i] + 1; + + gCPU.vr[vrD].sw[i] = (res >> 1); + } +} + +/* vmaxub Vector Maximum Unsigned Byte + * v.182 + */ +void ppc_opc_vmaxub() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint8 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = gCPU.vr[vrA].b[i]; + + if (res < gCPU.vr[vrB].b[i]) + res = gCPU.vr[vrB].b[i]; + + gCPU.vr[vrD].b[i] = res; + } +} + +/* vmaxuh Vector Maximum Unsigned Half Word + * v.183 + */ +void ppc_opc_vmaxuh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = gCPU.vr[vrA].h[i]; + + if (res < gCPU.vr[vrB].h[i]) + res = gCPU.vr[vrB].h[i]; + + gCPU.vr[vrD].h[i] = res; + } +} + +/* vmaxuw Vector Maximum Unsigned Word + * v.184 + */ +void ppc_opc_vmaxuw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].w[i]; + + if (res < gCPU.vr[vrB].w[i]) + res = gCPU.vr[vrB].w[i]; + + gCPU.vr[vrD].w[i] = res; + } +} + +/* vmaxsb Vector Maximum Signed Byte + * v.179 + */ +void ppc_opc_vmaxsb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint8 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = gCPU.vr[vrA].sb[i]; + + if (res < gCPU.vr[vrB].sb[i]) + res = gCPU.vr[vrB].sb[i]; + + gCPU.vr[vrD].sb[i] = res; + } +} + +/* vmaxsh Vector Maximum Signed Half Word + * v.180 + */ +void ppc_opc_vmaxsh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = gCPU.vr[vrA].sh[i]; + + if (res < gCPU.vr[vrB].sh[i]) + res = gCPU.vr[vrB].sh[i]; + + gCPU.vr[vrD].sh[i] = res; + } +} + +/* vmaxsw Vector Maximum Signed Word + * v.181 + */ +void ppc_opc_vmaxsw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].sw[i]; + + if (res < gCPU.vr[vrB].sw[i]) + res = gCPU.vr[vrB].sw[i]; + + gCPU.vr[vrD].sw[i] = res; + } +} + +/* vmaxfp Vector Maximum Floating Point + * v.178 + */ +void ppc_opc_vmaxfp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + float res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + res = gCPU.vr[vrA].f[i]; + + if (res < gCPU.vr[vrB].f[i]) + res = gCPU.vr[vrB].f[i]; + + gCPU.vr[vrD].f[i] = res; + } +} + +/* vminub Vector Minimum Unsigned Byte + * v.191 + */ +void ppc_opc_vminub() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint8 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = gCPU.vr[vrA].b[i]; + + if (res > gCPU.vr[vrB].b[i]) + res = gCPU.vr[vrB].b[i]; + + gCPU.vr[vrD].b[i] = res; + } +} + +/* vminuh Vector Minimum Unsigned Half Word + * v.192 + */ +void ppc_opc_vminuh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = gCPU.vr[vrA].h[i]; + + if (res > gCPU.vr[vrB].h[i]) + res = gCPU.vr[vrB].h[i]; + + gCPU.vr[vrD].h[i] = res; + } +} + +/* vminuw Vector Minimum Unsigned Word + * v.193 + */ +void ppc_opc_vminuw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + uint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].w[i]; + + if (res > gCPU.vr[vrB].w[i]) + res = gCPU.vr[vrB].w[i]; + + gCPU.vr[vrD].w[i] = res; + } +} + +/* vminsb Vector Minimum Signed Byte + * v.188 + */ +void ppc_opc_vminsb() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint8 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + res = gCPU.vr[vrA].sb[i]; + + if (res > gCPU.vr[vrB].sb[i]) + res = gCPU.vr[vrB].sb[i]; + + gCPU.vr[vrD].sb[i] = res; + } +} + +/* vminsh Vector Minimum Signed Half Word + * v.189 + */ +void ppc_opc_vminsh() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint16 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + res = gCPU.vr[vrA].sh[i]; + + if (res > gCPU.vr[vrB].sh[i]) + res = gCPU.vr[vrB].sh[i]; + + gCPU.vr[vrD].sh[i] = res; + } +} + +/* vminsw Vector Minimum Signed Word + * v.190 + */ +void ppc_opc_vminsw() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + sint32 res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + res = gCPU.vr[vrA].sw[i]; + + if (res > gCPU.vr[vrB].sw[i]) + res = gCPU.vr[vrB].sw[i]; + + gCPU.vr[vrD].sw[i] = res; + } +} + +/* vminfp Vector Minimum Floating Point + * v.187 + */ +void ppc_opc_vminfp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + float res; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + res = gCPU.vr[vrA].f[i]; + + if (res > gCPU.vr[vrB].f[i]) + res = gCPU.vr[vrB].f[i]; + + gCPU.vr[vrD].f[i] = res; + } +} + +/* vrfin Vector Round to Floating-Point Integer Nearest + * v.231 + */ +void ppc_opc_vrfin() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + /* Documentation doesn't dictate how this instruction should + * round from a middle point. With a test on a real G4, it was + * found to be round to nearest, with bias to even if equidistant. + * + * This is covered by the function rint() + */ + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = rintf(gCPU.vr[vrB].f[i]); + } +} + +/* vrfip Vector Round to Floating-Point Integer toward Plus Infinity + * v.232 + */ +void ppc_opc_vrfip() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = ceilf(gCPU.vr[vrB].f[i]); + } +} + +/* vrfim Vector Round to Floating-Point Integer toward Minus Infinity + * v.230 + */ +void ppc_opc_vrfim() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = floorf(gCPU.vr[vrB].f[i]); + } +} + +/* vrfiz Vector Round to Floating-Point Integer toward Zero + * v.233 + */ +void ppc_opc_vrfiz() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = truncf(gCPU.vr[vrD].f[i]); + } +} + +/* vrefp Vector Reciprocal Estimate Floating Point + * v.228 + */ +void ppc_opc_vrefp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + /* This emulation generates an exact value, instead of an estimate. + * This is technically within specs, but some test-suites expect the + * exact estimate value returned by G4s. These anomolous failures + * should be ignored. + */ + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = 1 / gCPU.vr[vrB].f[i]; + } +} + +/* vrsqrtefp Vector Reciprocal Square Root Estimate Floating Point + * v.237 + */ +void ppc_opc_vrsqrtefp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + /* This emulation generates an exact value, instead of an estimate. + * This is technically within specs, but some test-suites expect the + * exact estimate value returned by G4s. These anomolous failures + * should be ignored. + */ + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = 1 / sqrt(gCPU.vr[vrB].f[i]); + } +} + +/* vlogefp Vector Log2 Estimate Floating Point + * v.175 + */ +void ppc_opc_vlogefp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + /* This emulation generates an exact value, instead of an estimate. + * This is technically within specs, but some test-suites expect the + * exact estimate value returned by G4s. These anomolous failures + * should be ignored. + */ + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = log2(gCPU.vr[vrB].f[i]); + } +} + +/* vexptefp Vector 2 Raised to the Exponent Estimate Floating Point + * v.173 + */ +void ppc_opc_vexptefp() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + PPC_OPC_ASSERT(vrA==0); + + /* This emulation generates an exact value, instead of an estimate. + * This is technically within specs, but some test-suites expect the + * exact estimate value returned by G4s. These anomolous failures + * should be ignored. + */ + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = exp2(gCPU.vr[vrB].f[i]); + } +} + +/* vcfux Vector Convert from Unsigned Fixed-Point Word + * v.156 + */ +void ppc_opc_vcfux() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = ((float)gCPU.vr[vrB].w[i]) / (1 << uimm); + } +} + +/* vcfsx Vector Convert from Signed Fixed-Point Word + * v.155 + */ +void ppc_opc_vcfsx() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + gCPU.vr[vrD].f[i] = ((float)gCPU.vr[vrB].sw[i]) / (1 << uimm); + } +} + +/* vctsxs Vector Convert To Signed Fixed-Point Word Saturate + * v.171 + */ +void ppc_opc_vctsxs() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 uimm; + float ftmp; + sint32 tmp; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + ftmp = gCPU.vr[vrB].f[i] * (float)(1 << uimm); + ftmp = truncf(ftmp); + + tmp = (sint32)ftmp; + + if (ftmp > 2147483647.0) { + tmp = 2147483647; // 0x7fffffff + gCPU.vscr |= VSCR_SAT; + } else if (ftmp < -2147483648.0) { + tmp = -2147483648LL; // 0x80000000 + gCPU.vscr |= VSCR_SAT; + } + + gCPU.vr[vrD].sw[i] = tmp; + } +} + +/* vctuxs Vector Convert to Unsigned Fixed-Point Word Saturate + * v.172 + */ +void ppc_opc_vctuxs() +{ + VECTOR_DEBUG; + int vrD, vrB; + uint32 tmp, uimm; + float ftmp; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, uimm, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + ftmp = gCPU.vr[vrB].f[i] * (float)(1 << uimm); + ftmp = truncf(ftmp); + + tmp = (uint32)ftmp; + + if (ftmp > 4294967295.0) { + tmp = 0xffffffff; + gCPU.vscr |= VSCR_SAT; + } else if (ftmp < 0) { + tmp = 0; + gCPU.vscr |= VSCR_SAT; + } + + gCPU.vr[vrD].w[i] = tmp; + } +} + +/* vand Vector Logical AND + * v.147 + */ +void ppc_opc_vand() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + gCPU.vr[vrD].d[0] = gCPU.vr[vrA].d[0] & gCPU.vr[vrB].d[0]; + gCPU.vr[vrD].d[1] = gCPU.vr[vrA].d[1] & gCPU.vr[vrB].d[1]; +} + +/* vandc Vector Logical AND with Complement + * v.148 + */ +void ppc_opc_vandc() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + gCPU.vr[vrD].d[0] = gCPU.vr[vrA].d[0] & ~gCPU.vr[vrB].d[0]; + gCPU.vr[vrD].d[1] = gCPU.vr[vrA].d[1] & ~gCPU.vr[vrB].d[1]; +} + +/* vor Vector Logical OR + * v.217 + */ +void ppc_opc_vor() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + gCPU.vr[vrD].d[0] = gCPU.vr[vrA].d[0] | gCPU.vr[vrB].d[0]; + gCPU.vr[vrD].d[1] = gCPU.vr[vrA].d[1] | gCPU.vr[vrB].d[1]; +} + +/* vnor Vector Logical NOR + * v.216 + */ +void ppc_opc_vnor() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + gCPU.vr[vrD].d[0] = ~(gCPU.vr[vrA].d[0] | gCPU.vr[vrB].d[0]); + gCPU.vr[vrD].d[1] = ~(gCPU.vr[vrA].d[1] | gCPU.vr[vrB].d[1]); +} + +/* vxor Vector Logical XOR + * v.282 + */ +void ppc_opc_vxor() +{ + VECTOR_DEBUG_COMMON; + int vrD, vrA, vrB; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + gCPU.vr[vrD].d[0] = gCPU.vr[vrA].d[0] ^ gCPU.vr[vrB].d[0]; + gCPU.vr[vrD].d[1] = gCPU.vr[vrA].d[1] ^ gCPU.vr[vrB].d[1]; +} + +#define CR_CR6 (0x00f0) +#define CR_CR6_EQ (1<<7) +#define CR_CR6_NE_SOME (1<<6) +#define CR_CR6_NE (1<<5) +#define CR_CR6_EQ_SOME (1<<4) + +/* vcmpequbx Vector Compare Equal-to Unsigned Byte + * v.160 + */ +void ppc_opc_vcmpequbx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + if (gCPU.vr[vrA].b[i] == gCPU.vr[vrB].b[i]) { + gCPU.vr[vrD].b[i] = 0xff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].b[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpequhx Vector Compare Equal-to Unsigned Half Word + * v.161 + */ +void ppc_opc_vcmpequhx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + if (gCPU.vr[vrA].h[i] == gCPU.vr[vrB].h[i]) { + gCPU.vr[vrD].h[i] = 0xffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].h[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpequwx Vector Compare Equal-to Unsigned Word + * v.162 + */ +void ppc_opc_vcmpequwx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + if (gCPU.vr[vrA].w[i] == gCPU.vr[vrB].w[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpeqfpx Vector Compare Equal-to-Floating Point + * v.159 + */ +void ppc_opc_vcmpeqfpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + if (gCPU.vr[vrA].f[i] == gCPU.vr[vrB].f[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtubx Vector Compare Greater-Than Unsigned Byte + * v.168 + */ +void ppc_opc_vcmpgtubx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + if (gCPU.vr[vrA].b[i] > gCPU.vr[vrB].b[i]) { + gCPU.vr[vrD].b[i] = 0xff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].b[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtsbx Vector Compare Greater-Than Signed Byte + * v.165 + */ +void ppc_opc_vcmpgtsbx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<16; i++) { + if (gCPU.vr[vrA].sb[i] > gCPU.vr[vrB].sb[i]) { + gCPU.vr[vrD].b[i] = 0xff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].b[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtuhx Vector Compare Greater-Than Unsigned Half Word + * v.169 + */ +void ppc_opc_vcmpgtuhx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + if (gCPU.vr[vrA].h[i] > gCPU.vr[vrB].h[i]) { + gCPU.vr[vrD].h[i] = 0xffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].h[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtshx Vector Compare Greater-Than Signed Half Word + * v.166 + */ +void ppc_opc_vcmpgtshx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<8; i++) { + if (gCPU.vr[vrA].sh[i] > gCPU.vr[vrB].sh[i]) { + gCPU.vr[vrD].h[i] = 0xffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].h[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtuwx Vector Compare Greater-Than Unsigned Word + * v.170 + */ +void ppc_opc_vcmpgtuwx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + if (gCPU.vr[vrA].w[i] > gCPU.vr[vrB].w[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtswx Vector Compare Greater-Than Signed Word + * v.167 + */ +void ppc_opc_vcmpgtswx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { + if (gCPU.vr[vrA].sw[i] > gCPU.vr[vrB].sw[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgtfpx Vector Compare Greater-Than Floating-Point + * v.164 + */ +void ppc_opc_vcmpgtfpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + if (gCPU.vr[vrA].f[i] > gCPU.vr[vrB].f[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpgefpx Vector Compare Greater-Than-or-Equal-to Floating Point + * v.163 + */ +void ppc_opc_vcmpgefpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int tf=CR_CR6_EQ | CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + if (gCPU.vr[vrA].f[i] >= gCPU.vr[vrB].f[i]) { + gCPU.vr[vrD].w[i] = 0xffffffff; + tf &= ~CR_CR6_NE; + tf |= CR_CR6_EQ_SOME; + } else { + gCPU.vr[vrD].w[i] = 0; + tf &= ~CR_CR6_EQ; + tf |= CR_CR6_NE_SOME; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= tf; + } +} + +/* vcmpbfpx Vector Compare Bounds Floating Point + * v.157 + */ +void ppc_opc_vcmpbfpx() +{ + VECTOR_DEBUG; + int vrD, vrA, vrB; + int le, ge; + int ib=CR_CR6_NE; + PPC_OPC_TEMPL_X(gCPU.current_opc, vrD, vrA, vrB); + + for (int i=0; i<4; i++) { //FIXME: This might not comply with Java FP + le = (gCPU.vr[vrA].f[i] <= gCPU.vr[vrB].f[i]) ? 0 : 0x80000000; + ge = (gCPU.vr[vrA].f[i] >= -gCPU.vr[vrB].f[i]) ? 0 : 0x40000000; + + gCPU.vr[vrD].w[i] = le | ge; + if (le | ge) { + ib = 0; + } + } + + if (PPC_OPC_VRc & gCPU.current_opc) { + gCPU.cr &= ~CR_CR6; + gCPU.cr |= ib; + } +} diff --git a/ppc/pearpc/cpu/cpu_generic/ppc_vec.h b/ppc/pearpc/cpu/cpu_generic/ppc_vec.h new file mode 100644 index 00000000..9c9a08b0 --- /dev/null +++ b/ppc/pearpc/cpu/cpu_generic/ppc_vec.h @@ -0,0 +1,226 @@ +/* + * PearPC + * ppc_vec.h + * + * Copyright (C) 2004 Daniel Foesch (dfoesch@cs.nmsu.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __PPC_VEC_H__ +#define __PPC_VEC_H__ + +#define PPC_OPC_VRc (1<<10) + +/* Rather than write each function to be endianless, we're writing these + * defines to do an endianless access to elements of the vector. + * + * These are for ADDRESSED vector elements. Usually, most vector operations + * can be performed in either direction without care, so most of the + * for-loops should not use these, as it will introduce unneeded code + * for little-endian systems. + */ +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + +#define VECT_B(reg, index) ((reg).b[15 - (index)]) +#define VECT_SB(reg, index) ((reg).sb[15 - (index)]) +#define VECT_H(reg, index) ((reg).h[7 - (index)]) +#define VECT_SH(reg, index) ((reg).sh[7 - (index)]) +#define VECT_W(reg, index) ((reg).w[3 - (index)]) +#define VECT_SW(reg, index) ((reg).sw[3 - (index)]) +#define VECT_D(reg, index) ((reg).d[1 - (index)]) +#define VECT_SD(reg, index) ((reg).sd[1 - (index)]) + +#define VECT_EVEN(index) (((index) << 1) + 1) +#define VECT_ODD(index) (((index) << 1) + 0) + +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE + +#define VECT_B(reg, index) ((reg).b[(index)]) +#define VECT_SB(reg, index) ((reg).sb[(index)]) +#define VECT_H(reg, index) ((reg).h[(index)]) +#define VECT_SH(reg, index) ((reg).sh[(index)]) +#define VECT_W(reg, index) ((reg).w[(index)]) +#define VECT_SW(reg, index) ((reg).sw[(index)]) +#define VECT_D(reg, index) ((reg).d[(index)]) +#define VECT_SD(reg, index) ((reg).sd[(index)]) + +#define VECT_EVEN(index) (((index) << 1) + 0) +#define VECT_ODD(index) (((index) << 1) + 1) + +#else +#error Endianess not supported! +#endif + +//#define VECTOR_DEBUG fprintf(stderr, "[PPC/VEC] %s\n", __FUNCTION__) +#define VECTOR_DEBUG + +//#define VECTOR_DEBUG_COMMON fprintf(stderr, "[PPC/VEC] %s\n", __FUNCTION__) +#define VECTOR_DEBUG_COMMON + +/* Undefine this to turn of the MSR_VEC check for vector instructions. */ +//#define __VEC_EXC_OFF__ + +#include "system/types.h" + +#include "tools/snprintf.h" + +void ppc_opc_vperm(); +void ppc_opc_vsel(); +void ppc_opc_vsrb(); +void ppc_opc_vsrh(); +void ppc_opc_vsrw(); +void ppc_opc_vsrab(); +void ppc_opc_vsrah(); +void ppc_opc_vsraw(); +void ppc_opc_vsr(); +void ppc_opc_vsro(); +void ppc_opc_vslb(); +void ppc_opc_vslh(); +void ppc_opc_vslw(); +void ppc_opc_vsl(); +void ppc_opc_vslo(); +void ppc_opc_vsldoi(); +void ppc_opc_vrlb(); +void ppc_opc_vrlh(); +void ppc_opc_vrlw(); +void ppc_opc_vmrghb(); +void ppc_opc_vmrghh(); +void ppc_opc_vmrghw(); +void ppc_opc_vmrglb(); +void ppc_opc_vmrglh(); +void ppc_opc_vmrglw(); +void ppc_opc_vspltb(); +void ppc_opc_vsplth(); +void ppc_opc_vspltw(); +void ppc_opc_vspltisb(); +void ppc_opc_vspltish(); +void ppc_opc_vspltisw(); +void ppc_opc_mfvscr(); +void ppc_opc_mtvscr(); +void ppc_opc_vpkuhum(); +void ppc_opc_vpkuwum(); +void ppc_opc_vpkpx(); +void ppc_opc_vpkuhus(); +void ppc_opc_vpkshss(); +void ppc_opc_vpkuwus(); +void ppc_opc_vpkswss(); +void ppc_opc_vpkuhus(); +void ppc_opc_vpkshus(); +void ppc_opc_vpkuwus(); +void ppc_opc_vpkswus(); +void ppc_opc_vupkhsb(); +void ppc_opc_vupkhpx(); +void ppc_opc_vupkhsh(); +void ppc_opc_vupklsb(); +void ppc_opc_vupklpx(); +void ppc_opc_vupklsh(); +void ppc_opc_vaddubm(); +void ppc_opc_vadduhm(); +void ppc_opc_vadduwm(); +void ppc_opc_vaddfp(); +void ppc_opc_vaddcuw(); +void ppc_opc_vaddubs(); +void ppc_opc_vaddsbs(); +void ppc_opc_vadduhs(); +void ppc_opc_vaddshs(); +void ppc_opc_vadduws(); +void ppc_opc_vaddsws(); +void ppc_opc_vsububm(); +void ppc_opc_vsubuhm(); +void ppc_opc_vsubuwm(); +void ppc_opc_vsubfp(); +void ppc_opc_vsubcuw(); +void ppc_opc_vsububs(); +void ppc_opc_vsubsbs(); +void ppc_opc_vsubuhs(); +void ppc_opc_vsubshs(); +void ppc_opc_vsubuws(); +void ppc_opc_vsubsws(); +void ppc_opc_vmuleub(); +void ppc_opc_vmulesb(); +void ppc_opc_vmuleuh(); +void ppc_opc_vmulesh(); +void ppc_opc_vmuloub(); +void ppc_opc_vmulosb(); +void ppc_opc_vmulouh(); +void ppc_opc_vmulosh(); +void ppc_opc_vmaddfp(); +void ppc_opc_vmhaddshs(); +void ppc_opc_vmladduhm(); +void ppc_opc_vmhraddshs(); +void ppc_opc_vmsumubm(); +void ppc_opc_vmsumuhm(); +void ppc_opc_vmsummbm(); +void ppc_opc_vmsumshm(); +void ppc_opc_vmsumuhs(); +void ppc_opc_vmsumshs(); +void ppc_opc_vsum4ubs(); +void ppc_opc_vsum4sbs(); +void ppc_opc_vsum4shs(); +void ppc_opc_vsum2sws(); +void ppc_opc_vsumsws(); +void ppc_opc_vnmsubfp(); +void ppc_opc_vavgub(); +void ppc_opc_vavgsb(); +void ppc_opc_vavguh(); +void ppc_opc_vavgsh(); +void ppc_opc_vavguw(); +void ppc_opc_vavgsw(); +void ppc_opc_vmaxub(); +void ppc_opc_vmaxsb(); +void ppc_opc_vmaxuh(); +void ppc_opc_vmaxsh(); +void ppc_opc_vmaxuw(); +void ppc_opc_vmaxsw(); +void ppc_opc_vmaxfp(); +void ppc_opc_vminub(); +void ppc_opc_vminsb(); +void ppc_opc_vminuh(); +void ppc_opc_vminsh(); +void ppc_opc_vminuw(); +void ppc_opc_vminsw(); +void ppc_opc_vminfp(); +void ppc_opc_vrfin(); +void ppc_opc_vrfip(); +void ppc_opc_vrfim(); +void ppc_opc_vrfiz(); +void ppc_opc_vrefp(); +void ppc_opc_vrsqrtefp(); +void ppc_opc_vlogefp(); +void ppc_opc_vexptefp(); +void ppc_opc_vcfux(); +void ppc_opc_vcfsx(); +void ppc_opc_vctsxs(); +void ppc_opc_vctuxs(); +void ppc_opc_vand(); +void ppc_opc_vandc(); +void ppc_opc_vor(); +void ppc_opc_vnor(); +void ppc_opc_vxor(); +void ppc_opc_vcmpequbx(); +void ppc_opc_vcmpequhx(); +void ppc_opc_vcmpequwx(); +void ppc_opc_vcmpeqfpx(); +void ppc_opc_vcmpgtubx(); +void ppc_opc_vcmpgtsbx(); +void ppc_opc_vcmpgtuhx(); +void ppc_opc_vcmpgtshx(); +void ppc_opc_vcmpgtuwx(); +void ppc_opc_vcmpgtswx(); +void ppc_opc_vcmpgtfpx(); +void ppc_opc_vcmpgefpx(); +void ppc_opc_vcmpbfpx(); + +#endif diff --git a/ppc/pearpc/cpu/debug.h b/ppc/pearpc/cpu/debug.h new file mode 100644 index 00000000..11d8ca8f --- /dev/null +++ b/ppc/pearpc/cpu/debug.h @@ -0,0 +1,34 @@ +/* + * PearPC + * debug.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include "system/types.h" + +/* + * Debugger Interface + */ +void ppc_set_singlestep_v(bool v, const char *file, int line, const char *infoformat, ...); +void ppc_set_singlestep_nonverbose(bool v); + +#define SINGLESTEP(info) ppc_set_singlestep_v(true, __FILE__, __LINE__, info) + +#endif diff --git a/ppc/pearpc/cpu/mem.h b/ppc/pearpc/cpu/mem.h new file mode 100644 index 00000000..36f05c51 --- /dev/null +++ b/ppc/pearpc/cpu/mem.h @@ -0,0 +1,44 @@ +/* + * PearPC + * mem.h + * + * Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __MEM_H__ +#define __MEM_H__ + +#include "system/types.h" + +bool ppc_init_physical_memory(uint size); + +uint32 ppc_get_memory_size(); + +bool ppc_dma_write(uint32 dest, const void *src, uint32 size); +bool ppc_dma_read(void *dest, uint32 src, uint32 size); +bool ppc_dma_set(uint32 dest, int c, uint32 size); + +void ppc_cpu_map_framebuffer(uint32 pa, uint32 ea); + +/* + * These functions will be removed once we switch to openbios. + */ + +bool DEPRECATED ppc_prom_set_sdr1(uint32 newval, bool quiesce); +bool DEPRECATED ppc_prom_effective_to_physical(uint32 &result, uint32 ea); +bool DEPRECATED ppc_prom_page_create(uint32 ea, uint32 pa); + +#endif diff --git a/ppc/pearpc/info.h b/ppc/pearpc/info.h new file mode 100644 index 00000000..b438571b --- /dev/null +++ b/ppc/pearpc/info.h @@ -0,0 +1,35 @@ +/* + * PearPC + * info.h + * + * Copyright (C) 2003-2005 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __INFO_H__ +#define __INFO_H__ + +#define APPNAME "PearPC" +#define APPVERSION "0.5" +// this will show up in the win32 version resource: +#define APPVERSION_IN_NUMBERS 0,0,5,0 + +#define EMULATOR_MODEL "PowerPC ("APPNAME" "APPVERSION")" +#define COPYRIGHT "(c) 2003-2011 Sebastian Biallas " + +//#define PPC_CPU_ENABLE_SINGLESTEP + +#endif + diff --git a/ppc/pearpc/io/io.h b/ppc/pearpc/io/io.h new file mode 100644 index 00000000..202336e4 --- /dev/null +++ b/ppc/pearpc/io/io.h @@ -0,0 +1,236 @@ +/* + * PearPC + * io.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __IO_IO_H__ +#define __IO_IO_H__ + +#include "system/types.h" +#include +#include +#include "cpu/cpu.h" +#include "cpu/debug.h" +#include "cpu/mem.h" +#include "io.h" +//#include "io/graphic/gcard.h" +//#include "io/pic/pic.h" +//#include "io/pci/pci.h" +//#include "io/cuda/cuda.h" +//#include "io/nvram/nvram.h" +#include "debug/tracers.h" + +#define IO_MEM_ACCESS_OK 0 +#define IO_MEM_ACCESS_EXC 1 +#define IO_MEM_ACCESS_FATAL 2 + +extern bool uae_ppc_io_mem_write(uint32, uint32, int); +extern bool uae_ppc_io_mem_read(uint32, uint32&, int); +extern bool uae_ppc_io_mem_write64(uint32, uint64); +extern bool uae_ppc_io_mem_read64(uint32, uint64&); + +static inline int io_mem_write(uint32 addr, uint32 data, int size) +{ + if (uae_ppc_io_mem_write(addr, data, size)) + return IO_MEM_ACCESS_OK; +#if 0 + if (addr >= IO_GCARD_FRAMEBUFFER_PA_START && addr < IO_GCARD_FRAMEBUFFER_PA_END) { + gcard_write(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_PCI_PA_START && addr < IO_PCI_PA_END) { + pci_write(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_PIC_PA_START && addr < IO_PIC_PA_END) { + pic_write(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_CUDA_PA_START && addr < IO_CUDA_PA_END) { + cuda_write(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_NVRAM_PA_START && addr < IO_NVRAM_PA_END) { + nvram_write(addr, data, size); + return IO_MEM_ACCESS_OK; + } + // PCI and ISA must be checked at last + if (addr >= IO_PCI_DEVICE_PA_START && addr < IO_PCI_DEVICE_PA_END) { + pci_write_device(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_ISA_PA_START && addr < IO_ISA_PA_END) { + /* + * should raise exception here... + * but linux dont like this + */ + isa_write(addr, data, size); + return IO_MEM_ACCESS_OK; + /*if (isa_write(addr, data, size)) { + return IO_MEM_ACCESS_OK; + } else { + ppc_exception(PPC_EXC_MACHINE_CHECK); + return IO_MEM_ACCESS_EXC; + }*/ + } +#endif + IO_CORE_WARN("no one is responsible for address %08x (write: %08x from %08x)\n", addr, data, ppc_cpu_get_pc(0)); + SINGLESTEP(""); + ppc_machine_check_exception(); + return IO_MEM_ACCESS_EXC; +} + +static inline int io_mem_read(uint32 addr, uint32 &data, int size) +{ + if (uae_ppc_io_mem_read(addr, data, size)) + return IO_MEM_ACCESS_OK; + +#if 0 + if (addr >= IO_GCARD_FRAMEBUFFER_PA_START && addr < IO_GCARD_FRAMEBUFFER_PA_END) { + gcard_read(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_PCI_PA_START && addr < IO_PCI_PA_END) { + pci_read(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_PIC_PA_START && addr < IO_PIC_PA_END) { + pic_read(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_CUDA_PA_START && addr < IO_CUDA_PA_END) { + cuda_read(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_NVRAM_PA_START && addr < IO_NVRAM_PA_END) { + nvram_read(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr == 0xff000004) { + // wtf? + data = 1; + return IO_MEM_ACCESS_OK; + } + // PCI and ISA must be checked at last + if (addr >= IO_PCI_DEVICE_PA_START && addr < IO_PCI_DEVICE_PA_END) { + pci_read_device(addr, data, size); + return IO_MEM_ACCESS_OK; + } + if (addr >= IO_ISA_PA_START && addr < IO_ISA_PA_END) { + /* + * should raise exception here... + * but linux dont like this + */ + isa_read(addr, data, size); + return IO_MEM_ACCESS_OK; + /*if (isa_read(addr, data, size)) { + return IO_MEM_ACCESS_OK; + } else { + ppc_exception(PPC_EXC_MACHINE_CHECK); + return IO_MEM_ACCESS_EXC; + }*/ + } +#endif + IO_CORE_WARN("no one is responsible for address %08x (read from %08x)\n", addr, ppc_cpu_get_pc(0)); + SINGLESTEP(""); + ppc_machine_check_exception(); + return IO_MEM_ACCESS_EXC; +} + +static inline int io_mem_write64(uint32 addr, uint64 data) +{ + if (uae_ppc_io_mem_write64(addr, data)) + return IO_MEM_ACCESS_OK; +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_write64(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (write64: %016q from %08x)\n", addr, &data, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +static inline int io_mem_read64(uint32 addr, uint64 &data) +{ + if (uae_ppc_io_mem_read64(addr, data)) + return IO_MEM_ACCESS_OK; + +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_read64(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (read64 from %08x)\n", addr, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +static inline int io_mem_write128(uint32 addr, uint128 *data) +{ +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_write128(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (write128: %016q%016q from %08x)\n", addr, data->h, data->l, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +static inline int io_mem_write128_native(uint32 addr, uint128 *data) +{ +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_write128_native(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (write128: %016q%016q from %08x)\n", addr, data->h, data->l, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +static inline int io_mem_read128(uint32 addr, uint128 *data) +{ +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_read128(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (read128 from %08x)\n", addr, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +static inline int io_mem_read128_native(uint32 addr, uint128 *data) +{ +#if 0 + if ((addr >= IO_GCARD_FRAMEBUFFER_PA_START) && (addr < (IO_GCARD_FRAMEBUFFER_PA_END))) { + gcard_read128_native(addr, data); + return IO_MEM_ACCESS_OK; + } +#endif + IO_CORE_ERR("no one is responsible for address %08x (read128 from %08x)\n", addr, ppc_cpu_get_pc(0)); + return IO_MEM_ACCESS_FATAL; +} + +void io_init(); +void io_done(); +void io_init_config(); + +#endif diff --git a/ppc/pearpc/system/arch/generic/sysendian.h b/ppc/pearpc/system/arch/generic/sysendian.h new file mode 100644 index 00000000..7ede7a67 --- /dev/null +++ b/ppc/pearpc/system/arch/generic/sysendian.h @@ -0,0 +1,95 @@ +/* + * PearPC + * sysendian.h + * + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ +#define __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ + +#include "system/types.h" +#include "config.h" + +static inline FUNCTION_CONST uint32 ppc_bswap_word(uint32 data) +{ + return (data>>24)|((data>>8)&0xff00)|((data<<8)&0xff0000)|(data<<24); +} + +static inline FUNCTION_CONST uint64 ppc_bswap_dword(uint64 data) +{ + return (((uint64)ppc_bswap_word(data)) << 32) | (uint64)ppc_bswap_word(data >> 32); +} + +static inline FUNCTION_CONST uint16 ppc_bswap_half(uint16 data) +{ + return (data<<8)|(data>>8); +} + +#if HOST_ENDIANESS == HOST_ENDIANESS_LE + +/* + * Little-endian machine + */ + +# define ppc_dword_from_BE(data) (ppc_bswap_dword(data)) +# define ppc_word_from_BE(data) (ppc_bswap_word(data)) +# define ppc_half_from_BE(data) (ppc_bswap_half(data)) + +# define ppc_dword_from_LE(data) ((uint64)(data)) +# define ppc_word_from_LE(data) ((uint32)(data)) +# define ppc_half_from_LE(data) ((uint16)(data)) + +# define ppc_dword_to_LE(data) ppc_dword_from_LE(data) +# define ppc_word_to_LE(data) ppc_word_from_LE(data) +# define ppc_half_to_LE(data) ppc_half_from_LE(data) + +# define ppc_dword_to_BE(data) ppc_dword_from_BE(data) +# define ppc_word_to_BE(data) ppc_word_from_BE(data) +# define ppc_half_to_BE(data) ppc_half_from_BE(data) + +#elif HOST_ENDIANESS == HOST_ENDIANESS_BE + +/* + * Big-endian machine + */ +# define ppc_dword_from_BE(data) ((uint64)(data)) +# define ppc_word_from_BE(data) ((uint32)(data)) +# define ppc_half_from_BE(data) ((uint16)(data)) + +# define ppc_dword_from_LE(data) (ppc_bswap_dword(data)) +# define ppc_word_from_LE(data) (ppc_bswap_word(data)) +# define ppc_half_from_LE(data) (ppc_bswap_half(data)) + +# define ppc_dword_to_LE(data) ppc_dword_from_LE(data) +# define ppc_word_to_LE(data) ppc_word_from_LE(data) +# define ppc_half_to_LE(data) ppc_half_from_LE(data) + +# define ppc_dword_to_BE(data) ppc_dword_from_BE(data) +# define ppc_word_to_BE(data) ppc_word_from_BE(data) +# define ppc_half_to_BE(data) ppc_half_from_BE(data) + +#else + +/* + * Weird-endian machine + * HOST_ENDIANESS is neither little- nor big-endian? + */ +# error "What kind of a weird machine do you have? It's neither little- nor big-endian??? This is unsupported." + +#endif + +#endif diff --git a/ppc/pearpc/system/arch/generic/sysfeatures.h b/ppc/pearpc/system/arch/generic/sysfeatures.h new file mode 100644 index 00000000..0d960109 --- /dev/null +++ b/ppc/pearpc/system/arch/generic/sysfeatures.h @@ -0,0 +1,30 @@ +/* + * PearPC + * features.h - arch/generic variant + * + * Copyright (C) 2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ +#define __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ + +// PPC_FEATURE_PROVIDED_BY_ARCH +#if !defined(PPC_VIDEO_CONVERT_ACCEL_FEATURE) || (PPC_VIDEO_CONVERT_ACCEL_FEATURE < PPC_FEATURE_PROVIDED_BY_ARCH) +# undef PPC_VIDEO_CONVERT_ACCEL_FEATURE +# define PPC_VIDEO_CONVERT_ACCEL_FEATURE PPC_FEATURE_PROVIDED_BY_ARCH +#endif + +#endif diff --git a/ppc/pearpc/system/arch/sysendian.h b/ppc/pearpc/system/arch/sysendian.h new file mode 100644 index 00000000..2309adf3 --- /dev/null +++ b/ppc/pearpc/system/arch/sysendian.h @@ -0,0 +1,28 @@ +/* + * PearPC + * sysendian.h + * + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SYSENDIAN_H__ +#define __SYSTEM_ARCH_SYSENDIAN_H__ + +#include "config.h" + +#include SYSTEM_ARCH_SPECIFIC_ENDIAN_DIR + +#endif diff --git a/ppc/pearpc/system/arch/sysfeatures.h b/ppc/pearpc/system/arch/sysfeatures.h new file mode 100644 index 00000000..7138daad --- /dev/null +++ b/ppc/pearpc/system/arch/sysfeatures.h @@ -0,0 +1,28 @@ +/* + * PearPC + * sysfeatures.h + * + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SYSFEATURES_H__ +#define __SYSTEM_ARCH_SYSFEATURES_H__ + +#include "config.h" + +#include SYSTEM_ARCH_SPECIFIC_FEATURES_DIR + +#endif diff --git a/ppc/pearpc/system/arch/x86/sysendian.h b/ppc/pearpc/system/arch/x86/sysendian.h new file mode 100644 index 00000000..d19e6a64 --- /dev/null +++ b/ppc/pearpc/system/arch/x86/sysendian.h @@ -0,0 +1,65 @@ +/* + * PearPC + * sysendian.h + * + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ +#define __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ + +#include "system/types.h" + +static inline FUNCTION_CONST uint32 ppc_bswap_word(uint32 data) +{ +// asm ( +// "bswap %0": "=r" (data) : "0" (data) +// ); + data = (data >> 24) | ((data >> 8) & 0x0000ff00) | ((data << 8) & 0x00ff0000) | (data << 24); + return data; +} + +static inline FUNCTION_CONST uint16 ppc_bswap_half(uint16 data) +{ +// asm ( +// "xchgb %b0,%h0": "=q" (data): "0" (data) +// ); + data = (data >> 8) | (data << 8); + return data; +} + +static inline FUNCTION_CONST uint64 ppc_bswap_dword(uint64 data) +{ + return (((uint64)ppc_bswap_word(data)) << 32) | (uint64)ppc_bswap_word(data >> 32); +} + +# define ppc_dword_from_BE(data) (ppc_bswap_dword(data)) +# define ppc_word_from_BE(data) (ppc_bswap_word(data)) +# define ppc_half_from_BE(data) (ppc_bswap_half(data)) + +# define ppc_dword_from_LE(data) ((uint64)(data)) +# define ppc_word_from_LE(data) ((uint32)(data)) +# define ppc_half_from_LE(data) ((uint16)(data)) + +# define ppc_dword_to_LE(data) ppc_dword_from_LE(data) +# define ppc_word_to_LE(data) ppc_word_from_LE(data) +# define ppc_half_to_LE(data) ppc_half_from_LE(data) + +# define ppc_dword_to_BE(data) ppc_dword_from_BE(data) +# define ppc_word_to_BE(data) ppc_word_from_BE(data) +# define ppc_half_to_BE(data) ppc_half_from_BE(data) + +#endif diff --git a/ppc/pearpc/system/arch/x86/sysfeatures.h b/ppc/pearpc/system/arch/x86/sysfeatures.h new file mode 100644 index 00000000..5830769c --- /dev/null +++ b/ppc/pearpc/system/arch/x86/sysfeatures.h @@ -0,0 +1,30 @@ +/* + * PearPC + * features.h - arch/x86 variant + * + * Copyright (C) 2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ +#define __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ + +// PPC_FEATURE_PROVIDED_BY_ARCH +#if !defined(PPC_VIDEO_CONVERT_ACCEL_FEATURE) || (PPC_VIDEO_CONVERT_ACCEL_FEATURE < PPC_FEATURE_PROVIDED_BY_ARCH) +# undef PPC_VIDEO_CONVERT_ACCEL_FEATURE +# define PPC_VIDEO_CONVERT_ACCEL_FEATURE PPC_FEATURE_PROVIDED_BY_ARCH +#endif + +#endif diff --git a/ppc/pearpc/system/arch/x86/sysvaccel.cc b/ppc/pearpc/system/arch/x86/sysvaccel.cc new file mode 100644 index 00000000..c1ee003b --- /dev/null +++ b/ppc/pearpc/system/arch/x86/sysvaccel.cc @@ -0,0 +1,124 @@ +/* + * HT Editor + * sysvaccel.cc + * + * Copyright (C) 2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "system/sysvaccel.h" + +#include "tools/snprintf.h" + +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_2le555(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_2le565(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_4le888(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_2le555(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_2le565(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_4le888(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_convert_4be888_to_4le888(uint32 pixel, byte *input, byte *output); + +static inline void convertBaseColor(uint &b, uint fromBits, uint toBits) +{ + if (toBits > fromBits) { + b <<= toBits - fromBits; + } else { + b >>= fromBits - toBits; + } +} + +static inline void genericConvertDisplay( + const DisplayCharacteristics &aSrcChar, + const DisplayCharacteristics &aDestChar, + const void *aSrcBuf, + void *aDestBuf, + int firstLine, + int lastLine) +{ + byte *src = (byte*)aSrcBuf + aSrcChar.bytesPerPixel * aSrcChar.width * firstLine; + byte *dest = (byte*)aDestBuf + aDestChar.bytesPerPixel * aDestChar.width * firstLine; + for (int y=firstLine; y <= lastLine; y++) { + for (int x=0; x < aSrcChar.width; x++) { + uint r, g, b; + uint p; + switch (aSrcChar.bytesPerPixel) { + case 2: + p = (src[0] << 8) | src[1]; + break; + case 4: + p = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; + break; + default: + ht_printf("internal error in %s:%d\n", __FILE__, __LINE__); + exit(1); + break; + } + r = (p >> aSrcChar.redShift) & ((1<> aSrcChar.greenShift) & ((1<> aSrcChar.blueShift) & ((1<>8; + break; + case 3: + dest[0] = p; dest[1] = p>>8; dest[2] = p>>16; + break; + case 4: + *(uint32*)dest = p; + break; + default: + ht_printf("internal error in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + dest += aDestChar.bytesPerPixel; + src += aSrcChar.bytesPerPixel; + } + dest += aDestChar.scanLineLength - aDestChar.width*aDestChar.bytesPerPixel; + src += aSrcChar.scanLineLength - aSrcChar.width*aSrcChar.bytesPerPixel; + } +} + +void sys_convert_display( + const DisplayCharacteristics &aSrcChar, + const DisplayCharacteristics &aDestChar, + const void *aSrcBuf, + void *aDestBuf, + int firstLine, + int lastLine) +{ + byte *src = (byte*)aSrcBuf + aSrcChar.bytesPerPixel * aSrcChar.width * firstLine; + byte *dest = (byte*)aDestBuf + aDestChar.bytesPerPixel * aDestChar.width * firstLine; + uint32 pixel = (lastLine-firstLine+1) * aSrcChar.width; + if (aSrcChar.bytesPerPixel == 2 && aDestChar.bytesPerPixel == 2) { + if (aDestChar.redSize == 5 && aDestChar.greenSize == 6 && aDestChar.blueSize == 5) { + x86_mmx_convert_2be555_to_2le565(pixel, src, dest); + } else if (aDestChar.redSize == 5 && aDestChar.greenSize == 5 && aDestChar.blueSize == 5) { + x86_mmx_convert_2be555_to_2le555(pixel, src, dest); + } else { + genericConvertDisplay(aSrcChar, aDestChar, aSrcBuf, aDestBuf, firstLine, lastLine); + } + } else if (aSrcChar.bytesPerPixel == 2 && aDestChar.bytesPerPixel == 4) { + x86_mmx_convert_2be555_to_4le888(pixel, src, dest); + } else if (aSrcChar.bytesPerPixel == 4 && aDestChar.bytesPerPixel == 4) { + x86_convert_4be888_to_4le888(pixel, src, dest); + } else { + genericConvertDisplay(aSrcChar, aDestChar, aSrcBuf, aDestBuf, firstLine, lastLine); + } +} diff --git a/ppc/pearpc/system/arch/x86/vaccel.S b/ppc/pearpc/system/arch/x86/vaccel.S new file mode 100644 index 00000000..22c6814e --- /dev/null +++ b/ppc/pearpc/system/arch/x86/vaccel.S @@ -0,0 +1,423 @@ +/* + * PearPC + * vaccel.S + * + * Copyright (C) 2004-2006 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PREFIX +#define PREFIX +#endif + +#define EXPORT(sym) EXPORT2(PREFIX, sym) +#define EXPORT2(p, sym) EXPORT3(p, sym) +#define EXPORT3(p, sym) .globl p##sym; p##sym + +#define EXTERN(sym) EXTERN2(PREFIX, sym) +#define EXTERN2(p, sym) EXTERN3(p, sym) +#define EXTERN3(p, sym) p##sym + +.intel_syntax + +.text + +.balign 16 +d1: .long 0x00ff00ff + .long 0x00ff00ff + .long 0x00ff00ff + .long 0x00ff00ff +d2: .long 0xff00ff00 + .long 0xff00ff00 + .long 0xff00ff00 + .long 0xff00ff00 + +_2be555_mask_r: .long 0x7c007c00 + .long 0x7c007c00 + .long 0x7c007c00 + .long 0x7c007c00 +_2be555_mask_g: .long 0x03e003e0 + .long 0x03e003e0 + .long 0x03e003e0 + .long 0x03e003e0 +_2be555_mask_b: .long 0x001f001f + .long 0x001f001f + .long 0x001f001f + .long 0x001f001f + +.balign 16 +################################################################################# +## +## IN: eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_mmx_convert_2be555_to_2le555): + add %eax, 7 + shr %eax, 3 # we can convert 8 pixels at a time + movq %mm5, [d1] + movq %mm6, [d2] + jz 2f +1: + movq %mm1, [%edx] + movq %mm3, [%edx+8] + + ## convert big to little endian + movq %mm2, %mm1 + movq %mm4, %mm3 + pand %mm1, %mm5 + pand %mm2, %mm6 + pand %mm3, %mm5 + pand %mm4, %mm6 + psllw %mm1, 8 + psrlw %mm2, 8 + psllw %mm3, 8 + psrlw %mm4, 8 + por %mm1, %mm2 + por %mm3, %mm4 + + movq [%ecx], %mm1 + movq [%ecx+8], %mm3 + add %edx, 16 + add %ecx, 16 + dec %eax + jnz 1b + + emms +2: + ret + +.balign 16 +################################################################################# +## +## IN: %eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_mmx_convert_2be555_to_2le565): + add %eax, 7 + shr %eax, 3 # we can convert 8 pixels at a time + movq %mm0, [d1] + movq %mm7, [d2] + jz 2f +1: + movq %mm1, [%edx] + movq %mm3, [%edx+8] + ## convert big to little endian + movq %mm2, %mm1 + movq %mm4, %mm3 + pand %mm1, %mm0 + pand %mm2, %mm7 + pand %mm3, %mm0 + pand %mm4, %mm7 + psllw %mm1, 8 + psrlw %mm2, 8 + psllw %mm3, 8 + psrlw %mm4, 8 + por %mm1, %mm2 + por %mm4, %mm3 + + movq %mm2, %mm1 + movq %mm3, %mm1 + movq %mm5, %mm4 + movq %mm6, %mm4 + pand %mm1, [_2be555_mask_r] + pand %mm2, [_2be555_mask_g] + pand %mm3, [_2be555_mask_b] + pand %mm4, [_2be555_mask_r] + pand %mm5, [_2be555_mask_g] + pand %mm6, [_2be555_mask_b] + psllw %mm1, 1 # red + psllw %mm2, 1 # green +# psllw %mm3, 0 # blue + psllw %mm4, 1 # red + psllw %mm5, 1 # green +# psllw %mm6, 0 # blue + por %mm1, %mm2 + por %mm4, %mm5 + por %mm1, %mm3 + por %mm4, %mm6 + movq [%ecx], %mm1 + movq [%ecx+8], %mm4 + add %edx, 16 + add %ecx, 16 + dec %eax + jnz 1b + + emms +2: + ret + +.balign 16 +################################################################################# +## +## IN: %eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_mmx_convert_2be555_to_4le888): + add %eax, 3 + shr %eax, 2 # we can convert 4 pixels at a time + movq %mm7, [d1] + jz 2f + + pxor %mm0, %mm0 +1: + movq %mm1, [%edx] + + ## convert big to little endian + movq %mm3, %mm1 + pand %mm1, %mm7 + pand %mm3, [d2] + psllw %mm1, 8 + psrlw %mm3, 8 + por %mm1, %mm3 + + movq %mm2, %mm1 + movq %mm3, %mm1 + pand %mm1, [_2be555_mask_r] + pand %mm2, [_2be555_mask_g] + pand %mm3, [_2be555_mask_b] + movq %mm4, %mm1 + movq %mm5, %mm2 + movq %mm6, %mm3 + punpcklwd %mm1, %mm0 + punpcklwd %mm2, %mm0 + punpcklwd %mm3, %mm0 + punpckhwd %mm4, %mm0 + punpckhwd %mm5, %mm0 + punpckhwd %mm6, %mm0 + pslld %mm1, 16-10+3 # red + pslld %mm2, 8-5+3 # green + pslld %mm3, 0+3 # blue + pslld %mm4, 16-10+3 # red + pslld %mm5, 8-5+3 # green + pslld %mm6, 0+3 # blue + por %mm1, %mm2 + por %mm1, %mm3 + por %mm4, %mm5 + por %mm4, %mm6 + movq [%ecx], %mm1 + movq [%ecx+8], %mm4 + add %edx, 8 + add %ecx, 16 + dec %eax + jnz 1b + +2: + emms + ret + + +.balign 16 +################################################################################# +## +## IN: %eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_convert_4be888_to_4le888): + add %eax, 3 + shr %eax, 2 # we can convert 4 pixels at a time + jz 2f + + push %ebx + push %ebp + push %esi + push %edi +1: + mov %ebx, [%edx] + mov %ebp, [%edx+4] + mov %esi, [%edx+8] + mov %edi, [%edx+12] + ## convert big to little endian + bswap %ebx + bswap %ebp + bswap %esi + bswap %edi + add %edx, 16 + mov [%ecx], %ebx + mov [%ecx+4], %ebp + mov [%ecx+8], %esi + mov [%ecx+12], %edi + add %ecx, 16 + dec %eax + jnz 1b + + pop %edi + pop %esi + pop %ebp + pop %ebx +2: + ret + +.balign 16 +################################################################################# +## +## IN: eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_sse2_convert_2be555_to_2le555): + add %eax, 15 + shr %eax, 4 # we can convert 16 pixels at a time + movdqa %xmm5, [d1] + movdqa %xmm6, [d2] + jz 2f +1: + movdqa %xmm1, [%edx] + movdqa %xmm3, [%edx+16] + + ## convert big to little endian + movdqa %xmm2, %xmm1 + movdqa %xmm4, %xmm3 + pand %xmm1, %xmm5 + pand %xmm2, %xmm6 + pand %xmm3, %xmm5 + pand %xmm4, %xmm6 + psllw %xmm1, 8 + psrlw %xmm2, 8 + psllw %xmm3, 8 + psrlw %xmm4, 8 + por %xmm1, %xmm2 + por %xmm3, %xmm4 + + movdqa [%ecx], %xmm1 + movdqa [%ecx+16], %xmm3 + add %edx, 32 + add %ecx, 32 + dec %eax + jnz 1b + +2: + ret + +.balign 16 +################################################################################# +## +## IN: %eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_sse2_convert_2be555_to_2le565): + add %eax, 15 + shr %eax, 4 # we can convert 16 pixels at a time + movdqa %xmm0, [d1] + movdqa %xmm7, [d2] + jz 2f +1: + movdqa %xmm1, [%edx] + movdqa %xmm3, [%edx+16] + ## convert big to little endian + movdqa %xmm2, %xmm1 + movdqa %xmm4, %xmm3 + pand %xmm1, %xmm0 + pand %xmm2, %xmm7 + pand %xmm3, %xmm0 + pand %xmm4, %xmm7 + psllw %xmm1, 8 + psrlw %xmm2, 8 + psllw %xmm3, 8 + psrlw %xmm4, 8 + por %xmm1, %xmm2 + por %xmm4, %xmm3 + + movdqa %xmm2, %xmm1 + movdqa %xmm3, %xmm1 + movdqa %xmm5, %xmm4 + movdqa %xmm6, %xmm4 + pand %xmm1, [_2be555_mask_r] + pand %xmm2, [_2be555_mask_g] + pand %xmm3, [_2be555_mask_b] + pand %xmm4, [_2be555_mask_r] + pand %xmm5, [_2be555_mask_g] + pand %xmm6, [_2be555_mask_b] + psllw %xmm1, 1 # red + psllw %xmm2, 1 # green +# psllw %xmm3, 0 # blue + psllw %xmm4, 1 # red + psllw %xmm5, 1 # green +# psllw %xmm6, 0 # blue + por %xmm1, %xmm2 + por %xmm4, %xmm5 + por %xmm1, %xmm3 + por %xmm4, %xmm6 + movdqa [%ecx], %xmm1 + movdqa [%ecx+16], %xmm4 + add %edx, 32 + add %ecx, 32 + dec %eax + jnz 1b +2: + ret + +.balign 16 +################################################################################# +## +## IN: %eax -- number of pixels to convert +## %edx -- input +## %ecx -- output + +EXPORT(x86_sse2_convert_2be555_to_4le888): + add %eax, 3 + shr %eax, 3 # we can convert 8 pixels at a time + movdqa %xmm7, [d1] + jz 2f + + pxor %xmm0, %xmm0 +1: + movdqa %xmm1, [%edx] + + ## convert big to little endian + movdqa %xmm3, %xmm1 + pand %xmm1, %xmm7 + pand %xmm3, [d2] + psllw %xmm1, 8 + psrlw %xmm3, 8 + por %xmm1, %xmm3 + + movdqa %xmm2, %xmm1 + movdqa %xmm3, %xmm1 + pand %xmm1, [_2be555_mask_r] + pand %xmm2, [_2be555_mask_g] + pand %xmm3, [_2be555_mask_b] + movdqa %xmm4, %xmm1 + movdqa %xmm5, %xmm2 + movdqa %xmm6, %xmm3 + punpcklwd %xmm1, %xmm0 + punpcklwd %xmm2, %xmm0 + punpcklwd %xmm3, %xmm0 + punpckhwd %xmm4, %xmm0 + punpckhwd %xmm5, %xmm0 + punpckhwd %xmm6, %xmm0 + pslld %xmm1, 16-10+3 # red + pslld %xmm2, 8-5+3 # green + pslld %xmm3, 0+3 # blue + pslld %xmm4, 16-10+3 # red + pslld %xmm5, 8-5+3 # green + pslld %xmm6, 0+3 # blue + por %xmm1, %xmm2 + por %xmm1, %xmm3 + por %xmm4, %xmm5 + por %xmm4, %xmm6 + movdqa [%ecx], %xmm1 + movdqa [%ecx+16], %xmm4 + add %edx, 16 + add %ecx, 32 + dec %eax + jnz 1b +2: + ret + diff --git a/ppc/pearpc/system/arch/x86_64/sysendian.h b/ppc/pearpc/system/arch/x86_64/sysendian.h new file mode 100644 index 00000000..0dff4875 --- /dev/null +++ b/ppc/pearpc/system/arch/x86_64/sysendian.h @@ -0,0 +1,75 @@ +/* + * PearPC + * sysendian.h + * + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ +#define __SYSTEM_ARCH_SPECIFIC_SYSENDIAN_H__ + +#include "system/types.h" + +static inline FORCE_INLINE FUNCTION_CONST uint32 ppc_bswap_word(uint32 data) +{ +#if 0 + asm ( + "bswapl %0": "=r" (data) : "0" (data) + ); +#endif + data = (data >> 24) | ((data >> 8) & 0x0000ff00) | ((data << 8) & 0x00ff0000) | (data << 24); + return data; +} + +static inline FORCE_INLINE FUNCTION_CONST uint16 ppc_bswap_half(uint16 data) +{ +#if 0 + asm ( + "rolw $8, %0": "=r" (data): "0" (data) + ); +#endif + data = (data >> 8) | (data << 8); + return data; +} + +static inline FORCE_INLINE FUNCTION_CONST uint64 ppc_bswap_dword(uint64 data) +{ +#if 0 + asm ( + "bswapq %0": "=r" (data) : "0" (data) + ); +#endif + data = (ppc_bswap_word(data) << 32) | (ppc_bswap_word(data >> 32)); + return data; +} + +# define ppc_dword_from_BE(data) (ppc_bswap_dword(data)) +# define ppc_word_from_BE(data) (ppc_bswap_word(data)) +# define ppc_half_from_BE(data) (ppc_bswap_half(data)) + +# define ppc_dword_from_LE(data) ((uint64)(data)) +# define ppc_word_from_LE(data) ((uint32)(data)) +# define ppc_half_from_LE(data) ((uint16)(data)) + +# define ppc_dword_to_LE(data) ppc_dword_from_LE(data) +# define ppc_word_to_LE(data) ppc_word_from_LE(data) +# define ppc_half_to_LE(data) ppc_half_from_LE(data) + +# define ppc_dword_to_BE(data) ppc_dword_from_BE(data) +# define ppc_word_to_BE(data) ppc_word_from_BE(data) +# define ppc_half_to_BE(data) ppc_half_from_BE(data) + +#endif diff --git a/ppc/pearpc/system/arch/x86_64/sysfeatures.h b/ppc/pearpc/system/arch/x86_64/sysfeatures.h new file mode 100644 index 00000000..5830769c --- /dev/null +++ b/ppc/pearpc/system/arch/x86_64/sysfeatures.h @@ -0,0 +1,30 @@ +/* + * PearPC + * features.h - arch/x86 variant + * + * Copyright (C) 2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ +#define __SYSTEM_ARCH_SPECIFIC_FEATURES_H__ + +// PPC_FEATURE_PROVIDED_BY_ARCH +#if !defined(PPC_VIDEO_CONVERT_ACCEL_FEATURE) || (PPC_VIDEO_CONVERT_ACCEL_FEATURE < PPC_FEATURE_PROVIDED_BY_ARCH) +# undef PPC_VIDEO_CONVERT_ACCEL_FEATURE +# define PPC_VIDEO_CONVERT_ACCEL_FEATURE PPC_FEATURE_PROVIDED_BY_ARCH +#endif + +#endif diff --git a/ppc/pearpc/system/arch/x86_64/sysvaccel.cc b/ppc/pearpc/system/arch/x86_64/sysvaccel.cc new file mode 100644 index 00000000..30f3beca --- /dev/null +++ b/ppc/pearpc/system/arch/x86_64/sysvaccel.cc @@ -0,0 +1,130 @@ +/* + * HT Editor + * sysvaccel.cc + * + * Copyright (C) 2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "system/sysvaccel.h" + +#include "tools/snprintf.h" + +#if 0 +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_2le555(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_2le565(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_mmx_convert_2be555_to_4le888(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_2le555(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_2le565(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_sse2_convert_2be555_to_4le888(uint32 pixel, byte *input, byte *output); +extern "C" void __attribute__((regparm (3))) x86_convert_4be888_to_4le888(uint32 pixel, byte *input, byte *output); +#endif + +static inline void convertBaseColor(uint &b, uint fromBits, uint toBits) +{ + if (toBits > fromBits) { + b <<= toBits - fromBits; + } else { + b >>= fromBits - toBits; + } +} + +static inline void genericConvertDisplay( + const DisplayCharacteristics &aSrcChar, + const DisplayCharacteristics &aDestChar, + const void *aSrcBuf, + void *aDestBuf, + int firstLine, + int lastLine) +{ + byte *src = (byte*)aSrcBuf + aSrcChar.bytesPerPixel * aSrcChar.width * firstLine; + byte *dest = (byte*)aDestBuf + aDestChar.bytesPerPixel * aDestChar.width * firstLine; + for (int y=firstLine; y <= lastLine; y++) { + for (int x=0; x < aSrcChar.width; x++) { + uint r, g, b; + uint p; + switch (aSrcChar.bytesPerPixel) { + case 2: + p = (src[0] << 8) | src[1]; + break; + case 4: + p = (src[0] << 24) | (src[1] << 16) | (src[2] << 8) | src[3]; + break; + default: + ht_printf("internal error in %s:%d\n", __FILE__, __LINE__); + exit(1); + break; + } + r = (p >> aSrcChar.redShift) & ((1<> aSrcChar.greenShift) & ((1<> aSrcChar.blueShift) & ((1<>8; + break; + case 3: + dest[0] = p; dest[1] = p>>8; dest[2] = p>>16; + break; + case 4: + *(uint32*)dest = p; + break; + default: + ht_printf("internal error in %s:%d\n", __FILE__, __LINE__); + exit(1); + } + dest += aDestChar.bytesPerPixel; + src += aSrcChar.bytesPerPixel; + } + dest += aDestChar.scanLineLength - aDestChar.width*aDestChar.bytesPerPixel; + src += aSrcChar.scanLineLength - aSrcChar.width*aSrcChar.bytesPerPixel; + } +} + +void sys_convert_display( + const DisplayCharacteristics &aSrcChar, + const DisplayCharacteristics &aDestChar, + const void *aSrcBuf, + void *aDestBuf, + int firstLine, + int lastLine) +{ +#if 1 + genericConvertDisplay(aSrcChar, aDestChar, aSrcBuf, aDestBuf, firstLine, lastLine); +#else + byte *src = (byte*)aSrcBuf + aSrcChar.bytesPerPixel * aSrcChar.width * firstLine; + byte *dest = (byte*)aDestBuf + aDestChar.bytesPerPixel * aDestChar.width * firstLine; + uint32 pixel = (lastLine-firstLine+1) * aSrcChar.width; + if (aSrcChar.bytesPerPixel == 2 && aDestChar.bytesPerPixel == 2) { + if (aDestChar.redSize == 5 && aDestChar.greenSize == 6 && aDestChar.blueSize == 5) { + x86_mmx_convert_2be555_to_2le565(pixel, src, dest); + } else if (aDestChar.redSize == 5 && aDestChar.greenSize == 5 && aDestChar.blueSize == 5) { + x86_mmx_convert_2be555_to_2le555(pixel, src, dest); + } else { + genericConvertDisplay(aSrcChar, aDestChar, aSrcBuf, aDestBuf, firstLine, lastLine); + } + } else if (aSrcChar.bytesPerPixel == 2 && aDestChar.bytesPerPixel == 4) { + x86_mmx_convert_2be555_to_4le888(pixel, src, dest); + } else if (aSrcChar.bytesPerPixel == 4 && aDestChar.bytesPerPixel == 4) { + x86_convert_4be888_to_4le888(pixel, src, dest); + } else { + genericConvertDisplay(aSrcChar, aDestChar, aSrcBuf, aDestBuf, firstLine, lastLine); + } +#endif +} diff --git a/ppc/pearpc/system/systhread.h b/ppc/pearpc/system/systhread.h new file mode 100644 index 00000000..312b7157 --- /dev/null +++ b/ppc/pearpc/system/systhread.h @@ -0,0 +1,57 @@ +/* + * HT Editor + * systhread.h + * + * Copyright (C) 2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SYSTHREAD_H__ +#define __SYSTHREAD_H__ + +#include "types.h" + +typedef void * sys_mutex; +typedef void * sys_semaphore; +typedef void * sys_thread; + +typedef void * (*sys_thread_function)(void *); + +/* system-dependent (implementation in $MYSYSTEM/systhread.cc) */ +/* all return 0 on success */ +int sys_create_mutex(sys_mutex *m); +int sys_create_semaphore(sys_semaphore *s); +int sys_create_thread(sys_thread *t, int flags, sys_thread_function start_routine, void *arg); + +void sys_destroy_mutex(sys_mutex m); +void sys_destroy_semaphore(sys_semaphore s); +void sys_destroy_thread(sys_semaphore s); + +int sys_lock_mutex(sys_mutex m); +int sys_trylock_mutex(sys_mutex m); +void sys_unlock_mutex(sys_mutex m); + +void sys_signal_semaphore(sys_semaphore s); +void sys_signal_all_semaphore(sys_semaphore s); +void sys_wait_semaphore(sys_semaphore s); +void sys_wait_semaphore_bounded(sys_semaphore s, int ms); + +void sys_lock_semaphore(sys_semaphore s); +void sys_unlock_semaphore(sys_semaphore s); + +void sys_exit_thread(void *ret); +void * sys_join_thread(sys_thread t); + +#endif diff --git a/ppc/pearpc/system/types.h b/ppc/pearpc/system/types.h new file mode 100644 index 00000000..23193842 --- /dev/null +++ b/ppc/pearpc/system/types.h @@ -0,0 +1,128 @@ +/* + * PearPC + * types.h + * + * Copyright (C) 1999-2003 Sebastian Biallas (sb@biallas.net) + * Copyright (C) 1999-2004 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __TYPES_H__ +#define __TYPES_H__ + +#define HAVE_CONFIG_H +#ifdef HAVE_CONFIG_H +#include "config.h" +#endif + +#ifdef MIN +#undef MIN +#endif +#ifdef MAX +#undef MAX +#endif +#define MIN(a, b) ((a) < (b) ? (a) : (b)) +#define MAX(a, b) ((a) > (b) ? (a) : (b)) + +/* + * compiler magic + */ + +#ifdef __GNUC__ + + // FIXME: configure +# ifdef __i386__ +# define FASTCALL __attribute__((regparm (3))) +# else +# define FASTCALL +# endif + +# define FUNCTION_CONST __attribute__((const)) +# define PACKED __attribute__((packed)) +# define UNUSED __attribute__((unused)) +# define DEPRECATED __attribute__((deprecated)) +# define NORETURN __attribute__((noreturn)) +# define ALIGN_STRUCT(n) __attribute__((aligned(n))) +# define FORCE_INLINE __attribute__((always_inline)) + +#elif _MSC_VER + +#define FORCE_INLINE __forceinline +#define NOINLINE __declspec(noinline) + +# define FASTCALL +# define FUNCTION_CONST +# define PACKED __attribute__((packed)) +# define UNUSED __attribute__((unused)) +# define DEPRECATED +# define ALIGN_STRUCT(n) __attribute__((aligned(n))) +# define NORETURN __declspec(noreturn) + +typedef unsigned long long uint64; +typedef signed long long sint64; +typedef unsigned int uint32; +typedef signed int sint32; +typedef unsigned short uint16; +typedef signed short sint16; +typedef unsigned char uint8; +typedef signed char sint8; +typedef unsigned char byte; + +typedef unsigned int uint; + +#ifndef mode_t +#define mode_t int +#endif + +#endif /* !__GNUC__ */ + +/* + * integers + */ + +//#include SYSTEM_OSAPI_SPECIFIC_TYPES_HDR + +/* + * NULL + */ + +#ifndef NULL +# define NULL 0 +#endif + +//FIXME: configure somehow (?) +#ifndef UINT128 +# define UINT128 +typedef struct uint128 { + uint64 l; + uint64 h; +} uint128; +typedef struct sint128 { + sint64 l; + sint64 h; +} sint128; +#endif + +union float_uint32 { + uint32 u; + float f; +}; + +union double_uint64 { + uint64 u; + double d; +}; + +#endif diff --git a/ppc/pearpc/tools/debug.h b/ppc/pearpc/tools/debug.h new file mode 100644 index 00000000..33667b2e --- /dev/null +++ b/ppc/pearpc/tools/debug.h @@ -0,0 +1,33 @@ +/* + * HT Editor + * debug.h + * + * Copyright (C) 1999-2002 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __DEBUG_H__ +#define __DEBUG_H__ + +#include "system/types.h" + +void NORETURN ht_assert_failed(const char *file, int line, const char *assertion); + +#define ASSERT(a) if (!(a)) ht_assert_failed(__FILE__, __LINE__, (#a)); +#define HERE __FILE__, __LINE__ + +void debugDumpMem(void *buf, int len); + +#endif /* !__DEBUG_H__ */ diff --git a/ppc/pearpc/tools/endianess.h b/ppc/pearpc/tools/endianess.h new file mode 100644 index 00000000..fe347d7e --- /dev/null +++ b/ppc/pearpc/tools/endianess.h @@ -0,0 +1,54 @@ +/* + * HT Editor + * endianess.h + * + * Copyright (C) 1999-2002 Stefan Weyergraf + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __ENDIANESS_H__ +#define __ENDIANESS_H__ + +#include "system/types.h" + +// This is _NOT_ a bitmask! +#define STRUCT_ENDIAN_8 1 +#define STRUCT_ENDIAN_16 2 +#define STRUCT_ENDIAN_32 4 +#define STRUCT_ENDIAN_64 8 + +// This is a bitmask. +#define STRUCT_ENDIAN_HOST 128 + +typedef enum { + big_endian, + little_endian +} Endianess; + +#ifdef __cplusplus +extern "C" { +#endif + +void createForeignInt(void *buf, int i, int size, Endianess to_endianess); +void createForeignInt64(void *buf, uint64 i, int size, Endianess to_endianess); +int createHostInt(const void *buf, int size, Endianess from_endianess); +uint64 createHostInt64(const void *buf, int size, Endianess from_endianess); +void createHostStructx(void *buf, uint bufsize, const uint8 *table, Endianess from_endianess); + +#ifdef __cplusplus +} +#endif + +#endif /* __ENDIANESS_H__ */ diff --git a/ppc/pearpc/tools/snprintf.h b/ppc/pearpc/tools/snprintf.h new file mode 100644 index 00000000..234513ed --- /dev/null +++ b/ppc/pearpc/tools/snprintf.h @@ -0,0 +1,39 @@ +/* + * HT Editor + * snprintf.h + * + * Copyright (C) 1999-2003 Sebastian Biallas (sb@biallas.net) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __SNPRINTF_H__ +#define __SNPRINTF_H__ + +#include +#include +#include + +int ht_asprintf(char **ptr, const char *format, ...); +int ht_vasprintf(char **ptr, const char *format, va_list ap); + +int ht_snprintf(char *str, size_t count, const char *fmt, ...); +int ht_vsnprintf(char *str, size_t count, const char *fmt, va_list args); + +int ht_fprintf(FILE *file, const char *fmt, ...); +int ht_vfprintf(FILE *file, const char *fmt, va_list args); + +int ht_printf(const char *fmt, ...); +int ht_vprintf(const char *fmt, va_list args); +#endif diff --git a/ppc/pearpc/uaeglue.cpp b/ppc/pearpc/uaeglue.cpp new file mode 100644 index 00000000..a6b33415 --- /dev/null +++ b/ppc/pearpc/uaeglue.cpp @@ -0,0 +1,44 @@ + +#include +#include +#include + +#include "system/systhread.h" + +void write_log(const char *format, ...); + +int ht_printf(const char *fmt,...) +{ + char buffer[1000]; + va_list parms; + va_start(parms, fmt); + vsprintf(buffer, fmt, parms); + write_log(buffer); + va_end(parms); + return 0; +} +int ht_fprintf(FILE *f, const char *fmt, ...) +{ + return 0; +} +int ht_vfprintf(FILE *file, const char *fmt, va_list args) +{ + return 0; +} +int ht_snprintf(char *str, size_t count, const char *fmt, ...) +{ + return 0; +} +int ht_vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + return 0; +} + +void ht_assert_failed(const char *file, int line, const char *assertion) +{ +} + +void prom_quiesce() +{ +} + diff --git a/ppc/ppc.cpp b/ppc/ppc.cpp new file mode 100644 index 00000000..400e82d0 --- /dev/null +++ b/ppc/ppc.cpp @@ -0,0 +1,323 @@ + +#include "sysconfig.h" +#include "sysdeps.h" + +#include "options.h" +#include "threaddep/thread.h" +#include "ppc.h" +#include "memory.h" +#include "cpuboard.h" +#include "debug.h" +#include "custom.h" +#include "uae.h" + +#include "cpu/cpu.h" + +#define PPC_SYNC_WRITE 0 +#define PPC_ACCESS_LOG 0 + +volatile int ppc_state; + +static bool ppc_thread_running; +static smp_comm_pipe ppcrequests, ppcreturn; +static smp_comm_pipe ppcquery, ppcreply; +int ppc_cycle_count; + +#define CSPPC_PVR 0x00090204 +#define BLIZZPPC_PVR 0x00070101 + +static void *ppc_thread(void *v) +{ + for (;;) { + uae_u32 v = read_comm_pipe_u32_blocking(&ppcrequests); + if (v == 0xfffffff) + break; + ppc_cpu_init(currprefs.cpuboard_type == BOARD_BLIZZARDPPC ? BLIZZPPC_PVR : CSPPC_PVR); + ppc_cpu_set_pc(0, 0xfff00100); + ppc_cycle_count = 240000000 / (hblank_hz * 4); + ppc_state = PPC_STATE_ACTIVE; + ppc_cpu_run(); + if (ppc_state == PPC_STATE_ACTIVE || ppc_state == PPC_STATE_SLEEP) + ppc_state = PPC_STATE_STOP; + write_log(_T("ppc_cpu_run() exited.\n")); + write_comm_pipe_u32(&ppcreturn, 0, 0); + } + + ppc_thread_running = false; + return NULL; +} + +void uae_ppc_poll_queue(void) +{ + if (comm_pipe_has_data(&ppcquery)) { + uae_u32 addr = read_comm_pipe_u32_blocking(&ppcquery); + uae_u32 size = read_comm_pipe_u32_blocking(&ppcquery); + uae_u32 data = 0, data2 = 0; + if (size & 0x80) { + if (size & 0x08) + data2 = read_comm_pipe_u32_blocking(&ppcquery); + data = read_comm_pipe_u32_blocking(&ppcquery); + switch (size & 127) + { + case 8: + put_long(addr + 0, data2); + put_long(addr + 4, data); + break; + case 4: + put_long(addr, data); + break; + case 2: + put_word(addr, data); + break; + case 1: + put_byte(addr, data); + break; + } +#if PPC_SYNC_WRITE + write_comm_pipe_u32(&ppcreply, 0, 1); +#else + read_comm_pipe_u32_blocking(&ppcquery); +#endif + } else { + switch (size & 127) + { + case 8: + data2 = get_long(addr + 0); + data = get_long(addr + 4); + break; + case 4: + data = get_long(addr); + break; + case 2: + data = get_word(addr); + break; + case 1: + data = get_byte(addr); + break; + } + if (size & 0x08) + write_comm_pipe_u32(&ppcreply, data2, 0); + write_comm_pipe_u32(&ppcreply, data, 1); + } + } +} + +void uae_ppc_sync (void) +{ + while (comm_pipe_has_data(&ppcquery)); +} + +bool uae_ppc_direct_physical_memory_handle(uint32 addr, byte *&ptr) +{ + if (valid_address(addr, 0x1000)) { + ptr = get_real_address(addr); + return true; + } + return false; +} + +bool uae_ppc_io_mem_write(uint32 addr, uint32 data, int size) +{ + if (!valid_address(addr, size)) { + write_comm_pipe_u32(&ppcquery, addr, 0); + write_comm_pipe_u32(&ppcquery, size | 0x80, 0); + write_comm_pipe_u32(&ppcquery, data, 1); +#if PPC_SYNC_WRITE + read_comm_pipe_u32_blocking(&ppcreply); +#else + write_comm_pipe_u32(&ppcquery, data, 0); +#endif +#if PPC_ACCESS_LOG > 0 + write_log(_T("PPC io write %08x = %08x %d\n"), addr, data, size); +#endif + return true; + } + switch (size) + { + case 4: + put_long(addr, data); + break; + case 2: + put_word(addr, data); + break; + case 1: + put_byte(addr, data); + break; + default: + write_log(_T("unknown ppc write %d %08x\n"), addr, size); + return false; + } +#if PPC_ACCESS_LOG > 2 + write_log(_T("PPC mem write %08x = %08x %d\n"), addr, data, size); +#endif + return true; +} + +bool uae_ppc_io_mem_read(uint32 addr, uint32 &data, int size) +{ + uint32 v; + + if (!valid_address(addr, size)) { + write_comm_pipe_u32(&ppcquery, addr, 0); + write_comm_pipe_u32(&ppcquery, size, 1); + v = read_comm_pipe_u32_blocking(&ppcreply); +#if PPC_ACCESS_LOG > 0 + if (addr != 0xbfe001) + write_log(_T("PPC io read %08x=%08x %d\n"), addr, v, size); +#endif + data = v; + return true; + } + + switch (size) + { + case 4: + v = get_long(addr); + break; + case 2: + v = get_word(addr); + break; + case 1: + v = get_byte(addr); + break; + default: + write_log(_T("unknown ppc read %d %08x\n"), addr, size); + return false; + } + data = v; + +#if PPC_ACCESS_LOG > 2 + write_log(_T("PPC mem read %08x=%08x %d\n"), addr, v, size); +#endif + return true; +} + +bool uae_ppc_io_mem_write64(uint32 addr, uint64 data) +{ + if (!valid_address(addr, 8)) { +#if PPC_ACCESS_LOG > 0 + write_log(_T("PPC io write64 %08x = %08llx\n"), addr, data); +#endif + write_comm_pipe_u32(&ppcquery, addr, 0); + write_comm_pipe_u32(&ppcquery, 8 | 0x80, 0); + write_comm_pipe_u32(&ppcquery, data >> 32, 0); + write_comm_pipe_u32(&ppcquery, data & 0xffffffff, 1); +#if PPC_SYNC_WRITE + read_comm_pipe_u32_blocking(&ppcreply); +#else + write_comm_pipe_u32(&ppcquery, data, 0); +#endif + return true; + } + put_long(addr + 0, data >> 32); + put_long(addr + 4, data & 0xffffffff); +#if PPC_ACCESS_LOG > 2 + write_log(_T("PPC mem write64 %08x = %08llx\n"), addr, data); +#endif + return true; +} + +bool uae_ppc_io_mem_read64(uint32 addr, uint64 &data) +{ + uae_u32 v1, v2; + if (!valid_address(addr, 8)) { + write_comm_pipe_u32(&ppcquery, addr, 0); + write_comm_pipe_u32(&ppcquery, 8, 0); + v1 = read_comm_pipe_u32_blocking(&ppcreply); + v2 = read_comm_pipe_u32_blocking(&ppcreply); + data = ((uint64)v1 << 32) | v2; +#if PPC_ACCESS_LOG > 0 + write_log(_T("PPC io read64 %08x = %08llx\n"), addr, data); +#endif + return true; + } + v1 = get_long(addr + 0); + v2 = get_long(addr + 4); + data = ((uint64)v1 << 32) | v2; +#if PPC_ACCESS_LOG > 2 + write_log(_T("PPC mem read64 %08x = %08llx\n"), addr, data); +#endif + return true; +} + +void ppc_stop(void) +{ + if (ppc_thread_running && ppc_state) { + if (ppc_state == PPC_STATE_SLEEP) + ppc_state = PPC_STATE_ACTIVE; + write_log(_T("Stopping PPC.\n")); + ppc_cpu_stop(); + while (ppc_state != PPC_STATE_STOP && ppc_state != PPC_STATE_CRASH) { + uae_ppc_poll_queue(); + } + read_comm_pipe_u32_blocking(&ppcreturn); + write_log(_T("PPC stopped.\n")); + } +} + +void ppc_reboot(void) +{ + write_log(_T("Starting PPC thread.\n")); + if (!ppc_thread_running) { + ppc_thread_running = true; + init_comm_pipe(&ppcrequests, 10, 1); + init_comm_pipe(&ppcreturn, 10, 1); + init_comm_pipe(&ppcreply, 100, 1); + init_comm_pipe(&ppcquery, 100, 1); + uae_start_thread(_T("ppc"), ppc_thread, NULL, NULL); + } + write_comm_pipe_u32(&ppcrequests, 1, 1); +} + +void ppc_wakeup(void) +{ + if (ppc_state == PPC_STATE_SLEEP) + ppc_state = PPC_STATE_ACTIVE; +} + +void ppc_cpu_atomic_raise_ext_exception(void); +void ppc_cpu_atomic_cancel_ext_exception(void); + +void ppc_interrupt(bool active) +{ + if (active) { + ppc_cpu_atomic_raise_ext_exception(); + ppc_wakeup(); + } else { + ppc_cpu_atomic_cancel_ext_exception(); + } +} + + +// sleep until interrupt (or PPC stopped) +void ppc_doze(void) +{ + ppc_state = PPC_STATE_SLEEP; + while (ppc_state == PPC_STATE_SLEEP) { + sleep_millis(2); + } +} + +void ppc_crash(void) +{ + ppc_state = PPC_STATE_CRASH; + ppc_cpu_stop(); +} + +typedef void * sys_mutex; + +int sys_lock_mutex(sys_mutex m) +{ + uae_sem_wait(&m); + return 1; +} +void sys_unlock_mutex(sys_mutex m) +{ + uae_sem_post(&m); +} +int sys_create_mutex(sys_mutex *m) +{ + if (!(*m)) + uae_sem_init(m, 0, 1); + return 1; +} diff --git a/ppc/ppcd.cpp b/ppc/ppcd.cpp new file mode 100644 index 00000000..2d84c710 --- /dev/null +++ b/ppc/ppcd.cpp @@ -0,0 +1,1770 @@ +// Very accurate PowerPC Architecture disassembler (both 32 and 64-bit instructions are supported) + +// Branch Target in output parameters is NOT relative. Its already precalculated +// from effective address of current instruction. + +// Note, that old mnemonics and operands will be overwritten, after next disasm +// call. So dont forget to copy them away, if supposed to use them lately. + +// Instruction class can be combined from many flags. There can exist, for example, +// "FPU" + "LDST" instruction, except "ILLEGAL", which cannot be combined. + +// RLWINM-like instructions mask is placed in output "target" parameter. + +#include +#include + +#include "ppcd.h" + +#define POWERPC_32 // Use generic 32-bit model +//efine POWERPC_64 // Use generic 64-bit model +//efine GEKKO // Use Gekko (32-bit ISA) +//efine BROADWAY // Use Broadway (32-bit ISA) + +#define SIMPLIFIED // Allow simplified mnemonics +//efine UPPERCASE // Use upper case strings in output +#define COMMA ", " +#define LPAREN " (" +#define RPAREN ")" +#define HEX1 "0x" // prefix +#define HEX2 "" // suffix + +static int bigendian = -1; // Autodetect. + +// --------------------------------------------------------------------------- +// Implementation. Look away, code is messed :) +// Dont miss 'l' and '1'. + +static PPCD_CB *o; +#define Instr (o->instr) +#define DIS_PC (o->pc) + +// Simple decoder +#define DIS_RD ((Instr >> 21) & 0x1f) +#define DIS_RS DIS_RD +#define DIS_RA ((Instr >> 16) & 0x1f) +#define DIS_RB ((Instr >> 11) & 0x1f) +#define DIS_RC ((Instr >> 6) & 0x1f) +#define DIS_RE ((Instr >> 1) & 0x1f) +#define DIS_MB DIS_RC +#define DIS_ME DIS_RE +#define DIS_OE (Instr & 0x400) +#define DIS_SIMM ((s16)Instr) +#define DIS_UIMM (Instr & 0xffff) +#define DIS_CRM ((Instr >> 12) & 0xff) +#define AA (Instr & 2) +#define LK (Instr & 1) +#define AALK (Instr & 3) +#define Rc LK + +// GPRs. sp, sd1 and sd2 are named corresponding to PPC EABI. +static char *regname[] = { +#ifdef UPPERCASE + "R0" , "R1" , "R2", "R3" , "R4" , "R5" , "R6" , "R7" , + "R8" , "R9" , "R10", "R11", "R12", "R13", "R14", "R15", + "R16", "R17", "R18", "R19", "R20", "R21", "R22", "R23", + "R24", "R25", "R26", "R27", "R28", "R29", "R30", "R31" +#else + "r0" , "r1" , "r2", "r3" , "r4" , "r5" , "r6" , "r7" , + "r8" , "r9" , "r10", "r11", "r12", "r13", "r14", "r15", + "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", + "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31" +#endif +}; +#define REGD (regname[DIS_RD]) +#define REGS (regname[DIS_RS]) +#define REGA (regname[DIS_RA]) +#define REGB (regname[DIS_RB]) + +// Illegal instruction. +static void ill(void) +{ +#if 1 + o->mnemonic[0] = o->operands[0] = '\0'; +#else + strcpy(o->mnemonic, ".word"); + sprintf(o->operands, HEX1 "%08X" HEX2, Instr); +#endif + o->iclass = PPC_DISA_ILLEGAL; +} + +// Smart SIMM formatting (if hex=1, then force HEX; if s=1, use sign) +static char * simm(int val, int hex, int s) +{ + static char out[16]; + if( ((val >= -256) && (val <= 256)) && !hex) sprintf(out, "%i", val); + else + { + u16 hexval = (u16)val; + if((hexval & 0x8000) && s) sprintf(out, "-" HEX1 "%04X" HEX2, ((~hexval) & 0xffff) + 1); + else sprintf(out, HEX1 "%04X" HEX2, hexval); + } + return out; +} + +// Simple instruction form + reserved bitmask. +static void put(char * mnem, u32 mask, u32 chkval=0, int iclass=PPC_DISA_OTHER) +{ + if( (Instr & mask) != chkval ) { ill(); return; } + o->iclass |= iclass; + strncpy(o->mnemonic, mnem, sizeof(o->mnemonic)); +} + +// Simplified mnemonic trap conditions +static char * t_cond[32] = { + NULL, "lgt", "llt", NULL, "eq", "lge", "lle", NULL, + "gt", NULL, NULL, NULL, "ge", NULL, NULL, NULL, + "lt", NULL, NULL, NULL, "le", NULL, NULL, NULL, + "ne", NULL, NULL, NULL, NULL, NULL, NULL, NULL, +}; + +// Trap instructions. +static void trap(int L, int imm) +{ + static char t_mode[2] = { 'w', 'd' }; + int rd = DIS_RD; // TO + int s = (rd & 0x18) ? 1 : 0; +#ifdef SIMPLIFIED + if(t_cond[rd] != NULL) + { + sprintf(o->mnemonic, "t%c%s%c", t_mode[L & 1], t_cond[rd], imm ? 'i' : 0); + if(imm) sprintf(o->operands, "%s" COMMA "%s", REGA, simm(DIS_SIMM, 0, s)); + else sprintf(o->operands, "%s" COMMA "%s", REGA, REGB); + o->iclass |= PPC_DISA_SIMPLIFIED; + } + else +#endif + { + sprintf(o->mnemonic, "t%c%c", t_mode[L & 1], imm ? 'i' : 0); + if(imm) sprintf(o->operands, "%i" COMMA "%s" COMMA "%s", rd, REGA, simm(DIS_SIMM, 0, s)); + else sprintf(o->operands, "%i" COMMA "%s" COMMA "%s", rd, REGA, REGB); + } + if(L) o->iclass |= PPC_DISA_64; + o->r[1] = DIS_RA; if(!imm) o->r[2] = DIS_RB; + if(imm) + { + o->immed = Instr & 0xFFFF; + if(o->immed & 0x8000) o->immed |= 0xFFFF0000; + } +} + +// DAB mask +#define DAB_D 4 +#define DAB_A 2 +#define DAB_B 1 + +// ASB mask +#define ASB_A 4 +#define ASB_S 2 +#define ASB_B 1 + +// cr%i +#ifdef UPPERCASE + static char crname[] = "CR"; +#else + static char crname[] = "cr"; +#endif + +// fr%i +#ifdef UPPERCASE + static char fregname[] = "FR"; +#else + static char fregname[] = "fr"; +#endif + +// Integer instructions +// form: 'D' rD, rA, (s)IMM +// 'S' rA, rS, (s)IMM +// 'X' rD, rA, rB +// 'Z' rA, rS, rB +// 'F' frD, rA, rB +// dab LSB bits : [D][A][B] (D should always present) +// 'hex' for logic opcodes, 's' for alu opcodes, 'crfD' and 'L' for cmp opcodes +// 'imm': 1 to show immediate operand +static void integer(char *mnem, char form, int dab, int hex=0, int s=1, int crfD=0, int L=0, int imm=1) +{ + char * ptr = o->operands; + int rd = DIS_RD, ra = DIS_RA, rb = DIS_RB; + strncpy(o->mnemonic, mnem, sizeof(o->mnemonic)); + if(crfD) ptr += sprintf(ptr, "%s%i" COMMA, crname, rd >> 2); // CMP only + if(L) ptr += sprintf(ptr, "%i" COMMA, rd & 1); // CMP only + if(form == 'D') + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s", REGD); + if(dab & DAB_A) + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGA); + } + if(imm) ptr += sprintf(ptr, COMMA "%s", simm(s ? DIS_SIMM : DIS_UIMM, hex, s)); + } + else if(form == 'S') + { + if(dab & ASB_A) ptr += sprintf(ptr, "%s", REGA); + if(dab & ASB_S) + { + if(dab & ASB_A) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGS); + } + if(imm) ptr += sprintf(ptr, COMMA "%s", simm(s ? DIS_SIMM : DIS_UIMM, hex, s)); + } + else if(form == 'X') // DAB + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s", REGD); + if(dab & DAB_A) + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGA); + } + if(dab & DAB_B) + { + if(dab & (DAB_D|DAB_A)) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGB); + } + } + else if(form == 'F') // FPU DAB + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s%i", fregname, rd); + if(dab & DAB_A) + { + if(dab & DAB_D) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGA); + } + if(dab & DAB_B) + { + if(dab & (DAB_D|DAB_A)) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGB); + } + } + else if(form == 'Z') // ASB + { + if(dab & ASB_A) ptr += sprintf(ptr, "%s", REGA); + if(dab & ASB_S) + { + if(dab & ASB_A) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGS); + } + if(dab & ASB_B) + { + if(dab & (ASB_A|ASB_S)) ptr += sprintf(ptr, "%s", COMMA); + ptr += sprintf(ptr, "%s", REGB); + } + } + else { ill(); return; } + if(form == 'D' || form == 'X' || form == 'F') { o->r[0] = rd; o->r[1] = ra; } + if(form == 'S' || form == 'Z') { o->r[0] = ra; o->r[1] = rd; } + if(form == 'X' || form == 'Z' || form == 'F') o->r[2] = rb; + if((form == 'D' || form == 'S') && imm) + { + o->immed = Instr & 0xFFFF; + if(o->immed & 0x8000 && s) o->immed |= 0xFFFF0000; + } + o->iclass |= PPC_DISA_INTEGER; +} + +// Compare instructions (wraps to integer call) +static void cmp(char *l, char *i) +{ + char mnem[sizeof(o->mnemonic)]; + int rd = DIS_RD; + + if(rd & 2) { ill(); return; } // Reserved bit set + if(rd & 1) + { +#ifndef POWERPC_64 + { ill(); return; } +#endif + o->iclass |= PPC_DISA_64; + } + +#ifdef SIMPLIFIED + sprintf(mnem, "cmp%s%c%s", l, (rd & 1) ? 'd' : 'w', i); + integer(mnem, (*i == 'i') ? 'D' : 'X', DAB_A|DAB_B, 0, 1, (rd >> 2) ? 1 : 0, 0); + o->iclass |= PPC_DISA_SIMPLIFIED; +#else + sprintf(mnem, "cmp%s%s", l, i); + integer(mnem, (*i == 'i') ? 'D' : 'X', DAB_A|DAB_B, 0, 1, 1, 1); +#endif +} + +// Add immediate (wraps to integer call) +static void addi(char *suffix) +{ + char mnem[sizeof(o->mnemonic)]; + +#ifdef SIMPLIFIED + if( (suffix[0] == '\0') && (DIS_RA == 0) ) // Load immediate + { + integer("li", 'D', DAB_D, 0, 1); + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } + if( (suffix[0] == 's') && (DIS_RA == 0) ) // Load address HI + { + integer("lis", 'D', DAB_D, 1, 0); + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } + if(DIS_UIMM & 0x8000) + { + sprintf(mnem, "subi%s", suffix); + + // Fix immediate field. + u16 value = (u16)(~(DIS_UIMM) + 1); + Instr = (Instr & ~0xFFFF) | value; + + integer(mnem, 'D', DAB_D|DAB_A, 0, 1); + o->iclass |= PPC_DISA_SIMPLIFIED; + } + else + { + sprintf(mnem, "addi%s", suffix); + integer(mnem, 'D', DAB_D|DAB_A, 0, 0); + } +#else + sprintf(mnem, "addi%s", suffix); + integer(mnem, 'D', DAB_D|DAB_A); +#endif +} + +// Branch suffix: AA || LK. +static char *b_opt[4] = { "", "l", "a", "la" }; + +// Branch condition code: 4 * BO[1] + (BI & 3) +static char * b_cond[8] = { + "ge", "le", "ne", "ns", "lt", "gt", "eq", "so" +}; + +// Branch on CTR code: BO[0..3] +static char * b_ctr[16] = { + "dnzf", "dzf", NULL, NULL, "dnzt", "dzt", NULL, NULL, + "dnz", "dz", NULL, NULL, NULL, NULL, NULL, NULL +}; + +// Place target address in operands. Helper for bcx/bx calls. +static char *place_target(char *ptr, int comma) +{ + char *old; + u32 *t = (u32 *)&o->target; + + if(comma) ptr += sprintf(ptr, "%s", COMMA); + old = ptr; +#ifdef POWERPC_32 + ptr += sprintf(ptr, HEX1 "%08X" HEX2, (u32)o->target); +#endif +#ifdef POWERPC_64 + ptr = old; + if(bigendian) ptr += sprintf(ptr, HEX1 "%08X_%08X" HEX2, t[0], t[1]); + else ptr += sprintf(ptr, HEX1 "%08X_%08X" HEX2, t[1], t[0]); +#endif + return ptr; +} + +// Branch conditional. +// Disp:1 - branch with displacement.. +// Disp:0 - branch by register (L:1 for LR, L:0 for CTR). +static void bcx(int Disp, int L) +{ + u64 bd = 0; + int bo = DIS_RD, bi = DIS_RA; + char *r = Disp ? "" : (L ? "lr" : "ctr"); + char *ptr = o->operands; + + if( DIS_RB && !Disp ) { ill(); return; } + + o->operands[0] = '\0'; + o->target = 0; + o->iclass |= PPC_DISA_BRANCH; + + // Calculate displacement and target address + if(Disp) + { + bd = DIS_UIMM & ~3; + if(bd & 0x8000) bd |= 0xffffffffffff0000; + o->target = (AA ? 0 : DIS_PC) + bd; + } + else o->target = 0; + + // Calculate branch prediction hint + char y = (bo & 1) ^ ((((s64)bd < 0) && Disp) ? 1 : 0); + y = y ? '+' : '-'; + + if(bo & 4) // No CTR decrement // BO[2] + { + if(bo & 16) // Branch always // BO[0] + { +#ifdef SIMPLIFIED + sprintf(o->mnemonic, "b%s%s", r, b_opt[Disp ? AALK : LK]); + if(Disp) ptr = place_target(ptr, 0); + o->iclass |= PPC_DISA_SIMPLIFIED; + return; +#endif // SIMPLIFIED + } + else // Branch conditional + { + if(bo & 2) { ill(); return; } // BO[3] +#ifdef SIMPLIFIED + char *cond = b_cond[((bo & 8) >> 1) | (bi & 3)]; + if(cond != NULL) // BO[1] + { + sprintf(o->mnemonic, "b%s%s%s%c", cond, r, b_opt[Disp ? AALK : LK], y); + if(bi >= 4) ptr += sprintf(ptr, "%s%i", crname, bi >> 2); + if(Disp) ptr = place_target(ptr, bi >= 4); + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } +#endif // SIMPLIFIED + } + } + else // Decrement CTR + { + if(!L && !Disp) { ill(); return; } + if(bo & 8) { ill(); return; } // BO[1] +#ifdef SIMPLIFIED + if(b_ctr[bo >> 1]) + { + sprintf(o->mnemonic, "b%s%s%s%c", b_ctr[bo >> 1], r, b_opt[Disp ? AALK : LK], y); + if(!(bo & 16)) ptr += sprintf(ptr, "%i", bi); + if(Disp) ptr = place_target(ptr, !(bo & 16)); + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } +#endif // SIMPLIFIED + } + + // Not simplified standard form + sprintf(o->mnemonic, "bc%s%s", r, b_opt[Disp ? AALK : LK]); + ptr += sprintf(ptr, "%i" COMMA "%i", bo, bi); + if(Disp) ptr = place_target(ptr, 1); +} + +// Branch unconditional +static void bx(void) +{ + // Calculate displacement and target address + u64 bd = Instr & 0x03fffffc; + if(bd & 0x02000000) bd |= 0xfffffffffc000000; + o->target = (AA ? 0 : DIS_PC) + bd; + + o->iclass |= PPC_DISA_BRANCH; + sprintf(o->mnemonic, "b%s", b_opt[AALK]); + place_target(o->operands, 0); +} + +// Move CR field +static void mcrf(void) +{ + if(Instr & 0x63f801) { ill(); return; } + strncpy(o->mnemonic, "mcrf", sizeof(o->mnemonic)); + sprintf(o->operands, "%s%i" COMMA "%s%i", crname, DIS_RD >> 2, crname, DIS_RA >> 2); +} + +// CR logic operations +static void crop(char *name, char *simp="", int ddd=0, int daa=0) +{ + if(Instr & 1) { ill(); return; } + + int crfD = DIS_RD, crfA = DIS_RA, crfB = DIS_RB; + +#ifdef SIMPLIFIED + if( crfA == crfB ) + { + if( (crfD == crfA) && ddd ) + { + sprintf(o->mnemonic, "cr%s", simp); + sprintf(o->operands, "%i", crfD); + o->r[0] = crfD; + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } + if( daa ) + { + sprintf(o->mnemonic, "cr%s", simp); + sprintf(o->operands, "%i" COMMA "%i", crfD, crfA); + o->r[0] = crfD; o->r[1] = crfA; + o->iclass |= PPC_DISA_SIMPLIFIED; + return; + } + } +#endif + sprintf(o->mnemonic, "cr%s", name); + sprintf(o->operands, "%i" COMMA "%i" COMMA "%i", crfD, crfA, crfB); + o->r[0] = crfD; o->r[1] = crfA; o->r[2] = crfB; +} + +#define MASK32(b, e) \ +{ \ + u32 mask = ((u32)0xffffffff >> (b)) ^ (((e) >= 31) ? 0 : ((u32)0xffffffff) >> ((e) + 1)); \ + o->target = ((b) > (e)) ? (~mask) : (mask); \ +} + +#define MASK64(b, e) \ +{ \ + u64 mask = ((u64)0xffffffffffffffff >> (b)) ^ (((e) >= 63) ? 0 : ((u64)0xffffffffffffffff) >> ((e) + 1)); \ + o->target = ((b) > (e)) ? (~mask) : (mask); \ +} + +// Rotate left word. +static void rlw(char *name, int rb, int ins=0) +{ + int mb = DIS_MB, me = DIS_ME; + char * ptr = o->operands; + sprintf(o->mnemonic, "rlw%s%c", name, Rc ? '.' : '\0'); + ptr += sprintf(ptr, "%s" COMMA "%s" COMMA, REGA, REGS); + if(rb) ptr += sprintf(ptr, "%s" COMMA, REGB); + else ptr += sprintf(ptr, "%i" COMMA, DIS_RB); // sh + ptr += sprintf(ptr, "%i" COMMA "%i", mb, me); + + // Put mask in target. + MASK32(mb, me); +#ifdef POWERPC_64 + MASK64(mb+32, me+32); +#endif + + o->r[0] = DIS_RA; + o->r[1] = DIS_RS; + if(rb) o->r[2] = DIS_RB; + o->iclass |= PPC_DISA_INTEGER; +} + +// RLD mask +#define RLDM_LEFT 0 // MASK(b, 63) +#define RLDM_RIGHT 1 // MASK(0, e) +#define RLDM_INS 2 // MASK(b, ~n) + +// Rotate left double-word. +static void rld(char *name, int rb, int mtype) +{ +#ifdef POWERPC_64 + int m = DIS_MB, n = DIS_RB; + if(Instr & 0x20) m += 32; // b or e + if(Instr & 0x02) n += 32; // sh + + char * ptr = o->operands; + sprintf(o->mnemonic, "rld%s%c", name, Rc ? '.' : '\0'); + ptr += sprintf(ptr, "%s" COMMA "%s" COMMA, REGA, REGS); + if(rb) ptr += sprintf(ptr, "%s" COMMA, REGB); + else ptr += sprintf(ptr, "%i" COMMA, n); + ptr += sprintf(ptr, "%i", m); + + // Put mask in target. + switch(mtype) + { + case RLDM_LEFT: MASK64(m, 63); break; + case RLDM_RIGHT: MASK64(0, m); break; + case RLDM_INS: MASK64(m, ~n); break; + } + + o->r[0] = DIS_RA; + o->r[1] = DIS_RS; + if(rb) o->r[2] = DIS_RB; + o->iclass |= PPC_DISA_64 | PPC_DISA_INTEGER; +#endif +} + +// Load/Store. +static void ldst(char *name, int x/*indexed*/, int load=1, int L=0, int string=0, int fload=0) +{ + if(x) integer(name, fload ? 'F' : 'X', DAB_D|DAB_A|DAB_B); + else + { + int rd = DIS_RD, ra = DIS_RA; + s16 imm = DIS_SIMM; + strcpy (o->mnemonic, name); + if(fload) sprintf (o->operands, "%s%i" COMMA "%s" LPAREN "%s" RPAREN, fregname, rd, simm(imm, 0, 1), regname[ra]); + else sprintf (o->operands, "%s" COMMA "%s" LPAREN "%s" RPAREN, regname[rd], simm(imm, 0, 1), regname[ra]); + o->r[0] = rd; + o->r[1] = ra; + o->immed = DIS_UIMM & 0x8000 ? DIS_UIMM | 0xFFFF0000 : DIS_UIMM; + } + + o->iclass = PPC_DISA_LDST; + if(L) o->iclass |= PPC_DISA_64; + if(string) o->iclass |= PPC_DISA_STRING; + if(fload) o->iclass |= PPC_DISA_FPU; +} + +// Cache. +static void cache(char *name, int flag=PPC_DISA_OTHER) +{ + if (DIS_RD) { ill(); return; } + else + { + integer(name, 'X', DAB_A|DAB_B); + o->r[0] = o->r[1]; + o->r[1] = o->r[2]; + o->r[2] = 0; + o->iclass &= ~PPC_DISA_INTEGER; + o->iclass |= flag; + } +} + +static void movesr(char *name, int from, int L, int xform) +{ + int reg = DIS_RD, sreg = DIS_RA & 0xF, regb = DIS_RB; + + strncpy(o->mnemonic, name, sizeof(o->mnemonic)); + if(xform) + { + if(Instr & 0x001F0001) { ill(); return; } + sprintf(o->operands, "%s" COMMA "%s", regname[reg], regname[regb]); + o->r[0] = reg; + o->r[1] = regb; + } + else + { + if(Instr & 0x0010F801) { ill(); return; } + if(from) + { + sprintf(o->operands, "%s" COMMA "%i", regname[reg], sreg); + o->r[0] = reg; + o->r[1] = sreg; + } + else + { + sprintf(o->operands, "%i" COMMA "%s", sreg, regname[reg]); + o->r[0] = sreg; + o->r[1] = reg; + } + } + + if(L) o->iclass |= PPC_DISA_OEA | PPC_DISA_OPTIONAL | PPC_DISA_BRIDGE | PPC_DISA_64; + else o->iclass |= PPC_DISA_OEA | PPC_DISA_BRIDGE; +} + +static void mtcrf(void) +{ + int rs = DIS_RS, crm = DIS_CRM; + +#ifdef SIMPLIFIED + if(crm == 0xFF) + { + strncpy(o->mnemonic, "mtcr", sizeof(o->mnemonic)); + sprintf(o->operands, "%s", regname[rs]); + } + else +#endif + { + strncpy(o->mnemonic, "mtcrf", sizeof(o->mnemonic)); + sprintf(o->operands, HEX1 "%02X" HEX2 COMMA "%s", crm, regname[rs]); + } + o->r[0] = rs; +} + +static void mcrxr(void) +{ + if (Instr & 0x007FF800) { ill(); return; } + strcpy (o->mnemonic, "mcrxr"); + sprintf (o->operands, "%s%i", crname, DIS_RD >> 2); + o->r[0] = DIS_RD >> 2; +} + +static char *spr_name(int n) +{ + static char def[8]; + + switch(n) + { + // General architecture special-purpose registers. + case 1: return "XER"; + case 8: return "LR"; + case 9: return "CTR"; + case 18: return "DSISR"; + case 19: return "DAR"; + case 22: return "DEC"; + case 25: return "SDR1"; + case 26: return "SRR0"; + case 27: return "SRR1"; + case 272: return "SPRG0"; + case 273: return "SPRG1"; + case 274: return "SPRG2"; + case 275: return "SPRG3"; +#ifdef POWERPC_64 + case 280: return "ASR"; +#endif + case 284: return "TBL"; + case 285: return "TBU"; + case 287: return "PVR"; + case 528: return "IBAT0U"; + case 529: return "IBAT0L"; + case 530: return "IBAT1U"; + case 531: return "IBAT1L"; + case 532: return "IBAT2U"; + case 533: return "IBAT2L"; + case 534: return "IBAT3U"; + case 535: return "IBAT3L"; + case 536: return "DBAT0U"; + case 537: return "DBAT0L"; + case 538: return "DBAT1U"; + case 539: return "DBAT1L"; + case 540: return "DBAT2U"; + case 541: return "DBAT2L"; + case 542: return "DBAT3U"; + case 543: return "DBAT3L"; + + // Optional registers. +#if !defined(GEKKO) && !defined(BROADWAY) + case 282: return "EAR"; + case 1013: return "DABR"; + case 1022: return "FPECR"; + case 1023: return "PIR"; +#endif + + // Gekko-specific SPRs +#ifdef GEKKO + case 282: return "EAR"; + case 912: return "GQR0"; + case 913: return "GQR1"; + case 914: return "GQR2"; + case 915: return "GQR3"; + case 916: return "GQR4"; + case 917: return "GQR5"; + case 918: return "GQR6"; + case 919: return "GQR7"; + case 920: return "HID2"; + case 921: return "WPAR"; + case 922: return "DMAU"; + case 923: return "DMAL"; + case 936: return "UMMCR0"; + case 940: return "UMMCR1"; + case 937: return "UPMC1"; + case 938: return "UPMC2"; + case 939: return "USIA"; + case 941: return "UPMC3"; + case 942: return "UPMC4"; + case 943: return "USDA"; + case 952: return "MMCR0"; + case 953: return "PMC1"; + case 954: return "PMC2"; + case 955: return "SIA"; + case 956: return "MMCR1"; + case 957: return "PMC3"; + case 958: return "PMC4"; + case 959: return "SDA"; + case 1008: return "HID0"; + case 1009: return "HID1"; + case 1010: return "IABR"; + case 1013: return "DABR"; + case 1017: return "L2CR"; + case 1019: return "ICTC"; + case 1020: return "THRM1"; + case 1021: return "THRM2"; + case 1022: return "THRM3"; +#endif + } + + sprintf(def, "%u", n); + return def; +} + +static char *tbr_name(int n) +{ + static char def[8]; + + switch(n) + { + // General architecture time-base registers. + case 268: return "TBL"; + case 269: return "TBU"; + } + + sprintf(def, "%u", n); + return def; +} + +static void movespr(int from) +{ + int spr = (DIS_RB << 5) | DIS_RA, f = 1; + char *fix; + + if( !((spr == 1) || (spr == 8) || (spr == 9)) ) o->iclass |= PPC_DISA_OEA; + + // Handle simplified mnemonic + if (spr == 1) { fix = "xer"; o->iclass |= PPC_DISA_SIMPLIFIED; } + else if (spr == 8) { fix = "lr"; o->iclass |= PPC_DISA_SIMPLIFIED; } + else if (spr == 9) { fix = "ctr"; o->iclass |= PPC_DISA_SIMPLIFIED; } + else { fix = "spr"; f = 0; } + + // Mnemonics and operands. + sprintf (o->mnemonic, "m%c%s", from ? 'f' : 't', fix); + if (f) + { + sprintf (o->operands, "%s", regname[DIS_RD]); + o->r[0] = DIS_RD; + } + else + { + if (from) + { + sprintf (o->operands, "%s" COMMA "%s", regname[DIS_RD], spr_name(spr)); + o->r[0] = DIS_RD; + o->r[1] = spr; + } + else + { + sprintf (o->operands, "%s" COMMA "%s", spr_name(spr), regname[DIS_RD]); + o->r[0] = spr; + o->r[1] = DIS_RD; + } + } +} + +static void movetbr(void) +{ + int tbr = (DIS_RB << 5) | DIS_RA, f = 1; + char *fix; + + // Handle simplified mnemonic + if (tbr == 268) { fix = "tbl"; o->iclass |= PPC_DISA_SIMPLIFIED; } + else if (tbr == 269) { fix = "tbu"; o->iclass |= PPC_DISA_SIMPLIFIED; } + else { fix = "tb"; f = 0; } + + // Mnemonics and operands. + sprintf (o->mnemonic, "mf%s", fix); + if (f) + { + sprintf (o->operands, "%s", regname[DIS_RD]); + o->r[0] = DIS_RD; + } + else + { + sprintf (o->operands, "%s" COMMA "%s", regname[DIS_RD], tbr_name(tbr)); + o->r[0] = DIS_RD; + o->r[1] = tbr; + } +} + +static void srawi(void) +{ + int rs = DIS_RS, ra = DIS_RA, sh = DIS_RB; + sprintf (o->mnemonic, "srawi%c", Rc ? '.' : 0); + sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[ra], regname[rs], sh); + o->r[0] = ra; + o->r[1] = rs; + o->r[2] = sh; + o->iclass = PPC_DISA_INTEGER; +} + +static void sradi(void) +{ + int rs = DIS_RS, ra = DIS_RA, sh = (((Instr >> 1) & 1) << 5) | DIS_RB; + sprintf (o->mnemonic, "sradi%c", Rc ? '.' : 0); + sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[ra], regname[rs], sh); + o->r[0] = ra; + o->r[1] = rs; + o->r[2] = sh; + o->iclass = PPC_DISA_INTEGER | PPC_DISA_64; +} + +static void lsswi(char *name) +{ + int rd = DIS_RD, ra = DIS_RA, nb = DIS_RB; + strcpy (o->mnemonic, name); + sprintf (o->operands, "%s" COMMA "%s" COMMA "%i", regname[rd], regname[ra], nb); + o->r[0] = rd; + o->r[1] = ra; + o->r[2] = nb; + o->iclass = PPC_DISA_LDST | PPC_DISA_STRING; +} + +#define FPU_DAB 1 +#define FPU_DB 2 +#define FPU_DAC 3 +#define FPU_DACB 4 +#define FPU_D 5 + +static void fpu(char *name, u32 mask, int type, int flag=PPC_DISA_OTHER) +{ + int d = DIS_RD, a = DIS_RA, c = DIS_RC, b = DIS_RB; + + if(Instr & mask) { ill(); return; } + + strcpy (o->mnemonic, name); + + switch (type) + { + case FPU_DAB: + sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, b); + o->r[0] = d; o->r[1] = a; o->r[2] = b; + break; + case FPU_DB: + sprintf (o->operands, "%s%i" COMMA "%s%i", fregname, d, fregname, b); + o->r[0] = d; o->r[1] = b; + break; + case FPU_DAC: + sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c); + o->r[0] = d; o->r[1] = a; o->r[2] = c; + break; + case FPU_DACB: + sprintf (o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c, fregname, b); + o->r[0] = d; o->r[1] = a; o->r[2] = c; o->r[3] = b; + break; + case FPU_D: + sprintf (o->operands, "%s%i", fregname, d); + o->r[0] = d; + break; + } + + o->iclass = PPC_DISA_FPU | flag; +} + +static void fcmp(char *name) +{ + int crfd = DIS_RD >> 2, ra = DIS_RA, rb = DIS_RB; + + if (Instr & 0x00600001) { ill(); return; } + + strcpy (o->mnemonic, name); + sprintf (o->operands, "%i" COMMA "%s%i" COMMA "%s%i", crfd, fregname, ra, fregname, rb); + o->r[0] = crfd; o->r[1] = ra; o->r[2] = rb; + o->iclass = PPC_DISA_FPU; +} + +static void mtfsf(void) +{ + int fm = (Instr >> 17) & 0xFF, rb = DIS_RB; + + if(Instr & 0x02010000) { ill(); return; } + + sprintf (o->mnemonic, "mtfsf%c", Rc ? '.' : 0); + sprintf (o->operands, HEX1 "%02X" HEX2 COMMA "%s%i", fm, fregname, rb); + o->r[0] = fm; o->r[1] = rb; + o->iclass = PPC_DISA_FPU; +} + +static void mtfsb(char *name) +{ + int crbd = DIS_RD; + + if (Instr & 0x001FF800) { ill(); return; } + + strcpy (o->mnemonic, name); + sprintf (o->operands, "%i", crbd); + o->r[0] = crbd; + o->iclass = PPC_DISA_FPU; +} + +static void mcrfs(void) +{ + int crfD = DIS_RD >> 2, crfS = DIS_RA >> 2; + + if (Instr & 0x0063F801) { ill(); return; } + + strcpy (o->mnemonic, "mcrfs"); + sprintf (o->operands, "%s%i" COMMA "%s%i", crname, crfD, crname, crfS); + o->r[0] = crfD; o->r[1] = crfS; + o->iclass = PPC_DISA_FPU; +} + +static void mtfsfi(void) +{ + int crfD = DIS_RD >> 2, imm = DIS_RB >> 1; + + if (Instr & 0x007F0800) { ill(); return; } + + sprintf (o->mnemonic, "mtfsfi%c", Rc ? '.' : 0); + sprintf (o->operands, "%s%i" COMMA "%i", crname, crfD, imm); + o->r[0] = crfD; o->r[1] = imm; + o->iclass = PPC_DISA_FPU; +} + +/* + *********************************************************************************** + * Architecture-specific extensions: + * Processor model: GEKKO + *********************************************************************************** +*/ + +#ifdef GEKKO + +static void ps_cmpx(int n) +{ + static char *fix[] = { "u0", "o0", "u1", "o1" }; + if(Instr & 0x00600001) { ill(); return; } + sprintf(o->mnemonic, "ps_cmp%s", fix[n]); + o->r[0] = DIS_RD>>2; o->r[1] = DIS_RA; o->r[2] = DIS_RB; + sprintf(o->operands, "%s%d" COMMA "%s%d" COMMA "%s%d", crname, o->r[0], fregname, o->r[1], fregname, o->r[2]); + o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC; +} + +static char *ps_ldst_offs(unsigned long val) +{ + static char buf[8]; + + if(val == 0) + { + return "0"; + } + else + { + if(val <= 128) + { + sprintf(buf, "%i", val); + return buf; + } + + if(val & 0x800) sprintf(buf, "-" HEX1 "%03X" HEX2, ((~val) & 0xfff) + 1); + else sprintf(buf, HEX1 "%03X" HEX2, val); + + return buf; + } +} + +static void ps_ldst(char *fix) +{ + int s = DIS_RS, a = DIS_RA, d = (Instr & 0xfff); + sprintf(o->mnemonic, "psq_%s", fix); + sprintf( o->operands, "%s%i" COMMA "%s" LPAREN "%s" RPAREN COMMA "%i" COMMA "%i", + fregname, s, ps_ldst_offs(d), regname[a], (Instr >> 15) & 1, (Instr >> 12) & 7 ); + o->r[0] = s; o->r[1] = a; o->r[2] = DIS_RB >> 1; + o->immed = d & 0x800 ? d | 0xFFFFF000 : d; + o->iclass = PPC_DISA_FPU | PPC_DISA_LDST | PPC_DISA_SPECIFIC; +} + +static void ps_ldstx(char *fix) +{ + int d = DIS_RD, a = DIS_RA, b = DIS_RB; + if(Instr & 1) { ill(); return; } + sprintf(o->mnemonic, "psq_%s", fix); + sprintf(o->operands, "%s%i" COMMA "%s" COMMA "%s" COMMA "%i" COMMA "%i", fregname, d, regname[a], regname[b], (Instr >> 10) & 1, (Instr >> 7) & 7); + o->r[0] = d; o->r[1] = a; o->r[2] = b; o->r[3] = DIS_RC >> 1; + o->iclass = PPC_DISA_FPU | PPC_DISA_LDST | PPC_DISA_SPECIFIC; +} + +static void ps_dacb(char *fix) +{ + int a = DIS_RA, b = DIS_RB, c = DIS_RC, d = DIS_RD; + sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0); + sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c, fregname, b); + o->r[0] = d; o->r[1] = a; o->r[2] = c; o->r[3] = b; + o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC; +} + +static void ps_dac(char *fix) +{ + int a = DIS_RA, c = DIS_RC, d = DIS_RD; + if(Instr & 0x0000F800) { ill(); return; } + sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0); + sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, c); + o->r[0] = d; o->r[1] = a; o->r[2] = c; + o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC; +} + +static void ps_dab(char *fix, int unmask=0) +{ + int d = DIS_RD, a = DIS_RA, b = DIS_RB; + if(Instr & 0x000007C0 && !unmask) { ill(); return; } + sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0); + sprintf(o->operands, "%s%i" COMMA "%s%i" COMMA "%s%i", fregname, d, fregname, a, fregname, b); + o->r[0] = d; o->r[1] = a; o->r[2] = b; + o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC; +} + +static void ps_db(char *fix, int aonly=0) +{ + int d = DIS_RD, b = DIS_RB; + if(aonly) { if(Instr & 0x001F0000) { ill(); return; } } + else { if(Instr & 0x001F07C0) { ill(); return; } } + sprintf(o->mnemonic, "ps_%s%c", fix, Rc ? '.' : 0); + sprintf(o->operands, "%s%i" COMMA "%s%i", fregname, d, fregname, b); + o->r[0] = d; o->r[1] = b; + o->iclass = PPC_DISA_FPU | PPC_DISA_SPECIFIC; +} + +#endif /* END OF GEKKO */ + +// --------------------------------------------------------------------------- + +void PPCDisasm(PPCD_CB *discb) +{ + // Save parameters in local variables for static calls + o = discb; + if(o == NULL) return; + + // Detect endianness order. + if(bigendian == -1) + { + u8 test_value[2] = { 0xAA, 0xBB }; + u16 *value = (u16 *)test_value; + if(*value == 0xAABB) bigendian = 1; + else bigendian = 0; + } + + // Reset output parameters + o->iclass = PPC_DISA_OTHER; + o->r[0] = o->r[1] = o->r[2] = o->r[3] = 0; + o->immed = 0; + o->target = 0; + o->mnemonic[0] = o->operands[0] = '\0'; + + // Lets go! + + /* + * Main table + */ + + switch(Instr >> 26 /* Main opcode, base 8 */) { +#ifdef POWERPC_64 + case 002: trap(1, 1); break; // tdi +#endif + case 003: trap(0, 1); break; // twi + case 007: integer("mulli", 'D', DAB_D|DAB_A); break; // mulli + case 010: integer("subfic", 'D', DAB_D|DAB_A); break; // subfic + case 012: cmp("l", "i"); break; // cmpli + case 013: cmp("", "i"); break; // cmpi + case 014: addi("c"); break; // addic + case 015: addi("c."); break; // addic. + case 016: addi(""); break; // addi + case 017: addi("s"); break; // addis + case 020: bcx(1, 0); break; // bcx + case 021: put("sc", 0x03ffffff, 2); break; // sc + case 022: bx(); break; // bx + case 024: rlw("imi", 0, 1); break; // rlwimix + case 025: rlw("inm", 0); break; // rlwinmx + case 027: rlw("nm", 1); break; // rlwnmx + case 030: // ori +#ifdef SIMPLIFIED + if(Instr == 0x60000000) put("nop", 0, 0, PPC_DISA_INTEGER | PPC_DISA_SIMPLIFIED); + else +#endif + integer("ori", 'S', ASB_A|ASB_S, 1, 0); break; + case 031: integer("oris", 'S', ASB_A|ASB_S, 1, 0); break; // oris + case 032: integer("xori", 'S', ASB_A|ASB_S, 1, 0); break; // xori + case 033: integer("xoris", 'S', ASB_A|ASB_S, 1, 0); break; // xoris + case 034: integer("andi.", 'S', ASB_A|ASB_S, 1, 0); break; // andi. + case 035: integer("andis.", 'S', ASB_A|ASB_S, 1, 0); break; // andis. + case 040: ldst("lwz", 0, 1); break; // lwz + case 041: ldst("lwzu", 0, 1); break; // lwzu + case 042: ldst("lbz", 0, 1); break; // lbz + case 043: ldst("lbzu", 0, 1); break; // lbzu + case 044: ldst("stw", 0, 0); break; // stw + case 045: ldst("stwu", 0, 0); break; // stwu + case 046: ldst("stb", 0, 0); break; // stb + case 047: ldst("stbu", 0, 0); break; // stbu + case 050: ldst("lhz", 0, 1); break; // lhz + case 051: ldst("lhzu", 0, 1); break; // lhzu + case 052: ldst("lha", 0, 1); break; // lha + case 053: ldst("lhau", 0, 1); break; // lhau + case 054: ldst("sth", 0, 0); break; // sth + case 055: ldst("sthu", 0, 0); break; // sthu + case 056: ldst("lmw", 0, 1, 0, 1); break; // lmw + case 057: ldst("stmw", 0, 0, 0, 1); break; // stmw + case 060: ldst("lfs", 0, 1, 0, 0, 1); break; // lfs + case 061: ldst("lfsu", 0, 1, 0, 0, 1); break; // lfsu + case 062: ldst("lfd", 0, 1, 0, 0, 1); break; // lfd + case 063: ldst("lfdu", 0, 1, 0, 0, 1); break; // lfdu + case 064: ldst("stfs", 0, 0, 0, 0, 1); break; // stfs + case 065: ldst("stfsu", 0, 0, 0, 0, 1); break; // stfsu + case 066: ldst("stfd", 0, 0, 0, 0, 1); break; // stfd + case 067: ldst("stfdu", 0, 0, 0, 0, 1); break; // stfdu + + /* + * Extention 1. + */ + + case 023: + switch((Instr >> 1) & 0x3ff /* Extended opcode 023, base 8 */) { + case 00020: bcx(0, 1); break; // bclrx + case 01020: bcx(0, 0); break; // bcctrx + case 00000: mcrf(); break; // mcrf + case 00401: crop("and"); break; // crand + case 00201: crop("andc"); break; // crandc + case 00441: crop("eqv", "set", 1); break; // creqv + case 00341: crop("nand"); break; // crnand + case 00041: crop("nor", "not", 0, 1); break; // crnor + case 00701: crop("or", "move", 0, 1); break; // cror + case 00641: crop("orc"); break; // crorc + case 00301: crop("xor", "clr", 1); break; // crxor + case 00226: put("isync", 0x3fff801); break; // isync +#ifdef POWERPC_32 + case 00062: put("rfi", 0x3fff801, 0, PPC_DISA_OEA | PPC_DISA_BRIDGE ); break; // rfi +#endif +#ifdef POWERPC_64 + case 00022: put("rfid", 0x3fff801, 0, PPC_DISA_OEA | PPC_DISA_64 ); break; // rfid +#endif + default: ill(); break; + } break; + +#ifdef POWERPC_64 + case 036: + switch((Instr >> 1) & 0xf /* Rotate left double */) { + case 0x0: rld("icl", 0, RLDM_LEFT); break; // rldiclx + case 0x1: rld("icl", 0, RLDM_LEFT); break; + case 0x2: rld("icr", 0, RLDM_RIGHT); break; // rldicrx + case 0x3: rld("icr", 0, RLDM_RIGHT); break; + case 0x4: rld("ic", 0, RLDM_INS); break; // rldicx + case 0x5: rld("ic", 0, RLDM_INS); break; + case 0x6: rld("imi", 0, RLDM_INS); break; // rldimix + case 0x7: rld("imi", 0, RLDM_INS); break; + case 0x8: rld("cl", 1, RLDM_LEFT); break; // rldclx + case 0x9: rld("cr", 1, RLDM_RIGHT); break; // rldcrx + default: ill(); break; + } break; +#endif + + /* + * Extention 2. + */ + + #define OE 02000 + case 037: + switch(Instr & 0x7ff /* Extended opcode 037, base 8 */) { + case 00000: cmp("", ""); break; // cmp + case 00010: // tw +#ifdef SIMPLIFIED + if(Instr == 0x7FE00008) put("trap", 0, 0, PPC_DISA_SIMPLIFIED); + else +#endif + trap(0, 0); break; + case 00020: integer("subfc", 'X', DAB_D|DAB_A|DAB_B); break; // subfcx + case 00020|OE: integer("subfco", 'X', DAB_D|DAB_A|DAB_B); break; + case 00021: integer("subfc.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00021|OE: integer("subfco.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00024: integer("addc", 'X', DAB_D|DAB_A|DAB_B); break; // addcx + case 00024|OE: integer("addco", 'X', DAB_D|DAB_A|DAB_B); break; + case 00025: integer("addc.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00025|OE: integer("addco.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00026: integer("mulhwu", 'X', DAB_D|DAB_A|DAB_B); break; // mulhwu + case 00027: integer("mulhwu.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00046: if(DIS_RA | DIS_RB) ill(); // mfcr + else { integer("mfcr", 'D', DAB_D, 0,0,0,0,0); o->iclass = PPC_DISA_OTHER; } break; + case 00050: ldst("lwarx", 1); break; // lwarx + case 00056: ldst("lwzx", 1); break; // lwzx + case 00060: integer("slw", 'Z', ASB_A|ASB_S|ASB_B); break; // slwx + case 00061: integer("slw.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 00064: if(DIS_RB) ill(); // cntlzwx + else integer("cntlzw", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 00065: if(DIS_RB) ill(); + else integer("cntlzw.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 00070: integer("and", 'Z', ASB_A|ASB_S|ASB_B); break; // andx + case 00071: integer("and.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 00100: cmp("l", ""); break; // cmpl + case 00120: integer("subf", 'X', DAB_D|DAB_A|DAB_B); break; // subfx + case 00120|OE: integer("subfo", 'X', DAB_D|DAB_A|DAB_B); break; + case 00121: integer("subf.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00121|OE: integer("subfo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00154: cache("dcbst"); break; // dcbst + case 00156: ldst("lwzux", 1); break; // lwzux + case 00170: integer("andc", 'Z', ASB_A|ASB_S|ASB_B); break; // andcx + case 00171: integer("andc.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 00226: integer("mulhw", 'X', DAB_D|DAB_A|DAB_B); break; // mulhw + case 00227: integer("mulhw.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00246: if(DIS_RA || DIS_RB) ill(); // mfmsr + else { integer("mfmsr", 'X', DAB_D); o->iclass = PPC_DISA_OEA; } break; + case 00254: cache("dcbf"); break; // dcbf + case 00256: ldst("lbzx", 1, 1, 0); break; // lbzx + case 00320: if(DIS_RB) ill(); // negx + else integer("neg", 'X', DAB_D|DAB_A); break; + case 00321: if(DIS_RB) ill(); + else integer("neg.", 'X', DAB_D|DAB_A); break; + case 00320|OE: if(DIS_RB) ill(); + else integer("nego", 'X', DAB_D|DAB_A); break; + case 00321|OE: if(DIS_RB) ill(); + else integer("nego.", 'X', DAB_D|DAB_A); break; + case 00356: ldst("lbzux", 1, 1); break; // lbzux + case 00370: // norx +#ifdef SIMPLIFIED + if(DIS_RS == DIS_RB) { integer("not", 'Z', ASB_A|ASB_S); o->iclass |= PPC_DISA_SIMPLIFIED; } + else +#endif + integer("nor", 'Z', ASB_A|ASB_S|ASB_B); break; + case 00371: integer("nor.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 00420: integer("subfe", 'X', DAB_D|DAB_A|DAB_B); break; // subfex + case 00420|OE: integer("subfeo", 'X', DAB_D|DAB_A|DAB_B); break; + case 00421: integer("subfe.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00421|OE: integer("subfeo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00424: integer("adde", 'X', DAB_D|DAB_A|DAB_B); break; // addex + case 00424|OE: integer("addeo", 'X', DAB_D|DAB_A|DAB_B); break; + case 00425: integer("adde.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00425|OE: integer("addeo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00440: mtcrf(); break; // mtcrf +#ifdef POWERPC_32 + case 00444: if(DIS_RA || DIS_RB) ill(); // mtmsr + else { integer("mtmsr", 'X', DAB_D); o->iclass = PPC_DISA_OEA | PPC_DISA_BRIDGE; } break; +#endif + case 00455: ldst("stwcx.", 1, 0, 0); break; // stwcx. + case 00456: ldst("stwx", 1, 0, 0); break; // stwx + case 00556: ldst("stwux", 1, 0, 0); break; // stwux + case 00620: if(DIS_RB) ill(); // subfzex + else integer("subfze", 'X', DAB_D|DAB_A); break; + case 00620|OE: if(DIS_RB) ill(); + else integer("subfzeo", 'X', DAB_D|DAB_A); break; + case 00621: if(DIS_RB) ill(); + else integer("subfze.", 'X', DAB_D|DAB_A); break; + case 00621|OE: if(DIS_RB) ill(); + else integer("subfzeo.", 'X', DAB_D|DAB_A); break; + case 00624: if(DIS_RB) ill(); // addzex + else integer("addze", 'X', DAB_D|DAB_A); break; + case 00624|OE: if(DIS_RB) ill(); + else integer("addzeo", 'X', DAB_D|DAB_A); break; + case 00625: if(DIS_RB) ill(); + else integer("addze.", 'X', DAB_D|DAB_A); break; + case 00625|OE: if(DIS_RB) ill(); + else integer("addzeo.", 'X', DAB_D|DAB_A); break; +#ifdef POWERPC_32 + case 00644: movesr("mtsr", 0, 0, 0); break; // mtsr +#endif + case 00656: ldst("stbx", 1, 0, 0); break; // stbx + case 00720: if(DIS_RB) ill(); // subfmex + else integer("subfme", 'X', DAB_D|DAB_A); break; + case 00720|OE: if(DIS_RB) ill(); + else integer("subfmeo", 'X', DAB_D|DAB_A); break; + case 00721: if(DIS_RB) ill(); + else integer("subfme.", 'X', DAB_D|DAB_A); break; + case 00721|OE: if(DIS_RB) ill(); + else integer("subfmeo.", 'X', DAB_D|DAB_A); break; + case 00724: if(DIS_RB) ill(); // addmex + else integer("addme", 'X', DAB_D|DAB_A); break; + case 00724|OE: if(DIS_RB) ill(); + else integer("addmeo", 'X', DAB_D|DAB_A); break; + case 00725: if(DIS_RB) ill(); + else integer("addme.", 'X', DAB_D|DAB_A); break; + case 00725|OE: if(DIS_RB) ill(); + else integer("addmeo.", 'X', DAB_D|DAB_A); break; + case 00726: integer("mullw", 'X', DAB_D|DAB_A|DAB_B); break; // mullwx + case 00726|OE: integer("mullwo", 'X', DAB_D|DAB_A|DAB_B); break; + case 00727: integer("mullw.", 'X', DAB_D|DAB_A|DAB_B); break; + case 00727|OE: integer("mullwo.", 'X', DAB_D|DAB_A|DAB_B); break; +#ifdef POWERPC_32 + case 00744: movesr("mtsrin", 0, 0, 1); break; // mtsrin +#endif + case 00754: cache("dcbtst"); break; // dcbtst + case 00756: ldst("stbux", 1, 0, 0); break; // stbux + case 01024: integer("add", 'X', DAB_D|DAB_A|DAB_B); break; // addx + case 01024|OE: integer("addo", 'X', DAB_D|DAB_A|DAB_B); break; + case 01025: integer("add.", 'X', DAB_D|DAB_A|DAB_B); break; + case 01025|OE: integer("addo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 01054: cache("dcbt"); break; // dcbt + case 01056: ldst("lhzx", 1); break; // lhzx + case 01070: integer("eqv", 'Z', ASB_A|ASB_S|ASB_B); break; // eqvx + case 01071: integer("eqv.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 01144: if(DIS_RD || DIS_RA) ill(); // tlbie + else { integer("tlbie", 'X', DAB_B); o->iclass = PPC_DISA_OEA | PPC_DISA_OPTIONAL; o->r[0] = o->r[2]; o->r[2] = 0; } break; + case 01154: integer("eciwx", 'X', DAB_D|DAB_A|DAB_B); o->iclass = PPC_DISA_OPTIONAL; break; // eciwx + case 01156: ldst("lhzux", 1); break; // lhzux + case 01170: integer("xor", 'Z', ASB_A|ASB_S|ASB_B); break; // xorx + case 01171: integer("xor.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 01246: movespr(1); break; // mfspr + case 01256: ldst("lhax", 1); break; // lhax +#if !defined(GEKKO) + case 01344: put("tlbia", 0x03FFF800, 0, PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // tlbia +#endif + case 01346: movetbr(); break; // mftb + case 01356: ldst("lhaux", 1); break; // lhaux + case 01456: ldst("sthx", 1, 0); break; // sthx + case 01470: integer("orc", 'Z', ASB_A|ASB_S|ASB_B); break; // orcx + case 01471: integer("orc.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 01554: integer("ecowx", 'X', DAB_D|DAB_A|DAB_B); o->iclass = PPC_DISA_OPTIONAL; break; // ecowx + case 01556: ldst("sthux", 1, 0); break; // sthux + case 01570: integer("or", 'Z', ASB_A|ASB_S|ASB_B); break; // orx + case 01571: integer("or.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 01626: integer("divwu", 'X', DAB_D|DAB_A|DAB_B); break; // divwux + case 01626|OE: integer("divwuo", 'X', DAB_D|DAB_A|DAB_B); break; + case 01627: integer("divwu.", 'X', DAB_D|DAB_A|DAB_B); break; + case 01627|OE: integer("divwuo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 01646: movespr(0); break; // mtspr + case 01654: cache("dcbi", PPC_DISA_OEA); break; // dcbi + case 01670: integer("nand", 'Z', ASB_A|ASB_S|ASB_B); break; // nandx + case 01671: integer("nand.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 01726: integer("divw", 'X', DAB_D|DAB_A|DAB_B); break; // divwx + case 01726|OE: integer("divwo", 'X', DAB_D|DAB_A|DAB_B); break; + case 01727: integer("divw.", 'X', DAB_D|DAB_A|DAB_B); break; + case 01727|OE: integer("divwo.", 'X', DAB_D|DAB_A|DAB_B); break; + case 02000: mcrxr(); break; // mcrxr + case 02052: ldst("lswx", 1, 1, 0, 1); break; // lswx + case 02054: ldst("lwbrx", 1); break; // lwbrx + case 02056: ldst("lfsx", 1, 1, 0, 0, 1); break; // lfsx + case 02060: integer("srw", 'Z', ASB_A|ASB_S|ASB_B); break; // srwx + case 02061: integer("srw.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 02154: put("tlbsync", 0x03FFF800, 0, PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // tlbsync + case 02156: ldst("lfsux", 1, 1, 0, 0, 1); break; // lfsux +#ifdef POWERPC_32 + case 02246: movesr("mfsr", 1, 0, 0); break; // mfsr +#endif + case 02252: lsswi("lswi"); break; // lswi + case 02254: put("sync", 0x03FFF800, 0); break; // sync + case 02256: ldst("lfdx", 1, 1, 0, 0, 1); break; // lfdx + case 02356: ldst("lfdux", 1, 1, 0, 0, 1); break; // lfdux +#ifdef POWERPC_32 + case 02446: movesr("mfsrin", 1, 0, 1); break; // mfsrin +#endif + case 02452: ldst("stswx", 1, 1, 0, 1); break; // stswx + case 02454: ldst("stwbrx", 1, 0); break; // stwbrx + case 02456: ldst("stfsx", 1, 1, 0, 0, 1); break; // stfsx + case 02556: ldst("stfsux", 1, 1, 0, 0, 1); break; // stfsux + case 02652: lsswi("stswi"); break; // stswi + case 02656: ldst("stfdx", 1, 1, 0, 0, 1); break; // stfdx +#if !defined(GEKKO) + case 02754: cache("dcba", PPC_DISA_OPTIONAL); break; // dcba +#endif + case 02756: ldst("stfdux", 1, 1, 0, 0, 1); break; // stfdux + case 03054: ldst("lhbrx", 1); break; // lhbrx + case 03060: integer("sraw", 'Z', ASB_A|ASB_S|ASB_B); break; // srawx + case 03061: integer("sraw.", 'Z', ASB_A|ASB_S|ASB_B); break; + case 03160: srawi(); break; // srawi + case 03161: srawi(); break; + case 03254: put("eieio", 0x03FFF800, 0); break; // eieio + case 03454: ldst("sthbrx", 1, 0); break; // sthbrx + case 03464: if(DIS_RB) ill(); // extshx + else integer("extsh", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 03465: if(DIS_RB) ill(); + else integer("extsh.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 03564: if(DIS_RB) ill(); // extsbx + else integer("extsb", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 03565: if(DIS_RB) ill(); + else integer("extsb.", 'S', ASB_A|ASB_S, 0,0,0,0,0); break; + case 03654: cache("icbi"); break; // icbi + case 03656: ldst("stfiwx", 1, 1, 0, 0, 1); o->iclass |= PPC_DISA_OPTIONAL; break; // stfiwx + case 03754: cache("dcbz"); break; // dcbz +#ifdef POWERPC_64 + case 00022: integer("mulhdu", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulhdux + case 00023: integer("mulhdu.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 00052: ldst("ldx", 1, 1, 1); break; // ldx + case 00066: integer("sld", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // sldx + case 00067: integer("sld.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; + case 00152: ldst("ldux", 1, 1, 1); break; // ldux + case 00164: if(DIS_RB) ill(); // cntlzdx + else integer("cntlzd", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; break; + case 00165: if(DIS_RB) ill(); + else integer("cntlzd.", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; break; + case 00210: trap(1, 0); break; + case 00222: integer("mulhd", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulhdx + case 00223: integer("mulhd.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 00244: movesr("mtsrd", 0, 1, 0); break; // mtrsd + case 00250: ldst("ldarx", 1, 1, 1); break; // ldarx + case 00344: movesr("mtsrdin", 0, 1, 1); break; // mtrsdin + case 00452: ldst("stdx", 1, 0, 1); break; // stdx + case 00544: if(DIS_RA || DIS_RB) ill(); // mtmsrd + else { integer("mtmsrd", 'X', DAB_D); o->iclass = PPC_DISA_OEA | PPC_DISA_64; } break; + case 00552: ldst("stdux", 1, 0, 1); break; // stdux + case 00655: ldst("stdcx.", 1, 0, 1); break; // stdcx. + case 00722: integer("mulld", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // mulldx + case 00722|OE: integer("mulldo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 00723: integer("mulld.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 00723|OE: integer("mulldo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01252: ldst("lwax", 1, 1, 1); break; // lwax + case 01352: ldst("lwaux", 1, 1, 1); break; // lwaux + case 03164: sradi(); break; // sradi + case 03165: sradi(); break; + case 03166: sradi(); break; + case 03167: sradi(); break; + case 01544: if(DIS_RD || DIS_RA) ill(); // slbie + else { integer("slbie", 'X', DAB_B); o->iclass = PPC_DISA_64 | PPC_DISA_OEA | PPC_DISA_OPTIONAL; o->r[0] = o->r[2]; o->r[2] = 0; } break; + case 01622: integer("divdu", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // divdux + case 01622|OE: integer("divduo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01623: integer("divdu.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01623|OE: integer("divduo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01722: integer("divd", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; // divdx + case 01722|OE: integer("divdo", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01723: integer("divd.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01723|OE: integer("divdo.", 'X', DAB_D|DAB_A|DAB_B); o->iclass |= PPC_DISA_64; break; + case 01744: put("slbia", 0x03FFF800, 0, PPC_DISA_64 | PPC_DISA_OEA | PPC_DISA_OPTIONAL); break; // slbia + case 02066: integer("srd", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // srdx + case 02067: integer("srd.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; + case 03064: integer("srad", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; // sradx + case 03065: integer("srad.", 'Z', ASB_A|ASB_S|ASB_B); o->iclass |= PPC_DISA_64; break; + case 03664: if(DIS_RB) ill(); // extswx + else { integer("extsw", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; } break; + case 03665: if(DIS_RB) ill(); + else { integer("extsw.", 'S', ASB_A|ASB_S, 0,0,0,0,0); o->iclass |= PPC_DISA_64; } break; +#endif + default: ill(); break; + } break; + + /* + * Extention 3. + */ + +#ifdef POWERPC_64 + case 072: + switch(Instr & 3) { + case 0: Instr &= ~3; ldst("ld", 0, 1, 1); break; // ld + case 1: Instr &= ~3; ldst("ldu", 0, 1, 1); break; // ldu + case 2: Instr &= ~3; ldst("lwa", 0, 1, 1); break; // lwa + default: ill(); break; + } break; +#endif + + /* + * Extention 4. + */ + + #define MASK_D (0x1F << 21) + #define MASK_A (0x1F << 16) + #define MASK_B (0x1F << 11) + #define MASK_C (0x1F << 6) + case 073: + switch(Instr & 0x3F) { + case 044: fpu("fdivs", MASK_C, FPU_DAB); break; // fdivsx + case 045: fpu("fdivs.", MASK_C, FPU_DAB); break; + case 050: fpu("fsubs", MASK_C, FPU_DAB); break; // fsubsx + case 051: fpu("fsubs.", MASK_C, FPU_DAB); break; + case 052: fpu("fadds", MASK_C, FPU_DAB); break; // faddsx + case 053: fpu("fadds.", MASK_C, FPU_DAB); break; +#if !defined(GEKKO) + case 054: fpu("fsqrts", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fsqrtsx + case 055: fpu("fsqrts.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; +#endif + case 060: fpu("fres", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fresx + case 061: fpu("fres.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; + case 062: fpu("fmuls", MASK_B, FPU_DAC); break; // fmulsx + case 063: fpu("fmuls.", MASK_B, FPU_DAC); break; + case 070: fpu("fmsubs", 0, FPU_DACB); break; // fmsubsx + case 071: fpu("fmsubs.", 0, FPU_DACB); break; + case 072: fpu("fmadds", 0, FPU_DACB); break; // fmaddsx + case 073: fpu("fmadds.", 0, FPU_DACB); break; + case 074: fpu("fnmsubs", 0, FPU_DACB); break; // fnmsubsx + case 075: fpu("fnmsubs.", 0, FPU_DACB); break; + case 076: fpu("fnmadds", 0, FPU_DACB); break; // fnmaddsx + case 077: fpu("fnmadds.", 0, FPU_DACB); break; + default: ill(); break; + } break; + + /* + * Extention 5. + */ + +#ifdef POWERPC_64 + case 076: + switch(Instr & 3) { + case 0: Instr &= ~3; ldst("std", 0, 0, 1); break; // std + case 1: Instr &= ~3; ldst("stdu", 0, 0, 1); break; // stdu + default: ill(); break; + } break; +#endif + + /* + * Extention 6. + */ + + case 077: + switch(Instr & 0x3F) { + case 000: + switch(DIS_RC) + { + case 0: fcmp("fcmpu"); break; // fcmpu + case 1: fcmp("fcmpo"); break; // fcmpo + case 2: mcrfs(); break; // mcrfs + default: ill(); break; + } + break; + case 014: + switch(DIS_RC) + { + case 1: mtfsb("mtfsb1"); break; // mtfsb1 + case 2: mtfsb("mtfsb0"); break; // mtfsb0 + case 4: mtfsfi(); break; // mtfsfi + default: ill(); break; + } + break; + case 015: + switch(DIS_RC) + { + case 1: mtfsb("mtfsb1."); break; // mtfsb1. + case 2: mtfsb("mtfsb0."); break; // mtfsb0. + case 4: mtfsfi(); break; // mtfsfi. + default: ill(); break; + } + break; + case 016: + switch(DIS_RC) + { + case 18: fpu("mffs", MASK_A|MASK_B, FPU_D); break; // mffs + case 22: mtfsf(); break; // mtfsf + default: ill(); break; + } + break; + case 017: + switch(DIS_RC) + { + case 18: fpu("mffs.", MASK_A|MASK_B, FPU_D); break; // mffs. + case 22: mtfsf(); break; // mtfsf. + default: ill(); break; + } + break; + case 020: + switch(DIS_RC) + { + case 1: fpu("fneg", MASK_A, FPU_DB); break; // fneg + case 2: fpu("fmr", MASK_A, FPU_DB); break; // fmr + case 4: fpu("fnabs", MASK_A, FPU_DB); break; // fnabs + case 8: fpu("fabs", MASK_A, FPU_DB); break; // fabs + default: ill(); break; + } + break; + case 021: + switch(DIS_RC) + { + case 1: fpu("fneg.", MASK_A, FPU_DB); break; // fneg + case 2: fpu("fmr.", MASK_A, FPU_DB); break; // fmr + case 4: fpu("fnabs.", MASK_A, FPU_DB); break; // fnabs + case 8: fpu("fabs.", MASK_A, FPU_DB); break; // fabs + default: ill(); break; + } + break; + case 030: + switch(DIS_RC) + { + case 0: fpu("frsp", MASK_A, FPU_DB); break; // frsp + default: ill(); break; + } + break; + case 031: + switch(DIS_RC) + { + case 0: fpu("frsp.", MASK_A, FPU_DB); break; // frsp. + default: ill(); break; + } + break; + case 034: + switch(DIS_RC) + { + case 0: fpu("fctiw", MASK_A, FPU_DB); break; // fctiw +#ifdef POWERPC_64 + case 25: fpu("fctid", MASK_A, FPU_DB); break; // fctid + case 26: fpu("fcfid", MASK_A, FPU_DB); break; // fcfid +#endif + default: ill(); break; + } + break; + case 035: + switch(DIS_RC) + { + case 0: fpu("fctiw.", MASK_A, FPU_DB); break; // fctiw. +#ifdef POWERPC_64 + case 25: fpu("fctid.", MASK_A, FPU_DB); break; // fctid. + case 26: fpu("fcfid.", MASK_A, FPU_DB); break; // fcfid. +#endif + default: ill(); break; + } + break; + case 036: + switch(DIS_RC) + { + case 0: fpu("fctiwz", MASK_A, FPU_DB); break; // fctiwz +#ifdef POWERPC_64 + case 25: fpu("fctidz", MASK_A, FPU_DB); break; // fctidz +#endif + default: ill(); break; + } + break; + case 037: + switch(DIS_RC) + { + case 0: fpu("fctiwz.", MASK_A, FPU_DB); break; // fctiwz. +#ifdef POWERPC_64 + case 25: fpu("fctidz.", MASK_A, FPU_DB); break; // fctidz. +#endif + default: ill(); break; + } + break; + case 044: fpu("fdiv", MASK_C, FPU_DAB); break; // fdivx + case 045: fpu("fdiv.", MASK_C, FPU_DAB); break; + case 050: fpu("fsub", MASK_C, FPU_DAB); break; // fsubx + case 051: fpu("fsub.", MASK_C, FPU_DAB); break; + case 052: fpu("fadd", MASK_C, FPU_DAB); break; // faddx + case 053: fpu("fadd.", MASK_C, FPU_DAB); break; +#if !defined(GEKKO) + case 054: fpu("fsqrt", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // fsqrtx + case 055: fpu("fsqrt.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; +#endif + case 056: fpu("fsel", 0, FPU_DACB, PPC_DISA_OPTIONAL); break; // fselx + case 057: fpu("fsel.", 0, FPU_DACB, PPC_DISA_OPTIONAL); break; + case 062: fpu("fmul", MASK_B, FPU_DAC); break; // fmulx + case 063: fpu("fmul.", MASK_B, FPU_DAC); break; + case 064: fpu("frsqrte", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; // frsqrtex + case 065: fpu("frsqrte.", MASK_A|MASK_C, FPU_DB, PPC_DISA_OPTIONAL); break; + case 070: fpu("fmsub", 0, FPU_DACB); break; // fmsubx + case 071: fpu("fmsub.", 0, FPU_DACB); break; + case 072: fpu("fmadd", 0, FPU_DACB); break; // fmaddx + case 073: fpu("fmadd.", 0, FPU_DACB); break; + case 074: fpu("fnmsub", 0, FPU_DACB); break; // fnmsubx + case 075: fpu("fnmsub.", 0, FPU_DACB); break; + case 076: fpu("fnmadd", 0, FPU_DACB); break; // fnmaddx + case 077: fpu("fnmadd.", 0, FPU_DACB); break; + default: ill(); break; + } break; + + /* + *********************************************************************************** + * GEKKO Extention. + *********************************************************************************** + */ + +#ifdef GEKKO + case 004: + if(((Instr >> 1) & 0x3FF) == 1014) + { + cache("dcbz_l", PPC_DISA_SPECIFIC); // dcbz_l + } + else switch((Instr >> 1) & 0x1f) + { + case 0: ps_cmpx((Instr >> 6) & 3); break; // ps_cmpXX + case 6: if(Instr & 0x40) ps_ldstx("lux"); // ps_lux + else ps_ldstx("lx"); break; // ps_lx + case 7: if(Instr & 0x40) ps_ldstx("stux"); // ps_stux + else ps_ldstx("stx"); break; // ps_stx + case 8: + switch((Instr >> 6) & 0x1f) + { + case 1: ps_db("neg", 1); break; // ps_negx + case 2: ps_db("mr", 1); break; // ps_mrx + case 4: ps_db("nabs", 1); break; // ps_nabsx + case 8: ps_db("abs", 1); break; // ps_absx + default: ill(); break; + } break; + case 10: ps_dacb("sum0"); break; // ps_sum0x + case 11: ps_dacb("sum1"); break; // ps_sum1x + case 12: ps_dac("muls0"); break; // ps_muls0x + case 13: ps_dac("muls1"); break; // ps_muls1x + case 14: ps_dacb("madds0"); break; // ps_madds0x + case 15: ps_dacb("madds1"); break; // ps_madds1x + case 16: + switch((Instr >> 6) & 0x1f) + { + case 16: ps_dab("merge00", 1); break; // ps_merge00x + case 17: ps_dab("merge01", 1); break; // ps_merge11x + case 18: ps_dab("merge10", 1); break; // ps_merge10x + case 19: ps_dab("merge11", 1); break; // ps_merge11x + default: ill(); break; + } break; + case 18: ps_dab("div"); break; // ps_divx + case 20: ps_dab("sub"); break; // ps_subx + case 21: ps_dab("add"); break; // ps_addx + case 23: ps_dacb("sel"); break; // ps_selx + case 24: ps_db("res"); break; // ps_resx + case 25: ps_dac("mul"); break; // ps_mulx + case 26: ps_db("rsqrte"); break; // ps_rsqrtex + case 28: ps_dacb("msub"); break; // ps_msubx + case 29: ps_dacb("madd"); break; // ps_maddx + case 30: ps_dacb("nmsub"); break; // ps_nmsubx + case 31: ps_dacb("nmadd"); break; // ps_nmaddx + default: ill(); break; + } break; + + case 070: ps_ldst("l"); break; // psq_l + case 071: ps_ldst("lu"); break; // psq_lu + case 074: ps_ldst("st"); break; // psq_st + case 075: ps_ldst("stu"); break; // psq_stu +#endif /* GEKKO */ + + default: ill(); break; + } + +#ifdef UPPERCASE + strupr(o->mnemonic); +#endif +} + +char *PPCDisasmSimple(u64 pc, u32 instr) +{ + PPCD_CB dis_out; + static char output[256]; + + dis_out.pc = pc; + dis_out.instr = instr; + + PPCDisasm(&dis_out); + sprintf(output, "%08X %08X %-10s %s", pc, instr, dis_out.mnemonic, dis_out.operands); + return output; +} \ No newline at end of file diff --git a/ppc/ppcd.h b/ppc/ppcd.h new file mode 100644 index 00000000..2ebf7945 --- /dev/null +++ b/ppc/ppcd.h @@ -0,0 +1,56 @@ +#pragma once + +/* + * General Data Types. +*/ + +typedef signed char s8; +typedef signed short s16; +typedef signed long s32; +typedef unsigned char u8; +typedef unsigned short u16; +typedef unsigned long u32; +typedef float f32; +typedef double f64; + +typedef unsigned long long u64; +typedef signed long long s64; + +#ifndef __cplusplus +typedef enum { false = 0, true } bool; +#endif + +#define FASTCALL __fastcall +#define INLINE __inline + +// See some documentation in CPP file. + +// Instruction class +#define PPC_DISA_OTHER 0x0000 // No additional information +#define PPC_DISA_64 0x0001 // 64-bit architecture only +#define PPC_DISA_INTEGER 0x0002 // Integer-type instruction +#define PPC_DISA_BRANCH 0x0004 // Branch instruction +#define PPC_DISA_LDST 0x0008 // Load-store instruction +#define PPC_DISA_STRING 0x0010 // Load-store string/multiple +#define PPC_DISA_FPU 0x0020 // Floating-point instruction +#define PPC_DISA_OEA 0x0040 // Supervisor level +#define PPC_DISA_OPTIONAL 0x0200 // Optional +#define PPC_DISA_BRIDGE 0x0400 // Optional 64-bit bridge +#define PPC_DISA_SPECIFIC 0x0800 // Implementation-specific +#define PPC_DISA_ILLEGAL 0x1000 // Illegal +#define PPC_DISA_SIMPLIFIED 0x8000 // Simplified mnemonic is used + +typedef struct PPCD_CB +{ + u64 pc; // Program counter (input) + u32 instr; // Instruction (input) + char mnemonic[16]; // Instruction mnemonic. + char operands[64]; // Instruction operands. + u32 immed; // Immediate value (displacement for load/store, immediate operand for arithm./logic). + int r[4]; // Index value for operand registers and immediates. + u64 target; // Target address for branch instructions / Mask for RLWINM-like instructions + int iclass; // One or combination of PPC_DISA_* flags. +} PPCD_CB; + +void PPCDisasm(PPCD_CB *disa); +char* PPCDisasmSimple(u64 pc, u32 instr);