/* =========================================================================== 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" #include #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 ) { // clear the instruction cache for generated code msync(vm->codeBase, vm->codeLength, MS_INVALIDATE); } } 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