mirror of
https://github.com/DrBeef/ioq3quest.git
synced 2024-11-11 15:21:40 +00:00
fbf09d64c5
12 to 16. This is cannot be increased any further without major changes (there only 32 General Purporse Registers). Anyway, his change allows the cgame.qvm from the excessiveplus mod to work with ioquake3 on a PPC.
2071 lines
64 KiB
C
2071 lines
64 KiB
C
/*
|
|
===========================================================================
|
|
Copyright (C) 1999-2005 Id Software, Inc.
|
|
|
|
This file is part of Quake III Arena source code.
|
|
|
|
Quake III Arena source code is free software; you can redistribute it
|
|
and/or modify it under the terms of the GNU General Public License as
|
|
published by the Free Software Foundation; either version 2 of the License,
|
|
or (at your option) any later version.
|
|
|
|
Quake III Arena source code 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 Quake III Arena source code; if not, write to the Free Software
|
|
Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
|
|
===========================================================================
|
|
*/
|
|
// vm_ppc.c
|
|
// ppc dynamic compiler
|
|
|
|
#include "vm_local.h"
|
|
|
|
#ifdef MACOS_X
|
|
#include <CoreServices/CoreServices.h>
|
|
#endif
|
|
|
|
#define DEBUG_VM 0
|
|
|
|
#if DEBUG_VM
|
|
static char *opnames[256] = {
|
|
"OP_UNDEF",
|
|
|
|
"OP_IGNORE",
|
|
|
|
"OP_BREAK",
|
|
|
|
"OP_ENTER",
|
|
"OP_LEAVE",
|
|
"OP_CALL",
|
|
"OP_PUSH",
|
|
"OP_POP",
|
|
|
|
"OP_CONST",
|
|
|
|
"OP_LOCAL",
|
|
|
|
"OP_JUMP",
|
|
|
|
//-------------------
|
|
|
|
"OP_EQ",
|
|
"OP_NE",
|
|
|
|
"OP_LTI",
|
|
"OP_LEI",
|
|
"OP_GTI",
|
|
"OP_GEI",
|
|
|
|
"OP_LTU",
|
|
"OP_LEU",
|
|
"OP_GTU",
|
|
"OP_GEU",
|
|
|
|
"OP_EQF",
|
|
"OP_NEF",
|
|
|
|
"OP_LTF",
|
|
"OP_LEF",
|
|
"OP_GTF",
|
|
"OP_GEF",
|
|
|
|
//-------------------
|
|
|
|
"OP_LOAD1",
|
|
"OP_LOAD2",
|
|
"OP_LOAD4",
|
|
"OP_STORE1",
|
|
"OP_STORE2",
|
|
"OP_STORE4",
|
|
"OP_ARG",
|
|
|
|
"OP_BLOCK_COPY",
|
|
|
|
//-------------------
|
|
|
|
"OP_SEX8",
|
|
"OP_SEX16",
|
|
|
|
"OP_NEGI",
|
|
"OP_ADD",
|
|
"OP_SUB",
|
|
"OP_DIVI",
|
|
"OP_DIVU",
|
|
"OP_MODI",
|
|
"OP_MODU",
|
|
"OP_MULI",
|
|
"OP_MULU",
|
|
|
|
"OP_BAND",
|
|
"OP_BOR",
|
|
"OP_BXOR",
|
|
"OP_BCOM",
|
|
|
|
"OP_LSH",
|
|
"OP_RSHI",
|
|
"OP_RSHU",
|
|
|
|
"OP_NEGF",
|
|
"OP_ADDF",
|
|
"OP_SUBF",
|
|
"OP_DIVF",
|
|
"OP_MULF",
|
|
|
|
"OP_CVIF",
|
|
"OP_CVFI"
|
|
};
|
|
#endif
|
|
|
|
typedef enum {
|
|
R_REAL_STACK = 1,
|
|
// registers 3-11 are the parameter passing registers
|
|
|
|
// state
|
|
R_STACK = 3, // local
|
|
R_OPSTACK, // global
|
|
|
|
// constants
|
|
R_MEMBASE, // global
|
|
R_MEMMASK,
|
|
R_ASMCALL, // global
|
|
R_INSTRUCTIONS, // global
|
|
R_NUM_INSTRUCTIONS, // global
|
|
R_CVM, // currentVM
|
|
|
|
// temps
|
|
R_TOP = 11,
|
|
R_SECOND = 12,
|
|
R_EA = 2 // effective address calculation
|
|
|
|
} regNums_t;
|
|
|
|
#define RG_REAL_STACK r1
|
|
#define RG_STACK r3
|
|
#define RG_OPSTACK r4
|
|
#define RG_MEMBASE r5
|
|
#define RG_MEMMASK r6
|
|
#define RG_ASMCALL r7
|
|
#define RG_INSTRUCTIONS r8
|
|
#define RG_NUM_INSTRUCTIONS r9
|
|
#define RG_CVM r10
|
|
#define RG_TOP r12
|
|
#define RG_SECOND r13
|
|
#define RG_EA r14
|
|
|
|
// The deepest value I saw in the Quake3 games was 9.
|
|
#define OP_STACK_MAX_DEPTH 16
|
|
|
|
// These are all volatile and thus must be saved upon entry to the VM code.
|
|
// NOTE: These are General Purpose Registers (GPR) numbers like the
|
|
// R_ definitions in the regNums_t enum above (31 is the max)
|
|
static int opStackIntRegisters[OP_STACK_MAX_DEPTH] =
|
|
{
|
|
16, 17, 18, 19,
|
|
20, 21, 22, 23,
|
|
24, 25, 26, 27,
|
|
28, 29, 30, 31
|
|
};
|
|
|
|
static unsigned int *opStackLoadInstructionAddr[OP_STACK_MAX_DEPTH];
|
|
|
|
// We use different registers for the floating point
|
|
// operand stack (these are volatile in the PPC ABI)
|
|
// NOTE: these are Floating Point Register (FPR) numbers, not
|
|
// General Purpose Register (GPR) numbers
|
|
static int opStackFloatRegisters[OP_STACK_MAX_DEPTH] =
|
|
{
|
|
0, 1, 2, 3,
|
|
4, 5, 6, 7,
|
|
8, 9, 10, 11,
|
|
12, 13, 14, 15
|
|
};
|
|
|
|
static int opStackRegType[OP_STACK_MAX_DEPTH] =
|
|
{
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0,
|
|
0, 0, 0, 0
|
|
};
|
|
|
|
// this doesn't have the low order bits set for instructions i'm not using...
|
|
typedef enum {
|
|
PPC_TDI = 0x08000000,
|
|
PPC_TWI = 0x0c000000,
|
|
PPC_MULLI = 0x1c000000,
|
|
PPC_SUBFIC = 0x20000000,
|
|
PPC_CMPI = 0x28000000,
|
|
PPC_CMPLI = 0x2c000000,
|
|
PPC_ADDIC = 0x30000000,
|
|
PPC_ADDIC_ = 0x34000000,
|
|
PPC_ADDI = 0x38000000,
|
|
PPC_ADDIS = 0x3c000000,
|
|
PPC_BC = 0x40000000,
|
|
PPC_SC = 0x44000000,
|
|
PPC_B = 0x48000000,
|
|
|
|
PPC_MCRF = 0x4c000000,
|
|
PPC_BCLR = 0x4c000020,
|
|
PPC_RFID = 0x4c000000,
|
|
PPC_CRNOR = 0x4c000000,
|
|
PPC_RFI = 0x4c000000,
|
|
PPC_CRANDC = 0x4c000000,
|
|
PPC_ISYNC = 0x4c000000,
|
|
PPC_CRXOR = 0x4c000000,
|
|
PPC_CRNAND = 0x4c000000,
|
|
PPC_CREQV = 0x4c000000,
|
|
PPC_CRORC = 0x4c000000,
|
|
PPC_CROR = 0x4c000000,
|
|
//------------
|
|
PPC_BCCTR = 0x4c000420,
|
|
PPC_RLWIMI = 0x50000000,
|
|
PPC_RLWINM = 0x54000000,
|
|
PPC_RLWNM = 0x5c000000,
|
|
PPC_ORI = 0x60000000,
|
|
PPC_ORIS = 0x64000000,
|
|
PPC_XORI = 0x68000000,
|
|
PPC_XORIS = 0x6c000000,
|
|
PPC_ANDI_ = 0x70000000,
|
|
PPC_ANDIS_ = 0x74000000,
|
|
PPC_RLDICL = 0x78000000,
|
|
PPC_RLDICR = 0x78000000,
|
|
PPC_RLDIC = 0x78000000,
|
|
PPC_RLDIMI = 0x78000000,
|
|
PPC_RLDCL = 0x78000000,
|
|
PPC_RLDCR = 0x78000000,
|
|
PPC_CMP = 0x7c000000,
|
|
PPC_TW = 0x7c000000,
|
|
PPC_SUBFC = 0x7c000010,
|
|
PPC_MULHDU = 0x7c000000,
|
|
PPC_ADDC = 0x7c000014,
|
|
PPC_MULHWU = 0x7c000000,
|
|
PPC_MFCR = 0x7c000000,
|
|
PPC_LWAR = 0x7c000000,
|
|
PPC_LDX = 0x7c000000,
|
|
PPC_LWZX = 0x7c00002e,
|
|
PPC_SLW = 0x7c000030,
|
|
PPC_CNTLZW = 0x7c000000,
|
|
PPC_SLD = 0x7c000000,
|
|
PPC_AND = 0x7c000038,
|
|
PPC_CMPL = 0x7c000040,
|
|
PPC_SUBF = 0x7c000050,
|
|
PPC_LDUX = 0x7c000000,
|
|
//------------
|
|
PPC_DCBST = 0x7c000000,
|
|
PPC_LWZUX = 0x7c00006c,
|
|
PPC_CNTLZD = 0x7c000000,
|
|
PPC_ANDC = 0x7c000000,
|
|
PPC_TD = 0x7c000000,
|
|
PPC_MULHD = 0x7c000000,
|
|
PPC_MULHW = 0x7c000000,
|
|
PPC_MTSRD = 0x7c000000,
|
|
PPC_MFMSR = 0x7c000000,
|
|
PPC_LDARX = 0x7c000000,
|
|
PPC_DCBF = 0x7c000000,
|
|
PPC_LBZX = 0x7c0000ae,
|
|
PPC_NEG = 0x7c000000,
|
|
PPC_MTSRDIN = 0x7c000000,
|
|
PPC_LBZUX = 0x7c000000,
|
|
PPC_NOR = 0x7c0000f8,
|
|
PPC_SUBFE = 0x7c000000,
|
|
PPC_ADDE = 0x7c000000,
|
|
PPC_MTCRF = 0x7c000000,
|
|
PPC_MTMSR = 0x7c000000,
|
|
PPC_STDX = 0x7c000000,
|
|
PPC_STWCX_ = 0x7c000000,
|
|
PPC_STWX = 0x7c00012e,
|
|
PPC_MTMSRD = 0x7c000000,
|
|
PPC_STDUX = 0x7c000000,
|
|
PPC_STWUX = 0x7c00016e,
|
|
PPC_SUBFZE = 0x7c000000,
|
|
PPC_ADDZE = 0x7c000000,
|
|
PPC_MTSR = 0x7c000000,
|
|
PPC_STDCX_ = 0x7c000000,
|
|
PPC_STBX = 0x7c0001ae,
|
|
PPC_SUBFME = 0x7c000000,
|
|
PPC_MULLD = 0x7c000000,
|
|
//------------
|
|
PPC_ADDME = 0x7c000000,
|
|
PPC_MULLW = 0x7c0001d6,
|
|
PPC_MTSRIN = 0x7c000000,
|
|
PPC_DCBTST = 0x7c000000,
|
|
PPC_STBUX = 0x7c000000,
|
|
PPC_ADD = 0x7c000214,
|
|
PPC_DCBT = 0x7c000000,
|
|
PPC_LHZX = 0x7c00022e,
|
|
PPC_EQV = 0x7c000000,
|
|
PPC_TLBIE = 0x7c000000,
|
|
PPC_ECIWX = 0x7c000000,
|
|
PPC_LHZUX = 0x7c000000,
|
|
PPC_XOR = 0x7c000278,
|
|
PPC_MFSPR = 0x7c0002a6,
|
|
PPC_LWAX = 0x7c000000,
|
|
PPC_LHAX = 0x7c000000,
|
|
PPC_TLBIA = 0x7c000000,
|
|
PPC_MFTB = 0x7c000000,
|
|
PPC_LWAUX = 0x7c000000,
|
|
PPC_LHAUX = 0x7c000000,
|
|
PPC_STHX = 0x7c00032e,
|
|
PPC_ORC = 0x7c000338,
|
|
PPC_SRADI = 0x7c000000,
|
|
PPC_SLBIE = 0x7c000000,
|
|
PPC_ECOWX = 0x7c000000,
|
|
PPC_STHUX = 0x7c000000,
|
|
PPC_OR = 0x7c000378,
|
|
PPC_DIVDU = 0x7c000000,
|
|
PPC_DIVWU = 0x7c000396,
|
|
PPC_MTSPR = 0x7c0003a6,
|
|
PPC_DCBI = 0x7c000000,
|
|
PPC_NAND = 0x7c000000,
|
|
PPC_DIVD = 0x7c000000,
|
|
//------------
|
|
PPC_DIVW = 0x7c0003d6,
|
|
PPC_SLBIA = 0x7c000000,
|
|
PPC_MCRXR = 0x7c000000,
|
|
PPC_LSWX = 0x7c000000,
|
|
PPC_LWBRX = 0x7c000000,
|
|
PPC_LFSX = 0x7c00042e,
|
|
PPC_SRW = 0x7c000430,
|
|
PPC_SRD = 0x7c000000,
|
|
PPC_TLBSYNC = 0x7c000000,
|
|
PPC_LFSUX = 0x7c000000,
|
|
PPC_MFSR = 0x7c000000,
|
|
PPC_LSWI = 0x7c000000,
|
|
PPC_SYNC = 0x7c000000,
|
|
PPC_LFDX = 0x7c000000,
|
|
PPC_LFDUX = 0x7c000000,
|
|
PPC_MFSRIN = 0x7c000000,
|
|
PPC_STSWX = 0x7c000000,
|
|
PPC_STWBRX = 0x7c000000,
|
|
PPC_STFSX = 0x7c00052e,
|
|
PPC_STFSUX = 0x7c000000,
|
|
PPC_STSWI = 0x7c000000,
|
|
PPC_STFDX = 0x7c000000,
|
|
PPC_DCBA = 0x7c000000,
|
|
PPC_STFDUX = 0x7c000000,
|
|
PPC_LHBRX = 0x7c000000,
|
|
PPC_SRAW = 0x7c000630,
|
|
PPC_SRAD = 0x7c000000,
|
|
PPC_SRAWI = 0x7c000000,
|
|
PPC_EIEIO = 0x7c000000,
|
|
PPC_STHBRX = 0x7c000000,
|
|
PPC_EXTSH = 0x7c000734,
|
|
PPC_EXTSB = 0x7c000774,
|
|
PPC_ICBI = 0x7c000000,
|
|
//------------
|
|
PPC_STFIWX = 0x7c0007ae,
|
|
PPC_EXTSW = 0x7c000000,
|
|
PPC_DCBZ = 0x7c000000,
|
|
PPC_LWZ = 0x80000000,
|
|
PPC_LWZU = 0x84000000,
|
|
PPC_LBZ = 0x88000000,
|
|
PPC_LBZU = 0x8c000000,
|
|
PPC_STW = 0x90000000,
|
|
PPC_STWU = 0x94000000,
|
|
PPC_STB = 0x98000000,
|
|
PPC_STBU = 0x9c000000,
|
|
PPC_LHZ = 0xa0000000,
|
|
PPC_LHZU = 0xa4000000,
|
|
PPC_LHA = 0xa8000000,
|
|
PPC_LHAU = 0xac000000,
|
|
PPC_STH = 0xb0000000,
|
|
PPC_STHU = 0xb4000000,
|
|
PPC_LMW = 0xb8000000,
|
|
PPC_STMW = 0xbc000000,
|
|
PPC_LFS = 0xc0000000,
|
|
PPC_LFSU = 0xc4000000,
|
|
PPC_LFD = 0xc8000000,
|
|
PPC_LFDU = 0xcc000000,
|
|
PPC_STFS = 0xd0000000,
|
|
PPC_STFSU = 0xd4000000,
|
|
PPC_STFD = 0xd8000000,
|
|
PPC_STFDU = 0xdc000000,
|
|
PPC_LD = 0xe8000000,
|
|
PPC_LDU = 0xe8000001,
|
|
PPC_LWA = 0xe8000002,
|
|
PPC_FDIVS = 0xec000024,
|
|
PPC_FSUBS = 0xec000028,
|
|
PPC_FADDS = 0xec00002a,
|
|
//------------
|
|
PPC_FSQRTS = 0xec000000,
|
|
PPC_FRES = 0xec000000,
|
|
PPC_FMULS = 0xec000032,
|
|
PPC_FMSUBS = 0xec000000,
|
|
PPC_FMADDS = 0xec000000,
|
|
PPC_FNMSUBS = 0xec000000,
|
|
PPC_FNMADDS = 0xec000000,
|
|
PPC_STD = 0xf8000000,
|
|
PPC_STDU = 0xf8000001,
|
|
PPC_FCMPU = 0xfc000000,
|
|
PPC_FRSP = 0xfc000018,
|
|
PPC_FCTIW = 0xfc000000,
|
|
PPC_FCTIWZ = 0xfc00001e,
|
|
PPC_FDIV = 0xfc000000,
|
|
PPC_FSUB = 0xfc000028,
|
|
PPC_FADD = 0xfc000000,
|
|
PPC_FSQRT = 0xfc000000,
|
|
PPC_FSEL = 0xfc000000,
|
|
PPC_FMUL = 0xfc000000,
|
|
PPC_FRSQRTE = 0xfc000000,
|
|
PPC_FMSUB = 0xfc000000,
|
|
PPC_FMADD = 0xfc000000,
|
|
PPC_FNMSUB = 0xfc000000,
|
|
PPC_FNMADD = 0xfc000000,
|
|
PPC_FCMPO = 0xfc000000,
|
|
PPC_MTFSB1 = 0xfc000000,
|
|
PPC_FNEG = 0xfc000050,
|
|
PPC_MCRFS = 0xfc000000,
|
|
PPC_MTFSB0 = 0xfc000000,
|
|
PPC_FMR = 0xfc000000,
|
|
PPC_MTFSFI = 0xfc000000,
|
|
PPC_FNABS = 0xfc000000,
|
|
PPC_FABS = 0xfc000000,
|
|
//------------
|
|
PPC_MFFS = 0xfc000000,
|
|
PPC_MTFSF = 0xfc000000,
|
|
PPC_FCTID = 0xfc000000,
|
|
PPC_FCTIDZ = 0xfc000000,
|
|
PPC_FCFID = 0xfc000000
|
|
|
|
} ppcOpcodes_t;
|
|
|
|
|
|
// the newly generated code
|
|
static unsigned *buf;
|
|
static int compiledOfs; // in dwords
|
|
static int pass;
|
|
|
|
// fromt the original bytecode
|
|
static byte *code;
|
|
static int pc;
|
|
|
|
void AsmCall( void );
|
|
|
|
double itofConvert[2];
|
|
|
|
static int Constant4( void ) {
|
|
int v;
|
|
|
|
v = code[pc] | (code[pc+1]<<8) | (code[pc+2]<<16) | (code[pc+3]<<24);
|
|
pc += 4;
|
|
return v;
|
|
}
|
|
|
|
static int Constant1( void ) {
|
|
int v;
|
|
|
|
v = code[pc];
|
|
pc += 1;
|
|
return v;
|
|
}
|
|
|
|
static void Emit4( char *opname, int i ) {
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("\t\t\t%p %s\t%08lx\n",&buf[compiledOfs],opname,i&0x3ffffff);
|
|
#endif
|
|
buf[ compiledOfs ] = i;
|
|
compiledOfs++;
|
|
}
|
|
|
|
static void Inst( char *opname, int opcode, int destReg, int aReg, int bReg ) {
|
|
unsigned r;
|
|
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("\t\t\t%p %s\tr%d,r%d,r%d\n",&buf[compiledOfs],opname,destReg,aReg,bReg);
|
|
#endif
|
|
r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ;
|
|
buf[ compiledOfs ] = r;
|
|
compiledOfs++;
|
|
}
|
|
|
|
static void Inst4( char *opname, int opcode, int destReg, int aReg, int bReg, int cReg ) {
|
|
unsigned r;
|
|
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("\t\t\t%p %s\tr%d,r%d,r%d,r%d\n",&buf[compiledOfs],opname,destReg,aReg,bReg,cReg);
|
|
#endif
|
|
r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) | ( cReg << 6 );
|
|
buf[ compiledOfs ] = r;
|
|
compiledOfs++;
|
|
}
|
|
|
|
static void InstImm( char *opname, int opcode, int destReg, int aReg, int immediate ) {
|
|
unsigned r;
|
|
|
|
if ( immediate > 32767 || immediate < -32768 ) {
|
|
Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range, opcode %x,%d,%d", immediate, opcode, destReg, aReg );
|
|
}
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("\t\t\t%p %s\tr%d,r%d,0x%x\n",&buf[compiledOfs],opname,destReg,aReg,immediate);
|
|
#endif
|
|
r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff );
|
|
buf[ compiledOfs ] = r;
|
|
compiledOfs++;
|
|
}
|
|
|
|
static void InstImmU( char *opname, int opcode, int destReg, int aReg, int immediate ) {
|
|
unsigned r;
|
|
|
|
if ( immediate > 0xffff || immediate < 0 ) {
|
|
Com_Error( ERR_FATAL, "VM_Compile: immediate value %i out of range", immediate );
|
|
}
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("\t\t\t%p %s\tr%d,r%d,0x%x\n",&buf[compiledOfs],opname,destReg,aReg,immediate);
|
|
#endif
|
|
r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff );
|
|
buf[ compiledOfs ] = r;
|
|
compiledOfs++;
|
|
}
|
|
|
|
static int pop0, pop1, oc0, oc1;
|
|
static vm_t *tvm;
|
|
static int instruction;
|
|
static byte *jused;
|
|
|
|
static void spillOpStack(int depth)
|
|
{
|
|
// Store out each register on the operand stack to it's correct location.
|
|
int i;
|
|
|
|
for(i = 0; i < depth; i++)
|
|
{
|
|
assert(opStackRegType[i]);
|
|
assert(opStackRegType[i] == 1);
|
|
switch(opStackRegType[i])
|
|
{
|
|
case 1: // Integer register
|
|
InstImm( "stw", PPC_STW, opStackIntRegisters[i], R_OPSTACK, i*4+4);
|
|
break;
|
|
case 2: // Float register
|
|
InstImm( "stfs", PPC_STFS, opStackFloatRegisters[i], R_OPSTACK, i*4+4);
|
|
break;
|
|
}
|
|
opStackRegType[i] = 0;
|
|
}
|
|
}
|
|
|
|
static void loadOpStack(int depth)
|
|
{
|
|
// Back off operand stack pointer and reload all operands.
|
|
// InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -(depth)*4 );
|
|
|
|
int i;
|
|
|
|
for(i = 0; i < depth; i++)
|
|
{
|
|
assert(opStackRegType[i] == 0);
|
|
// For now we're stuck reloading everything as an integer.
|
|
opStackLoadInstructionAddr[i] = &buf[compiledOfs];
|
|
InstImm( "lwz", PPC_LWZ, opStackIntRegisters[i], R_OPSTACK, i*4+4);
|
|
opStackRegType[i] = 1;
|
|
}
|
|
}
|
|
|
|
static void makeFloat(int depth)
|
|
{
|
|
//assert(opStackRegType[depth] == 1);
|
|
if(opStackRegType[depth] == 1)
|
|
{
|
|
unsigned instruction;
|
|
unsigned destReg, aReg, bReg, imm;
|
|
|
|
if(opStackLoadInstructionAddr[depth])
|
|
{
|
|
// Repatch load instruction to use LFS instead of LWZ
|
|
instruction = *opStackLoadInstructionAddr[depth];
|
|
// Figure out if it's LWZ or LWZX
|
|
if((instruction & 0xfc000000) == PPC_LWZ)
|
|
{
|
|
//printf("patching LWZ at %p to LFS at depth %ld\n",opStackLoadInstructionAddr[depth],depth);
|
|
//printf("old instruction: %08lx\n",instruction);
|
|
// Extract registers
|
|
destReg = (instruction >> 21) & 31;
|
|
aReg = (instruction >> 16) & 31;
|
|
imm = instruction & 0xffff;
|
|
|
|
// Calculate correct FP register to use.
|
|
// THIS ASSUMES REGISTER USAGE FOR THE STACK IS n, n+1, n+2, etc!
|
|
//printf("old dest: %ld\n",destReg);
|
|
destReg = (destReg - opStackIntRegisters[0]) + opStackFloatRegisters[0];
|
|
instruction = PPC_LFS | ( destReg << 21 ) | ( aReg << 16 ) | imm ;
|
|
//printf("new dest: %ld\n",destReg);
|
|
//printf("new instruction: %08lx\n",instruction);
|
|
}
|
|
else
|
|
{
|
|
//printf("patching LWZX at %p to LFSX at depth %ld\n",opStackLoadInstructionAddr[depth],depth);
|
|
//printf("old instruction: %08lx\n",instruction);
|
|
// Extract registers
|
|
destReg = (instruction >> 21) & 31;
|
|
aReg = (instruction >> 16) & 31;
|
|
bReg = (instruction >> 11) & 31;
|
|
// Calculate correct FP register to use.
|
|
// THIS ASSUMES REGISTER USAGE FOR THE STACK IS n, n+1, n+2, etc!
|
|
//printf("old dest: %ld\n",destReg);
|
|
destReg = (destReg - opStackIntRegisters[0]) + opStackFloatRegisters[0];
|
|
instruction = PPC_LFSX | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ;
|
|
//printf("new dest: %ld\n",destReg);
|
|
//printf("new instruction: %08lx\n",instruction);
|
|
}
|
|
*opStackLoadInstructionAddr[depth] = instruction;
|
|
opStackLoadInstructionAddr[depth] = 0;
|
|
}
|
|
else
|
|
{
|
|
//printf("doing float constant load at %p for depth %ld\n",&buf[compiledOfs],depth);
|
|
// It was likely loaded as a constant so we have to save/load it. A more
|
|
// interesting implementation might be to generate code to do a "PC relative"
|
|
// load from the VM code region.
|
|
InstImm( "stw", PPC_STW, opStackIntRegisters[depth], R_OPSTACK, depth*4+4);
|
|
// For XXX make sure we force enough NOPs to get the load into
|
|
// another dispatch group to avoid pipeline flush.
|
|
Inst( "ori", PPC_ORI, 0, 0, 0 );
|
|
Inst( "ori", PPC_ORI, 0, 0, 0 );
|
|
Inst( "ori", PPC_ORI, 0, 0, 0 );
|
|
Inst( "ori", PPC_ORI, 0, 0, 0 );
|
|
InstImm( "lfs", PPC_LFS, opStackFloatRegisters[depth], R_OPSTACK, depth*4+4);
|
|
}
|
|
opStackRegType[depth] = 2;
|
|
}
|
|
}
|
|
|
|
// TJW: Unused
|
|
#if 0
|
|
static void fltop() {
|
|
if (rtopped == qfalse) {
|
|
InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack
|
|
}
|
|
}
|
|
#endif
|
|
|
|
#if 0
|
|
static void fltopandsecond() {
|
|
InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 ); // get value from opstack
|
|
InstImm( PPC_LFS, R_SECOND, R_OPSTACK, -4 ); // get value from opstack
|
|
InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 );
|
|
rtopped = qfalse;
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
#define assertInteger(depth) assert(opStackRegType[depth] == 1)
|
|
|
|
/*
|
|
=================
|
|
VM_Compile
|
|
=================
|
|
*/
|
|
void VM_Compile( vm_t *vm, vmHeader_t *header ) {
|
|
int op;
|
|
int maxLength;
|
|
int v;
|
|
int i;
|
|
int opStackDepth;
|
|
|
|
int mainFunction;
|
|
|
|
// set up the into-to-float variables
|
|
((int *)itofConvert)[0] = 0x43300000;
|
|
((int *)itofConvert)[1] = 0x80000000;
|
|
((int *)itofConvert)[2] = 0x43300000;
|
|
|
|
// allocate a very large temp buffer, we will shrink it later
|
|
maxLength = header->codeLength * 8;
|
|
buf = Z_Malloc( maxLength );
|
|
jused = Z_Malloc(header->instructionCount + 2);
|
|
Com_Memset(jused, 0, header->instructionCount+2);
|
|
|
|
// compile everything twice, so the second pass will have valid instruction
|
|
// pointers for branches
|
|
for ( pass = -1 ; pass < 2 ; pass++ ) {
|
|
|
|
// translate all instructions
|
|
pc = 0;
|
|
mainFunction = 0;
|
|
opStackDepth = 0;
|
|
|
|
pop0 = 343545;
|
|
pop1 = 2443545;
|
|
oc0 = -2343535;
|
|
oc1 = 24353454;
|
|
tvm = vm;
|
|
code = (byte *)header + header->codeOffset;
|
|
compiledOfs = 0;
|
|
#ifndef __GNUC__
|
|
// metrowerks seems to require this header in front of functions
|
|
Emit4( (int)(buf+2) );
|
|
Emit4( 0 );
|
|
#endif
|
|
|
|
for ( instruction = 0 ; instruction < header->instructionCount ; instruction++ ) {
|
|
if ( compiledOfs*4 > maxLength - 16 ) {
|
|
Com_Error( ERR_DROP, "VM_Compile: maxLength exceeded" );
|
|
}
|
|
|
|
op = code[ pc ];
|
|
if ( !pass ) {
|
|
vm->instructionPointers[ instruction ] = compiledOfs * 4;
|
|
}
|
|
pc++;
|
|
switch ( op ) {
|
|
case 0:
|
|
break;
|
|
case OP_BREAK:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08lx BREAK\n",instruction);
|
|
#endif
|
|
InstImmU( "addi", PPC_ADDI, R_TOP, 0, 0 );
|
|
InstImm( "lwz", PPC_LWZ, R_TOP, R_TOP, 0 ); // *(int *)0 to crash to debugger
|
|
break;
|
|
case OP_ENTER:
|
|
opStackDepth = 0;
|
|
v = Constant4();
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x ENTER\t%04x\n",instruction,v);
|
|
#endif
|
|
opStackRegType[opStackDepth] = 0;
|
|
mainFunction++;
|
|
if(mainFunction == 1)
|
|
{
|
|
// Main VM entry point is the first thing we compile, so save off operand stack
|
|
// registers here. This avoids issues with trying to trick the native compiler
|
|
// into doing it, and properly matches the PowerPC ABI
|
|
InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, -OP_STACK_MAX_DEPTH*4 ); // sub R_STACK, R_STACK, imm
|
|
for(i = 0; i < OP_STACK_MAX_DEPTH; i++)
|
|
InstImm( "stw", PPC_STW, opStackIntRegisters[i], R_REAL_STACK, i*4);
|
|
}
|
|
InstImm( "addi", PPC_ADDI, R_STACK, R_STACK, -v ); // sub R_STACK, R_STACK, imm
|
|
break;
|
|
case OP_CONST:
|
|
v = Constant4();
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x CONST\t%08x\n",instruction,v);
|
|
#endif
|
|
opStackLoadInstructionAddr[opStackDepth] = 0;
|
|
if ( v < 32768 && v >= -32768 ) {
|
|
InstImmU( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth], 0, v & 0xffff );
|
|
} else {
|
|
InstImmU( "addis", PPC_ADDIS, opStackIntRegisters[opStackDepth], 0, (v >> 16)&0xffff );
|
|
if ( v & 0xffff ) {
|
|
InstImmU( "ori", PPC_ORI, opStackIntRegisters[opStackDepth], opStackIntRegisters[opStackDepth], v & 0xffff );
|
|
}
|
|
}
|
|
opStackRegType[opStackDepth] = 1;
|
|
opStackDepth += 1;
|
|
if (code[pc] == OP_JUMP) {
|
|
jused[v] = 1;
|
|
}
|
|
break;
|
|
case OP_LOCAL:
|
|
oc1 = Constant4();
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LOCAL\t%08x\n",instruction,oc1);
|
|
#endif
|
|
if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) {
|
|
oc1 &= vm->dataMask;
|
|
}
|
|
InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth], R_STACK, oc1 );
|
|
opStackRegType[opStackDepth] = 1;
|
|
opStackLoadInstructionAddr[opStackDepth] = 0;
|
|
opStackDepth += 1;
|
|
break;
|
|
case OP_ARG:
|
|
v = Constant1();
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x ARG \t%08x\n",instruction,v);
|
|
#endif
|
|
InstImm( "addi", PPC_ADDI, R_EA, R_STACK, v ); // location to put it
|
|
if(opStackRegType[opStackDepth-1] == 1)
|
|
Inst( "stwx", PPC_STWX, opStackIntRegisters[opStackDepth-1], R_EA, R_MEMBASE );
|
|
else
|
|
Inst( "stfsx", PPC_STFSX, opStackFloatRegisters[opStackDepth-1], R_EA, R_MEMBASE );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
|
|
break;
|
|
case OP_CALL:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x CALL\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assert(opStackDepth > 0);
|
|
Inst( "mflr", PPC_MFSPR, R_SECOND, 8, 0 ); // move from link register
|
|
InstImm( "stwu", PPC_STWU, R_SECOND, R_REAL_STACK, -16 ); // save off the old return address
|
|
|
|
// Spill operand stack registers.
|
|
spillOpStack(opStackDepth);
|
|
|
|
// We need to leave R_OPSTACK pointing to the top entry on the stack, which is the call address.
|
|
// It will be consumed (and R4 decremented) by the AsmCall code.
|
|
InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, opStackDepth*4);
|
|
|
|
Inst( "mtctr", PPC_MTSPR, R_ASMCALL, 9, 0 ); // move to count register
|
|
Inst( "bctrl", PPC_BCCTR | 1, 20, 0, 0 ); // jump and link to the count register
|
|
|
|
// R4 now points to the top of the operand stack, which has the return value in it. We want to
|
|
// back off the pointer to point to the base of our local operand stack and then reload the stack.
|
|
|
|
InstImm("addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -opStackDepth*4);
|
|
|
|
// Reload operand stack.
|
|
loadOpStack(opStackDepth);
|
|
|
|
InstImm( "lwz", PPC_LWZ, R_SECOND, R_REAL_STACK, 0 ); // fetch the old return address
|
|
InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 );
|
|
Inst( "mtlr", PPC_MTSPR, R_SECOND, 8, 0 ); // move to link register
|
|
break;
|
|
case OP_PUSH:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x PUSH\n",instruction);
|
|
#endif
|
|
opStackRegType[opStackDepth] = 1; // Garbage int value.
|
|
opStackDepth += 1;
|
|
break;
|
|
case OP_POP:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x POP\n",instruction);
|
|
#endif
|
|
opStackDepth -= 1;
|
|
opStackRegType[opStackDepth] = 0; // ??
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_LEAVE:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LEAVE\n",instruction);
|
|
#endif
|
|
assert(opStackDepth == 1);
|
|
assert(opStackRegType[0] != 0);
|
|
// Save return value onto top of op stack. We also have to increment R_OPSTACK
|
|
switch(opStackRegType[0])
|
|
{
|
|
case 1: // Integer register
|
|
InstImm( "stw", PPC_STWU, opStackIntRegisters[0], R_OPSTACK, 4);
|
|
break;
|
|
case 2: // Float register
|
|
InstImm( "stfs", PPC_STFSU, opStackFloatRegisters[0], R_OPSTACK, 4);
|
|
break;
|
|
}
|
|
InstImm( "addi", PPC_ADDI, R_STACK, R_STACK, Constant4() ); // add R_STACK, R_STACK, imm
|
|
if(mainFunction == 1)
|
|
{
|
|
for(i = 0; i < OP_STACK_MAX_DEPTH; i++)
|
|
InstImm( "lwz", PPC_LWZ, opStackIntRegisters[i], R_REAL_STACK, i*4);
|
|
InstImm( "addi", PPC_ADDI, R_REAL_STACK, R_REAL_STACK, OP_STACK_MAX_DEPTH*4 );
|
|
}
|
|
opStackDepth--;
|
|
opStackRegType[opStackDepth] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth] = 0;
|
|
Inst( "blr", PPC_BCLR, 20, 0, 0 ); // branch unconditionally to link register
|
|
break;
|
|
case OP_LOAD4:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LOAD4\n",instruction);
|
|
#endif
|
|
// We should try to figure out whether to use LWZX or LFSX based
|
|
// on some kind of code analysis after subsequent passes. I think what
|
|
// we could do is store the compiled load instruction address along with
|
|
// the register type. When we hit the first mismatched operator, we go back
|
|
// and patch the load. Since LCC's operand stack should be at 0 depth by the
|
|
// time we hit a branch, this should work fairly well. FIXME FIXME FIXME.
|
|
assertInteger(opStackDepth-1);
|
|
opStackLoadInstructionAddr[opStackDepth-1] = &buf[ compiledOfs ];
|
|
Inst( "lwzx", PPC_LWZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base
|
|
opStackRegType[opStackDepth-1] = 1;
|
|
break;
|
|
case OP_LOAD2:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LOAD2\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
Inst( "lhzx", PPC_LHZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base
|
|
opStackRegType[opStackDepth-1] = 1;
|
|
break;
|
|
case OP_LOAD1:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LOAD1\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
Inst( "lbzx", PPC_LBZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );// load from memory base
|
|
opStackRegType[opStackDepth-1] = 1;
|
|
break;
|
|
case OP_STORE4:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x STORE4\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-2);
|
|
if(opStackRegType[opStackDepth-1] == 1)
|
|
Inst( "stwx", PPC_STWX, opStackIntRegisters[opStackDepth-1],
|
|
opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base
|
|
else
|
|
Inst( "stfsx", PPC_STFSX, opStackFloatRegisters[opStackDepth-1],
|
|
opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
break;
|
|
case OP_STORE2:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x STORE2\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "sthx", PPC_STHX, opStackIntRegisters[opStackDepth-1],
|
|
opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
break;
|
|
case OP_STORE1:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x STORE1\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "stbx", PPC_STBX, opStackIntRegisters[opStackDepth-1],
|
|
opStackIntRegisters[opStackDepth-2], R_MEMBASE ); // store from memory base
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
break;
|
|
|
|
case OP_EQ:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x EQ\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 2, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (v&0x3ffffff) );
|
|
break;
|
|
case OP_NE:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x NE\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 2, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 2, v );
|
|
|
|
break;
|
|
case OP_LTI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LTI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 0, v );
|
|
break;
|
|
case OP_LEI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LEI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 1, v );
|
|
break;
|
|
case OP_GTI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GTI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 1, v );
|
|
break;
|
|
case OP_GEI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GEI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmp", PPC_CMP, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 0, v );
|
|
break;
|
|
case OP_LTU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LTU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmpl", PPC_CMPL, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 0, v );
|
|
break;
|
|
case OP_LEU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LEU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmpl", PPC_CMPL, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 1, v );
|
|
break;
|
|
case OP_GTU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GTU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmpl", PPC_CMPL, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 1, v );
|
|
break;
|
|
case OP_GEU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GEU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "cmpl", PPC_CMPL, 0, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 0, v );
|
|
break;
|
|
|
|
case OP_EQF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x EQF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 2, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 2, v );
|
|
break;
|
|
case OP_NEF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x NEF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 2, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 2, v );
|
|
break;
|
|
case OP_LTF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LTF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 0, v );
|
|
break;
|
|
case OP_LEF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LEF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 1, v );
|
|
break;
|
|
case OP_GTF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GTF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 4, 1, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 12, 1, v );
|
|
break;
|
|
case OP_GEF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x GEF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fcmpu", PPC_FCMPU, 0, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
i = Constant4();
|
|
jused[i] = 1;
|
|
InstImm( "bc", PPC_BC, 12, 0, 8 );
|
|
if ( pass==1 ) {
|
|
v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
|
|
} else {
|
|
v = 0;
|
|
}
|
|
Emit4("b", PPC_B | (unsigned int)(v&0x3ffffff) );
|
|
// InstImm( "bc", PPC_BC, 4, 0, v );
|
|
break;
|
|
|
|
case OP_NEGI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x NEGI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
InstImm( "subfic", PPC_SUBFIC, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 );
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_ADD:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x ADD\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_SUB:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x SUB\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_DIVI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x DIVI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "divw", PPC_DIVW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_DIVU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x DIVU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "divwu", PPC_DIVWU, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_MODI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x MODI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "divw", PPC_DIVW, R_EA, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
Inst( "mullw", PPC_MULLW, R_EA, opStackIntRegisters[opStackDepth-1], R_EA );
|
|
Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], R_EA, opStackIntRegisters[opStackDepth-2] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_MODU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x MODU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "divwu", PPC_DIVWU, R_EA, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
Inst( "mullw", PPC_MULLW, R_EA, opStackIntRegisters[opStackDepth-1], R_EA );
|
|
Inst( "subf", PPC_SUBF, opStackIntRegisters[opStackDepth-2], R_EA, opStackIntRegisters[opStackDepth-2] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_MULI:
|
|
case OP_MULU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x MULI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "mullw", PPC_MULLW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-2] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_BAND:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x BAND\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "and", PPC_AND, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_BOR:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x BOR\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "or", PPC_OR, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_BXOR:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x BXOR\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "xor", PPC_XOR, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_BCOM:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x BCOM\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
Inst( "nor", PPC_NOR, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1] );
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_LSH:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x LSH\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "slw", PPC_SLW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_RSHI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x RSHI\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "sraw", PPC_SRAW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_RSHU:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x RSHU\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
Inst( "srw", PPC_SRW, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
|
|
case OP_NEGF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x NEGF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
Inst( "fneg", PPC_FNEG, opStackFloatRegisters[opStackDepth-1], 0, opStackFloatRegisters[opStackDepth-1] );
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_ADDF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x ADDF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fadds", PPC_FADDS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_SUBF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x SUBF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fsubs", PPC_FSUBS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_DIVF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x DIVF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst( "fdivs", PPC_FDIVS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
case OP_MULF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x MULF\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
makeFloat(opStackDepth-2);
|
|
Inst4( "fmuls", PPC_FMULS, opStackFloatRegisters[opStackDepth-2], opStackFloatRegisters[opStackDepth-2], 0, opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
|
|
case OP_CVIF:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x CVIF\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
//makeInteger(opStackDepth-1);
|
|
v = (int)&itofConvert;
|
|
InstImmU( "addis", PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff );
|
|
InstImmU( "ori", PPC_ORI, R_EA, R_EA, v & 0xffff );
|
|
InstImmU( "xoris", PPC_XORIS, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0x8000 );
|
|
InstImm( "stw", PPC_STW, opStackIntRegisters[opStackDepth-1], R_EA, 12 );
|
|
InstImm( "lfd", PPC_LFD, opStackFloatRegisters[opStackDepth-1], R_EA, 0 );
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
InstImm( "lfd", PPC_LFD, 13, R_EA, 8 );
|
|
Inst( "fsub", PPC_FSUB, opStackFloatRegisters[opStackDepth-1], 13, opStackFloatRegisters[opStackDepth-1] );
|
|
opStackRegType[opStackDepth-1] = 2;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
// Inst( PPC_FRSP, R_TOP, 0, R_TOP );
|
|
break;
|
|
case OP_CVFI:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x CVFI\n",instruction);
|
|
#endif
|
|
makeFloat(opStackDepth-1);
|
|
|
|
InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, opStackDepth*4);
|
|
|
|
Inst( "fctiwz", PPC_FCTIWZ, opStackFloatRegisters[opStackDepth-1], 0, opStackFloatRegisters[opStackDepth-1] );
|
|
Inst( "stfiwx", PPC_STFIWX, opStackFloatRegisters[opStackDepth-1], 0, R_OPSTACK ); // save value to opstack (dummy area now)
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
Inst( "ori", PPC_ORI, 0, 0, 0);
|
|
InstImm( "lwz", PPC_LWZ, opStackIntRegisters[opStackDepth-1], R_OPSTACK, 0 );
|
|
|
|
InstImm( "addi", PPC_ADDI, R_OPSTACK, R_OPSTACK, -opStackDepth*4);
|
|
|
|
opStackRegType[opStackDepth-1] = 1;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_SEX8:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x SEX8\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
Inst( "extsb", PPC_EXTSB, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 );
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
case OP_SEX16:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x SEX16\n",instruction);
|
|
#endif
|
|
assertInteger(opStackDepth-1);
|
|
Inst( "extsh", PPC_EXTSH, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 0 );
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
break;
|
|
|
|
case OP_BLOCK_COPY:
|
|
v = Constant4() >> 2;
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x BLOCK_COPY\t%08lx\n",instruction,v<<2);
|
|
#endif
|
|
assert(opStackDepth >= 2);
|
|
assertInteger(opStackDepth-1);
|
|
assertInteger(opStackDepth-2);
|
|
InstImmU( "addi", PPC_ADDI, R_EA, 0, v ); // count
|
|
// FIXME: range check
|
|
Inst( "mtctr", PPC_MTSPR, R_EA, 9, 0 ); // move to count register
|
|
|
|
Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_MEMBASE );
|
|
InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], -4 );
|
|
Inst( "add", PPC_ADD, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], R_MEMBASE );
|
|
InstImm( "addi", PPC_ADDI, opStackIntRegisters[opStackDepth-2], opStackIntRegisters[opStackDepth-2], -4 );
|
|
|
|
InstImm( "lwzu", PPC_LWZU, R_EA, opStackIntRegisters[opStackDepth-1], 4 ); // source
|
|
InstImm( "stwu", PPC_STWU, R_EA, opStackIntRegisters[opStackDepth-2], 4 ); // dest
|
|
Inst( "b", PPC_BC | 0xfff8 , 16, 0, 0 ); // loop
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackRegType[opStackDepth-2] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-2] = 0;
|
|
opStackDepth -= 2;
|
|
break;
|
|
|
|
case OP_JUMP:
|
|
#if DEBUG_VM
|
|
if(pass == 1)
|
|
printf("%08x JUMP\n",instruction);
|
|
#endif
|
|
assert(opStackDepth == 1);
|
|
assertInteger(opStackDepth-1);
|
|
|
|
Inst( "rlwinm", PPC_RLWINM | ( 29 << 1 ), opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], 2 );
|
|
// FIXME: range check
|
|
Inst( "lwzx", PPC_LWZX, opStackIntRegisters[opStackDepth-1], opStackIntRegisters[opStackDepth-1], R_INSTRUCTIONS );
|
|
Inst( "mtctr", PPC_MTSPR, opStackIntRegisters[opStackDepth-1], 9, 0 ); // move to count register
|
|
Inst( "bctr", PPC_BCCTR, 20, 0, 0 ); // jump to the count register
|
|
opStackRegType[opStackDepth-1] = 0;
|
|
opStackLoadInstructionAddr[opStackDepth-1] = 0;
|
|
opStackDepth -= 1;
|
|
break;
|
|
default:
|
|
Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc );
|
|
}
|
|
pop0 = pop1;
|
|
pop1 = op;
|
|
assert(opStackDepth >= 0);
|
|
assert(opStackDepth < OP_STACK_MAX_DEPTH);
|
|
|
|
//printf("%4d\t%s\n",opStackDepth,opnames[op]);
|
|
}
|
|
|
|
Com_Printf( "VM file %s pass %d compiled to %i bytes of code\n", vm->name, (pass+1), compiledOfs*4 );
|
|
|
|
if ( pass == 0 ) {
|
|
// copy to an exact size buffer on the hunk
|
|
vm->codeLength = compiledOfs * 4;
|
|
vm->codeBase = Hunk_Alloc( vm->codeLength, h_low );
|
|
Com_Memcpy( vm->codeBase, buf, vm->codeLength );
|
|
|
|
//printf("codeBase: %p\n",vm->codeBase);
|
|
|
|
Z_Free( buf );
|
|
|
|
// offset all the instruction pointers for the new location
|
|
for ( i = 0 ; i < header->instructionCount ; i++ ) {
|
|
vm->instructionPointers[i] += (int)vm->codeBase;
|
|
//printf("%08x %08lx\n",i,vm->instructionPointers[i]);
|
|
}
|
|
|
|
// go back over it in place now to fixup reletive jump targets
|
|
buf = (unsigned *)vm->codeBase;
|
|
} else if ( pass == 1 ) {
|
|
#ifdef MACOS_X
|
|
// On Mac OS X, the following library routine clears the instruction cache for generated code
|
|
MakeDataExecutable(vm->codeBase, vm->codeLength);
|
|
#else
|
|
#warning Need to clear the instruction cache for generated code
|
|
#endif
|
|
}
|
|
}
|
|
if(0)
|
|
{
|
|
char buf[256];
|
|
printf("wait..\n");
|
|
gets(buf);
|
|
}
|
|
Z_Free( jused );
|
|
}
|
|
|
|
/*
|
|
==============
|
|
VM_CallCompiled
|
|
|
|
This function is called directly by the generated code
|
|
==============
|
|
*/
|
|
int VM_CallCompiled( vm_t *vm, int *args ) {
|
|
int stack[1024];
|
|
int programStack;
|
|
int stackOnEntry;
|
|
byte *image;
|
|
|
|
currentVM = vm;
|
|
|
|
//printf("VM_CallCompiled: %p %08lx %08lx %08lx\n",
|
|
// vm, args[0],args[1],args[2]);
|
|
|
|
// interpret the code
|
|
vm->currentlyInterpreting = qtrue;
|
|
|
|
// we might be called recursively, so this might not be the very top
|
|
programStack = vm->programStack;
|
|
stackOnEntry = programStack;
|
|
image = vm->dataBase;
|
|
|
|
// set up the stack frame
|
|
programStack -= 48;
|
|
|
|
*(int *)&image[ programStack + 44] = args[9];
|
|
*(int *)&image[ programStack + 40] = args[8];
|
|
*(int *)&image[ programStack + 36] = args[7];
|
|
*(int *)&image[ programStack + 32] = args[6];
|
|
*(int *)&image[ programStack + 28] = args[5];
|
|
*(int *)&image[ programStack + 24] = args[4];
|
|
*(int *)&image[ programStack + 20] = args[3];
|
|
*(int *)&image[ programStack + 16] = args[2];
|
|
*(int *)&image[ programStack + 12] = args[1];
|
|
*(int *)&image[ programStack + 8 ] = args[0];
|
|
*(int *)&image[ programStack + 4 ] = 0; // return stack
|
|
*(int *)&image[ programStack ] = -1; // will terminate the loop on return
|
|
|
|
// Cheesy... manually save registers used by VM call...
|
|
// off we go into generated code...
|
|
// the PPC calling standard says the parms will all go into R3 - R11, so
|
|
// no special asm code is needed here
|
|
#ifdef __GNUC__
|
|
((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))(
|
|
programStack, (int)&stack,
|
|
(int)image, vm->dataMask, (int)&AsmCall,
|
|
(int)vm->instructionPointers, vm->instructionPointersLength,
|
|
(int)vm );
|
|
#else
|
|
((void(*)(int, int, int, int, int, int, int, int))(vm->codeBase))(
|
|
programStack, (int)&stack,
|
|
(int)image, vm->dataMask, *(int *)&AsmCall /* skip function pointer header */,
|
|
(int)vm->instructionPointers, vm->instructionPointersLength,
|
|
(int)vm );
|
|
#endif
|
|
vm->programStack = stackOnEntry;
|
|
|
|
vm->currentlyInterpreting = qfalse;
|
|
|
|
return stack[1];
|
|
}
|
|
|
|
|
|
/*
|
|
==================
|
|
AsmCall
|
|
|
|
Put this at end of file because gcc messes up debug line numbers
|
|
==================
|
|
*/
|
|
#ifdef __GNUC__
|
|
|
|
void AsmCall( void ) {
|
|
asm (
|
|
// pop off the destination instruction
|
|
" lwz r12,0(r4) \n" // RG_TOP, 0(RG_OPSTACK)
|
|
" addi r4,r4,-4 \n" // RG_OPSTACK, RG_OPSTACK, -4 \n"
|
|
|
|
// see if it is a system trap
|
|
" cmpwi r12,0 \n" // RG_TOP, 0 \n"
|
|
" bc 12,0, systemTrap \n"
|
|
|
|
// calling another VM function, so lookup in instructionPointers
|
|
" slwi r12,r12,2 \n" // RG_TOP,RG_TOP,2
|
|
// FIXME: range check
|
|
" lwzx r12, r8, r12 \n" // RG_TOP, RG_INSTRUCTIONS(RG_TOP)
|
|
" mtctr r12 \n" // RG_TOP
|
|
);
|
|
|
|
#if defined(MACOS_X) && defined(__OPTIMIZE__)
|
|
// On Mac OS X, gcc doesn't push a frame when we are optimized, so trying to tear it down results in grave disorder.
|
|
//#warning Mac OS X optimization on, not popping GCC AsmCall frame
|
|
#else
|
|
// Mac OS X Server and unoptimized compiles include a GCC AsmCall frame
|
|
asm (
|
|
" lwz r1,0(r1) \n" // pop off the GCC AsmCall frame
|
|
" lmw r30,-8(r1) \n"
|
|
);
|
|
#endif
|
|
|
|
asm (
|
|
" bcctr 20,0 \n" // when it hits a leave, it will branch to the current link register
|
|
|
|
// calling a system trap
|
|
"systemTrap: \n"
|
|
// convert to positive system call number
|
|
" subfic r12,r12,-1 \n"
|
|
|
|
// save all our registers, including the current link register
|
|
" mflr r13 \n" // RG_SECOND // copy off our link register
|
|
" addi r1,r1,-92 \n" // required 24 byets of linkage, 32 bytes of parameter, plus our saves
|
|
" stw r3,56(r1) \n" // RG_STACK, -36(REAL_STACK)
|
|
" stw r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK)
|
|
" stw r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK)
|
|
" stw r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK)
|
|
" stw r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK)
|
|
" stw r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK)
|
|
" stw r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK)
|
|
" stw r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK)
|
|
" stw r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK) // link register
|
|
|
|
// save the vm stack position to allow recursive VM entry
|
|
" addi r13,r3,-4 \n" // RG_TOP, RG_STACK, -4
|
|
" stw r13,0(r10) \n" //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM)
|
|
|
|
// save the system call number as the 0th parameter
|
|
" add r3,r3,r5 \n" // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls
|
|
" stwu r12,4(r3) \n" // RG_TOP, 4(r3)
|
|
|
|
// make the system call with the address of all the VM parms as a parameter
|
|
// vm->systemCalls( &parms )
|
|
" lwz r12,4(r10) \n" // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM)
|
|
" mtctr r12 \n" // RG_TOP
|
|
" bcctrl 20,0 \n"
|
|
" mr r12,r3 \n" // RG_TOP, r3
|
|
|
|
// pop our saved registers
|
|
" lwz r3,56(r1) \n" // RG_STACK, 0(RG_REAL_STACK)
|
|
" lwz r4,60(r1) \n" // RG_OPSTACK, 4(RG_REAL_STACK)
|
|
" lwz r5,64(r1) \n" // RG_MEMBASE, 8(RG_REAL_STACK)
|
|
" lwz r6,68(r1) \n" // RG_MEMMASK, 12(RG_REAL_STACK)
|
|
" lwz r7,72(r1) \n" // RG_ASMCALL, 16(RG_REAL_STACK)
|
|
" lwz r8,76(r1) \n" // RG_INSTRUCTIONS, 20(RG_REAL_STACK)
|
|
" lwz r9,80(r1) \n" // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK)
|
|
" lwz r10,84(r1) \n" // RG_VM, 28(RG_REAL_STACK)
|
|
" lwz r13,88(r1) \n" // RG_SECOND, 32(RG_REAL_STACK)
|
|
" addi r1,r1,92 \n" // RG_REAL_STACK, RG_REAL_STACK, 36
|
|
|
|
// restore the old link register
|
|
" mtlr r13 \n" // RG_SECOND
|
|
|
|
// save off the return value
|
|
" stwu r12,4(r4) \n" // RG_TOP, 0(RG_OPSTACK)
|
|
|
|
// GCC adds its own prolog / epliog code
|
|
);
|
|
}
|
|
#else
|
|
|
|
// codewarrior version
|
|
|
|
void asm AsmCall( void ) {
|
|
|
|
// pop off the destination instruction
|
|
|
|
lwz r12,0(r4) // RG_TOP, 0(RG_OPSTACK)
|
|
|
|
addi r4,r4,-4 // RG_OPSTACK, RG_OPSTACK, -4
|
|
|
|
|
|
|
|
// see if it is a system trap
|
|
|
|
cmpwi r12,0 // RG_TOP, 0
|
|
|
|
bc 12,0, systemTrap
|
|
|
|
|
|
|
|
// calling another VM function, so lookup in instructionPointers
|
|
|
|
slwi r12,r12,2 // RG_TOP,RG_TOP,2
|
|
|
|
// FIXME: range check
|
|
|
|
lwzx r12, r8, r12 // RG_TOP, RG_INSTRUCTIONS(RG_TOP)
|
|
|
|
mtctr r12 // RG_TOP
|
|
|
|
|
|
|
|
bcctr 20,0 // when it hits a leave, it will branch to the current link register
|
|
|
|
|
|
|
|
// calling a system trap
|
|
|
|
systemTrap:
|
|
|
|
// convert to positive system call number
|
|
|
|
subfic r12,r12,-1
|
|
|
|
|
|
|
|
// save all our registers, including the current link register
|
|
|
|
mflr r13 // RG_SECOND // copy off our link register
|
|
|
|
addi r1,r1,-92 // required 24 byets of linkage, 32 bytes of parameter, plus our saves
|
|
|
|
stw r3,56(r1) // RG_STACK, -36(REAL_STACK)
|
|
|
|
stw r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK)
|
|
|
|
stw r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK)
|
|
|
|
stw r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK)
|
|
|
|
stw r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK)
|
|
|
|
stw r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK)
|
|
|
|
stw r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK)
|
|
|
|
stw r10,84(r1) // RG_VM, 28(RG_REAL_STACK)
|
|
|
|
stw r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK) // link register
|
|
|
|
|
|
|
|
// save the vm stack position to allow recursive VM entry
|
|
|
|
addi r13,r3,-4 // RG_TOP, RG_STACK, -4
|
|
|
|
stw r13,0(r10) //RG_TOP, VM_OFFSET_PROGRAM_STACK(RG_VM)
|
|
|
|
|
|
|
|
// save the system call number as the 0th parameter
|
|
|
|
add r3,r3,r5 // r3, RG_STACK, RG_MEMBASE // r3 is the first parameter to vm->systemCalls
|
|
|
|
stwu r12,4(r3) // RG_TOP, 4(r3)
|
|
|
|
|
|
|
|
// make the system call with the address of all the VM parms as a parameter
|
|
|
|
// vm->systemCalls( &parms )
|
|
|
|
lwz r12,4(r10) // RG_TOP, VM_OFFSET_SYSTEM_CALL(RG_VM)
|
|
|
|
|
|
|
|
// perform macos cross fragment fixup crap
|
|
|
|
lwz r9,0(r12)
|
|
|
|
stw r2,52(r1) // save old TOC
|
|
|
|
lwz r2,4(r12)
|
|
|
|
|
|
|
|
mtctr r9 // RG_TOP
|
|
|
|
bcctrl 20,0
|
|
|
|
|
|
|
|
lwz r2,52(r1) // restore TOC
|
|
|
|
|
|
|
|
mr r12,r3 // RG_TOP, r3
|
|
|
|
|
|
|
|
// pop our saved registers
|
|
|
|
lwz r3,56(r1) // RG_STACK, 0(RG_REAL_STACK)
|
|
|
|
lwz r4,60(r1) // RG_OPSTACK, 4(RG_REAL_STACK)
|
|
|
|
lwz r5,64(r1) // RG_MEMBASE, 8(RG_REAL_STACK)
|
|
|
|
lwz r6,68(r1) // RG_MEMMASK, 12(RG_REAL_STACK)
|
|
|
|
lwz r7,72(r1) // RG_ASMCALL, 16(RG_REAL_STACK)
|
|
|
|
lwz r8,76(r1) // RG_INSTRUCTIONS, 20(RG_REAL_STACK)
|
|
|
|
lwz r9,80(r1) // RG_NUM_INSTRUCTIONS, 24(RG_REAL_STACK)
|
|
|
|
lwz r10,84(r1) // RG_VM, 28(RG_REAL_STACK)
|
|
|
|
lwz r13,88(r1) // RG_SECOND, 32(RG_REAL_STACK)
|
|
|
|
addi r1,r1,92 // RG_REAL_STACK, RG_REAL_STACK, 36
|
|
|
|
|
|
|
|
// restore the old link register
|
|
|
|
mtlr r13 // RG_SECOND
|
|
|
|
|
|
|
|
// save off the return value
|
|
|
|
stwu r12,4(r4) // RG_TOP, 0(RG_OPSTACK)
|
|
|
|
|
|
|
|
blr
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
#endif
|