mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-06 06:51:18 +12:00
4785 lines
No EOL
193 KiB
C++
4785 lines
No EOL
193 KiB
C++
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterInternal.h"
|
|
#include "Cafe/HW/Espresso/Interpreter/PPCInterpreterHelper.h"
|
|
#include "Cafe/HW/Espresso/EspressoISA.h"
|
|
#include "PPCRecompiler.h"
|
|
#include "PPCRecompilerIml.h"
|
|
#include "IML/IML.h"
|
|
#include "IML/IMLRegisterAllocatorRanges.h"
|
|
#include "PPCFunctionBoundaryTracker.h"
|
|
|
|
struct PPCBasicBlockInfo
|
|
{
|
|
PPCBasicBlockInfo(uint32 startAddress, const std::set<uint32>& entryAddresses) : startAddress(startAddress), lastAddress(startAddress)
|
|
{
|
|
isEnterable = entryAddresses.find(startAddress) != entryAddresses.end();
|
|
}
|
|
|
|
uint32 startAddress;
|
|
uint32 lastAddress; // inclusive
|
|
bool isEnterable{ false };
|
|
//uint32 enterableAddress{}; -> covered by startAddress
|
|
bool hasContinuedFlow{ true }; // non-branch path goes to next segment (lastAddress+4), assumed by default
|
|
bool hasBranchTarget{ false };
|
|
uint32 branchTarget{};
|
|
|
|
// associated IML segments
|
|
IMLSegment* firstSegment{}; // first segment in chain, used as branch target for other segments
|
|
IMLSegment* appendSegment{}; // last segment in chain, new instructions should be appended to this segment
|
|
|
|
void SetInitialSegment(IMLSegment* seg)
|
|
{
|
|
cemu_assert_debug(!firstSegment);
|
|
cemu_assert_debug(!appendSegment);
|
|
firstSegment = seg;
|
|
appendSegment = seg;
|
|
}
|
|
|
|
IMLSegment* GetFirstSegmentInChain()
|
|
{
|
|
return firstSegment;
|
|
}
|
|
|
|
IMLSegment* GetSegmentForInstructionAppend()
|
|
{
|
|
return appendSegment;
|
|
}
|
|
};
|
|
|
|
bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext);
|
|
uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext);
|
|
|
|
IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
//if( ppcImlGenContext->imlListCount+1 > ppcImlGenContext->imlListSize )
|
|
//{
|
|
// sint32 newSize = ppcImlGenContext->imlListCount*2 + 2;
|
|
// ppcImlGenContext->imlList = (IMLInstruction*)realloc(ppcImlGenContext->imlList, sizeof(IMLInstruction)*newSize);
|
|
// ppcImlGenContext->imlListSize = newSize;
|
|
//}
|
|
//IMLInstruction* imlInstruction = ppcImlGenContext->imlList+ppcImlGenContext->imlListCount;
|
|
//memset(imlInstruction, 0x00, sizeof(IMLInstruction));
|
|
//imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default
|
|
//imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction;
|
|
//ppcImlGenContext->imlListCount++;
|
|
//return imlInstruction;
|
|
|
|
IMLInstruction& inst = ppcImlGenContext->currentOutputSegment->imlList.emplace_back();
|
|
memset(&inst, 0x00, sizeof(IMLInstruction));
|
|
inst.crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default
|
|
//imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction;
|
|
|
|
return &inst;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* 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")
|
|
IMLInstruction* 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")
|
|
IMLInstruction* 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)
|
|
{
|
|
// Store name (e.g. "'r3' = t0" which translates to MOV [ESP+offset_r3], reg32)
|
|
IMLInstruction* 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;
|
|
}
|
|
|
|
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")
|
|
IMLInstruction* 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, IMLInstruction* 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(IMLInstruction));
|
|
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, IMLInstruction* imlInstruction, uint32 jumpmarkAddress)
|
|
{
|
|
__debugbreak();
|
|
|
|
//// jump
|
|
//if (imlInstruction == NULL)
|
|
// imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
//else
|
|
// memset(imlInstruction, 0, sizeof(IMLInstruction));
|
|
//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, IMLInstruction* imlInstruction)
|
|
{
|
|
// jump
|
|
if (imlInstruction == NULL)
|
|
imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
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, IMLInstruction* 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)
|
|
IMLInstruction* 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)
|
|
{
|
|
__debugbreak();
|
|
|
|
//// conditional jump
|
|
//IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
//imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
//imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
//imlInstruction->op_conditionalJump.jumpAccordingToSegment = false;
|
|
//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_conditionalJumpSegment(ppcImlGenContext_t* ppcImlGenContext, uint32 jumpCondition, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet)
|
|
{
|
|
// conditional jump
|
|
IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext);
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_conditionalJump.jumpAccordingToSegment = true;
|
|
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
|
|
IMLInstruction* 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
|
|
IMLInstruction* 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
|
|
IMLInstruction* 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
|
|
IMLInstruction* 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;
|
|
}
|
|
|
|
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)
|
|
{
|
|
// split before and after to make sure the macro is in an isolated segment that we can make enterable
|
|
PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock);
|
|
ppcImlGenContext->currentOutputSegment->SetEnterable(ppcImlGenContext->ppcAddressOfCurrentInstruction);
|
|
PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext)->make_macro(PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0);
|
|
IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock);
|
|
middleSeg->SetLinkBranchTaken(nullptr);
|
|
middleSeg->SetLinkBranchNotTaken(nullptr);
|
|
}
|
|
|
|
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)
|
|
{
|
|
printf("PPCRecompilerImlGen_MFTB(): Not supported\n");
|
|
return false;
|
|
|
|
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);
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0);
|
|
IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock);
|
|
|
|
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))
|
|
{
|
|
cemu_assert_suspicious();
|
|
}
|
|
}
|
|
// 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
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
return true;
|
|
}
|
|
// is jump destination within recompiled function?
|
|
if( ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest) )
|
|
{
|
|
// jump to target within same function
|
|
PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext, nullptr);
|
|
}
|
|
else
|
|
{
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext);
|
|
|
|
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;
|
|
}
|
|
PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock;
|
|
IMLSegment* blSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock);
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, conditionMustBeTrue);
|
|
blSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
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_conditionalJumpSegment(ppcImlGenContext, PPCREC_JUMP_CONDITION_E, PPCREC_CR_REG_TEMP, 0, false);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, PPCREC_JUMP_CONDITION_NE, PPCREC_CR_REG_TEMP, 0, false);
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// branch always, no condition and no decrementer
|
|
// not supported
|
|
return false;
|
|
}
|
|
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 (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest))
|
|
{
|
|
// near jump
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, conditionMustBeTrue);
|
|
}
|
|
else
|
|
{
|
|
// far jump
|
|
debug_printf("PPCRecompilerImlGen_BC(): Far jump not supported yet");
|
|
return false;
|
|
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, jumpCondition, crRegister, crBit, !conditionMustBeTrue);
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch);
|
|
//ppcImlGenContext->emitInst().make_ppcEnter(ppcImlGenContext->ppcAddressOfCurrentInstruction + 4);
|
|
}
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
// BCCTR or BCLR
|
|
bool PPCRecompilerImlGen_BCSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 sprReg)
|
|
{
|
|
PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext);
|
|
|
|
Espresso::BOField BO;
|
|
uint32 BI;
|
|
bool LK;
|
|
Espresso::decodeOp_BCSPR(opcode, BO, BI, LK);
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
|
|
uint32 branchDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + sprReg);
|
|
if (LK)
|
|
{
|
|
if (sprReg == SPR_LR)
|
|
{
|
|
// if the branch target is LR, then preserve it in a temporary
|
|
cemu_assert_suspicious(); // this case needs testing
|
|
uint32 tmpRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, nullptr, PPCREC_IML_OP_ASSIGN, tmpRegister, branchDestReg);
|
|
branchDestReg = tmpRegister;
|
|
}
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
if (!BO.decrementerIgnore())
|
|
{
|
|
cemu_assert_unimplemented();
|
|
return false;
|
|
}
|
|
else if (!BO.conditionIgnore())
|
|
{
|
|
// no decrementer but CR check
|
|
cemu_assert_debug(ppcImlGenContext->currentBasicBlock->hasContinuedFlow);
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget);
|
|
// generate jump condition
|
|
uint32 jumpCondition = 0;
|
|
if (!BO.conditionInverted())
|
|
{
|
|
// CR bit must be set
|
|
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;
|
|
}
|
|
|
|
// write the dynamic branch instruction to a new segment that is set as a branch target for the current segment
|
|
PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock;
|
|
IMLSegment* bctrSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock);
|
|
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, !BO.conditionInverted());
|
|
|
|
|
|
bctrSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_B_TO_REG, branchDestReg, 0, 0);
|
|
}
|
|
else
|
|
{
|
|
// branch always, no condition and no decrementer check
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasContinuedFlow);
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget);
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_TO_REG, branchDestReg, 0, 0);
|
|
|
|
}
|
|
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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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.
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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.
|
|
//ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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
|
|
ppcImlGenContext->emitInst().make_macro(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;
|
|
ppcImlGenContext->emitInst().make_macro(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_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;
|
|
}
|
|
|
|
void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* 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(IMLSegmentPoint* 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(IMLSegment* imlSegment, sint32 index, sint32 shiftBackCount)
|
|
{
|
|
cemu_assert_debug(index >= 0 && index <= imlSegment->imlList.size());
|
|
|
|
imlSegment->imlList.insert(imlSegment->imlList.begin() + index, shiftBackCount, {});
|
|
|
|
memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction) * shiftBackCount);
|
|
|
|
// fill empty space with NOP instructions
|
|
for (sint32 i = 0; i < shiftBackCount; i++)
|
|
{
|
|
imlSegment->imlList[index + i].type = PPCREC_IML_TYPE_NONE;
|
|
}
|
|
|
|
// update position of segment points
|
|
if (imlSegment->segmentPointList)
|
|
{
|
|
IMLSegmentPoint* 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;
|
|
}
|
|
}
|
|
}
|
|
|
|
IMLInstruction* PPCRecompiler_insertInstruction(IMLSegment* imlSegment, sint32 index)
|
|
{
|
|
PPCRecompiler_pushBackIMLInstructions(imlSegment, index, 1);
|
|
return imlSegment->imlList.data() + index;
|
|
}
|
|
|
|
IMLInstruction* PPCRecompiler_appendInstruction(IMLSegment* imlSegment)
|
|
{
|
|
size_t index = imlSegment->imlList.size();
|
|
imlSegment->imlList.emplace_back();
|
|
memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction));
|
|
return imlSegment->imlList.data() + index;
|
|
}
|
|
|
|
IMLSegment* PPCRecompilerIml_appendSegment(ppcImlGenContext_t* ppcImlGenContext)
|
|
{
|
|
IMLSegment* segment = new IMLSegment();
|
|
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 IMLSegment();
|
|
}
|
|
|
|
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: // BCLR
|
|
if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_LR) == 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: // BCCTR
|
|
if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_CTR) == 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;
|
|
}
|
|
|
|
// returns false if code flow is not interrupted
|
|
// continueDefaultPath: Controls if
|
|
bool PPCRecompiler_CheckIfInstructionEndsSegment(PPCFunctionBoundaryTracker& boundaryTracker, uint32 instructionAddress, uint32 opcode, bool& makeNextInstEnterable, bool& continueDefaultPath, bool& hasBranchTarget, uint32& branchTarget)
|
|
{
|
|
hasBranchTarget = false;
|
|
branchTarget = 0xFFFFFFFF;
|
|
makeNextInstEnterable = false;
|
|
continueDefaultPath = false;
|
|
switch (Espresso::GetPrimaryOpcode(opcode))
|
|
{
|
|
case Espresso::PrimaryOpcode::VIRTUAL_HLE:
|
|
{
|
|
makeNextInstEnterable = true;
|
|
hasBranchTarget = false;
|
|
continueDefaultPath = false;
|
|
return true;
|
|
}
|
|
case Espresso::PrimaryOpcode::BC:
|
|
{
|
|
uint32 BD, BI;
|
|
Espresso::BOField BO;
|
|
bool AA, LK;
|
|
Espresso::decodeOp_BC(opcode, BD, BO, BI, AA, LK);
|
|
if (!LK)
|
|
{
|
|
hasBranchTarget = true;
|
|
branchTarget = (AA ? BD : BD) + instructionAddress;
|
|
if (!boundaryTracker.ContainsAddress(branchTarget))
|
|
hasBranchTarget = false; // far jump
|
|
}
|
|
makeNextInstEnterable = LK;
|
|
continueDefaultPath = true;
|
|
return true;
|
|
}
|
|
case Espresso::PrimaryOpcode::B:
|
|
{
|
|
uint32 LI;
|
|
bool AA, LK;
|
|
Espresso::decodeOp_B(opcode, LI, AA, LK);
|
|
if (!LK)
|
|
{
|
|
hasBranchTarget = true;
|
|
branchTarget = AA ? LI : LI + instructionAddress;
|
|
if (!boundaryTracker.ContainsAddress(branchTarget))
|
|
hasBranchTarget = false; // far jump
|
|
}
|
|
makeNextInstEnterable = LK;
|
|
continueDefaultPath = false;
|
|
return true;
|
|
}
|
|
case Espresso::PrimaryOpcode::GROUP_19:
|
|
switch (Espresso::GetGroup19Opcode(opcode))
|
|
{
|
|
case Espresso::Opcode19::BCLR:
|
|
case Espresso::Opcode19::BCCTR:
|
|
{
|
|
Espresso::BOField BO;
|
|
uint32 BI;
|
|
bool LK;
|
|
Espresso::decodeOp_BCSPR(opcode, BO, BI, LK);
|
|
continueDefaultPath = !BO.conditionIgnore() || !BO.decrementerIgnore(); // if branch is always taken then there is no continued path
|
|
makeNextInstEnterable = Espresso::DecodeLK(opcode);
|
|
return true;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case Espresso::PrimaryOpcode::GROUP_31:
|
|
switch (Espresso::GetGroup31Opcode(opcode))
|
|
{
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return false;
|
|
}
|
|
|
|
void PPCRecompiler_DetermineBasicBlockRange(std::vector<PPCBasicBlockInfo>& basicBlockList, PPCFunctionBoundaryTracker& boundaryTracker, uint32 ppcStart, uint32 ppcEnd, const std::set<uint32>& combinedBranchTargets, const std::set<uint32>& entryAddresses)
|
|
{
|
|
cemu_assert_debug(ppcStart <= ppcEnd);
|
|
|
|
uint32 currentAddr = ppcStart;
|
|
|
|
PPCBasicBlockInfo* curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses);
|
|
|
|
uint32 basicBlockStart = currentAddr;
|
|
while (currentAddr <= ppcEnd)
|
|
{
|
|
curBlockInfo->lastAddress = currentAddr;
|
|
uint32 opcode = memory_readU32(currentAddr);
|
|
bool nextInstIsEnterable = false;
|
|
bool hasBranchTarget = false;
|
|
bool hasContinuedFlow = false;
|
|
uint32 branchTarget = 0;
|
|
if (PPCRecompiler_CheckIfInstructionEndsSegment(boundaryTracker, currentAddr, opcode, nextInstIsEnterable, hasContinuedFlow, hasBranchTarget, branchTarget))
|
|
{
|
|
curBlockInfo->hasBranchTarget = hasBranchTarget;
|
|
curBlockInfo->branchTarget = branchTarget;
|
|
curBlockInfo->hasContinuedFlow = hasContinuedFlow;
|
|
// start new basic block, except if this is the last instruction
|
|
if (currentAddr >= ppcEnd)
|
|
break;
|
|
curBlockInfo = &basicBlockList.emplace_back(currentAddr + 4, entryAddresses);
|
|
curBlockInfo->isEnterable = curBlockInfo->isEnterable || nextInstIsEnterable;
|
|
currentAddr += 4;
|
|
continue;
|
|
}
|
|
currentAddr += 4;
|
|
if (currentAddr <= ppcEnd)
|
|
{
|
|
if (combinedBranchTargets.find(currentAddr) != combinedBranchTargets.end())
|
|
{
|
|
// instruction is branch target, start new basic block
|
|
curBlockInfo = &basicBlockList.emplace_back(currentAddr, entryAddresses);
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
std::vector<PPCBasicBlockInfo> PPCRecompiler_DetermineBasicBlockRange(PPCFunctionBoundaryTracker& boundaryTracker, const std::set<uint32>& entryAddresses)
|
|
{
|
|
cemu_assert(!entryAddresses.empty());
|
|
std::vector<PPCBasicBlockInfo> basicBlockList;
|
|
|
|
const std::set<uint32> branchTargets = boundaryTracker.GetBranchTargets();
|
|
auto funcRanges = boundaryTracker.GetRanges();
|
|
|
|
std::set<uint32> combinedBranchTargets = branchTargets;
|
|
combinedBranchTargets.insert(entryAddresses.begin(), entryAddresses.end());
|
|
|
|
for (auto& funcRangeIt : funcRanges)
|
|
PPCRecompiler_DetermineBasicBlockRange(basicBlockList, boundaryTracker, funcRangeIt.startAddress, funcRangeIt.startAddress + funcRangeIt.length - 4, combinedBranchTargets, entryAddresses);
|
|
|
|
// mark all segments that start at entryAddresses as enterable (debug code for verification, can be removed)
|
|
size_t numMarkedEnterable = 0;
|
|
for (auto& basicBlockIt : basicBlockList)
|
|
{
|
|
if (entryAddresses.find(basicBlockIt.startAddress) != entryAddresses.end())
|
|
{
|
|
cemu_assert_debug(basicBlockIt.isEnterable);
|
|
numMarkedEnterable++;
|
|
}
|
|
}
|
|
cemu_assert_debug(numMarkedEnterable == entryAddresses.size());
|
|
|
|
// todo - inline BL, currently this is done in the instruction handler of BL but this will mean that instruction cycle increasing is ignored
|
|
|
|
return basicBlockList;
|
|
}
|
|
|
|
bool PPCIMLGen_FillBasicBlock(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
|
|
{
|
|
ppcImlGenContext.currentOutputSegment = basicBlockInfo.GetSegmentForInstructionAppend();
|
|
ppcImlGenContext.currentInstruction = (uint32*)(memory_base + basicBlockInfo.startAddress);
|
|
|
|
uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction;
|
|
uint32* endCurrentInstruction = (uint32*)(memory_base + basicBlockInfo.lastAddress);
|
|
|
|
while (ppcImlGenContext.currentInstruction <= endCurrentInstruction)
|
|
{
|
|
uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base);
|
|
ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction;
|
|
if (PPCRecompiler_decodePPCInstruction(&ppcImlGenContext))
|
|
{
|
|
debug_printf("Recompiler encountered unsupported instruction at 0x%08x\n", addressOfCurrentInstruction);
|
|
ppcImlGenContext.currentOutputSegment = nullptr;
|
|
return false;
|
|
}
|
|
}
|
|
ppcImlGenContext.currentOutputSegment = nullptr;
|
|
return true;
|
|
}
|
|
|
|
// returns split segment from which the continued segment is available via seg->GetBranchNotTaken()
|
|
IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
|
|
{
|
|
IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend();
|
|
|
|
IMLSegment* continuedSegment = ppcImlGenContext.InsertSegment(ppcImlGenContext.GetSegmentIndex(writeSegment) + 1);
|
|
|
|
continuedSegment->SetLinkBranchTaken(writeSegment->GetBranchTaken());
|
|
continuedSegment->SetLinkBranchNotTaken(writeSegment->GetBranchNotTaken());
|
|
|
|
writeSegment->SetLinkBranchNotTaken(continuedSegment);
|
|
writeSegment->SetLinkBranchTaken(nullptr);
|
|
|
|
if (ppcImlGenContext.currentOutputSegment == writeSegment)
|
|
ppcImlGenContext.currentOutputSegment = continuedSegment;
|
|
|
|
cemu_assert_debug(basicBlockInfo.appendSegment == writeSegment);
|
|
basicBlockInfo.appendSegment = continuedSegment;
|
|
|
|
return writeSegment;
|
|
}
|
|
|
|
// generates a new segment and sets it as branch target for the current write segment. Returns the created segment
|
|
IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
|
|
{
|
|
IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend();
|
|
IMLSegment* branchTargetSegment = ppcImlGenContext.NewSegment();
|
|
cemu_assert_debug(!writeSegment->GetBranchTaken()); // must not have a target already
|
|
writeSegment->SetLinkBranchTaken(branchTargetSegment);
|
|
return branchTargetSegment;
|
|
}
|
|
|
|
// verify that current instruction is the last instruction of the active basic block
|
|
void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext)
|
|
{
|
|
cemu_assert_debug(ppcImlGenContext.currentBasicBlock->lastAddress == ppcImlGenContext.ppcAddressOfCurrentInstruction);
|
|
}
|
|
|
|
void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo)
|
|
{
|
|
IMLSegment* imlSegment = basicBlockInfo.GetFirstSegmentInChain();
|
|
if (!basicBlockInfo.hasBranchTarget)
|
|
return;
|
|
if (basicBlockInfo.branchTarget > basicBlockInfo.startAddress)
|
|
return;
|
|
|
|
// exclude non-infinite tight loops
|
|
if (IMLAnalyzer_IsTightFiniteLoop(imlSegment))
|
|
return;
|
|
|
|
// make the segment enterable so execution can return after passing a check
|
|
basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress);
|
|
|
|
IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo);
|
|
splitSeg->AppendInstruction()->make_cjump_cycle_check();
|
|
|
|
IMLSegment* exitSegment = ppcImlGenContext.NewSegment();
|
|
splitSeg->SetLinkBranchTaken(exitSegment);
|
|
|
|
exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0);
|
|
}
|
|
|
|
void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext)
|
|
{
|
|
for (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
bool isLastSegment = segIt == ppcImlGenContext.segmentList2.back();
|
|
// handle empty segment
|
|
if (segIt->imlList.empty())
|
|
{
|
|
cemu_assert_debug(segIt->GetBranchNotTaken());
|
|
continue;
|
|
}
|
|
// check last instruction of segment
|
|
IMLInstruction* imlInstruction = segIt->GetLastInstruction();
|
|
if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK)
|
|
{
|
|
cemu_assert_debug(segIt->GetBranchTaken());
|
|
if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE)
|
|
{
|
|
cemu_assert_debug(segIt->GetBranchNotTaken());
|
|
}
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO)
|
|
{
|
|
auto macroType = imlInstruction->operation;
|
|
switch (macroType)
|
|
{
|
|
case PPCREC_IML_MACRO_B_TO_REG:
|
|
case PPCREC_IML_MACRO_BL:
|
|
case PPCREC_IML_MACRO_B_FAR:
|
|
case PPCREC_IML_MACRO_HLE:
|
|
case PPCREC_IML_MACRO_LEAVE:
|
|
segIt->nextSegmentIsUncertain = true;
|
|
break;
|
|
case PPCREC_IML_MACRO_DEBUGBREAK:
|
|
case PPCREC_IML_MACRO_COUNT_CYCLES:
|
|
case PPCREC_IML_MACRO_MFTB:
|
|
break;
|
|
default:
|
|
cemu_assert_unimplemented();
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
bool PPCRecompiler_GenerateIML(ppcImlGenContext_t& ppcImlGenContext, PPCFunctionBoundaryTracker& boundaryTracker, std::set<uint32>& entryAddresses)
|
|
{
|
|
std::vector<PPCBasicBlockInfo> basicBlockList = PPCRecompiler_DetermineBasicBlockRange(boundaryTracker, entryAddresses);
|
|
|
|
// create segments
|
|
std::unordered_map<uint32, PPCBasicBlockInfo*> addrToBB;
|
|
ppcImlGenContext.segmentList2.resize(basicBlockList.size());
|
|
for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
{
|
|
PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i];
|
|
IMLSegment* seg = new IMLSegment();
|
|
seg->ppcAddress = basicBlockInfo.startAddress;
|
|
if(basicBlockInfo.isEnterable)
|
|
seg->SetEnterable(basicBlockInfo.startAddress);
|
|
ppcImlGenContext.segmentList2[i] = seg;
|
|
cemu_assert_debug(addrToBB.find(basicBlockInfo.startAddress) == addrToBB.end());
|
|
basicBlockInfo.SetInitialSegment(seg);
|
|
addrToBB.emplace(basicBlockInfo.startAddress, &basicBlockInfo);
|
|
}
|
|
// link segments
|
|
for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
{
|
|
PPCBasicBlockInfo& bbInfo = basicBlockList[i];
|
|
cemu_assert_debug(bbInfo.GetFirstSegmentInChain() == bbInfo.GetSegmentForInstructionAppend());
|
|
IMLSegment* seg = ppcImlGenContext.segmentList2[i];
|
|
if (bbInfo.hasBranchTarget)
|
|
{
|
|
PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.branchTarget];
|
|
cemu_assert_debug(targetBB);
|
|
IMLSegment_SetLinkBranchTaken(seg, targetBB->GetFirstSegmentInChain());
|
|
}
|
|
if (bbInfo.hasContinuedFlow)
|
|
{
|
|
PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.lastAddress + 4];
|
|
if (!targetBB)
|
|
{
|
|
cemuLog_log(LogType::Recompiler, "Recompiler was unable to link segment [0x{:08x}-0x{:08x}] to 0x{:08x}", bbInfo.startAddress, bbInfo.lastAddress, bbInfo.lastAddress + 4);
|
|
return false;
|
|
}
|
|
cemu_assert_debug(targetBB);
|
|
IMLSegment_SetLinkBranchNotTaken(seg, targetBB->GetFirstSegmentInChain());
|
|
}
|
|
}
|
|
// we assume that all unreachable segments are potentially enterable
|
|
// todo - mark them as such
|
|
|
|
|
|
// generate cycle counters
|
|
// in theory we could generate these as part of FillBasicBlock() but in the future we might use more complex logic to emit fewer operations
|
|
for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
{
|
|
PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i];
|
|
IMLSegment* seg = basicBlockInfo.GetSegmentForInstructionAppend();
|
|
|
|
uint32 ppcInstructionCount = (basicBlockInfo.lastAddress - basicBlockInfo.startAddress + 4) / 4;
|
|
cemu_assert_debug(ppcInstructionCount > 0);
|
|
|
|
PPCRecompiler_pushBackIMLInstructions(seg, 0, 1);
|
|
seg->imlList[0].type = PPCREC_IML_TYPE_MACRO;
|
|
seg->imlList[0].crRegister = PPC_REC_INVALID_REGISTER;
|
|
seg->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES;
|
|
seg->imlList[0].op_macro.param = ppcInstructionCount;
|
|
}
|
|
|
|
// generate cycle check instructions
|
|
// note: Introduces new segments
|
|
for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
{
|
|
PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i];
|
|
PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext, basicBlockInfo);
|
|
}
|
|
|
|
// fill in all the basic blocks
|
|
// note: This step introduces new segments as is necessary for some instructions
|
|
for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
{
|
|
PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i];
|
|
ppcImlGenContext.currentBasicBlock = &basicBlockInfo;
|
|
if (!PPCIMLGen_FillBasicBlock(ppcImlGenContext, basicBlockInfo))
|
|
return false;
|
|
ppcImlGenContext.currentBasicBlock = nullptr;
|
|
}
|
|
|
|
// mark segments with unknown jump destination (e.g. BLR and most macros)
|
|
PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext);
|
|
|
|
// debug - check segment graph
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
//for (size_t i = 0; i < basicBlockList.size(); i++)
|
|
//{
|
|
// IMLSegment* seg = ppcImlGenContext.segmentList2[i];
|
|
// if (seg->list_prevSegments.empty())
|
|
// {
|
|
// cemu_assert_debug(seg->isEnterable);
|
|
// }
|
|
//}
|
|
// debug - check if suffix instructions are at the end of segments and if they are present for branching segments
|
|
for (size_t segIndex = 0; segIndex < ppcImlGenContext.segmentList2.size(); segIndex++)
|
|
{
|
|
IMLSegment* seg = ppcImlGenContext.segmentList2[segIndex];
|
|
IMLSegment* nextSeg = (segIndex+1) < ppcImlGenContext.segmentList2.size() ? ppcImlGenContext.segmentList2[segIndex + 1] : nullptr;
|
|
|
|
if (seg->imlList.size() > 0)
|
|
{
|
|
for (size_t f = 0; f < seg->imlList.size() - 1; f++)
|
|
{
|
|
if (seg->imlList[f].IsSuffixInstruction())
|
|
{
|
|
debug_printf("---------------- SegmentDump (Suffix instruction at wrong pos in segment 0x%x):\n", (int)segIndex);
|
|
IMLDebug_Dump(&ppcImlGenContext);
|
|
__debugbreak();
|
|
}
|
|
}
|
|
}
|
|
if (seg->nextSegmentBranchTaken)
|
|
{
|
|
if (!seg->HasSuffixInstruction())
|
|
{
|
|
debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", (int)segIndex);
|
|
IMLDebug_Dump(&ppcImlGenContext);
|
|
__debugbreak();
|
|
}
|
|
}
|
|
if (seg->nextSegmentBranchNotTaken)
|
|
{
|
|
// if branch not taken, flow must continue to next segment in sequence
|
|
cemu_assert_debug(seg->nextSegmentBranchNotTaken == nextSeg);
|
|
}
|
|
// more detailed checks based on actual suffix instruction
|
|
if (seg->imlList.size() > 0)
|
|
{
|
|
IMLInstruction* inst = seg->GetLastInstruction();
|
|
if (inst->type == PPCREC_IML_TYPE_MACRO && inst->op_macro.param == PPCREC_IML_MACRO_B_FAR)
|
|
{
|
|
cemu_assert_debug(!seg->GetBranchTaken());
|
|
cemu_assert_debug(!seg->GetBranchNotTaken());
|
|
}
|
|
if (inst->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK)
|
|
{
|
|
cemu_assert_debug(seg->GetBranchTaken());
|
|
cemu_assert_debug(seg->GetBranchNotTaken());
|
|
}
|
|
if (inst->type == PPCREC_IML_TYPE_CJUMP)
|
|
{
|
|
if (inst->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE)
|
|
{
|
|
if (!seg->GetBranchTaken() || !seg->GetBranchNotTaken())
|
|
{
|
|
debug_printf("---------------- SegmentDump (Missing branch for CJUMP in segment 0x%x):\n", (int)segIndex);
|
|
IMLDebug_Dump(&ppcImlGenContext);
|
|
cemu_assert_error();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// proper error checking for branch-always (or branch-never if invert bit is set)
|
|
}
|
|
}
|
|
}
|
|
segIndex++;
|
|
}
|
|
#endif
|
|
|
|
|
|
// todos:
|
|
// - basic block determination should look for the B(L) B(L) pattern. Or maybe just mark every bb without any input segments as an entry segment
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set<uint32>& entryAddresses, PPCFunctionBoundaryTracker& boundaryTracker)
|
|
{
|
|
ppcImlGenContext.functionRef = ppcRecFunc; // todo - remove this and replace internally with boundary tracker
|
|
ppcImlGenContext.boundaryTracker = &boundaryTracker;
|
|
|
|
if (!PPCRecompiler_GenerateIML(ppcImlGenContext, boundaryTracker, entryAddresses))
|
|
return false;
|
|
|
|
// 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++;
|
|
// ppcImlGenContext.emitInst().make_jumpmark(addressOfCurrentInstruction);
|
|
// if (entryAddresses.find(addressOfCurrentInstruction) != entryAddresses.end())
|
|
// {
|
|
// // add PPCEnter for addresses that are in entryAddresses
|
|
// ppcImlGenContext.emitInst().make_ppcEnter(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)
|
|
// ppcImlGenContext.emitInst().make_ppcEnter(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
|
|
// ppcImlGenContext.emitInst().make_ppcEnter(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 )
|
|
// {
|
|
// debug_printf("Failed recompile due to unknown instruction at 0x%08x\n", unsupportedInstructionLastOffset);
|
|
// return false;
|
|
// }
|
|
// // optimize unused jumpmarks away
|
|
// // first, flag all jumpmarks as unused
|
|
// std::map<uint32, IMLInstruction*> 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(IMLInstruction));
|
|
// }
|
|
// 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
|
|
// IMLSegment* 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 )
|
|
// {
|
|
// IMLSegment* ppcRecSegment = PPCRecompilerIml_appendSegment(&ppcImlGenContext);
|
|
// ppcRecSegment->startOffset = segmentStart;
|
|
// ppcRecSegment->count = segmentImlIndex-segmentStart;
|
|
// ppcRecSegment->ppcAddress = 0xFFFFFFFF;
|
|
// segmentStart = segmentImlIndex;
|
|
// }
|
|
// }
|
|
// segmentImlIndex++;
|
|
// }
|
|
// if( segmentImlIndex != segmentStart )
|
|
// {
|
|
// // final segment
|
|
// IMLSegment* ppcRecSegment = PPCRecompilerIml_appendSegment(&ppcImlGenContext);
|
|
// ppcRecSegment->startOffset = segmentStart;
|
|
// ppcRecSegment->count = segmentImlIndex-segmentStart;
|
|
// ppcRecSegment->ppcAddress = 0xFFFFFFFF;
|
|
// segmentStart = segmentImlIndex;
|
|
// }
|
|
// // move iml instructions into the segments
|
|
// for (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
// {
|
|
// uint32 imlStartIndex = segIt->startOffset;
|
|
// uint32 imlCount = segIt->count;
|
|
// if( imlCount > 0 )
|
|
// {
|
|
// cemu_assert_debug(segIt->imlList.empty());
|
|
// segIt->imlList.insert(segIt->imlList.begin(), ppcImlGenContext.imlList + imlStartIndex, ppcImlGenContext.imlList + imlStartIndex + imlCount);
|
|
//
|
|
// }
|
|
// else
|
|
// {
|
|
// // empty segments are allowed so we can handle multiple PPC entry addresses pointing to the same code
|
|
// cemu_assert_debug(segIt->imlList.empty());
|
|
// }
|
|
// segIt->startOffset = 9999999;
|
|
// segIt->count = 9999999;
|
|
// }
|
|
// // clear segment-independent iml list
|
|
// free(ppcImlGenContext.imlList);
|
|
// ppcImlGenContext.imlList = nullptr;
|
|
// 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 (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
// {
|
|
// uint32 segmentPPCAddrMin = 0xFFFFFFFF;
|
|
// uint32 segmentPPCAddrMax = 0x00000000;
|
|
// for(sint32 i=0; i< segIt->imlList.size(); 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 (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
// {
|
|
// while (segIt->imlList.size() > 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 (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
if (segIt->nextSegmentBranchNotTaken == nullptr || segIt->nextSegmentBranchTaken == nullptr)
|
|
continue; // not a branching segment
|
|
IMLInstruction* lastInstruction = segIt->GetLastInstruction();
|
|
if (lastInstruction->type != PPCREC_IML_TYPE_CJUMP || lastInstruction->op_conditionalJump.crRegisterIndex != 0)
|
|
continue;
|
|
IMLSegment* conditionalSegment = segIt->nextSegmentBranchNotTaken;
|
|
IMLSegment* finalSegment = segIt->nextSegmentBranchTaken;
|
|
if (segIt->nextSegmentBranchTaken != segIt->nextSegmentBranchNotTaken->nextSegmentBranchNotTaken)
|
|
continue;
|
|
if (segIt->nextSegmentBranchNotTaken->imlList.size() > 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->imlList.size(); f++)
|
|
{
|
|
IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + 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->imlList.size(); f++)
|
|
{
|
|
IMLInstruction* imlInstruction = conditionalSegment->imlList.data() + 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
|
|
IMLSegment_RemoveLink(segIt, conditionalSegment);
|
|
IMLSegment_RemoveLink(segIt, finalSegment);
|
|
IMLSegment_RemoveLink(conditionalSegment, finalSegment);
|
|
IMLSegment_SetLinkBranchNotTaken(segIt, finalSegment);
|
|
// remove all instructions from conditional segment
|
|
conditionalSegment->imlList.clear();
|
|
|
|
// 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()
|
|
IMLSegment_RemoveLink(segIt, finalSegment);
|
|
if (finalSegment->nextSegmentBranchNotTaken)
|
|
{
|
|
IMLSegment* tempSegment = finalSegment->nextSegmentBranchNotTaken;
|
|
IMLSegment_RemoveLink(finalSegment, tempSegment);
|
|
IMLSegment_SetLinkBranchNotTaken(segIt, tempSegment);
|
|
}
|
|
if (finalSegment->nextSegmentBranchTaken)
|
|
{
|
|
IMLSegment* tempSegment = finalSegment->nextSegmentBranchTaken;
|
|
IMLSegment_RemoveLink(finalSegment, tempSegment);
|
|
IMLSegment_SetLinkBranchTaken(segIt, tempSegment);
|
|
}
|
|
// copy IML instructions
|
|
cemu_assert_debug(segIt != finalSegment);
|
|
for (sint32 f = 0; f < finalSegment->imlList.size(); f++)
|
|
{
|
|
memcpy(PPCRecompiler_appendInstruction(segIt), finalSegment->imlList.data() + f, sizeof(IMLInstruction));
|
|
}
|
|
finalSegment->imlList.clear();
|
|
}
|
|
|
|
// 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 (IMLSegment* 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->imlList.size(); 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;
|
|
// }
|
|
//}
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompiler_FixLoops(ppcImlGenContext_t& ppcImlGenContext)
|
|
{
|
|
return; // deprecated
|
|
|
|
//// 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?)
|
|
// IMLSegment* imlSegment = ppcImlGenContext.segmentList2[s];
|
|
// if (imlSegment->imlList.empty())
|
|
// continue;
|
|
// if (imlSegment->imlList[imlSegment->imlList.size() - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlList.size() - 1].op_conditionalJump.jumpmarkAddress > imlSegment->ppcAddrMin)
|
|
// continue;
|
|
// if (imlSegment->imlList[imlSegment->imlList.size() - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlList.size() - 1].op_conditionalJump.jumpAccordingToSegment)
|
|
// continue;
|
|
|
|
// // exclude non-infinite tight loops
|
|
// if (IMLAnalyzer_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");
|
|
|
|
// PPCRecompilerIml_insertSegments(&ppcImlGenContext, s, 2);
|
|
// imlSegment = NULL;
|
|
// IMLSegment* imlSegmentP0 = ppcImlGenContext.segmentList2[s + 0];
|
|
// IMLSegment* imlSegmentP1 = ppcImlGenContext.segmentList2[s + 1];
|
|
// IMLSegment* imlSegmentP2 = ppcImlGenContext.segmentList2[s + 2];
|
|
// // create entry point segment
|
|
// PPCRecompilerIml_insertSegments(&ppcImlGenContext, ppcImlGenContext.segmentList2.size(), 1);
|
|
// IMLSegment* imlSegmentPEntry = ppcImlGenContext.segmentList2[ppcImlGenContext.segmentList2.size() - 1];
|
|
// // relink segments
|
|
// IMLSegment_RelinkInputSegment(imlSegmentP2, imlSegmentP0);
|
|
// IMLSegment_SetLinkBranchNotTaken(imlSegmentP0, imlSegmentP1);
|
|
// IMLSegment_SetLinkBranchTaken(imlSegmentP0, imlSegmentP2);
|
|
// IMLSegment_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.data() + 0);
|
|
|
|
// // skip the newly created segments
|
|
// s += 2;
|
|
//}
|
|
} |