/*
===========================================================================
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"

#pragma opt_pointer_analysis off


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

// 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	= 0x7c000000,
	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	= 0x7c000000,
	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

// 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( int i ) {
	buf[ compiledOfs ] = i;
	compiledOfs++;
}

static void Inst( int opcode, int destReg, int aReg, int bReg ) {
    unsigned		r;

    r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) ;
    buf[ compiledOfs ] = r;
    compiledOfs++;
}

static void Inst4( int opcode, int destReg, int aReg, int bReg, int cReg ) {
    unsigned		r;

    r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( bReg << 11 ) | ( cReg << 6 );
    buf[ compiledOfs ] = r;
    compiledOfs++;
}

static void InstImm( 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 );
	}
	r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff );
	buf[ compiledOfs ] = r;
	compiledOfs++;
}

static void InstImmU( 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 );
	}
    r = opcode | ( destReg << 21 ) | ( aReg << 16 ) | ( immediate & 0xffff );
	buf[ compiledOfs ] = r;
	compiledOfs++;
}

static qboolean	rtopped;
static int pop0, pop1, oc0, oc1;
static vm_t *tvm;
static int instruction;
static byte *jused;
static int pass;

static void ltop() {
    if (rtopped == qfalse) {
	InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 );		// get value from opstack
    }
}

static void ltopandsecond() {
    if (pass>=0 && buf[compiledOfs-1] == (PPC_STWU |  R_TOP<<21 | R_OPSTACK<<16 | 4 ) && jused[instruction]==0 ) {
	compiledOfs--;
	if (!pass) {
	    tvm->instructionPointers[instruction] = compiledOfs * 4;
	}
	InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );	// get value from opstack
	InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
    } else if (pass>=0 && buf[compiledOfs-1] == (PPC_STW |  R_TOP<<21 | R_OPSTACK<<16 | 0 )  && jused[instruction]==0 ) {
	compiledOfs--;
	if (!pass) {
	    tvm->instructionPointers[instruction] = compiledOfs * 4;
	}
	InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 );	// get value from opstack
	InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 );
    } else {
	ltop();		// get value from opstack
	InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 );	// get value from opstack
	InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 );
    }
    rtopped = qfalse;
}

// TJW: Unused
#if 0
static void fltop() {
    if (rtopped == qfalse) {
	InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
    }
}
#endif

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;
}

/*
=================
VM_Compile
=================
*/
void VM_Compile( vm_t *vm, vmHeader_t *header ) {
	int		op;
	int		maxLength;
	int		v;
	int		i;
        
	// 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++ ) {

	rtopped = qfalse;
        // translate all instructions
        pc = 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:
                InstImmU( PPC_ADDI, R_TOP, 0, 0 );
                InstImm( PPC_LWZ, R_TOP, R_TOP, 0 );			// *(int *)0 to crash to debugger
		rtopped = qfalse;
                break;
            case OP_ENTER:
                InstImm( PPC_ADDI, R_STACK, R_STACK, -Constant4() );	// sub R_STACK, R_STACK, imm
		rtopped = qfalse;
                break;
            case OP_CONST:
                v = Constant4();
		if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) {
		    v &= vm->dataMask;
		}
                if ( v < 32768 && v >= -32768 ) {
                    InstImmU( PPC_ADDI, R_TOP, 0, v & 0xffff );
                } else {
                    InstImmU( PPC_ADDIS, R_TOP, 0, (v >> 16)&0xffff );
                    if ( v & 0xffff ) {
                        InstImmU( PPC_ORI, R_TOP, R_TOP, v & 0xffff );
                    }
                }
		if (code[pc] == OP_LOAD4) {
		    Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		} else if (code[pc] == OP_LOAD2) {
		    Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		} else if (code[pc] == OP_LOAD1) {
		    Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		}
		if (code[pc] == OP_STORE4) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );	// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		} else if (code[pc] == OP_STORE2) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );	// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		} else if (code[pc] == OP_STORE1) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );	// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		}
		if (code[pc] == OP_JUMP) {
		    jused[v] = 1;
		}
		InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 );
		rtopped = qtrue;
		break;
            case OP_LOCAL:
		oc0 = oc1;
		oc1 = Constant4();
		if (code[pc] == OP_LOAD4 || code[pc] == OP_LOAD2 || code[pc] == OP_LOAD1) {
		    oc1 &= vm->dataMask;
		}
                InstImm( PPC_ADDI, R_TOP, R_STACK, oc1 );
		if (code[pc] == OP_LOAD4) {
		    Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		} else if (code[pc] == OP_LOAD2) {
		    Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		} else if (code[pc] == OP_LOAD1) {
		    Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
		    pc++;
		    instruction++;
		}
		if (code[pc] == OP_STORE4) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );		// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		} else if (code[pc] == OP_STORE2) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );		// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		} else if (code[pc] == OP_STORE1) {
		    InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, 0 );		// get value from opstack
		    InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		    //Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );	// mask it
		    Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE );	// store from memory base
		    pc++;
		    instruction++;
		    rtopped = qfalse;
		    break;
		}
                InstImm( PPC_STWU, R_TOP, R_OPSTACK, 4 );
		rtopped = qtrue;
                break;
            case OP_ARG:
                ltop();							// get value from opstack
                InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
                InstImm( PPC_ADDI, R_EA, R_STACK, Constant1() );	// location to put it
                Inst( PPC_STWX, R_TOP, R_EA, R_MEMBASE );
		rtopped = qfalse;
                break;
            case OP_CALL:
                Inst( PPC_MFSPR, R_SECOND, 8, 0 );			// move from link register
                InstImm( PPC_STWU, R_SECOND, R_REAL_STACK, -16 );	// save off the old return address

                Inst( PPC_MTSPR, R_ASMCALL, 9, 0 );			// move to count register
                Inst( PPC_BCCTR | 1, 20, 0, 0 );			// jump and link to the count register

                InstImm( PPC_LWZ, R_SECOND, R_REAL_STACK, 0 );		// fetch the old return address
                InstImm( PPC_ADDI, R_REAL_STACK, R_REAL_STACK, 16 );
                Inst( PPC_MTSPR, R_SECOND, 8, 0 );			// move to link register
		rtopped = qfalse;
                break;
            case OP_PUSH:
                InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, 4 );
		rtopped = qfalse;
                break;
            case OP_POP:
                InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
		rtopped = qfalse;
                break;
            case OP_LEAVE:
                InstImm( PPC_ADDI, R_STACK, R_STACK, Constant4() );	// add R_STACK, R_STACK, imm
                Inst( PPC_BCLR, 20, 0, 0 );							// branch unconditionally to link register
		rtopped = qfalse;
                break;
            case OP_LOAD4:
                ltop();							// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP );		// mask it
                Inst( PPC_LWZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );
		rtopped = qtrue;
                break;
            case OP_LOAD2:
                ltop();							// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP );		// mask it
                Inst( PPC_LHZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );
		rtopped = qtrue;
                break;
            case OP_LOAD1:
                ltop();							// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_TOP, R_TOP );		// mask it
                Inst( PPC_LBZX, R_TOP, R_TOP, R_MEMBASE );		// load from memory base
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );
		rtopped = qtrue;
                break;
            case OP_STORE4:
                ltopandsecond();					// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );		// mask it
                Inst( PPC_STWX, R_TOP, R_SECOND, R_MEMBASE );		// store from memory base
		rtopped = qfalse;
                break;
            case OP_STORE2:
                ltopandsecond();					// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );		// mask it
                Inst( PPC_STHX, R_TOP, R_SECOND, R_MEMBASE );		// store from memory base
		rtopped = qfalse;
                break;
            case OP_STORE1:
                ltopandsecond();					// get value from opstack
		//Inst( PPC_AND, R_MEMMASK, R_SECOND, R_SECOND );		// mask it
                Inst( PPC_STBX, R_TOP, R_SECOND, R_MEMBASE );		// store from memory base
		rtopped = qfalse;
                break;

            case OP_EQ:
                ltopandsecond();					// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 4, 2, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];                    
                } else {
                    v = 0;             
                }
                Emit4(PPC_B | (v&0x3ffffff) );
				rtopped = qfalse;
                break;
            case OP_NE:
                ltopandsecond();					// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 12, 2, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];                    
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 2, v );

				rtopped = qfalse;
                break;
            case OP_LTI:
                ltopandsecond();					// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 4, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 0, v );
				rtopped = qfalse;
                break;
            case OP_LEI:
                ltopandsecond();					// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 12, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 1, v );
				rtopped = qfalse;
                break;
            case OP_GTI:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 4, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 1, v );
				rtopped = qfalse;
                break;
            case OP_GEI:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMP, 0, R_SECOND, R_TOP );
                i = Constant4();
				jused[i] = 1;
                InstImm( PPC_BC, 12, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 0, v );
				rtopped = qfalse;
                break;
            case OP_LTU:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMPL, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 4, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 0, v );
		rtopped = qfalse;
                break;
            case OP_LEU:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMPL, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 12, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 1, v );
		rtopped = qfalse;
                break;
            case OP_GTU:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMPL, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 4, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 1, v );
		rtopped = qfalse;
                break;
            case OP_GEU:
                ltopandsecond();		// get value from opstack
                Inst( PPC_CMPL, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 12, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 0, v );
		rtopped = qfalse;
                break;
                
            case OP_EQF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_TOP, R_SECOND );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 4, 2, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 2, v );
		rtopped = qfalse;
                break;			
            case OP_NEF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_TOP, R_SECOND );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 12, 2, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 2, v );
		rtopped = qfalse;
                break;			
            case OP_LTF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 4, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 0, v );
		rtopped = qfalse;
                break;			
            case OP_LEF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 12, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 1, v );
		rtopped = qfalse;
                break;			
            case OP_GTF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 4, 1, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 12, 1, v );
		rtopped = qfalse;
                break;			
            case OP_GEF:
                fltopandsecond();		// get value from opstack
                Inst( PPC_FCMPU, 0, R_SECOND, R_TOP );
                i = Constant4();
		jused[i] = 1;
                InstImm( PPC_BC, 12, 0, 8 );
                if ( pass==1 ) {
                    v = vm->instructionPointers[ i ] - (int)&buf[compiledOfs];
                } else {
                    v = 0;
                }
                Emit4(PPC_B | (unsigned int)(v&0x3ffffff) );
//                InstImm( PPC_BC, 4, 0, v );
		rtopped = qfalse;
                break;

            case OP_NEGI:
                ltop();		// get value from opstack
                InstImm( PPC_SUBFIC, R_TOP, R_TOP, 0 );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_ADD:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_ADD, R_TOP, R_TOP, R_SECOND );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_SUB:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_SUBF, R_TOP, R_TOP, R_SECOND );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_DIVI:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_DIVW, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_DIVU:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_DIVWU, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_MODI:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_DIVW, R_EA, R_SECOND, R_TOP );
                Inst( PPC_MULLW, R_EA, R_TOP, R_EA );
                Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_MODU:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_DIVWU, R_EA, R_SECOND, R_TOP );
                Inst( PPC_MULLW, R_EA, R_TOP, R_EA );
                Inst( PPC_SUBF, R_TOP, R_EA, R_SECOND );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_MULI:
            case OP_MULU:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_MULLW, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_BAND:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_AND, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_BOR:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_OR, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_BXOR:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_XOR, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_BCOM:
                ltop();		// get value from opstack
                Inst( PPC_NOR, R_TOP, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_LSH:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_SLW, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_RSHI:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_SRAW, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;
            case OP_RSHU:
                ltop();		// get value from opstack
                InstImm( PPC_LWZU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_SRW, R_SECOND, R_TOP, R_TOP );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qtrue;
                break;

            case OP_NEGF:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                Inst( PPC_FNEG, R_TOP, 0, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_ADDF:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_FADDS, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_SUBF:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_FSUBS, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_DIVF:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst( PPC_FDIVS, R_TOP, R_SECOND, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_MULF:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                InstImm( PPC_LFSU, R_SECOND, R_OPSTACK, -4 );		// get value from opstack
                Inst4( PPC_FMULS, R_TOP, R_SECOND, 0, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;

            case OP_CVIF:
                v = (int)&itofConvert;
                InstImmU( PPC_ADDIS, R_EA, 0, (v >> 16)&0xffff );
                InstImmU( PPC_ORI, R_EA, R_EA, v & 0xffff );
		InstImm( PPC_LWZ, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                InstImmU( PPC_XORIS, R_TOP, R_TOP, 0x8000 );
                InstImm( PPC_STW, R_TOP, R_EA, 12 );
                InstImm( PPC_LFD, R_TOP, R_EA, 0 );
                InstImm( PPC_LFD, R_SECOND, R_EA, 8 );
                Inst( PPC_FSUB, R_TOP, R_SECOND, R_TOP );
    //            Inst( PPC_FRSP, R_TOP, 0, R_TOP );
                InstImm( PPC_STFS, R_TOP, R_OPSTACK, 0 );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_CVFI:
                InstImm( PPC_LFS, R_TOP, R_OPSTACK, 0 );		// get value from opstack
                Inst( PPC_FCTIWZ, R_TOP, 0, R_TOP );
                Inst( PPC_STFIWX, R_TOP, 0, R_OPSTACK );		// save value to opstack
		rtopped = qfalse;
                break;
            case OP_SEX8:
                ltop();	// get value from opstack
                Inst( PPC_EXTSB, R_TOP, R_TOP, 0 );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );
		rtopped = qtrue;
                break;
            case OP_SEX16:
                ltop();	// get value from opstack
                Inst( PPC_EXTSH, R_TOP, R_TOP, 0 );
                InstImm( PPC_STW, R_TOP, R_OPSTACK, 0 );
		rtopped = qtrue;
                break;

            case OP_BLOCK_COPY:
                v = Constant4() >> 2;
                ltop();		// source
                InstImm( PPC_LWZ, R_SECOND, R_OPSTACK, -4 );	// dest
                InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -8 );
                InstImmU( PPC_ADDI, R_EA, 0, v );				// count
				// FIXME: range check
              	Inst( PPC_MTSPR, R_EA, 9, 0 );					// move to count register

                Inst( PPC_ADD, R_TOP, R_TOP, R_MEMBASE );
                InstImm( PPC_ADDI, R_TOP, R_TOP, -4 );
                Inst( PPC_ADD, R_SECOND, R_SECOND, R_MEMBASE );
                InstImm( PPC_ADDI, R_SECOND, R_SECOND, -4 );

                InstImm( PPC_LWZU, R_EA, R_TOP, 4 );		// source
                InstImm( PPC_STWU, R_EA, R_SECOND, 4 );	// dest
                Inst( PPC_BC | 0xfff8 , 16, 0, 0 );					// loop
		rtopped = qfalse;
                break;

            case OP_JUMP:
                ltop();	// get value from opstack
                InstImm( PPC_ADDI, R_OPSTACK, R_OPSTACK, -4 );
                Inst( PPC_RLWINM | ( 29 << 1 ), R_TOP, R_TOP, 2 );
		// FIXME: range check
		Inst( PPC_LWZX, R_TOP, R_TOP, R_INSTRUCTIONS );
                Inst( PPC_MTSPR, R_TOP, 9, 0 );		// move to count register
                Inst( PPC_BCCTR, 20, 0, 0 );		// jump to the count register
		rtopped = qfalse;
                break;
            default:
                Com_Error( ERR_DROP, "VM_CompilePPC: bad opcode %i at instruction %i, offset %i", op, instruction, pc );
            }
	    pop0 = pop1;
	    pop1 = 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 );
	    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;
	    }

	    // go back over it in place now to fixup reletive jump targets
	    buf = (unsigned *)vm->codeBase;
	}
    }
    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;

	// 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

	// 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 / epilog 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
