ioq3quest/code/qcommon/vm_ppc_new.c
Tony J. White = fbf09d64c5 * Increased the number of registers used for the opStack in the PPC vm from
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.
2006-09-24 02:33:08 +00:00

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