mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 14:59:26 -04:00
5004 lines
206 KiB
C++
5004 lines
206 KiB
C++
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
|
|
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterHelper.h"
|
|
#include "PPCRecompiler.h"
|
|
#include "PPCRecompilerIml.h"
|
|
#include "PPCRecompilerX64.h"
|
|
#include "PPCRecompilerImlRanges.h"
|
|
#include "util/helpers/StringBuf.h"
|
|
|
|
bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext);
|
|
uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext);
|
|
uint32 PPCRecompiler_getInstructionByOffset(ppcImlGenContext_t* ppcImlGenContext, uint32 offset);
|
|
|
|
PPCRecImlInstruction_t* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
if( ppcImlGenContext->imlListCount+1 > ppcImlGenContext->imlListSize )
|
|
{
|
|
sint32 newSize = ppcImlGenContext->imlListCount*2 + 2;
|
|
ppcImlGenContext->imlList = (PPCRecImlInstruction_t*)realloc(ppcImlGenContext->imlList, sizeof(PPCRecImlInstruction_t)*newSize);
|
|
ppcImlGenContext->imlListSize = newSize;
|
|
}
|
|
PPCRecImlInstruction_t* imlInstruction = ppcImlGenContext->imlList+ppcImlGenContext->imlListCount;
|
|
memset(imlInstruction, 0x00, sizeof(PPCRecImlInstruction_t));
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default
|
|
imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction;
|
|
ppcImlGenContext->imlListCount++;
|
|
return imlInstruction;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_jumpmark(ppcImlGenContext_t* ppcImlGenContext, uint32 address)
|
|
{
|
|
// no-op that indicates possible destination of a jump
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_JUMPMARK;
|
|
imlInstruction->op_jumpmark.address = address;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext_t* ppcImlGenContext, uint32 macroId, uint32 param, uint32 param2, uint16 paramU16)
|
|
{
|
|
// no-op that indicates possible destination of a jump
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_MACRO;
|
|
imlInstruction->operation = macroId;
|
|
imlInstruction->op_macro.param = param;
|
|
imlInstruction->op_macro.param2 = param2;
|
|
imlInstruction->op_macro.paramU16 = paramU16;
|
|
}
|
|
|
|
/*
|
|
* Generates a marker for Interpreter -> Recompiler entrypoints
|
|
* PPC_ENTER iml instructions have no associated PPC address but the instruction itself has one
|
|
*/
|
|
void PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext_t* ppcImlGenContext, uint32 ppcAddress)
|
|
{
|
|
// no-op that indicates possible destination of a jump
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_PPC_ENTER;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->op_ppcEnter.ppcAddress = ppcAddress;
|
|
imlInstruction->op_ppcEnter.x64Offset = 0;
|
|
imlInstruction->associatedPPCAddress = 0;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerResult, uint8 registerA, uint8 crRegister, uint8 crMode)
|
|
{
|
|
// operation with two register operands (e.g. "t0 = t1")
|
|
if(imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_R_R;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = crRegister;
|
|
imlInstruction->crMode = crMode;
|
|
imlInstruction->op_r_r.registerResult = registerResult;
|
|
imlInstruction->op_r_r.registerA = registerA;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, uint8 registerB, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0)
|
|
{
|
|
// operation with three register operands (e.g. "t0 = t1 + t4")
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_R_R_R;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = crRegister;
|
|
imlInstruction->crMode = crMode;
|
|
imlInstruction->op_r_r_r.registerResult = registerResult;
|
|
imlInstruction->op_r_r_r.registerA = registerA;
|
|
imlInstruction->op_r_r_r.registerB = registerB;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, sint32 immS32, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0)
|
|
{
|
|
// operation with two register operands and one signed immediate (e.g. "t0 = t1 + 1234")
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_R_R_S32;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = crRegister;
|
|
imlInstruction->crMode = crMode;
|
|
imlInstruction->op_r_r_s32.registerResult = registerResult;
|
|
imlInstruction->op_r_r_s32.registerA = registerA;
|
|
imlInstruction->op_r_r_s32.immS32 = immS32;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_name_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, uint32 name, uint32 copyWidth, bool signExtend, bool bigEndian)
|
|
{
|
|
// Store name (e.g. "'r3' = t0" which translates to MOV [ESP+offset_r3], reg32)
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_NAME_R;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->op_r_name.registerIndex = registerIndex;
|
|
imlInstruction->op_r_name.name = name;
|
|
imlInstruction->op_r_name.copyWidth = copyWidth;
|
|
imlInstruction->op_r_name.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(bigEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 copyWidth, bool signExtend, bool bigEndian, uint8 crRegister, uint32 crMode)
|
|
{
|
|
// two variations:
|
|
// operation without store (e.g. "'r3' < 123" which has no effect other than updating a condition flags register)
|
|
// operation with store (e.g. "'r3' = 123")
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_R_S32;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = crRegister;
|
|
imlInstruction->crMode = crMode;
|
|
imlInstruction->op_r_immS32.registerIndex = registerIndex;
|
|
imlInstruction->op_r_immS32.immS32 = immS32;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet)
|
|
{
|
|
if(imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
else
|
|
memset(imlInstruction, 0, sizeof(PPCRecImlInstruction_t));
|
|
imlInstruction->type = PPCREC_IML_TYPE_CONDITIONAL_R_S32;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
// r_s32 operation
|
|
imlInstruction->op_conditional_r_s32.registerIndex = registerIndex;
|
|
imlInstruction->op_conditional_r_s32.immS32 = immS32;
|
|
// condition
|
|
imlInstruction->op_conditional_r_s32.crRegisterIndex = crRegisterIndex;
|
|
imlInstruction->op_conditional_r_s32.crBitIndex = crBitIndex;
|
|
imlInstruction->op_conditional_r_s32.bitMustBeSet = bitMustBeSet;
|
|
}
|
|
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint32 jumpmarkAddress)
|
|
{
|
|
// jump
|
|
if (imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
else
|
|
memset(imlInstruction, 0, sizeof(PPCRecImlInstruction_t));
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress;
|
|
imlInstruction->op_conditionalJump.jumpAccordingToSegment = false;
|
|
imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE;
|
|
imlInstruction->op_conditionalJump.crRegisterIndex = 0;
|
|
imlInstruction->op_conditionalJump.crBitIndex = 0;
|
|
imlInstruction->op_conditionalJump.bitMustBeSet = false;
|
|
}
|
|
|
|
// jump based on segment branches
|
|
void PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction)
|
|
{
|
|
// jump
|
|
if (imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->associatedPPCAddress = 0;
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_conditionalJump.jumpmarkAddress = 0;
|
|
imlInstruction->op_conditionalJump.jumpAccordingToSegment = true;
|
|
imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE;
|
|
imlInstruction->op_conditionalJump.crRegisterIndex = 0;
|
|
imlInstruction->op_conditionalJump.crBitIndex = 0;
|
|
imlInstruction->op_conditionalJump.bitMustBeSet = false;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction)
|
|
{
|
|
if (imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_NO_OP;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->crMode = 0;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 crD, uint8 crA, uint8 crB)
|
|
{
|
|
// multiple variations:
|
|
// operation involving only one cr bit (like clear crD bit)
|
|
// operation involving three cr bits (like crD = crA or crB)
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_CR;
|
|
imlInstruction->operation = operation;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->crMode = 0;
|
|
imlInstruction->op_cr.crD = crD;
|
|
imlInstruction->op_cr.crA = crA;
|
|
imlInstruction->op_cr.crB = crB;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext_t* ppcImlGenContext, uint32 jumpmarkAddress, uint32 jumpCondition, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet)
|
|
{
|
|
// conditional jump
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress;
|
|
imlInstruction->op_conditionalJump.condition = jumpCondition;
|
|
imlInstruction->op_conditionalJump.crRegisterIndex = crRegisterIndex;
|
|
imlInstruction->op_conditionalJump.crBitIndex = crBitIndex;
|
|
imlInstruction->op_conditionalJump.bitMustBeSet = bitMustBeSet;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian)
|
|
{
|
|
// load from memory
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_LOAD;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_storeLoad.registerData = registerDestination;
|
|
imlInstruction->op_storeLoad.registerMem = registerMemory;
|
|
imlInstruction->op_storeLoad.immS32 = immS32;
|
|
imlInstruction->op_storeLoad.copyWidth = copyWidth;
|
|
//imlInstruction->op_storeLoad.flags = (signExtend ? PPCREC_IML_OP_FLAG_SIGNEXTEND : 0) | (switchEndian ? PPCREC_IML_OP_FLAG_SWITCHENDIAN : 0);
|
|
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
imlInstruction->op_storeLoad.flags2.signExtend = signExtend;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
|
|
{
|
|
// load from memory
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_LOAD_INDEXED;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_storeLoad.registerData = registerDestination;
|
|
imlInstruction->op_storeLoad.registerMem = registerMemory1;
|
|
imlInstruction->op_storeLoad.registerMem2 = registerMemory2;
|
|
imlInstruction->op_storeLoad.copyWidth = copyWidth;
|
|
//imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0);
|
|
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
imlInstruction->op_storeLoad.flags2.signExtend = signExtend;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext_t* ppcImlGenContext, uint8 registerSource, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool switchEndian)
|
|
{
|
|
// load from memory
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_STORE;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_storeLoad.registerData = registerSource;
|
|
imlInstruction->op_storeLoad.registerMem = registerMemory;
|
|
imlInstruction->op_storeLoad.immS32 = immS32;
|
|
imlInstruction->op_storeLoad.copyWidth = copyWidth;
|
|
//imlInstruction->op_storeLoad.flags = (switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0);
|
|
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
imlInstruction->op_storeLoad.flags2.signExtend = false;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian)
|
|
{
|
|
// load from memory
|
|
PPCRecImlInstruction_t* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_STORE_INDEXED;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_storeLoad.registerData = registerDestination;
|
|
imlInstruction->op_storeLoad.registerMem = registerMemory1;
|
|
imlInstruction->op_storeLoad.registerMem2 = registerMemory2;
|
|
imlInstruction->op_storeLoad.copyWidth = copyWidth;
|
|
//imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0);
|
|
imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian;
|
|
imlInstruction->op_storeLoad.flags2.signExtend = signExtend;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_memory_memory(ppcImlGenContext_t* ppcImlGenContext, PPCRecImlInstruction_t* imlInstruction, uint8 srcMemReg, sint32 srcImmS32, uint8 dstMemReg, sint32 dstImmS32, uint8 copyWidth)
|
|
{
|
|
// copy from memory to memory
|
|
if(imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_MEM2MEM;
|
|
imlInstruction->operation = 0;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_mem2mem.src.registerMem = srcMemReg;
|
|
imlInstruction->op_mem2mem.src.immS32 = srcImmS32;
|
|
imlInstruction->op_mem2mem.dst.registerMem = dstMemReg;
|
|
imlInstruction->op_mem2mem.dst.immS32 = dstImmS32;
|
|
imlInstruction->op_mem2mem.copyWidth = copyWidth;
|
|
}
|
|
|
|
uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
if( mappedName == PPCREC_NAME_NONE )
|
|
{
|
|
debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(): Invalid mappedName parameter\n");
|
|
return PPC_REC_INVALID_REGISTER;
|
|
}
|
|
for(uint32 i=0; i<(PPC_REC_MAX_VIRTUAL_GPR-1); i++)
|
|
{
|
|
if( ppcImlGenContext->mappedRegister[i] == PPCREC_NAME_NONE )
|
|
{
|
|
ppcImlGenContext->mappedRegister[i] = mappedName;
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32 PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
for(uint32 i=0; i< PPC_REC_MAX_VIRTUAL_GPR; i++)
|
|
{
|
|
if( ppcImlGenContext->mappedRegister[i] == mappedName )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return PPC_REC_INVALID_REGISTER;
|
|
}
|
|
|
|
uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
if( mappedName == PPCREC_NAME_NONE )
|
|
{
|
|
debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(): Invalid mappedName parameter\n");
|
|
return PPC_REC_INVALID_REGISTER;
|
|
}
|
|
for(uint32 i=0; i<255; i++)
|
|
{
|
|
if( ppcImlGenContext->mappedFPRRegister[i] == PPCREC_NAME_NONE )
|
|
{
|
|
ppcImlGenContext->mappedFPRRegister[i] = mappedName;
|
|
return i;
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
uint32 PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
for(uint32 i=0; i<255; i++)
|
|
{
|
|
if( ppcImlGenContext->mappedFPRRegister[i] == mappedName )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
return PPC_REC_INVALID_REGISTER;
|
|
}
|
|
|
|
/*
|
|
* Loads a PPC gpr into any of the available IML registers
|
|
* If loadNew is false, it will reuse already loaded instances
|
|
*/
|
|
uint32 PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew)
|
|
{
|
|
if( loadNew == false )
|
|
{
|
|
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName);
|
|
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
|
|
return loadedRegisterIndex;
|
|
}
|
|
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName);
|
|
return registerIndex;
|
|
}
|
|
|
|
/*
|
|
* Reuse already loaded register if present
|
|
* Otherwise create new IML register and map the name. The register contents will be undefined
|
|
*/
|
|
uint32 PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName);
|
|
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
|
|
return loadedRegisterIndex;
|
|
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName);
|
|
return registerIndex;
|
|
}
|
|
|
|
/*
|
|
* Loads a PPC fpr into any of the available IML FPU registers
|
|
* If loadNew is false, it will check first if the fpr is already loaded into any IML register
|
|
*/
|
|
uint32 PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew)
|
|
{
|
|
if( loadNew == false )
|
|
{
|
|
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName);
|
|
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
|
|
return loadedRegisterIndex;
|
|
}
|
|
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName);
|
|
return registerIndex;
|
|
}
|
|
|
|
/*
|
|
* Checks if a PPC fpr register is already loaded into any IML register
|
|
* If no, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register)
|
|
*/
|
|
uint32 PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName)
|
|
{
|
|
uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName);
|
|
if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER )
|
|
return loadedRegisterIndex;
|
|
uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName);
|
|
return registerIndex;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_TW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
//#ifdef CEMU_DEBUG_ASSERT
|
|
// PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0);
|
|
//#endif
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MTSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 rD, spr1, spr2, spr;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2);
|
|
spr = spr1 | (spr2<<5);
|
|
if (spr == SPR_CTR || spr == SPR_LR)
|
|
{
|
|
uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
if (gprReg == PPC_REC_INVALID_REGISTER)
|
|
gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg);
|
|
}
|
|
else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7)
|
|
{
|
|
uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
if (gprReg == PPC_REC_INVALID_REGISTER)
|
|
gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg);
|
|
ppcImlGenContext->tracking.modifiesGQR[spr - SPR_UGQR0] = true;
|
|
}
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MFSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 rD, spr1, spr2, spr;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2);
|
|
spr = spr1 | (spr2<<5);
|
|
if (spr == SPR_LR || spr == SPR_CTR)
|
|
{
|
|
uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr);
|
|
uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg);
|
|
}
|
|
else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7)
|
|
{
|
|
uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr);
|
|
uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg);
|
|
}
|
|
else
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 rD, spr1, spr2, spr;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2);
|
|
spr = spr1 | (spr2<<5);
|
|
|
|
if (spr == 268 || spr == 269)
|
|
{
|
|
// TBL / TBU
|
|
uint32 param2 = spr | (rD << 16);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MFCR, gprReg, 0, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MTCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 rS;
|
|
uint32 crMask;
|
|
PPC_OPC_TEMPL_XFX(opcode, rS, crMask);
|
|
uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MTCRF, gprReg, crMask, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_CMP(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 cr;
|
|
int rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, cr, rA, rB);
|
|
cr >>= 2;
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_SIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_SIGNED);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_CMPL(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 cr;
|
|
int rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, cr, rA, rB);
|
|
cr >>= 2;
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_CMPI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 cr;
|
|
int rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, cr, rA, imm);
|
|
cr >>= 2;
|
|
sint32 b = imm;
|
|
// load gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_SIGNED, gprRegister, b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_SIGNED);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_CMPLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 cr;
|
|
int rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_UImm(opcode, cr, rA, imm);
|
|
cr >>= 2;
|
|
uint32 b = imm;
|
|
// load gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegister, (sint32)b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED);
|
|
}
|
|
|
|
bool PPCRecompiler_canInlineFunction(MPTR functionPtr, sint32* functionInstructionCount)
|
|
{
|
|
for (sint32 i = 0; i < 6; i++)
|
|
{
|
|
uint32 opcode = memory_readU32(functionPtr+i*4);
|
|
switch ((opcode >> 26))
|
|
{
|
|
case 14: // ADDI
|
|
case 15: // ADDIS
|
|
continue;
|
|
case 19: // opcode category 19
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 16:
|
|
if (opcode == 0x4E800020)
|
|
{
|
|
*functionInstructionCount = i;
|
|
return true; // BLR
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
case 32: // LWZ
|
|
case 33: // LWZU
|
|
case 34: // LBZ
|
|
case 35: // LBZU
|
|
case 36: // STW
|
|
case 37: // STWU
|
|
case 38: // STB
|
|
case 39: // STBU
|
|
case 40: // LHZ
|
|
case 41: // LHZU
|
|
case 42: // LHA
|
|
case 43: // LHAU
|
|
case 44: // STH
|
|
case 45: // STHU
|
|
case 46: // LMW
|
|
case 47: // STMW
|
|
case 48: // LFS
|
|
case 49: // LFSU
|
|
case 50: // LFD
|
|
case 51: // LFDU
|
|
case 52: // STFS
|
|
case 53: // STFSU
|
|
case 54: // STFD
|
|
case 55: // STFDU
|
|
continue;
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PPCRecompiler_generateInlinedCode(ppcImlGenContext_t* ppcImlGenContext, uint32 startAddress, sint32 instructionCount)
|
|
{
|
|
for (sint32 i = 0; i < instructionCount; i++)
|
|
{
|
|
ppcImlGenContext->ppcAddressOfCurrentInstruction = startAddress + i*4;
|
|
ppcImlGenContext->cyclesSinceLastBranch++;
|
|
if (PPCRecompiler_decodePPCInstruction(ppcImlGenContext))
|
|
{
|
|
assert_dbg();
|
|
}
|
|
}
|
|
// add range
|
|
ppcRecRange_t recRange;
|
|
recRange.ppcAddress = startAddress;
|
|
recRange.ppcSize = instructionCount*4 + 4; // + 4 because we have to include the BLR
|
|
ppcImlGenContext->functionRef->list_ranges.push_back(recRange);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_B(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 li;
|
|
PPC_OPC_TEMPL_I(opcode, li);
|
|
uint32 jumpAddressDest = li;
|
|
if( (opcode&PPC_OPC_AA) == 0 )
|
|
{
|
|
jumpAddressDest = li + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction;
|
|
}
|
|
if( opcode&PPC_OPC_LK )
|
|
{
|
|
// function call
|
|
// check if function can be inlined
|
|
sint32 inlineFuncInstructionCount = 0;
|
|
if (PPCRecompiler_canInlineFunction(jumpAddressDest, &inlineFuncInstructionCount))
|
|
{
|
|
// generate NOP iml instead of BL macro (this assures that segment PPC range remains intact)
|
|
PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext, NULL);
|
|
//cemuLog_log(LogType::Force, "Inline func 0x{:08x} at {:08x}", jumpAddressDest, ppcImlGenContext->ppcAddressOfCurrentInstruction);
|
|
uint32* prevInstructionPtr = ppcImlGenContext->currentInstruction;
|
|
ppcImlGenContext->currentInstruction = (uint32*)memory_getPointerFromVirtualOffset(jumpAddressDest);
|
|
PPCRecompiler_generateInlinedCode(ppcImlGenContext, jumpAddressDest, inlineFuncInstructionCount);
|
|
ppcImlGenContext->currentInstruction = prevInstructionPtr;
|
|
return true;
|
|
}
|
|
// generate funtion call instructions
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4);
|
|
return true;
|
|
}
|
|
// is jump destination within recompiled function?
|
|
if( jumpAddressDest >= ppcImlGenContext->functionRef->ppcAddress && jumpAddressDest < (ppcImlGenContext->functionRef->ppcAddress + ppcImlGenContext->functionRef->ppcSize) )
|
|
{
|
|
// generate instruction
|
|
PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext, NULL, jumpAddressDest);
|
|
}
|
|
else
|
|
{
|
|
// todo: Inline this jump destination if possible (in many cases it's a bunch of GPR/FPR store instructions + BLR)
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_B(opcode, BO, BI, BD);
|
|
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
uint32 jumpCondition = 0;
|
|
bool conditionMustBeTrue = (BO&8)!=0;
|
|
bool useDecrementer = (BO&4)==0; // bit not set -> decrement
|
|
bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0
|
|
bool ignoreCondition = (BO&16)!=0;
|
|
|
|
uint32 jumpAddressDest = BD;
|
|
if( (opcode&PPC_OPC_AA) == 0 )
|
|
{
|
|
jumpAddressDest = BD + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction;
|
|
}
|
|
|
|
if( opcode&PPC_OPC_LK )
|
|
{
|
|
// conditional function calls are not supported
|
|
if( ignoreCondition == false )
|
|
{
|
|
// generate jump condition
|
|
if( conditionMustBeTrue )
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_GE;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_LE;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NE;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_L;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_G;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_E;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW;
|
|
}
|
|
// generate instruction
|
|
//PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0);
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, !conditionMustBeTrue);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// generate iml instructions depending on flags
|
|
if( useDecrementer )
|
|
{
|
|
if( ignoreCondition == false )
|
|
return false; // not supported for the moment
|
|
uint32 ctrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_CTR, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUB, ctrRegister, 1, 0, false, false, PPCREC_CR_REG_TEMP, PPCREC_CR_MODE_ARITHMETIC);
|
|
if( decrementerMustBeZero )
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, PPCREC_JUMP_CONDITION_E, PPCREC_CR_REG_TEMP, 0, false);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, PPCREC_JUMP_CONDITION_NE, PPCREC_CR_REG_TEMP, 0, false);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// branch always, no condition and no decrementer
|
|
debugBreakpoint();
|
|
crRegister = PPC_REC_INVALID_REGISTER; // not necessary but lets optimizer know we dont care for cr register on this instruction
|
|
}
|
|
else
|
|
{
|
|
// generate jump condition
|
|
if( conditionMustBeTrue )
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_GE;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_LE;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NE;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_L;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_G;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_E;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW;
|
|
}
|
|
|
|
if (jumpAddressDest >= ppcImlGenContext->functionRef->ppcAddress && jumpAddressDest < (ppcImlGenContext->functionRef->ppcAddress + ppcImlGenContext->functionRef->ppcSize))
|
|
{
|
|
// near jump
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, jumpAddressDest, jumpCondition, crRegister, crBit, conditionMustBeTrue);
|
|
}
|
|
else
|
|
{
|
|
// far jump
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, jumpCondition, crRegister, crBit, !conditionMustBeTrue);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BCLR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_XL(opcode, BO, BI, BD);
|
|
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
|
|
uint32 jumpCondition = 0;
|
|
|
|
bool conditionMustBeTrue = (BO&8)!=0;
|
|
bool useDecrementer = (BO&4)==0; // bit not set -> decrement
|
|
bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0
|
|
bool ignoreCondition = (BO&16)!=0;
|
|
bool saveLR = (opcode&PPC_OPC_LK)!=0;
|
|
// since we skip this instruction if the condition is true, we need to invert the logic
|
|
bool invertedConditionMustBeTrue = !conditionMustBeTrue;
|
|
if( useDecrementer )
|
|
{
|
|
cemu_assert_debug(false);
|
|
return false; // unsupported
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// store LR
|
|
if( saveLR )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4);
|
|
}
|
|
else
|
|
{
|
|
// branch always, no condition and no decrementer
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// store LR
|
|
if( saveLR )
|
|
{
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// generate jump condition
|
|
if( invertedConditionMustBeTrue )
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_L;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_G;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_E;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_GE;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_LE;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NE;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW;
|
|
}
|
|
// jump if BCLR condition NOT met (jump to jumpmark of next instruction, essentially skipping current instruction)
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, invertedConditionMustBeTrue);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BCCTR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_XL(opcode, BO, BI, BD);
|
|
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
|
|
uint32 jumpCondition = 0;
|
|
|
|
bool conditionMustBeTrue = (BO&8)!=0;
|
|
bool useDecrementer = (BO&4)==0; // bit not set -> decrement
|
|
bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0
|
|
bool ignoreCondition = (BO&16)!=0;
|
|
bool saveLR = (opcode&PPC_OPC_LK)!=0;
|
|
// since we skip this instruction if the condition is true, we need to invert the logic
|
|
bool invertedConditionMustBeTrue = !conditionMustBeTrue;
|
|
if( useDecrementer )
|
|
{
|
|
assert_dbg();
|
|
// if added, dont forget inverted logic
|
|
debug_printf("Rec: BCLR unsupported decrementer\n");
|
|
return false; // unsupported
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// store LR
|
|
if( saveLR )
|
|
{
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4);
|
|
}
|
|
else
|
|
{
|
|
// branch always, no condition and no decrementer
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// store LR
|
|
if( saveLR )
|
|
{
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// generate jump condition
|
|
if( invertedConditionMustBeTrue )
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_L;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_G;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_E;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if( crBit == 0 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_GE;
|
|
else if( crBit == 1 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_LE;
|
|
else if( crBit == 2 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NE;
|
|
else if( crBit == 3 )
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW;
|
|
}
|
|
// jump if BCLR condition NOT met (jump to jumpmark of next instruction, essentially skipping current instruction)
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, invertedConditionMustBeTrue);
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ISYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// does not need to be translated
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// does not need to be translated
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB];
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; -> Update carry
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = hCPU->gpr[rA] + hCPU->gpr[rB] + ca;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//uint32 ca = hCPU->xer_ca;
|
|
//hCPU->gpr[rD] = a + ca;
|
|
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
// move rA to rD
|
|
if( registerRA != registerRD )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//uint32 ca = hCPU->xer_ca;
|
|
//hCPU->gpr[rD] = a + ca + -1;
|
|
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
// move rA to rD
|
|
if( registerRA != registerRD )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
//hCPU->gpr[rD] = (rA ? (int)hCPU->gpr[rA] : 0) + (int)imm;
|
|
if( rA != 0 )
|
|
{
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, imm);
|
|
}
|
|
else
|
|
{
|
|
// rA not used, instruction is value assignment
|
|
// rD = imm
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm);
|
|
if( rA != 0 )
|
|
{
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, (sint32)imm);
|
|
}
|
|
else
|
|
{
|
|
// rA not used, instruction turns into simple value assignment
|
|
// rD = imm
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// rD = rA + imm;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm);
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// this opcode is identical to ADDIC but additionally it updates CR0
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// rD = rA + imm;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
|
// rD = rB - rA
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + ca;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
if( rB != 0 )
|
|
debugBreakpoint();
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
|
// rD = rB - rA
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, registerRB);
|
|
if (opcode & PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//hCPU->gpr[rD] = ~a + imm + 1;
|
|
// cr0 is never affected
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, imm);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// mulli instruction does not modify any flags
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand, (sint32)imm);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = hCPU->gpr[rA] * hCPU->gpr[rB];
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_OE)
|
|
{
|
|
return false;
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = ((sint64)(sint32)hCPU->gpr[rA] * (sint64)(sint32)hCPU->gpr[rB])>>32;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (hCPU->gpr[rA] * hCPU->gpr[rB])>>32;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = (sint32)a / (sint32)b;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = (uint32)a / (uint32)b;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, SH, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
|
|
uint32 mask = ppc_mask(MB, ME);
|
|
//uint32 v = ppc_word_rotl(hCPU->gpr[rS], SH);
|
|
//hCPU->gpr[rA] = v & mask;
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// handle special forms of RLWINM
|
|
if( SH == 0 && SH == (ME-SH) && MB == 0 )
|
|
{
|
|
// CLRRWI
|
|
// todo
|
|
}
|
|
else if( ME == (31-SH) && MB == 0 )
|
|
{
|
|
// SLWI
|
|
if(opcode&PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH);
|
|
return true;
|
|
}
|
|
else if( SH == (32-MB) && ME == 31 )
|
|
{
|
|
// SRWI
|
|
if(opcode&PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB);
|
|
return true;
|
|
}
|
|
// general handler
|
|
if( registerRA != registerRS )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRA, registerRS);
|
|
if( SH != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, SH, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
if(opcode&PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
if( mask != 0xFFFFFFFF )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, SH, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// pack RLWIMI parameters into single integer
|
|
uint32 vImm = MB|(ME<<8)|(SH<<16);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RLWIMI, registerRA, registerRS, (sint32)vImm, PPC_REC_INVALID_REGISTER, 0);
|
|
if (opcode & PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRA, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME);
|
|
// uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]);
|
|
uint32 mask = ppc_mask(MB, ME);
|
|
// uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]);
|
|
// hCPU->gpr[rA] = v & mask;
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, registerRS, registerRB);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
if( mask != 0xFFFFFFFF )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRAW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
//uint32 SH = hCPU->gpr[rB] & 0x3f;
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS];
|
|
//hCPU->xer_ca = 0;
|
|
//if (hCPU->gpr[rA] & 0x80000000) {
|
|
// uint32 ca = 0;
|
|
// for (uint32 i=0; i < SH; i++) {
|
|
// if (hCPU->gpr[rA] & 1) ca = 1;
|
|
// hCPU->gpr[rA] >>= 1;
|
|
// hCPU->gpr[rA] |= 0x80000000;
|
|
// }
|
|
// if (ca) hCPU->xer_ca = 1;
|
|
//} else {
|
|
// if (SH > 31) {
|
|
// hCPU->gpr[rA] = 0;
|
|
// } else {
|
|
// hCPU->gpr[rA] >>= SH;
|
|
// }
|
|
//}
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rA]);
|
|
//}
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( (opcode&PPC_OPC_RC) != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRAWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA;
|
|
uint32 SH;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, SH);
|
|
cemu_assert_debug(SH < 32);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
PPC_ASSERT(rB==0);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if ( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if ( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
PPC_ASSERT(rB==0);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
return false;
|
|
}
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_CNTLZW, registerRA, registerRS);
|
|
|
|
//uint32 n=0;
|
|
//uint32 x=0x80000000;
|
|
//uint32 v=hCPU->gpr[rS];
|
|
//while (!(v & x)) {
|
|
// n++;
|
|
// if (n==32) break;
|
|
// x>>=1;
|
|
//}
|
|
//hCPU->gpr[rA] = n;
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rA]);
|
|
//}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//hCPU->gpr[rD] = -((signed int)hCPU->gpr[rA]);
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
|
//}
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LWZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 32, false, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LWZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// add imm to memory register
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LHA(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, true, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LHAU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// add imm to memory register
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, true, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LHZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
// note: Darksiders 2 has this instruction form but it is never executed.
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, false, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LHZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// add imm to memory register
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load byte
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 8, false, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LBZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// add imm to memory register
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load byte
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 8, false, true);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LWZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
return false;
|
|
}
|
|
// hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]);
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LWZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
return false;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// add rB to rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
// load word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 32, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = 0;
|
|
if( rA )
|
|
gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
if (destinationRegister == PPC_REC_INVALID_REGISTER)
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register
|
|
// load word
|
|
if( rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false, false);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LHAX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, true, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LHAUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// add rB to rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
// load half word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, true, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LHZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LHZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// add rB to rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
// load hald word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, false, true);
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LHBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false) : 0;
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
if (destinationRegister == PPC_REC_INVALID_REGISTER)
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register
|
|
// load half word (little-endian)
|
|
if (rA == 0)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 16, false, false);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, false);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LBZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// special case where rA is ignored and only rB is used
|
|
return false;
|
|
}
|
|
// hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]);
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load byte
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 8, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LBZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
if (rA == 0)
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD);
|
|
if (destinationRegister == PPC_REC_INVALID_REGISTER)
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register
|
|
// add rB to rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
// load byte
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 8, false, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LWARX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rD, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, rB);
|
|
// load memory rA and rB into register
|
|
uint32 gprRegisterA = rA != 0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0;
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load word
|
|
if( rA != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_LOAD_LWARX_MARKER, false, true);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_LOAD_LWARX_MARKER, false, true);
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
//uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm;
|
|
sint32 index = 0;
|
|
while( rD <= 31 )
|
|
{
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load word
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm+index*4, 32, false, true);
|
|
// next
|
|
rD++;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
// note: Darksiders 2 has this instruction form but it is never executed.
|
|
//PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 32, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// store&update instructions where rD==rA store the register contents without added imm, therefore we need to handle it differently
|
|
// get memory gpr register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// get source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister
|
|
// add imm to memory register early if possible
|
|
if( rD != rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 32, true);
|
|
// add imm to memory register late if we couldn't do it early
|
|
if( rD == rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 16, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STHU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// get memory gpr register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// get source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister
|
|
// add imm to memory register early if possible
|
|
if( rD != rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 16, true);
|
|
// add imm to memory register late if we couldn't do it early
|
|
if( rD == rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rS;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister
|
|
// store byte
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 8, true);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STBU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return;
|
|
}
|
|
// get memory gpr register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// get source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister
|
|
// add imm to memory register early if possible
|
|
if( rD != rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
// store byte
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 8, true);
|
|
// add imm to memory register late if we couldn't do it early
|
|
if( rD == rA )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
// generic indexed store (STWX, STHX, STBX, STWUX. If bitReversed == true -> STHBRX)
|
|
bool PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth, bool byteReversed = false)
|
|
{
|
|
sint32 rA, rS, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// prepare registers
|
|
uint32 gprRegisterA;
|
|
if(rA != 0)
|
|
gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// store word
|
|
if (rA == 0)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, storeBitWidth, !byteReversed);
|
|
}
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, !byteReversed);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth)
|
|
{
|
|
sint32 rA, rS, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
if( rA == 0 )
|
|
{
|
|
// not supported
|
|
return false;
|
|
}
|
|
if( rS == rA || rS == rB )
|
|
{
|
|
// prepare registers
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, true);
|
|
// update EA after store
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
return true;
|
|
}
|
|
// prepare registers
|
|
uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// update EA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB);
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegisterA, 0, storeBitWidth, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_STWCX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rS, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// prepare registers
|
|
uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0;
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// store word
|
|
if( rA != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_STORE_STWCX_MARKER, false, true);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_STORE_STWCX_MARKER, true);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_STWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rS, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// prepare registers
|
|
uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0;
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// store word
|
|
if( rA != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false);
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_STMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm);
|
|
sint32 index = 0;
|
|
while( rS <= 31 )
|
|
{
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm+index*4, 32, true);
|
|
// next
|
|
rS++;
|
|
index++;
|
|
}
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_LSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD, nb;
|
|
PPC_OPC_TEMPL_X(opcode, rD, rA, nb);
|
|
if( nb == 0 )
|
|
nb = 32;
|
|
if( nb == 4 )
|
|
{
|
|
// if nb == 4 this instruction immitates LWZ
|
|
if( rA == 0 )
|
|
{
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg(); // special form where gpr is ignored and only imm is used
|
|
#endif
|
|
return false;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true);
|
|
return true;
|
|
}
|
|
else if( nb == 2 )
|
|
{
|
|
// if nb == 2 this instruction immitates a LHZ but the result is shifted left by 16 bits
|
|
if( rA == 0 )
|
|
{
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg(); // special form where gpr is ignored and only imm is used
|
|
#endif
|
|
return false;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true);
|
|
// shift
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, destinationRegister, destinationRegister, 16);
|
|
return true;
|
|
}
|
|
else if( nb == 3 )
|
|
{
|
|
// if nb == 3 this instruction loads a 3-byte big-endian and the result is shifted left by 8 bits
|
|
if( rA == 0 )
|
|
{
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg(); // special form where gpr is ignored and only imm is used
|
|
#endif
|
|
return false;
|
|
}
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if destination register is already loaded
|
|
uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( destinationRegister == PPC_REC_INVALID_REGISTER )
|
|
destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register
|
|
// load half
|
|
PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, PPC_REC_STORE_LSWI_3, false, true);
|
|
return true;
|
|
}
|
|
debug_printf("PPCRecompilerImlGen_LSWI(): Unsupported nb value %d\n", nb);
|
|
return false;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rS, nb;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, nb);
|
|
if( nb == 0 )
|
|
nb = 32;
|
|
if( nb == 4 )
|
|
{
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister
|
|
// store word
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, 32, true);
|
|
return true;
|
|
}
|
|
else if( nb == 2 )
|
|
{
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister
|
|
// store half-word (shifted << 16)
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_2, false);
|
|
return true;
|
|
}
|
|
else if( nb == 3 )
|
|
{
|
|
// load memory gpr into register
|
|
uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// load source register
|
|
uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister
|
|
// store 3-byte-word (shifted << 8)
|
|
PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_3, false);
|
|
return true;
|
|
}
|
|
debug_printf("PPCRecompilerImlGen_STSWI(): Unsupported nb value %d\n", nb);
|
|
return false;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_DCBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rA, rB;
|
|
rA = (opcode>>16)&0x1F;
|
|
rB = (opcode>>11)&0x1F;
|
|
// prepare registers
|
|
uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0;
|
|
uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
// store
|
|
if( rA != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterA, gprRegisterB);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterB, gprRegisterB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_OR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// check for MR mnemonic
|
|
if( rS == rB )
|
|
{
|
|
// simple register copy
|
|
if( rA != rS ) // check if no-op
|
|
{
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
// no effect but CR is updated
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprSourceReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
// no-op
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS | rA
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
|
|
{
|
|
// make sure we don't overwrite rS or rA
|
|
if( gprSource1Reg == gprDestReg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
// fixme: merge CR update into OR instruction above
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS
|
|
if( gprDestReg != gprSource1Reg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
|
|
}
|
|
// rA |= rB
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// hCPU->gpr[rA] = hCPU->gpr[rS] | ~hCPU->gpr[rB];
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
//hCPU->gpr[rA] = ~(hCPU->gpr[rS] | hCPU->gpr[rB]);
|
|
// check for NOT mnemonic
|
|
if( rS == rB )
|
|
{
|
|
// simple register copy with NOT
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS | rA
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
|
|
{
|
|
// make sure we don't overwrite rS or rA
|
|
if( gprSource1Reg == gprDestReg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg);
|
|
}
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
// fixme: merge CR update into OR instruction above
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS
|
|
if( gprDestReg != gprSource1Reg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
|
|
}
|
|
// rA |= rB
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_AND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
// check for MR mnemonic
|
|
if( rS == rB )
|
|
{
|
|
// simple register copy
|
|
if( rA != rS ) // check if no-op
|
|
{
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_unimplemented(); // no-op -> verify this case
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS & rA
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
|
|
{
|
|
// make sure we don't overwrite rS or rA
|
|
if( gprSource1Reg == gprDestReg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource1Reg);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS
|
|
if( gprDestReg != gprSource1Reg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
|
|
}
|
|
// rA &= rB
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS] & ~hCPU->gpr[rB];
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
if( rS == rB )
|
|
{
|
|
// result is always 0 -> replace with XOR rA,rA
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg);
|
|
}
|
|
}
|
|
else if( rA == rB )
|
|
{
|
|
// rB already in rA, therefore we complement rA first and then AND it with rS
|
|
sint32 gprRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = ~rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA);
|
|
// rA &= rS
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a & (~b) is the same as ~((~a) | b)
|
|
sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
sint32 gprRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprRS = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
// move rS to rA (if required)
|
|
if( gprRA != gprRS )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprRA, gprRS);
|
|
}
|
|
// rS already in rA, therefore we complement rS first and then OR it with rB
|
|
// rA = ~rA
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA);
|
|
// rA |= rB
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprRA, gprRB);
|
|
// rA = ~rA
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_ANDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm);
|
|
// ANDI. always sets cr0 flags
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA &= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm);
|
|
// ANDI. always sets cr0 flags
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA &= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
if( rS == rB )
|
|
{
|
|
// xor register with itself
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS ^ rA
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
|
|
{
|
|
// make sure we don't overwrite rS or rA
|
|
if( gprSource1Reg == gprDestReg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS
|
|
if( gprDestReg != gprSource1Reg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
|
|
}
|
|
// rA ^= rB
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PPCRecompilerImlGen_EQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
if( rS == rB )
|
|
{
|
|
// xor register with itself, then invert
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
|
|
}
|
|
else
|
|
{
|
|
// rA = ~(rS ^ rA)
|
|
sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg )
|
|
{
|
|
// make sure we don't overwrite rS or rA
|
|
if( gprSource1Reg == gprDestReg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// rA = rS
|
|
if( gprDestReg != gprSource1Reg )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg);
|
|
}
|
|
// rA ^= rB
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_ORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm);
|
|
// ORI does not set cr0 flags
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS] | imm;
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA |= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm);
|
|
// ORI does not set cr0 flags
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS] | imm;
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA |= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_XORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm);
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm;
|
|
// XORI does not set cr0 flags
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA |= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
void PPCRecompilerImlGen_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm);
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm;
|
|
// XORIS does not set cr0 flags
|
|
sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS);
|
|
sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// rA = rS
|
|
if( gprDestReg != gprSourceReg )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg);
|
|
// rA |= imm32
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CROR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_OR, crD, crA, crB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CRORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ORC, crD, crA, crB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CRAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_AND, crD, crA, crB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CRANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ANDC, crD, crA, crB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CRXOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
if (crA == crB)
|
|
{
|
|
// both operands equal, clear bit in crD
|
|
// PPC's assert() uses this to pass a parameter to OSPanic
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_CLEAR, crD, 0, 0);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CREQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int crD, crA, crB;
|
|
PPC_OPC_TEMPL_X(opcode, crD, crA, crB);
|
|
if (crA == crB)
|
|
{
|
|
// both operands equal, set bit in crD
|
|
PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_SET, crD, 0, 0);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
return false;
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_HLE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
uint32 hleFuncId = opcode&0xFFFF;
|
|
PPCRecompilerImlGen_generateNewInstruction_macro(ppcImlGenContext, PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0);
|
|
return true;
|
|
}
|
|
|
|
uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction));
|
|
ppcImlGenContext->currentInstruction += 1;
|
|
return v;
|
|
}
|
|
|
|
uint32 PPCRecompiler_getInstructionByOffset(ppcImlGenContext_t* ppcImlGenContext, uint32 offset)
|
|
{
|
|
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction + offset/4));
|
|
return v;
|
|
}
|
|
|
|
uint32 PPCRecompiler_getCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction));
|
|
return v;
|
|
}
|
|
|
|
uint32 PPCRecompiler_getPreviousInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction-1));
|
|
return v;
|
|
}
|
|
|
|
char _tempOpcodename[32];
|
|
|
|
const char* PPCRecompiler_getOpcodeDebugName(PPCRecImlInstruction_t* iml)
|
|
{
|
|
uint32 op = iml->operation;
|
|
if (op == PPCREC_IML_OP_ASSIGN)
|
|
return "MOV";
|
|
else if (op == PPCREC_IML_OP_ADD)
|
|
return "ADD";
|
|
else if (op == PPCREC_IML_OP_SUB)
|
|
return "SUB";
|
|
else if (op == PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY)
|
|
return "ADDCSC";
|
|
else if (op == PPCREC_IML_OP_OR)
|
|
return "OR";
|
|
else if (op == PPCREC_IML_OP_AND)
|
|
return "AND";
|
|
else if (op == PPCREC_IML_OP_XOR)
|
|
return "XOR";
|
|
else if (op == PPCREC_IML_OP_LEFT_SHIFT)
|
|
return "LSH";
|
|
else if (op == PPCREC_IML_OP_RIGHT_SHIFT)
|
|
return "RSH";
|
|
else if (op == PPCREC_IML_OP_MULTIPLY_SIGNED)
|
|
return "MULS";
|
|
else if (op == PPCREC_IML_OP_DIVIDE_SIGNED)
|
|
return "DIVS";
|
|
|
|
sprintf(_tempOpcodename, "OP0%02x_T%d", iml->operation, iml->type);
|
|
return _tempOpcodename;
|
|
}
|
|
|
|
void PPCRecDebug_addRegisterParam(StringBuf& strOutput, sint32 virtualRegister, bool isLast = false)
|
|
{
|
|
if (isLast)
|
|
{
|
|
if (virtualRegister < 10)
|
|
strOutput.addFmt("t{} ", virtualRegister);
|
|
else
|
|
strOutput.addFmt("t{}", virtualRegister);
|
|
return;
|
|
}
|
|
if (virtualRegister < 10)
|
|
strOutput.addFmt("t{} , ", virtualRegister);
|
|
else
|
|
strOutput.addFmt("t{}, ", virtualRegister);
|
|
}
|
|
|
|
void PPCRecDebug_addS32Param(StringBuf& strOutput, sint32 val, bool isLast = false)
|
|
{
|
|
if (isLast)
|
|
{
|
|
strOutput.addFmt("0x{:08x}", val);
|
|
return;
|
|
}
|
|
strOutput.addFmt("0x{:08x}, ", val);
|
|
}
|
|
|
|
void PPCRecompilerDebug_printLivenessRangeInfo(StringBuf& currentLineText, PPCRecImlSegment_t* imlSegment, sint32 offset)
|
|
{
|
|
// pad to 70 characters
|
|
sint32 index = currentLineText.getLen();
|
|
while (index < 70)
|
|
{
|
|
debug_printf(" ");
|
|
index++;
|
|
}
|
|
raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges;
|
|
while (subrangeItr)
|
|
{
|
|
if (offset == subrangeItr->start.index)
|
|
{
|
|
if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index)
|
|
{
|
|
debug_printf("*%-2d", subrangeItr->range->virtualRegister);
|
|
}
|
|
else
|
|
{
|
|
debug_printf("|%-2d", subrangeItr->range->virtualRegister);
|
|
}
|
|
}
|
|
else if (false)//subrange->isDirtied && i == subrange->becomesDirtyAtIndex.index )
|
|
{
|
|
debug_printf("* ");
|
|
}
|
|
else if (offset >= subrangeItr->start.index && offset < subrangeItr->end.index)
|
|
{
|
|
debug_printf("| ");
|
|
}
|
|
else
|
|
{
|
|
debug_printf(" ");
|
|
}
|
|
index += 3;
|
|
// next
|
|
subrangeItr = subrangeItr->link_segmentSubrangesGPR.next;
|
|
}
|
|
}
|
|
|
|
void PPCRecompiler_dumpIMLSegment(PPCRecImlSegment_t* imlSegment, sint32 segmentIndex, bool printLivenessRangeInfo)
|
|
{
|
|
StringBuf strOutput(1024);
|
|
|
|
strOutput.addFmt("SEGMENT 0x{:04x} 0x{:08x} PPC 0x{:08x} - 0x{:08x} Loop-depth {}", segmentIndex, imlSegment->ppcAddress, imlSegment->ppcAddrMin, imlSegment->ppcAddrMax, imlSegment->loopDepth);
|
|
if (imlSegment->isEnterable)
|
|
{
|
|
strOutput.addFmt(" ENTERABLE (0x{:08x})", imlSegment->enterPPCAddress);
|
|
}
|
|
else if( imlSegment->isJumpDestination )
|
|
{
|
|
strOutput.addFmt(" JUMP-DEST (0x{:08x})", imlSegment->jumpDestinationPPCAddress);
|
|
}
|
|
|
|
debug_printf("%s\n", strOutput.c_str());
|
|
|
|
strOutput.reset();
|
|
strOutput.addFmt("SEGMENT NAME 0x{:016x}", (uintptr_t)imlSegment);
|
|
debug_printf("%s", strOutput.c_str());
|
|
|
|
if (printLivenessRangeInfo)
|
|
{
|
|
PPCRecompilerDebug_printLivenessRangeInfo(strOutput, imlSegment, RA_INTER_RANGE_START);
|
|
}
|
|
debug_printf("\n");
|
|
|
|
sint32 lineOffsetParameters = 18;
|
|
|
|
for(sint32 i=0; i<imlSegment->imlListCount; i++)
|
|
{
|
|
// don't log NOP instructions unless they have an associated PPC address
|
|
if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_NO_OP && imlSegment->imlList[i].associatedPPCAddress == MPTR_NULL)
|
|
continue;
|
|
strOutput.reset();
|
|
strOutput.addFmt("{:08x} ", imlSegment->imlList[i].associatedPPCAddress);
|
|
if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME || imlSegment->imlList[i].type == PPCREC_IML_TYPE_NAME_R)
|
|
{
|
|
if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_NAME)
|
|
strOutput.add("LD_NAME");
|
|
else
|
|
strOutput.add("ST_NAME");
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_name.registerIndex);
|
|
|
|
strOutput.addFmt("name_{} (", imlSegment->imlList[i].op_r_name.registerIndex, imlSegment->imlList[i].op_r_name.name);
|
|
if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_R0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_R0+999) )
|
|
{
|
|
strOutput.addFmt("r{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_R0);
|
|
}
|
|
else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_SPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_SPR0+999) )
|
|
{
|
|
strOutput.addFmt("spr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_SPR0);
|
|
}
|
|
else
|
|
strOutput.add("ukn");
|
|
strOutput.add(")");
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R )
|
|
{
|
|
strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList+i));
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r.registerResult);
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r.registerA, true);
|
|
|
|
if( imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER )
|
|
{
|
|
strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister);
|
|
}
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R_R )
|
|
{
|
|
strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i));
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerResult);
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerA);
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_r.registerB, true);
|
|
if( imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER )
|
|
{
|
|
strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister);
|
|
}
|
|
}
|
|
else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_R_S32)
|
|
{
|
|
strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i));
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_s32.registerResult);
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_r_s32.registerA);
|
|
PPCRecDebug_addS32Param(strOutput, imlSegment->imlList[i].op_r_r_s32.immS32, true);
|
|
|
|
if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER)
|
|
{
|
|
strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister);
|
|
}
|
|
}
|
|
else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_R_S32)
|
|
{
|
|
strOutput.addFmt("{}", PPCRecompiler_getOpcodeDebugName(imlSegment->imlList + i));
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_r_immS32.registerIndex);
|
|
PPCRecDebug_addS32Param(strOutput, imlSegment->imlList[i].op_r_immS32.immS32, true);
|
|
|
|
if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER)
|
|
{
|
|
strOutput.addFmt(" -> CR{}", imlSegment->imlList[i].crRegister);
|
|
}
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_JUMPMARK )
|
|
{
|
|
strOutput.addFmt("jm_{:08x}:", imlSegment->imlList[i].op_jumpmark.address);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_PPC_ENTER )
|
|
{
|
|
strOutput.addFmt("ppcEnter_{:08x}:", imlSegment->imlList[i].op_ppcEnter.ppcAddress);
|
|
}
|
|
else if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE ||
|
|
imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE_INDEXED )
|
|
{
|
|
if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD || imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED)
|
|
strOutput.add("LD_");
|
|
else
|
|
strOutput.add("ST_");
|
|
|
|
if (imlSegment->imlList[i].op_storeLoad.flags2.signExtend)
|
|
strOutput.add("S");
|
|
else
|
|
strOutput.add("U");
|
|
strOutput.addFmt("{}", imlSegment->imlList[i].op_storeLoad.copyWidth);
|
|
|
|
while ((sint32)strOutput.getLen() < lineOffsetParameters)
|
|
strOutput.add(" ");
|
|
|
|
PPCRecDebug_addRegisterParam(strOutput, imlSegment->imlList[i].op_storeLoad.registerData);
|
|
|
|
if(imlSegment->imlList[i].type == PPCREC_IML_TYPE_LOAD_INDEXED || imlSegment->imlList[i].type == PPCREC_IML_TYPE_STORE_INDEXED)
|
|
strOutput.addFmt("[t{}+t{}]", imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.registerMem2);
|
|
else
|
|
strOutput.addFmt("[t{}+{}]", imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32);
|
|
}
|
|
else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_MEM2MEM)
|
|
{
|
|
strOutput.addFmt("{} [t{}+{}] = [t{}+{}]", imlSegment->imlList[i].op_mem2mem.copyWidth, imlSegment->imlList[i].op_mem2mem.dst.registerMem, imlSegment->imlList[i].op_mem2mem.dst.immS32, imlSegment->imlList[i].op_mem2mem.src.registerMem, imlSegment->imlList[i].op_mem2mem.src.immS32);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_CJUMP )
|
|
{
|
|
if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_E)
|
|
strOutput.add("JE");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NE)
|
|
strOutput.add("JNE");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_G)
|
|
strOutput.add("JG");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_GE)
|
|
strOutput.add("JGE");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_L)
|
|
strOutput.add("JL");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_LE)
|
|
strOutput.add("JLE");
|
|
else if (imlSegment->imlList[i].op_conditionalJump.condition == PPCREC_JUMP_CONDITION_NONE)
|
|
strOutput.add("JALW"); // jump always
|
|
else
|
|
cemu_assert_unimplemented();
|
|
strOutput.addFmt(" jm_{:08x} (cr{})", imlSegment->imlList[i].op_conditionalJump.jumpmarkAddress, imlSegment->imlList[i].crRegister);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_NO_OP )
|
|
{
|
|
strOutput.add("NOP");
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_MACRO )
|
|
{
|
|
if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BLR )
|
|
{
|
|
strOutput.addFmt("MACRO BLR 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BLRL )
|
|
{
|
|
strOutput.addFmt("MACRO BLRL 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BCTR )
|
|
{
|
|
strOutput.addFmt("MACRO BCTR 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BCTRL )
|
|
{
|
|
strOutput.addFmt("MACRO BCTRL 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_BL )
|
|
{
|
|
strOutput.addFmt("MACRO BL 0x{:08x} -> 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_B_FAR )
|
|
{
|
|
strOutput.addFmt("MACRO B_FAR 0x{:08x} -> 0x{:08x} cycles (depr): {}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2, (sint32)imlSegment->imlList[i].op_macro.paramU16);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_LEAVE )
|
|
{
|
|
strOutput.addFmt("MACRO LEAVE ppc: 0x{:08x}", imlSegment->imlList[i].op_macro.param);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_HLE )
|
|
{
|
|
strOutput.addFmt("MACRO HLE ppcAddr: 0x{:08x} funcId: 0x{:08x}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_MFTB )
|
|
{
|
|
strOutput.addFmt("MACRO MFTB ppcAddr: 0x{:08x} sprId: 0x{:08x}", imlSegment->imlList[i].op_macro.param, imlSegment->imlList[i].op_macro.param2);
|
|
}
|
|
else if( imlSegment->imlList[i].operation == PPCREC_IML_MACRO_COUNT_CYCLES )
|
|
{
|
|
strOutput.addFmt("MACRO COUNT_CYCLES cycles: {}", imlSegment->imlList[i].op_macro.param);
|
|
}
|
|
else
|
|
{
|
|
strOutput.addFmt("MACRO ukn operation {}", imlSegment->imlList[i].operation);
|
|
}
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_NAME )
|
|
{
|
|
strOutput.addFmt("fpr_t{} = name_{} (", imlSegment->imlList[i].op_r_name.registerIndex, imlSegment->imlList[i].op_r_name.name);
|
|
if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_FPR0+999) )
|
|
{
|
|
strOutput.addFmt("fpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_FPR0);
|
|
}
|
|
else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0+999) )
|
|
{
|
|
strOutput.addFmt("tempFpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_TEMPORARY_FPR0);
|
|
}
|
|
else
|
|
strOutput.add("ukn");
|
|
strOutput.add(")");
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_NAME_R )
|
|
{
|
|
strOutput.addFmt("name_{} (", imlSegment->imlList[i].op_r_name.name);
|
|
if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_FPR0+999) )
|
|
{
|
|
strOutput.addFmt("fpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_FPR0);
|
|
}
|
|
else if( imlSegment->imlList[i].op_r_name.name >= PPCREC_NAME_TEMPORARY_FPR0 && imlSegment->imlList[i].op_r_name.name < (PPCREC_NAME_TEMPORARY_FPR0+999) )
|
|
{
|
|
strOutput.addFmt("tempFpr{}", imlSegment->imlList[i].op_r_name.name-PPCREC_NAME_TEMPORARY_FPR0);
|
|
}
|
|
else
|
|
strOutput.add("ukn");
|
|
strOutput.addFmt(") = fpr_t{}", imlSegment->imlList[i].op_r_name.registerIndex);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_LOAD )
|
|
{
|
|
strOutput.addFmt("fpr_t{} = ", imlSegment->imlList[i].op_storeLoad.registerData);
|
|
if( imlSegment->imlList[i].op_storeLoad.flags2.signExtend )
|
|
strOutput.add("S");
|
|
else
|
|
strOutput.add("U");
|
|
strOutput.addFmt("{} [t{}+{}] mode {}", imlSegment->imlList[i].op_storeLoad.copyWidth / 8, imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32, imlSegment->imlList[i].op_storeLoad.mode);
|
|
if (imlSegment->imlList[i].op_storeLoad.flags2.notExpanded)
|
|
{
|
|
strOutput.addFmt(" <No expand>");
|
|
}
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_STORE )
|
|
{
|
|
if( imlSegment->imlList[i].op_storeLoad.flags2.signExtend )
|
|
strOutput.add("S");
|
|
else
|
|
strOutput.add("U");
|
|
strOutput.addFmt("{} [t{}+{}]", imlSegment->imlList[i].op_storeLoad.copyWidth/8, imlSegment->imlList[i].op_storeLoad.registerMem, imlSegment->imlList[i].op_storeLoad.immS32);
|
|
strOutput.addFmt("= fpr_t{} mode {}\n", imlSegment->imlList[i].op_storeLoad.registerData, imlSegment->imlList[i].op_storeLoad.mode);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R )
|
|
{
|
|
strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i]));
|
|
strOutput.addFmt("fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r.registerOperand);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R_R_R )
|
|
{
|
|
strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i]));
|
|
strOutput.addFmt("fpr{:02d}, fpr{:02d}, fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandA, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandB, imlSegment->imlList[i].op_fpr_r_r_r_r.registerOperandC);
|
|
}
|
|
else if( imlSegment->imlList[i].type == PPCREC_IML_TYPE_FPR_R_R_R )
|
|
{
|
|
strOutput.addFmt("{:-6} ", PPCRecompiler_getOpcodeDebugName(&imlSegment->imlList[i]));
|
|
strOutput.addFmt("fpr{:02d}, fpr{:02d}, fpr{:02d}", imlSegment->imlList[i].op_fpr_r_r_r.registerResult, imlSegment->imlList[i].op_fpr_r_r_r.registerOperandA, imlSegment->imlList[i].op_fpr_r_r_r.registerOperandB);
|
|
}
|
|
else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK)
|
|
{
|
|
strOutput.addFmt("CYCLE_CHECK jm_{:08x}\n", imlSegment->imlList[i].op_conditionalJump.jumpmarkAddress);
|
|
}
|
|
else if (imlSegment->imlList[i].type == PPCREC_IML_TYPE_CONDITIONAL_R_S32)
|
|
{
|
|
strOutput.addFmt("t{} ", imlSegment->imlList[i].op_conditional_r_s32.registerIndex);
|
|
bool displayAsHex = false;
|
|
if (imlSegment->imlList[i].operation == PPCREC_IML_OP_ASSIGN)
|
|
{
|
|
displayAsHex = true;
|
|
strOutput.add("=");
|
|
}
|
|
else
|
|
strOutput.addFmt("(unknown operation CONDITIONAL_R_S32 {})", imlSegment->imlList[i].operation);
|
|
if (displayAsHex)
|
|
strOutput.addFmt(" 0x{:x}", imlSegment->imlList[i].op_conditional_r_s32.immS32);
|
|
else
|
|
strOutput.addFmt(" {}", imlSegment->imlList[i].op_conditional_r_s32.immS32);
|
|
strOutput.add(" (conditional)");
|
|
if (imlSegment->imlList[i].crRegister != PPC_REC_INVALID_REGISTER)
|
|
{
|
|
strOutput.addFmt(" -> and update CR{}", imlSegment->imlList[i].crRegister);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
strOutput.addFmt("Unknown iml type {}", imlSegment->imlList[i].type);
|
|
}
|
|
debug_printf("%s", strOutput.c_str());
|
|
if (printLivenessRangeInfo)
|
|
{
|
|
PPCRecompilerDebug_printLivenessRangeInfo(strOutput, imlSegment, i);
|
|
}
|
|
debug_printf("\n");
|
|
}
|
|
// all ranges
|
|
if (printLivenessRangeInfo)
|
|
{
|
|
debug_printf("Ranges-VirtReg ");
|
|
raLivenessSubrange_t* subrangeItr = imlSegment->raInfo.linkedList_allSubranges;
|
|
while(subrangeItr)
|
|
{
|
|
debug_printf("v%-2d", subrangeItr->range->virtualRegister);
|
|
subrangeItr = subrangeItr->link_segmentSubrangesGPR.next;
|
|
}
|
|
debug_printf("\n");
|
|
debug_printf("Ranges-PhysReg ");
|
|
subrangeItr = imlSegment->raInfo.linkedList_allSubranges;
|
|
while (subrangeItr)
|
|
{
|
|
debug_printf("p%-2d", subrangeItr->range->physicalRegister);
|
|
subrangeItr = subrangeItr->link_segmentSubrangesGPR.next;
|
|
}
|
|
debug_printf("\n");
|
|
}
|
|
// branch info
|
|
debug_printf("Links from: ");
|
|
for (sint32 i = 0; i < imlSegment->list_prevSegments.size(); i++)
|
|
{
|
|
if (i)
|
|
debug_printf(", ");
|
|
debug_printf("%p", (void*)imlSegment->list_prevSegments[i]);
|
|
}
|
|
debug_printf("\n");
|
|
debug_printf("Links to: ");
|
|
if (imlSegment->nextSegmentBranchNotTaken)
|
|
debug_printf("%p (no branch), ", (void*)imlSegment->nextSegmentBranchNotTaken);
|
|
if (imlSegment->nextSegmentBranchTaken)
|
|
debug_printf("%p (branch)", (void*)imlSegment->nextSegmentBranchTaken);
|
|
debug_printf("\n");
|
|
}
|
|
|
|
void PPCRecompiler_dumpIML(PPCRecFunction_t* PPCRecFunction, ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
for (size_t i = 0; i < ppcImlGenContext->segmentList2.size(); i++)
|
|
{
|
|
PPCRecompiler_dumpIMLSegment(ppcImlGenContext->segmentList2[i], i);
|
|
debug_printf("\n");
|
|
}
|
|
}
|
|
|
|
void PPCRecompilerIml_setSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint, PPCRecImlSegment_t* imlSegment, sint32 index)
|
|
{
|
|
segmentPoint->imlSegment = imlSegment;
|
|
segmentPoint->index = index;
|
|
if (imlSegment->segmentPointList)
|
|
imlSegment->segmentPointList->prev = segmentPoint;
|
|
segmentPoint->prev = nullptr;
|
|
segmentPoint->next = imlSegment->segmentPointList;
|
|
imlSegment->segmentPointList = segmentPoint;
|
|
}
|
|
|
|
void PPCRecompilerIml_removeSegmentPoint(ppcRecompilerSegmentPoint_t* segmentPoint)
|
|
{
|
|
if (segmentPoint->prev)
|
|
segmentPoint->prev->next = segmentPoint->next;
|
|
else
|
|
segmentPoint->imlSegment->segmentPointList = segmentPoint->next;
|
|
if (segmentPoint->next)
|
|
segmentPoint->next->prev = segmentPoint->prev;
|
|
}
|
|
|
|
/*
|
|
* Insert multiple no-op instructions
|
|
* Warning: Can invalidate any previous instruction structs from the same segment
|
|
*/
|
|
void PPCRecompiler_pushBackIMLInstructions(PPCRecImlSegment_t* imlSegment, sint32 index, sint32 shiftBackCount)
|
|
{
|
|
cemu_assert(index >= 0 && index <= imlSegment->imlListCount);
|
|
|
|
if (imlSegment->imlListCount + shiftBackCount > imlSegment->imlListSize)
|
|
{
|
|
sint32 newSize = imlSegment->imlListCount + shiftBackCount + std::max(2, imlSegment->imlListSize/2);
|
|
imlSegment->imlList = (PPCRecImlInstruction_t*)realloc(imlSegment->imlList, sizeof(PPCRecImlInstruction_t)*newSize);
|
|
imlSegment->imlListSize = newSize;
|
|
}
|
|
for (sint32 i = (sint32)imlSegment->imlListCount - 1; i >= index; i--)
|
|
{
|
|
memcpy(imlSegment->imlList + (i + shiftBackCount), imlSegment->imlList + i, sizeof(PPCRecImlInstruction_t));
|
|
}
|
|
// fill empty space with NOP instructions
|
|
for (sint32 i = 0; i < shiftBackCount; i++)
|
|
{
|
|
imlSegment->imlList[index + i].type = PPCREC_IML_TYPE_NONE;
|
|
}
|
|
imlSegment->imlListCount += shiftBackCount;
|
|
|
|
if (imlSegment->segmentPointList)
|
|
{
|
|
ppcRecompilerSegmentPoint_t* segmentPoint = imlSegment->segmentPointList;
|
|
while (segmentPoint)
|
|
{
|
|
if (segmentPoint->index != RA_INTER_RANGE_START && segmentPoint->index != RA_INTER_RANGE_END)
|
|
{
|
|
if (segmentPoint->index >= index)
|
|
segmentPoint->index += shiftBackCount;
|
|
}
|
|
// next
|
|
segmentPoint = segmentPoint->next;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Insert and return new instruction at index
|
|
* Warning: Can invalidate any previous instruction structs from the same segment
|
|
*/
|
|
PPCRecImlInstruction_t* PPCRecompiler_insertInstruction(PPCRecImlSegment_t* imlSegment, sint32 index)
|
|
{
|
|
PPCRecompiler_pushBackIMLInstructions(imlSegment, index, 1);
|
|
return imlSegment->imlList + index;
|
|
}
|
|
|
|
/*
|
|
* Append and return new instruction at the end of the segment
|
|
* Warning: Can invalidate any previous instruction structs from the same segment
|
|
*/
|
|
PPCRecImlInstruction_t* PPCRecompiler_appendInstruction(PPCRecImlSegment_t* imlSegment)
|
|
{
|
|
sint32 index = imlSegment->imlListCount;
|
|
if (index >= imlSegment->imlListSize)
|
|
{
|
|
sint32 newSize = index+1;
|
|
imlSegment->imlList = (PPCRecImlInstruction_t*)realloc(imlSegment->imlList, sizeof(PPCRecImlInstruction_t)*newSize);
|
|
imlSegment->imlListSize = newSize;
|
|
}
|
|
imlSegment->imlListCount++;
|
|
memset(imlSegment->imlList + index, 0, sizeof(PPCRecImlInstruction_t));
|
|
return imlSegment->imlList + index;
|
|
}
|
|
|
|
PPCRecImlSegment_t* PPCRecompilerIml_appendSegment(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
PPCRecImlSegment_t* segment = new PPCRecImlSegment_t();
|
|
ppcImlGenContext->segmentList2.emplace_back(segment);
|
|
return segment;
|
|
}
|
|
|
|
void PPCRecompilerIml_insertSegments(ppcImlGenContext_t* ppcImlGenContext, sint32 index, sint32 count)
|
|
{
|
|
ppcImlGenContext->segmentList2.insert(ppcImlGenContext->segmentList2.begin() + index, count, nullptr);
|
|
for (sint32 i = 0; i < count; i++)
|
|
ppcImlGenContext->segmentList2[index + i] = new PPCRecImlSegment_t();
|
|
}
|
|
|
|
void PPCRecompiler_freeContext(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
if (ppcImlGenContext->imlList)
|
|
{
|
|
free(ppcImlGenContext->imlList);
|
|
ppcImlGenContext->imlList = nullptr;
|
|
}
|
|
|
|
for (PPCRecImlSegment_t* imlSegment : ppcImlGenContext->segmentList2)
|
|
{
|
|
free(imlSegment->imlList);
|
|
delete imlSegment;
|
|
}
|
|
ppcImlGenContext->segmentList2.clear();
|
|
|
|
//for(sint32 i=0; i<ppcImlGenContext->segmentListCount; i++)
|
|
//{
|
|
// free(ppcImlGenContext->segmentList[i]->imlList);
|
|
// delete ppcImlGenContext->segmentList[i];
|
|
//}
|
|
//ppcImlGenContext->segmentListCount = 0;
|
|
//if (ppcImlGenContext->segmentList)
|
|
//{
|
|
// free(ppcImlGenContext->segmentList);
|
|
// ppcImlGenContext->segmentList = nullptr;
|
|
//}
|
|
}
|
|
|
|
bool PPCRecompiler_isSuffixInstruction(PPCRecImlInstruction_t* iml)
|
|
{
|
|
if (iml->type == PPCREC_IML_TYPE_MACRO && (iml->operation == PPCREC_IML_MACRO_BLR || iml->operation == PPCREC_IML_MACRO_BCTR) ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BL ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_B_FAR ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BLRL ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_BCTRL ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_LEAVE ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_HLE ||
|
|
iml->type == PPCREC_IML_TYPE_MACRO && iml->operation == PPCREC_IML_MACRO_MFTB ||
|
|
iml->type == PPCREC_IML_TYPE_PPC_ENTER ||
|
|
iml->type == PPCREC_IML_TYPE_CJUMP ||
|
|
iml->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK)
|
|
return true;
|
|
return false;
|
|
}
|
|
|
|
bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
bool unsupportedInstructionFound = false;
|
|
|
|
uint32 opcode = PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext);
|
|
switch ((opcode >> 26))
|
|
{
|
|
case 1:
|
|
if (PPCRecompilerImlGen_HLE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 4: // opcode category - paired single
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 0: // subcategory compare
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 0:
|
|
PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext, opcode);
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 1:
|
|
PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext, opcode);
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 2:
|
|
PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext, opcode);
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 8: //Sub category - move/negate
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 1: // PS negate
|
|
if (PPCRecompilerImlGen_PS_NEG(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 2: // PS move register
|
|
if (PPCRecompilerImlGen_PS_MR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 8: // PS abs
|
|
if (PPCRecompilerImlGen_PS_ABS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 10:
|
|
if (PPCRecompilerImlGen_PS_SUM0(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 11:
|
|
if (PPCRecompilerImlGen_PS_SUM1(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 12: // multiply scalar
|
|
if (PPCRecompilerImlGen_PS_MULS0(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 13: // multiply scalar
|
|
if (PPCRecompilerImlGen_PS_MULS1(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 14: // multiply add scalar
|
|
if (PPCRecompilerImlGen_PS_MADDS0(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 15: // multiply add scalar
|
|
if (PPCRecompilerImlGen_PS_MADDS1(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 16: // sub category - merge
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 16:
|
|
if (PPCRecompilerImlGen_PS_MERGE00(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 17:
|
|
if (PPCRecompilerImlGen_PS_MERGE01(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 18:
|
|
if (PPCRecompilerImlGen_PS_MERGE10(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 19:
|
|
if (PPCRecompilerImlGen_PS_MERGE11(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 18: // divide paired
|
|
if (PPCRecompilerImlGen_PS_DIV(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 20: // sub paired
|
|
if (PPCRecompilerImlGen_PS_SUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 21: // add paired
|
|
if (PPCRecompilerImlGen_PS_ADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 23: // select paired
|
|
if (PPCRecompilerImlGen_PS_SEL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 25: // multiply paired
|
|
if (PPCRecompilerImlGen_PS_MUL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 24: // reciprocal paired
|
|
if (PPCRecompilerImlGen_PS_RES(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 26: // reciprocal squareroot paired
|
|
if (PPCRecompilerImlGen_PS_RSQRTE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 28: // multiply sub paired
|
|
if (PPCRecompilerImlGen_PS_MSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 29: // multiply add paired
|
|
if (PPCRecompilerImlGen_PS_MADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 30: // negative multiply sub paired
|
|
if (PPCRecompilerImlGen_PS_NMSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 31: // negative multiply add paired
|
|
if (PPCRecompilerImlGen_PS_NMADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 7: // MULLI
|
|
PPCRecompilerImlGen_MULLI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 8: // SUBFIC
|
|
PPCRecompilerImlGen_SUBFIC(ppcImlGenContext, opcode);
|
|
break;
|
|
case 10: // CMPLI
|
|
PPCRecompilerImlGen_CMPLI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 11: // CMPI
|
|
PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 12: // ADDIC
|
|
if (PPCRecompilerImlGen_ADDIC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 13: // ADDIC.
|
|
if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 14: // ADDI
|
|
if (PPCRecompilerImlGen_ADDI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 15: // ADDIS
|
|
if (PPCRecompilerImlGen_ADDIS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 16: // BC
|
|
if (PPCRecompilerImlGen_BC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 17:
|
|
if (PPC_getBits(opcode, 30, 1) == 1)
|
|
{
|
|
// SC -> no-op
|
|
}
|
|
else
|
|
{
|
|
unsupportedInstructionFound = true;
|
|
}
|
|
break;
|
|
case 18: // B
|
|
if (PPCRecompilerImlGen_B(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 19: // opcode category 19
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 16:
|
|
if (PPCRecompilerImlGen_BCLR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 129:
|
|
if (PPCRecompilerImlGen_CRANDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 150:
|
|
if (PPCRecompilerImlGen_ISYNC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 193:
|
|
if (PPCRecompilerImlGen_CRXOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 257:
|
|
if (PPCRecompilerImlGen_CRAND(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 289:
|
|
if (PPCRecompilerImlGen_CREQV(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 417:
|
|
if (PPCRecompilerImlGen_CRORC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 449:
|
|
if (PPCRecompilerImlGen_CROR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 528:
|
|
if (PPCRecompilerImlGen_BCCTR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_RLWIMI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_RLWINM(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_RLWNM(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 24:
|
|
PPCRecompilerImlGen_ORI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 25:
|
|
PPCRecompilerImlGen_ORIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 26:
|
|
PPCRecompilerImlGen_XORI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 27:
|
|
PPCRecompilerImlGen_XORIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 28:
|
|
PPCRecompilerImlGen_ANDI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 29:
|
|
PPCRecompilerImlGen_ANDIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 31: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 0:
|
|
PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode);
|
|
break;
|
|
case 4:
|
|
PPCRecompilerImlGen_TW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 8:
|
|
// todo: Check if we can optimize this pattern:
|
|
// SUBFC + SUBFE
|
|
// SUBFC
|
|
if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 10:
|
|
if (PPCRecompilerImlGen_ADDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 11:
|
|
if (PPCRecompilerImlGen_MULHWU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 19:
|
|
if (PPCRecompilerImlGen_MFCR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_LWZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 24:
|
|
if (PPCRecompilerImlGen_SLW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 26:
|
|
if (PPCRecompilerImlGen_CNTLZW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_AND(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 32:
|
|
PPCRecompilerImlGen_CMPL(ppcImlGenContext, opcode);
|
|
break;
|
|
case 40:
|
|
if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 54:
|
|
// DBCST - Generates no code
|
|
break;
|
|
case 55:
|
|
if (PPCRecompilerImlGen_LWZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 60:
|
|
if (PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 75:
|
|
if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 86:
|
|
// DCBF -> No-Op
|
|
break;
|
|
case 87:
|
|
if (PPCRecompilerImlGen_LBZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 104:
|
|
if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 119:
|
|
if (PPCRecompilerImlGen_LBZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 124:
|
|
if (PPCRecompilerImlGen_NOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 136:
|
|
if (PPCRecompilerImlGen_SUBFE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 138:
|
|
if (PPCRecompilerImlGen_ADDE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 144:
|
|
PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode);
|
|
break;
|
|
case 150:
|
|
if (PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 151:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 183:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 32) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 200:
|
|
if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 202:
|
|
if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 215:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 234:
|
|
if (PPCRecompilerImlGen_ADDME(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 235:
|
|
if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 247:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 8) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 266:
|
|
if (PPCRecompilerImlGen_ADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 279:
|
|
if (PPCRecompilerImlGen_LHZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 284:
|
|
PPCRecompilerImlGen_EQV(ppcImlGenContext, opcode);
|
|
break;
|
|
case 311:
|
|
if (PPCRecompilerImlGen_LHZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 316:
|
|
if (PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 339:
|
|
if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 343:
|
|
if (PPCRecompilerImlGen_LHAX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 371:
|
|
if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 375:
|
|
if (PPCRecompilerImlGen_LHAUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 407:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 412:
|
|
if (PPCRecompilerImlGen_ORC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 439:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 16) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 444:
|
|
if (PPCRecompilerImlGen_OR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 459:
|
|
PPCRecompilerImlGen_DIVWU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 467:
|
|
if (PPCRecompilerImlGen_MTSPR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 491:
|
|
if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 534:
|
|
if (PPCRecompilerImlGen_LWBRX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 535:
|
|
if (PPCRecompilerImlGen_LFSX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 536:
|
|
if (PPCRecompilerImlGen_SRW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 567:
|
|
if (PPCRecompilerImlGen_LFSUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 597:
|
|
if (PPCRecompilerImlGen_LSWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 598:
|
|
PPCRecompilerImlGen_SYNC(ppcImlGenContext, opcode);
|
|
break;
|
|
case 599:
|
|
if (PPCRecompilerImlGen_LFDX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 631:
|
|
if (PPCRecompilerImlGen_LFDUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 662:
|
|
if (PPCRecompilerImlGen_STWBRX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 663:
|
|
if (PPCRecompilerImlGen_STFSX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 695:
|
|
if (PPCRecompilerImlGen_STFSUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 725:
|
|
if (PPCRecompilerImlGen_STSWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 727:
|
|
if (PPCRecompilerImlGen_STFDX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 790:
|
|
PPCRecompilerImlGen_LHBRX(ppcImlGenContext, opcode);
|
|
break;
|
|
case 792:
|
|
if (PPCRecompilerImlGen_SRAW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 824:
|
|
if (PPCRecompilerImlGen_SRAWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 918: // STHBRX
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 922:
|
|
if (PPCRecompilerImlGen_EXTSH(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 954:
|
|
if (PPCRecompilerImlGen_EXTSB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 983:
|
|
if (PPCRecompilerImlGen_STFIWX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 1014:
|
|
if (PPCRecompilerImlGen_DCBZ(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 32:
|
|
PPCRecompilerImlGen_LWZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 33:
|
|
PPCRecompilerImlGen_LWZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 34:
|
|
PPCRecompilerImlGen_LBZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 35:
|
|
PPCRecompilerImlGen_LBZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 36:
|
|
PPCRecompilerImlGen_STW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 37:
|
|
PPCRecompilerImlGen_STWU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 38:
|
|
PPCRecompilerImlGen_STB(ppcImlGenContext, opcode);
|
|
break;
|
|
case 39:
|
|
PPCRecompilerImlGen_STBU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 40:
|
|
PPCRecompilerImlGen_LHZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 41:
|
|
PPCRecompilerImlGen_LHZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 42:
|
|
PPCRecompilerImlGen_LHA(ppcImlGenContext, opcode);
|
|
break;
|
|
case 43:
|
|
PPCRecompilerImlGen_LHAU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 44:
|
|
PPCRecompilerImlGen_STH(ppcImlGenContext, opcode);
|
|
break;
|
|
case 45:
|
|
PPCRecompilerImlGen_STHU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 46:
|
|
PPCRecompilerImlGen_LMW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 47:
|
|
PPCRecompilerImlGen_STMW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 48:
|
|
if (PPCRecompilerImlGen_LFS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 49:
|
|
if (PPCRecompilerImlGen_LFSU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 50:
|
|
if (PPCRecompilerImlGen_LFD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 51:
|
|
if (PPCRecompilerImlGen_LFDU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 52:
|
|
if (PPCRecompilerImlGen_STFS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 53:
|
|
if (PPCRecompilerImlGen_STFSU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 54:
|
|
if (PPCRecompilerImlGen_STFD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 55:
|
|
if (PPCRecompilerImlGen_STFDU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 56:
|
|
if (PPCRecompilerImlGen_PSQ_L(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 57:
|
|
if (PPCRecompilerImlGen_PSQ_LU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 59: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 18:
|
|
if (PPCRecompilerImlGen_FDIVS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_FSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_FADDS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 24:
|
|
if (PPCRecompilerImlGen_FRES(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 25:
|
|
if (PPCRecompilerImlGen_FMULS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_FMSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 29:
|
|
if (PPCRecompilerImlGen_FMADDS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 30:
|
|
if (PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 60:
|
|
if (PPCRecompilerImlGen_PSQ_ST(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 61:
|
|
if (PPCRecompilerImlGen_PSQ_STU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 63: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 0:
|
|
if (PPCRecompilerImlGen_FCMPU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 12:
|
|
if (PPCRecompilerImlGen_FRSP(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 15:
|
|
if (PPCRecompilerImlGen_FCTIWZ(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 18:
|
|
if (PPCRecompilerImlGen_FDIV(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_FSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_FADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_FSEL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 25:
|
|
if (PPCRecompilerImlGen_FMUL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 26:
|
|
if (PPCRecompilerImlGen_FRSQRTE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_FMSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 29:
|
|
if (PPCRecompilerImlGen_FMADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 30:
|
|
if (PPCRecompilerImlGen_FNMSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 32:
|
|
if (PPCRecompilerImlGen_FCMPO(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 40:
|
|
if (PPCRecompilerImlGen_FNEG(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 72:
|
|
if (PPCRecompilerImlGen_FMR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 136:
|
|
if (PPCRecompilerImlGen_FNABS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 264:
|
|
if (PPCRecompilerImlGen_FABS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
return unsupportedInstructionFound;
|
|
}
|
|
|
|
bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set<uint32>& entryAddresses)
|
|
{
|
|
//ppcImlGenContext_t ppcImlGenContext = { 0 };
|
|
ppcImlGenContext.functionRef = ppcRecFunc;
|
|
// add entire range
|
|
ppcRecRange_t recRange;
|
|
recRange.ppcAddress = ppcRecFunc->ppcAddress;
|
|
recRange.ppcSize = ppcRecFunc->ppcSize;
|
|
ppcRecFunc->list_ranges.push_back(recRange);
|
|
// process ppc instructions
|
|
ppcImlGenContext.currentInstruction = (uint32*)memory_getPointerFromVirtualOffset(ppcRecFunc->ppcAddress);
|
|
bool unsupportedInstructionFound = false;
|
|
sint32 numPPCInstructions = ppcRecFunc->ppcSize/4;
|
|
sint32 unsupportedInstructionCount = 0;
|
|
uint32 unsupportedInstructionLastOffset = 0;
|
|
uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction;
|
|
uint32* endCurrentInstruction = ppcImlGenContext.currentInstruction + numPPCInstructions;
|
|
|
|
while(ppcImlGenContext.currentInstruction < endCurrentInstruction)
|
|
{
|
|
uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base);
|
|
ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction;
|
|
ppcImlGenContext.cyclesSinceLastBranch++;
|
|
PPCRecompilerImlGen_generateNewInstruction_jumpmark(&ppcImlGenContext, addressOfCurrentInstruction);
|
|
|
|
if (entryAddresses.find(addressOfCurrentInstruction) != entryAddresses.end())
|
|
{
|
|
// add PPCEnter for addresses that are in entryAddresses
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction);
|
|
}
|
|
else if(ppcImlGenContext.currentInstruction != firstCurrentInstruction)
|
|
{
|
|
// add PPCEnter mark if code is seemingly unreachable (for example if between two unconditional jump instructions without jump goal)
|
|
uint32 opcodeCurrent = PPCRecompiler_getCurrentInstruction(&ppcImlGenContext);
|
|
uint32 opcodePrevious = PPCRecompiler_getPreviousInstruction(&ppcImlGenContext);
|
|
if( ((opcodePrevious>>26) == 18) && ((opcodeCurrent>>26) == 18) )
|
|
{
|
|
// between two B(L) instructions
|
|
// todo: for BL only if they are not inlineable
|
|
|
|
bool canInlineFunction = false;
|
|
if ((opcodePrevious & PPC_OPC_LK) && (opcodePrevious & PPC_OPC_AA) == 0)
|
|
{
|
|
uint32 li;
|
|
PPC_OPC_TEMPL_I(opcodePrevious, li);
|
|
sint32 inlineSize = 0;
|
|
if (PPCRecompiler_canInlineFunction(li + addressOfCurrentInstruction - 4, &inlineSize))
|
|
canInlineFunction = true;
|
|
}
|
|
if( canInlineFunction == false && (opcodePrevious & PPC_OPC_LK) == false)
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction);
|
|
}
|
|
if( ((opcodePrevious>>26) == 19) && PPC_getBits(opcodePrevious, 30, 10) == 528 )
|
|
{
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_XL(opcodePrevious, BO, BI, BD);
|
|
if( (BO & 16) && (opcodePrevious&PPC_OPC_LK) == 0 )
|
|
{
|
|
// after unconditional BCTR instruction
|
|
PPCRecompilerImlGen_generateNewInstruction_ppcEnter(&ppcImlGenContext, addressOfCurrentInstruction);
|
|
}
|
|
}
|
|
}
|
|
|
|
unsupportedInstructionFound = PPCRecompiler_decodePPCInstruction(&ppcImlGenContext);
|
|
if( unsupportedInstructionFound )
|
|
{
|
|
unsupportedInstructionCount++;
|
|
unsupportedInstructionLastOffset = ppcImlGenContext.ppcAddressOfCurrentInstruction;
|
|
unsupportedInstructionFound = false;
|
|
//break;
|
|
}
|
|
}
|
|
ppcImlGenContext.ppcAddressOfCurrentInstruction = 0; // reset current instruction offset (any future generated IML instruction will be assigned to ppc address 0)
|
|
if( unsupportedInstructionCount > 0 || unsupportedInstructionFound )
|
|
{
|
|
// could not compile function
|
|
debug_printf("Failed recompile due to unknown instruction at 0x%08x\n", unsupportedInstructionLastOffset);
|
|
PPCRecompiler_freeContext(&ppcImlGenContext);
|
|
return false;
|
|
}
|
|
// optimize unused jumpmarks away
|
|
// first, flag all jumpmarks as unused
|
|
std::map<uint32, PPCRecImlInstruction_t*> map_jumpMarks;
|
|
for(sint32 i=0; i<ppcImlGenContext.imlListCount; i++)
|
|
{
|
|
if( ppcImlGenContext.imlList[i].type == PPCREC_IML_TYPE_JUMPMARK )
|
|
{
|
|
ppcImlGenContext.imlList[i].op_jumpmark.flags |= PPCREC_IML_OP_FLAG_UNUSED;
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
if (map_jumpMarks.find(ppcImlGenContext.imlList[i].op_jumpmark.address) != map_jumpMarks.end())
|
|
assert_dbg();
|
|
#endif
|
|
map_jumpMarks.emplace(ppcImlGenContext.imlList[i].op_jumpmark.address, ppcImlGenContext.imlList+i);
|
|
}
|
|
}
|
|
// second, unflag jumpmarks that have at least one reference
|
|
for(sint32 i=0; i<ppcImlGenContext.imlListCount; i++)
|
|
{
|
|
if( ppcImlGenContext.imlList[i].type == PPCREC_IML_TYPE_CJUMP )
|
|
{
|
|
uint32 jumpDest = ppcImlGenContext.imlList[i].op_conditionalJump.jumpmarkAddress;
|
|
auto jumpMarkIml = map_jumpMarks.find(jumpDest);
|
|
if (jumpMarkIml != map_jumpMarks.end())
|
|
jumpMarkIml->second->op_jumpmark.flags &= ~PPCREC_IML_OP_FLAG_UNUSED;
|
|
}
|
|
}
|
|
// lastly, remove jumpmarks that still have the unused flag set
|
|
sint32 currentImlIndex = 0;
|
|
for(sint32 i=0; i<ppcImlGenContext.imlListCount; i++)
|
|
{
|
|
if( ppcImlGenContext.imlList[i].type == PPCREC_IML_TYPE_JUMPMARK && (ppcImlGenContext.imlList[i].op_jumpmark.flags&PPCREC_IML_OP_FLAG_UNUSED) )
|
|
{
|
|
continue; // skip this instruction
|
|
}
|
|
// move back instruction
|
|
if( currentImlIndex < i )
|
|
{
|
|
memcpy(ppcImlGenContext.imlList+currentImlIndex, ppcImlGenContext.imlList+i, sizeof(PPCRecImlInstruction_t));
|
|
}
|
|
currentImlIndex++;
|
|
}
|
|
// fix intermediate instruction count
|
|
ppcImlGenContext.imlListCount = currentImlIndex;
|
|
// divide iml instructions into segments
|
|
// each segment is defined by one or more instructions with no branches or jump destinations in between
|
|
// a branch instruction may only be the very last instruction of a segment
|
|
cemu_assert_debug(ppcImlGenContext.segmentList2.empty());
|
|
|
|
sint32 segmentStart = 0;
|
|
sint32 segmentImlIndex = 0;
|
|
while( segmentImlIndex < ppcImlGenContext.imlListCount )
|
|
{
|
|
bool genNewSegment = false;
|
|
// segment definition:
|
|
// If we encounter a branch instruction -> end of segment after current instruction
|
|
// If we encounter a jumpmark -> end of segment before current instruction
|
|
// If we encounter ppc_enter -> end of segment before current instruction
|
|
if( ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_CJUMP ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BLR || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BLRL || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BCTR || ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BCTRL)) ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_BL)) ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_B_FAR)) ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_LEAVE)) ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_HLE)) ||
|
|
(ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_MACRO && (ppcImlGenContext.imlList[segmentImlIndex].operation == PPCREC_IML_MACRO_MFTB)) )
|
|
{
|
|
// segment ends after current instruction
|
|
PPCRecImlSegment_t* ppcRecSegment = PPCRecompilerIml_appendSegment(&ppcImlGenContext);
|
|
ppcRecSegment->startOffset = segmentStart;
|
|
ppcRecSegment->count = segmentImlIndex-segmentStart+1;
|
|
ppcRecSegment->ppcAddress = 0xFFFFFFFF;
|
|
segmentStart = segmentImlIndex+1;
|
|
}
|
|
else if( ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_JUMPMARK ||
|
|
ppcImlGenContext.imlList[segmentImlIndex].type == PPCREC_IML_TYPE_PPC_ENTER )
|
|
{
|
|
// segment ends before current instruction
|
|
if( segmentImlIndex > segmentStart )
|
|
{
|
|
PPCRecImlSegment_t* ppcRecSegment = PPCRecompilerIml_appendSegment(&ppcImlGenContext);
|
|
ppcRecSegment->startOffset = segmentStart;
|
|
ppcRecSegment->count = segmentImlIndex-segmentStart;
|
|
ppcRecSegment->ppcAddress = 0xFFFFFFFF;
|
|
segmentStart = segmentImlIndex;
|
|
}
|
|
}
|
|
segmentImlIndex++;
|
|
}
|
|
if( segmentImlIndex != segmentStart )
|
|
{
|
|
// final segment
|
|
PPCRecImlSegment_t* ppcRecSegment = PPCRecompilerIml_appendSegment(&ppcImlGenContext);
|
|
ppcRecSegment->startOffset = segmentStart;
|
|
ppcRecSegment->count = segmentImlIndex-segmentStart;
|
|
ppcRecSegment->ppcAddress = 0xFFFFFFFF;
|
|
segmentStart = segmentImlIndex;
|
|
}
|
|
// move iml instructions into the segments
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
uint32 imlStartIndex = segIt->startOffset;
|
|
uint32 imlCount = segIt->count;
|
|
if( imlCount > 0 )
|
|
{
|
|
segIt->imlListSize = imlCount + 4;
|
|
segIt->imlList = (PPCRecImlInstruction_t*)malloc(sizeof(PPCRecImlInstruction_t)* segIt->imlListSize);
|
|
segIt->imlListCount = imlCount;
|
|
memcpy(segIt->imlList, ppcImlGenContext.imlList+imlStartIndex, sizeof(PPCRecImlInstruction_t)*imlCount);
|
|
}
|
|
else
|
|
{
|
|
// empty segments are allowed so we can handle multiple PPC entry addresses pointing to the same code
|
|
segIt->imlList = nullptr;
|
|
segIt->imlListSize = 0;
|
|
segIt->imlListCount = 0;
|
|
}
|
|
segIt->startOffset = 9999999;
|
|
segIt->count = 9999999;
|
|
}
|
|
// clear segment-independent iml list
|
|
free(ppcImlGenContext.imlList);
|
|
ppcImlGenContext.imlList = NULL;
|
|
ppcImlGenContext.imlListCount = 999999; // set to high number to force crash in case old code still uses ppcImlGenContext.imlList
|
|
// calculate PPC address of each segment based on iml instructions inside that segment (we need this info to calculate how many cpu cycles each segment takes)
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
uint32 segmentPPCAddrMin = 0xFFFFFFFF;
|
|
uint32 segmentPPCAddrMax = 0x00000000;
|
|
for(sint32 i=0; i< segIt->imlListCount; i++)
|
|
{
|
|
if(segIt->imlList[i].associatedPPCAddress == 0 )
|
|
continue;
|
|
//if( ppcImlGenContext.segmentList[s]->imlList[i].type == PPCREC_IML_TYPE_JUMPMARK || ppcImlGenContext.segmentList[s]->imlList[i].type == PPCREC_IML_TYPE_NO_OP )
|
|
// continue; // jumpmarks and no-op instructions must not affect segment ppc address range
|
|
segmentPPCAddrMin = std::min(segIt->imlList[i].associatedPPCAddress, segmentPPCAddrMin);
|
|
segmentPPCAddrMax = std::max(segIt->imlList[i].associatedPPCAddress, segmentPPCAddrMax);
|
|
}
|
|
if( segmentPPCAddrMin != 0xFFFFFFFF )
|
|
{
|
|
segIt->ppcAddrMin = segmentPPCAddrMin;
|
|
segIt->ppcAddrMax = segmentPPCAddrMax;
|
|
}
|
|
else
|
|
{
|
|
segIt->ppcAddrMin = 0;
|
|
segIt->ppcAddrMax = 0;
|
|
}
|
|
}
|
|
// certain instructions can change the segment state
|
|
// ppcEnter instruction marks a segment as enterable (BL, BCTR, etc. instructions can enter at this location from outside)
|
|
// jumpmarks mark the segment as a jump destination (within the same function)
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
while (segIt->imlListCount > 0)
|
|
{
|
|
if (segIt->imlList[0].type == PPCREC_IML_TYPE_PPC_ENTER)
|
|
{
|
|
// mark segment as enterable
|
|
if (segIt->isEnterable)
|
|
assert_dbg(); // should not happen?
|
|
segIt->isEnterable = true;
|
|
segIt->enterPPCAddress = segIt->imlList[0].op_ppcEnter.ppcAddress;
|
|
// remove ppc_enter instruction
|
|
segIt->imlList[0].type = PPCREC_IML_TYPE_NO_OP;
|
|
segIt->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
segIt->imlList[0].associatedPPCAddress = 0;
|
|
}
|
|
else if(segIt->imlList[0].type == PPCREC_IML_TYPE_JUMPMARK )
|
|
{
|
|
// mark segment as jump destination
|
|
if(segIt->isJumpDestination )
|
|
assert_dbg(); // should not happen?
|
|
segIt->isJumpDestination = true;
|
|
segIt->jumpDestinationPPCAddress = segIt->imlList[0].op_jumpmark.address;
|
|
// remove jumpmark instruction
|
|
segIt->imlList[0].type = PPCREC_IML_TYPE_NO_OP;
|
|
segIt->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
segIt->imlList[0].associatedPPCAddress = 0;
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
// the first segment is always enterable as the recompiled functions entrypoint
|
|
ppcImlGenContext.segmentList2[0]->isEnterable = true;
|
|
ppcImlGenContext.segmentList2[0]->enterPPCAddress = ppcImlGenContext.functionRef->ppcAddress;
|
|
|
|
// link segments for further inter-segment optimization
|
|
PPCRecompilerIML_linkSegments(&ppcImlGenContext);
|
|
|
|
// optimization pass - replace segments with conditional MOVs if possible
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
if (segIt->nextSegmentBranchNotTaken == nullptr || segIt->nextSegmentBranchTaken == nullptr)
|
|
continue; // not a branching segment
|
|
PPCRecImlInstruction_t* lastInstruction = PPCRecompilerIML_getLastInstruction(segIt);
|
|
if (lastInstruction->type != PPCREC_IML_TYPE_CJUMP || lastInstruction->op_conditionalJump.crRegisterIndex != 0)
|
|
continue;
|
|
PPCRecImlSegment_t* conditionalSegment = segIt->nextSegmentBranchNotTaken;
|
|
PPCRecImlSegment_t* finalSegment = segIt->nextSegmentBranchTaken;
|
|
if (segIt->nextSegmentBranchTaken != segIt->nextSegmentBranchNotTaken->nextSegmentBranchNotTaken)
|
|
continue;
|
|
if (segIt->nextSegmentBranchNotTaken->imlListCount > 4)
|
|
continue;
|
|
if (conditionalSegment->list_prevSegments.size() != 1)
|
|
continue; // the reduced segment must not be the target of any other branch
|
|
if (conditionalSegment->isEnterable)
|
|
continue;
|
|
// check if the segment contains only iml instructions that can be turned into conditional moves (Value assignment, register assignment)
|
|
bool canReduceSegment = true;
|
|
for (sint32 f = 0; f < conditionalSegment->imlListCount; f++)
|
|
{
|
|
PPCRecImlInstruction_t* imlInstruction = conditionalSegment->imlList+f;
|
|
if( imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN)
|
|
continue;
|
|
// todo: Register to register copy
|
|
canReduceSegment = false;
|
|
break;
|
|
}
|
|
|
|
if( canReduceSegment == false )
|
|
continue;
|
|
|
|
// remove the branch instruction
|
|
uint8 branchCond_crRegisterIndex = lastInstruction->op_conditionalJump.crRegisterIndex;
|
|
uint8 branchCond_crBitIndex = lastInstruction->op_conditionalJump.crBitIndex;
|
|
bool branchCond_bitMustBeSet = lastInstruction->op_conditionalJump.bitMustBeSet;
|
|
|
|
PPCRecompilerImlGen_generateNewInstruction_noOp(&ppcImlGenContext, lastInstruction);
|
|
|
|
// append conditional moves based on branch condition
|
|
for (sint32 f = 0; f < conditionalSegment->imlListCount; f++)
|
|
{
|
|
PPCRecImlInstruction_t* imlInstruction = conditionalSegment->imlList + f;
|
|
if (imlInstruction->type == PPCREC_IML_TYPE_R_S32 && imlInstruction->operation == PPCREC_IML_OP_ASSIGN)
|
|
PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(&ppcImlGenContext, PPCRecompiler_appendInstruction(segIt), PPCREC_IML_OP_ASSIGN, imlInstruction->op_r_immS32.registerIndex, imlInstruction->op_r_immS32.immS32, branchCond_crRegisterIndex, branchCond_crBitIndex, !branchCond_bitMustBeSet);
|
|
else
|
|
assert_dbg();
|
|
}
|
|
// update segment links
|
|
// source segment: imlSegment, conditional/removed segment: conditionalSegment, final segment: finalSegment
|
|
PPCRecompilerIML_removeLink(segIt, conditionalSegment);
|
|
PPCRecompilerIML_removeLink(segIt, finalSegment);
|
|
PPCRecompilerIML_removeLink(conditionalSegment, finalSegment);
|
|
PPCRecompilerIml_setLinkBranchNotTaken(segIt, finalSegment);
|
|
// remove all instructions from conditional segment
|
|
conditionalSegment->imlListCount = 0;
|
|
|
|
// if possible, merge imlSegment with finalSegment
|
|
if (finalSegment->isEnterable == false && finalSegment->list_prevSegments.size() == 1)
|
|
{
|
|
// todo: Clean this up and move into separate function PPCRecompilerIML_mergeSegments()
|
|
PPCRecompilerIML_removeLink(segIt, finalSegment);
|
|
if (finalSegment->nextSegmentBranchNotTaken)
|
|
{
|
|
PPCRecImlSegment_t* tempSegment = finalSegment->nextSegmentBranchNotTaken;
|
|
PPCRecompilerIML_removeLink(finalSegment, tempSegment);
|
|
PPCRecompilerIml_setLinkBranchNotTaken(segIt, tempSegment);
|
|
}
|
|
if (finalSegment->nextSegmentBranchTaken)
|
|
{
|
|
PPCRecImlSegment_t* tempSegment = finalSegment->nextSegmentBranchTaken;
|
|
PPCRecompilerIML_removeLink(finalSegment, tempSegment);
|
|
PPCRecompilerIml_setLinkBranchTaken(segIt, tempSegment);
|
|
}
|
|
// copy IML instructions
|
|
for (sint32 f = 0; f < finalSegment->imlListCount; f++)
|
|
{
|
|
memcpy(PPCRecompiler_appendInstruction(segIt), finalSegment->imlList + f, sizeof(PPCRecImlInstruction_t));
|
|
}
|
|
finalSegment->imlListCount = 0;
|
|
|
|
//PPCRecompiler_dumpIML(ppcRecFunc, &ppcImlGenContext);
|
|
}
|
|
|
|
// todo: If possible, merge with the segment following conditionalSegment (merging is only possible if the segment is not an entry point or has no other jump sources)
|
|
}
|
|
|
|
// insert cycle counter instruction in every segment that has a cycle count greater zero
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
if( segIt->ppcAddrMin == 0 )
|
|
continue;
|
|
// count number of PPC instructions in segment
|
|
// note: This algorithm correctly counts inlined functions but it doesn't count NO-OP instructions like ISYNC since they generate no IML instructions
|
|
uint32 lastPPCInstAddr = 0;
|
|
uint32 ppcCount2 = 0;
|
|
for (sint32 i = 0; i < segIt->imlListCount; i++)
|
|
{
|
|
if (segIt->imlList[i].associatedPPCAddress == 0)
|
|
continue;
|
|
if (segIt->imlList[i].associatedPPCAddress == lastPPCInstAddr)
|
|
continue;
|
|
lastPPCInstAddr = segIt->imlList[i].associatedPPCAddress;
|
|
ppcCount2++;
|
|
}
|
|
//uint32 ppcCount = imlSegment->ppcAddrMax-imlSegment->ppcAddrMin+4; -> No longer works with inlined functions
|
|
uint32 cycleCount = ppcCount2;// ppcCount / 4;
|
|
if( cycleCount > 0 )
|
|
{
|
|
PPCRecompiler_pushBackIMLInstructions(segIt, 0, 1);
|
|
segIt->imlList[0].type = PPCREC_IML_TYPE_MACRO;
|
|
segIt->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
segIt->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES;
|
|
segIt->imlList[0].op_macro.param = cycleCount;
|
|
}
|
|
}
|
|
|
|
// find segments that have a (conditional) jump instruction that points in reverse direction of code flow
|
|
// for these segments there is a risk that the recompiler could get trapped in an infinite busy loop.
|
|
// todo: We should do a loop-detection prepass where we flag segments that are actually in a loop. We can then use this information below to avoid generating the scheduler-exit code for segments that aren't actually in a loop despite them referencing an earlier segment (which could be an exit segment for example)
|
|
uint32 currentLoopEscapeJumpMarker = 0xFF000000; // start in an area where no valid code can be located
|
|
for(size_t s=0; s<ppcImlGenContext.segmentList2.size(); s++)
|
|
{
|
|
// todo: This currently uses segment->ppcAddrMin which isn't really reliable. (We already had a problem where function inlining would generate falsified segment ranges by omitting the branch instruction). Find a better solution (use jumpmark/enterable offsets?)
|
|
PPCRecImlSegment_t* imlSegment = ppcImlGenContext.segmentList2[s];
|
|
if( imlSegment->imlListCount == 0 )
|
|
continue;
|
|
if (imlSegment->imlList[imlSegment->imlListCount - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlListCount - 1].op_conditionalJump.jumpmarkAddress > imlSegment->ppcAddrMin)
|
|
continue;
|
|
if (imlSegment->imlList[imlSegment->imlListCount - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlListCount - 1].op_conditionalJump.jumpAccordingToSegment)
|
|
continue;
|
|
// exclude non-infinite tight loops
|
|
if (PPCRecompilerImlAnalyzer_isTightFiniteLoop(imlSegment))
|
|
continue;
|
|
// potential loop segment found, split this segment into four:
|
|
// P0: This segment checks if the remaining cycles counter is still above zero. If yes, it jumps to segment P2 (it's also the jump destination for other segments)
|
|
// P1: This segment consists only of a single ppc_leave instruction and is usually skipped. Register unload instructions are later inserted here.
|
|
// P2: This segment contains the iml instructions of the original segment
|
|
// PEntry: This segment is used to enter the function, it jumps to P0
|
|
// All segments are considered to be part of the same PPC instruction range
|
|
// The first segment also retains the jump destination and enterable properties from the original segment.
|
|
//debug_printf("--- Insert cycle counter check ---\n");
|
|
//PPCRecompiler_dumpIML(ppcRecFunc, &ppcImlGenContext);
|
|
|
|
PPCRecompilerIml_insertSegments(&ppcImlGenContext, s, 2);
|
|
imlSegment = NULL;
|
|
PPCRecImlSegment_t* imlSegmentP0 = ppcImlGenContext.segmentList2[s+0];
|
|
PPCRecImlSegment_t* imlSegmentP1 = ppcImlGenContext.segmentList2[s+1];
|
|
PPCRecImlSegment_t* imlSegmentP2 = ppcImlGenContext.segmentList2[s+2];
|
|
// create entry point segment
|
|
PPCRecompilerIml_insertSegments(&ppcImlGenContext, ppcImlGenContext.segmentList2.size(), 1);
|
|
PPCRecImlSegment_t* imlSegmentPEntry = ppcImlGenContext.segmentList2[ppcImlGenContext.segmentList2.size()-1];
|
|
// relink segments
|
|
PPCRecompilerIML_relinkInputSegment(imlSegmentP2, imlSegmentP0);
|
|
PPCRecompilerIml_setLinkBranchNotTaken(imlSegmentP0, imlSegmentP1);
|
|
PPCRecompilerIml_setLinkBranchTaken(imlSegmentP0, imlSegmentP2);
|
|
PPCRecompilerIml_setLinkBranchTaken(imlSegmentPEntry, imlSegmentP0);
|
|
// update segments
|
|
uint32 enterPPCAddress = imlSegmentP2->ppcAddrMin;
|
|
if (imlSegmentP2->isEnterable)
|
|
enterPPCAddress = imlSegmentP2->enterPPCAddress;
|
|
imlSegmentP0->ppcAddress = 0xFFFFFFFF;
|
|
imlSegmentP1->ppcAddress = 0xFFFFFFFF;
|
|
imlSegmentP2->ppcAddress = 0xFFFFFFFF;
|
|
cemu_assert_debug(imlSegmentP2->ppcAddrMin != 0);
|
|
// move segment properties from segment P2 to segment P0
|
|
imlSegmentP0->isJumpDestination = imlSegmentP2->isJumpDestination;
|
|
imlSegmentP0->jumpDestinationPPCAddress = imlSegmentP2->jumpDestinationPPCAddress;
|
|
imlSegmentP0->isEnterable = false;
|
|
//imlSegmentP0->enterPPCAddress = imlSegmentP2->enterPPCAddress;
|
|
imlSegmentP0->ppcAddrMin = imlSegmentP2->ppcAddrMin;
|
|
imlSegmentP0->ppcAddrMax = imlSegmentP2->ppcAddrMax;
|
|
imlSegmentP2->isJumpDestination = false;
|
|
imlSegmentP2->jumpDestinationPPCAddress = 0;
|
|
imlSegmentP2->isEnterable = false;
|
|
imlSegmentP2->enterPPCAddress = 0;
|
|
imlSegmentP2->ppcAddrMin = 0;
|
|
imlSegmentP2->ppcAddrMax = 0;
|
|
// setup enterable segment
|
|
if( enterPPCAddress != 0 && enterPPCAddress != 0xFFFFFFFF )
|
|
{
|
|
imlSegmentPEntry->isEnterable = true;
|
|
imlSegmentPEntry->ppcAddress = enterPPCAddress;
|
|
imlSegmentPEntry->enterPPCAddress = enterPPCAddress;
|
|
}
|
|
// assign new jumpmark to segment P2
|
|
imlSegmentP2->isJumpDestination = true;
|
|
imlSegmentP2->jumpDestinationPPCAddress = currentLoopEscapeJumpMarker;
|
|
currentLoopEscapeJumpMarker++;
|
|
// create ppc_leave instruction in segment P1
|
|
PPCRecompiler_pushBackIMLInstructions(imlSegmentP1, 0, 1);
|
|
imlSegmentP1->imlList[0].type = PPCREC_IML_TYPE_MACRO;
|
|
imlSegmentP1->imlList[0].operation = PPCREC_IML_MACRO_LEAVE;
|
|
imlSegmentP1->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlSegmentP1->imlList[0].op_macro.param = imlSegmentP0->ppcAddrMin;
|
|
imlSegmentP1->imlList[0].associatedPPCAddress = imlSegmentP0->ppcAddrMin;
|
|
// create cycle-based conditional instruction in segment P0
|
|
PPCRecompiler_pushBackIMLInstructions(imlSegmentP0, 0, 1);
|
|
imlSegmentP0->imlList[0].type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK;
|
|
imlSegmentP0->imlList[0].operation = 0;
|
|
imlSegmentP0->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlSegmentP0->imlList[0].op_conditionalJump.jumpmarkAddress = imlSegmentP2->jumpDestinationPPCAddress;
|
|
imlSegmentP0->imlList[0].associatedPPCAddress = imlSegmentP0->ppcAddrMin;
|
|
// jump instruction for PEntry
|
|
PPCRecompiler_pushBackIMLInstructions(imlSegmentPEntry, 0, 1);
|
|
PPCRecompilerImlGen_generateNewInstruction_jumpSegment(&ppcImlGenContext, imlSegmentPEntry->imlList + 0);
|
|
|
|
// skip the newly created segments
|
|
s += 2;
|
|
}
|
|
|
|
// isolate entry points from function flow (enterable segments must not be the target of any other segment)
|
|
// this simplifies logic during register allocation
|
|
PPCRecompilerIML_isolateEnterableSegments(&ppcImlGenContext);
|
|
|
|
// if GQRs can be predicted, optimize PSQ load/stores
|
|
PPCRecompiler_optimizePSQLoadAndStore(&ppcImlGenContext);
|
|
|
|
// count number of used registers
|
|
uint32 numLoadedFPRRegisters = 0;
|
|
for(uint32 i=0; i<255; i++)
|
|
{
|
|
if( ppcImlGenContext.mappedFPRRegister[i] )
|
|
numLoadedFPRRegisters++;
|
|
}
|
|
|
|
// insert name store instructions at the end of each segment but before branch instructions
|
|
for (PPCRecImlSegment_t* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
if(segIt->imlListCount == 0 )
|
|
continue; // ignore empty segments
|
|
// analyze segment for register usage
|
|
PPCImlOptimizerUsedRegisters_t registersUsed;
|
|
for(sint32 i=0; i<segIt->imlListCount; i++)
|
|
{
|
|
PPCRecompiler_checkRegisterUsage(&ppcImlGenContext, segIt->imlList+i, ®istersUsed);
|
|
//PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, registersUsed.readGPR1);
|
|
sint32 accessedTempReg[5];
|
|
// intermediate FPRs
|
|
accessedTempReg[0] = registersUsed.readFPR1;
|
|
accessedTempReg[1] = registersUsed.readFPR2;
|
|
accessedTempReg[2] = registersUsed.readFPR3;
|
|
accessedTempReg[3] = registersUsed.readFPR4;
|
|
accessedTempReg[4] = registersUsed.writtenFPR1;
|
|
for(sint32 f=0; f<5; f++)
|
|
{
|
|
if( accessedTempReg[f] == -1 )
|
|
continue;
|
|
uint32 regName = ppcImlGenContext.mappedFPRRegister[accessedTempReg[f]];
|
|
if( regName >= PPCREC_NAME_FPR0 && regName < PPCREC_NAME_FPR0+32 )
|
|
{
|
|
segIt->ppcFPRUsed[regName - PPCREC_NAME_FPR0] = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// merge certain float load+store patterns (must happen before FPR register remapping)
|
|
PPCRecompiler_optimizeDirectFloatCopies(&ppcImlGenContext);
|
|
// delay byte swapping for certain load+store patterns
|
|
PPCRecompiler_optimizeDirectIntegerCopies(&ppcImlGenContext);
|
|
|
|
if (numLoadedFPRRegisters > 0)
|
|
{
|
|
if (PPCRecompiler_manageFPRRegisters(&ppcImlGenContext) == false)
|
|
{
|
|
PPCRecompiler_freeContext(&ppcImlGenContext);
|
|
return false;
|
|
}
|
|
}
|
|
|
|
PPCRecompilerImm_allocateRegisters(&ppcImlGenContext);
|
|
|
|
// remove redundant name load and store instructions
|
|
PPCRecompiler_reorderConditionModifyInstructions(&ppcImlGenContext);
|
|
PPCRecompiler_removeRedundantCRUpdates(&ppcImlGenContext);
|
|
return true;
|
|
}
|