mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 14:59:26 -04:00
Intermediate commit while I'm still fixing things but I didn't want to pile on too many changes in a single commit. New: Reworked PPC->IML converter to first create a graph of basic blocks and then turn those into IML segment(s). This was mainly done to decouple IML design from having PPC specific knowledge like branch target addresses. The previous design also didn't allow to preserve cycle counting properly in all cases since it was based on IML instruction counting. The new solution supports functions with non-continuous body. A pretty common example for this is when functions end with a trailing B instruction to some other place. Current limitations: - BL inlining not implemented - MFTB not implemented - BCCTR and BCLR are only partially implemented Undo vcpkg change
5053 lines
No EOL
205 KiB
C++
5053 lines
No EOL
205 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->associatedPPCAddress = 0;
|
|
imlInstruction->type = PPCREC_IML_TYPE_CJUMP;
|
|
imlInstruction->crRegister = PPC_REC_INVALID_REGISTER;
|
|
imlInstruction->op_conditionalJump.jumpmarkAddress = 0;
|
|
imlInstruction->op_conditionalJump.jumpAccordingToSegment = true;
|
|
imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE;
|
|
imlInstruction->op_conditionalJump.crRegisterIndex = 0;
|
|
imlInstruction->op_conditionalJump.crBitIndex = 0;
|
|
imlInstruction->op_conditionalJump.bitMustBeSet = false;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext_t* ppcImlGenContext, 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;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BCLR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext);
|
|
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_XL(opcode, BO, BI, BD);
|
|
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
|
|
uint32 jumpCondition = 0;
|
|
|
|
bool conditionMustBeTrue = (BO&8)!=0;
|
|
bool useDecrementer = (BO&4)==0; // bit not set -> decrement
|
|
bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0
|
|
bool ignoreCondition = (BO&16)!=0;
|
|
bool saveLR = (opcode&PPC_OPC_LK)!=0;
|
|
// since we skip this instruction if the condition is true, we need to invert the logic
|
|
//bool invertedConditionMustBeTrue = !conditionMustBeTrue;
|
|
if( useDecrementer )
|
|
{
|
|
cemu_assert_debug(false);
|
|
return false; // unsupported
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// branch always, no condition and no decrementer check
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasContinuedFlow);
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget);
|
|
if( saveLR )
|
|
{
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BLRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
else
|
|
{
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cemu_assert_debug(ppcImlGenContext->currentBasicBlock->hasContinuedFlow);
|
|
cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget);
|
|
|
|
//debug_printf("[Rec-Disable] BCLR with condition or LR\n");
|
|
//return false;
|
|
|
|
// store LR
|
|
if( saveLR )
|
|
{
|
|
cemu_assert_unimplemented(); // todo - this is difficult to handle because it needs to jump to the unmodified LR (we should cache it in a register which we pass to the macro?)
|
|
return false;
|
|
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// generate jump condition
|
|
if(conditionMustBeTrue)
|
|
{
|
|
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;
|
|
}
|
|
|
|
//if(conditionMustBeTrue)
|
|
// ppcImlGenContext->emitInst().make_debugbreak(ppcImlGenContext->ppcAddressOfCurrentInstruction);
|
|
|
|
// write the BCTR 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, conditionMustBeTrue);
|
|
|
|
bctrSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_BLR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_BCCTR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext);
|
|
|
|
uint32 BO, BI, BD;
|
|
PPC_OPC_TEMPL_XL(opcode, BO, BI, BD);
|
|
|
|
uint32 crRegister = BI/4;
|
|
uint32 crBit = BI%4;
|
|
|
|
uint32 jumpCondition = 0;
|
|
|
|
bool conditionMustBeTrue = (BO&8)!=0;
|
|
bool useDecrementer = (BO&4)==0; // bit not set -> decrement
|
|
bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0
|
|
bool ignoreCondition = (BO&16)!=0;
|
|
bool saveLR = (opcode&PPC_OPC_LK)!=0;
|
|
|
|
// since we skip this instruction if the condition is true, we need to invert the logic
|
|
bool invertedConditionMustBeTrue = !conditionMustBeTrue;
|
|
if( useDecrementer )
|
|
{
|
|
assert_dbg();
|
|
// if added, dont forget inverted logic
|
|
debug_printf("Rec: BCLR unsupported decrementer\n");
|
|
return false; // unsupported
|
|
}
|
|
else
|
|
{
|
|
if( ignoreCondition )
|
|
{
|
|
// branch always, no condition and no decrementer
|
|
if( saveLR )
|
|
{
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction+4)&0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
if (saveLR)
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BCTRL, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
else
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
else
|
|
{
|
|
// get jump condition
|
|
if (invertedConditionMustBeTrue)
|
|
{
|
|
if (crBit == 0)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_L;
|
|
else if (crBit == 1)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_G;
|
|
else if (crBit == 2)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_E;
|
|
else if (crBit == 3)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW;
|
|
}
|
|
else
|
|
{
|
|
if (crBit == 0)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_GE;
|
|
else if (crBit == 1)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_LE;
|
|
else if (crBit == 2)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NE;
|
|
else if (crBit == 3)
|
|
jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW;
|
|
}
|
|
|
|
// debug checks
|
|
//if (saveLR)
|
|
// cemu_assert_debug(ppcImlGenContext->currentBasicBlock->);
|
|
|
|
// we always store LR
|
|
if (saveLR)
|
|
{
|
|
uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_LR);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, (ppcImlGenContext->ppcAddressOfCurrentInstruction + 4) & 0x7FFFFFFF, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
|
|
// write the BCTR instruction to a new segment that is set as a branch target for the current segment
|
|
__debugbreak();
|
|
PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock;
|
|
IMLSegment* bctrSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock);
|
|
|
|
//PPCBasicBlockInfo* bctrSeg = currentBasicBlock->Get
|
|
__debugbreak();
|
|
|
|
|
|
// jump if BCLR condition NOT met (jump to jumpmark of next instruction, essentially skipping current instruction)
|
|
PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction+4, jumpCondition, crRegister, crBit, invertedConditionMustBeTrue);
|
|
ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BCTR, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, ppcImlGenContext->cyclesSinceLastBranch);
|
|
}
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ISYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// does not need to be translated
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// does not need to be translated
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB];
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; -> Update carry
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = hCPU->gpr[rA] + hCPU->gpr[rB] + ca;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//uint32 ca = hCPU->xer_ca;
|
|
//hCPU->gpr[rD] = a + ca;
|
|
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
// move rA to rD
|
|
if( registerRA != registerRD )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//uint32 ca = hCPU->xer_ca;
|
|
//hCPU->gpr[rD] = a + ca + -1;
|
|
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
// move rA to rD
|
|
if( registerRA != registerRD )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA);
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
//hCPU->gpr[rD] = (rA ? (int)hCPU->gpr[rA] : 0) + (int)imm;
|
|
if( rA != 0 )
|
|
{
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, imm);
|
|
}
|
|
else
|
|
{
|
|
// rA not used, instruction is value assignment
|
|
// rD = imm
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm);
|
|
if( rA != 0 )
|
|
{
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, (sint32)imm);
|
|
}
|
|
else
|
|
{
|
|
// rA not used, instruction turns into simple value assignment
|
|
// rD = imm
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// rD = rA + imm;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm);
|
|
// never updates any cr
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
// this opcode is identical to ADDIC but additionally it updates CR0
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// rD = rA + imm;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
// check if rD is already loaded, else use new temporary register
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
|
// rD = rB - rA
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + ca;
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
if( rB != 0 )
|
|
debugBreakpoint();
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1;
|
|
// rD = rB - rA
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, registerRB);
|
|
if (opcode & PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SUBFIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
//uint32 a = hCPU->gpr[rA];
|
|
//hCPU->gpr[rD] = ~a + imm + 1;
|
|
// cr0 is never affected
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, imm);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rD, rA;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
// mulli instruction does not modify any flags
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand, (sint32)imm);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = hCPU->gpr[rA] * hCPU->gpr[rB];
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_OE)
|
|
{
|
|
return false;
|
|
}
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = ((sint64)(sint32)hCPU->gpr[rA] * (sint64)(sint32)hCPU->gpr[rB])>>32;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
//hCPU->gpr[rD] = (hCPU->gpr[rA] * hCPU->gpr[rB])>>32;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = (sint32)a / (sint32)b;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
// hCPU->gpr[rD] = (uint32)a / (uint32)b;
|
|
uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false);
|
|
uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, SH, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
|
|
uint32 mask = ppc_mask(MB, ME);
|
|
//uint32 v = ppc_word_rotl(hCPU->gpr[rS], SH);
|
|
//hCPU->gpr[rA] = v & mask;
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// handle special forms of RLWINM
|
|
if( SH == 0 && SH == (ME-SH) && MB == 0 )
|
|
{
|
|
// CLRRWI
|
|
// todo
|
|
}
|
|
else if( ME == (31-SH) && MB == 0 )
|
|
{
|
|
// SLWI
|
|
if(opcode&PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH);
|
|
return true;
|
|
}
|
|
else if( SH == (32-MB) && ME == 31 )
|
|
{
|
|
// SRWI
|
|
if(opcode&PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB);
|
|
return true;
|
|
}
|
|
// general handler
|
|
if( registerRA != registerRS )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRA, registerRS);
|
|
if( SH != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, SH, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
if(opcode&PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
if( mask != 0xFFFFFFFF )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, SH, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
// pack RLWIMI parameters into single integer
|
|
uint32 vImm = MB|(ME<<8)|(SH<<16);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RLWIMI, registerRA, registerRS, (sint32)vImm, PPC_REC_INVALID_REGISTER, 0);
|
|
if (opcode & PPC_OPC_RC)
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRA, registerRA, 0, PPCREC_CR_MODE_LOGICAL);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB, MB, ME;
|
|
PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME);
|
|
// uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]);
|
|
uint32 mask = ppc_mask(MB, ME);
|
|
// uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]);
|
|
// hCPU->gpr[rA] = v & mask;
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, registerRS, registerRB);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
if( mask != 0xFFFFFFFF )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRAW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
//uint32 SH = hCPU->gpr[rB] & 0x3f;
|
|
//hCPU->gpr[rA] = hCPU->gpr[rS];
|
|
//hCPU->xer_ca = 0;
|
|
//if (hCPU->gpr[rA] & 0x80000000) {
|
|
// uint32 ca = 0;
|
|
// for (uint32 i=0; i < SH; i++) {
|
|
// if (hCPU->gpr[rA] & 1) ca = 1;
|
|
// hCPU->gpr[rA] >>= 1;
|
|
// hCPU->gpr[rA] |= 0x80000000;
|
|
// }
|
|
// if (ca) hCPU->xer_ca = 1;
|
|
//} else {
|
|
// if (SH > 31) {
|
|
// hCPU->gpr[rA] = 0;
|
|
// } else {
|
|
// hCPU->gpr[rA] >>= SH;
|
|
// }
|
|
//}
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rA]);
|
|
//}
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( (opcode&PPC_OPC_RC) != 0 )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRAWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA;
|
|
uint32 SH;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, SH);
|
|
cemu_assert_debug(SH < 32);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if( opcode&PPC_OPC_RC )
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH, 0, PPCREC_CR_MODE_LOGICAL);
|
|
else
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH);
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if (opcode & PPC_OPC_RC)
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
|
|
bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
PPC_ASSERT(rB==0);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if ( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
if ( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rS, rA, rB;
|
|
PPC_OPC_TEMPL_X(opcode, rS, rA, rB);
|
|
PPC_ASSERT(rB==0);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
return false;
|
|
}
|
|
uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false);
|
|
uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA);
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_CNTLZW, registerRA, registerRS);
|
|
|
|
//uint32 n=0;
|
|
//uint32 x=0x80000000;
|
|
//uint32 v=hCPU->gpr[rS];
|
|
//while (!(v & x)) {
|
|
// n++;
|
|
// if (n==32) break;
|
|
// x>>=1;
|
|
//}
|
|
//hCPU->gpr[rA] = n;
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rA]);
|
|
//}
|
|
|
|
|
|
return true;
|
|
}
|
|
|
|
bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
sint32 rD, rA, rB;
|
|
PPC_OPC_TEMPL_XO(opcode, rD, rA, rB);
|
|
PPC_ASSERT(rB == 0);
|
|
//hCPU->gpr[rD] = -((signed int)hCPU->gpr[rA]);
|
|
//if (Opcode & PPC_OPC_RC) {
|
|
// // update cr0 flags
|
|
// ppc_update_cr0(hCPU, hCPU->gpr[rD]);
|
|
//}
|
|
uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false);
|
|
uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD);
|
|
if( opcode&PPC_OPC_RC )
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA, 0, PPCREC_CR_MODE_ARITHMETIC);
|
|
}
|
|
else
|
|
{
|
|
PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA);
|
|
}
|
|
return true;
|
|
}
|
|
|
|
void PPCRecompilerImlGen_LWZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode)
|
|
{
|
|
int rA, rD;
|
|
uint32 imm;
|
|
PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm);
|
|
if( rA == 0 )
|
|
{
|
|
// special form where gpr is ignored and only imm is used
|
|
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:
|
|
if (PPCRecompilerImlGen_BCLR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 129:
|
|
if (PPCRecompilerImlGen_CRANDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 150:
|
|
if (PPCRecompilerImlGen_ISYNC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 193:
|
|
if (PPCRecompilerImlGen_CRXOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 257:
|
|
if (PPCRecompilerImlGen_CRAND(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 289:
|
|
if (PPCRecompilerImlGen_CREQV(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 417:
|
|
if (PPCRecompilerImlGen_CRORC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 449:
|
|
if (PPCRecompilerImlGen_CROR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 528:
|
|
if (PPCRecompilerImlGen_BCCTR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_RLWIMI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_RLWINM(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_RLWNM(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 24:
|
|
PPCRecompilerImlGen_ORI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 25:
|
|
PPCRecompilerImlGen_ORIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 26:
|
|
PPCRecompilerImlGen_XORI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 27:
|
|
PPCRecompilerImlGen_XORIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 28:
|
|
PPCRecompilerImlGen_ANDI(ppcImlGenContext, opcode);
|
|
break;
|
|
case 29:
|
|
PPCRecompilerImlGen_ANDIS(ppcImlGenContext, opcode);
|
|
break;
|
|
case 31: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 0:
|
|
PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode);
|
|
break;
|
|
case 4:
|
|
PPCRecompilerImlGen_TW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 8:
|
|
// todo: Check if we can optimize this pattern:
|
|
// SUBFC + SUBFE
|
|
// SUBFC
|
|
if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 10:
|
|
if (PPCRecompilerImlGen_ADDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 11:
|
|
if (PPCRecompilerImlGen_MULHWU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 19:
|
|
if (PPCRecompilerImlGen_MFCR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_LWZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 24:
|
|
if (PPCRecompilerImlGen_SLW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 26:
|
|
if (PPCRecompilerImlGen_CNTLZW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_AND(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 32:
|
|
PPCRecompilerImlGen_CMPL(ppcImlGenContext, opcode);
|
|
break;
|
|
case 40:
|
|
if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 54:
|
|
// DBCST - Generates no code
|
|
break;
|
|
case 55:
|
|
if (PPCRecompilerImlGen_LWZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 60:
|
|
if (PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 75:
|
|
if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 86:
|
|
// DCBF -> No-Op
|
|
break;
|
|
case 87:
|
|
if (PPCRecompilerImlGen_LBZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 104:
|
|
if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 119:
|
|
if (PPCRecompilerImlGen_LBZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 124:
|
|
if (PPCRecompilerImlGen_NOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 136:
|
|
if (PPCRecompilerImlGen_SUBFE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 138:
|
|
if (PPCRecompilerImlGen_ADDE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 144:
|
|
PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode);
|
|
break;
|
|
case 150:
|
|
if (PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 151:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 183:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 32) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 200:
|
|
if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 202:
|
|
if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 215:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 234:
|
|
if (PPCRecompilerImlGen_ADDME(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 235:
|
|
if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 247:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 8) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 266:
|
|
if (PPCRecompilerImlGen_ADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 279:
|
|
if (PPCRecompilerImlGen_LHZX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 284:
|
|
PPCRecompilerImlGen_EQV(ppcImlGenContext, opcode);
|
|
break;
|
|
case 311:
|
|
if (PPCRecompilerImlGen_LHZUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 316:
|
|
if (PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 339:
|
|
if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 343:
|
|
if (PPCRecompilerImlGen_LHAX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 371:
|
|
if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 375:
|
|
if (PPCRecompilerImlGen_LHAUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 407:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 412:
|
|
if (PPCRecompilerImlGen_ORC(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 439:
|
|
if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 16) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 444:
|
|
if (PPCRecompilerImlGen_OR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 459:
|
|
PPCRecompilerImlGen_DIVWU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 467:
|
|
if (PPCRecompilerImlGen_MTSPR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 491:
|
|
if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 534:
|
|
if (PPCRecompilerImlGen_LWBRX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 535:
|
|
if (PPCRecompilerImlGen_LFSX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 536:
|
|
if (PPCRecompilerImlGen_SRW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 567:
|
|
if (PPCRecompilerImlGen_LFSUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 597:
|
|
if (PPCRecompilerImlGen_LSWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 598:
|
|
PPCRecompilerImlGen_SYNC(ppcImlGenContext, opcode);
|
|
break;
|
|
case 599:
|
|
if (PPCRecompilerImlGen_LFDX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 631:
|
|
if (PPCRecompilerImlGen_LFDUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 662:
|
|
if (PPCRecompilerImlGen_STWBRX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 663:
|
|
if (PPCRecompilerImlGen_STFSX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 695:
|
|
if (PPCRecompilerImlGen_STFSUX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 725:
|
|
if (PPCRecompilerImlGen_STSWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 727:
|
|
if (PPCRecompilerImlGen_STFDX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 790:
|
|
PPCRecompilerImlGen_LHBRX(ppcImlGenContext, opcode);
|
|
break;
|
|
case 792:
|
|
if (PPCRecompilerImlGen_SRAW(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 824:
|
|
if (PPCRecompilerImlGen_SRAWI(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 918: // STHBRX
|
|
if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 922:
|
|
if (PPCRecompilerImlGen_EXTSH(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 954:
|
|
if (PPCRecompilerImlGen_EXTSB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 983:
|
|
if (PPCRecompilerImlGen_STFIWX(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
case 1014:
|
|
if (PPCRecompilerImlGen_DCBZ(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 32:
|
|
PPCRecompilerImlGen_LWZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 33:
|
|
PPCRecompilerImlGen_LWZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 34:
|
|
PPCRecompilerImlGen_LBZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 35:
|
|
PPCRecompilerImlGen_LBZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 36:
|
|
PPCRecompilerImlGen_STW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 37:
|
|
PPCRecompilerImlGen_STWU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 38:
|
|
PPCRecompilerImlGen_STB(ppcImlGenContext, opcode);
|
|
break;
|
|
case 39:
|
|
PPCRecompilerImlGen_STBU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 40:
|
|
PPCRecompilerImlGen_LHZ(ppcImlGenContext, opcode);
|
|
break;
|
|
case 41:
|
|
PPCRecompilerImlGen_LHZU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 42:
|
|
PPCRecompilerImlGen_LHA(ppcImlGenContext, opcode);
|
|
break;
|
|
case 43:
|
|
PPCRecompilerImlGen_LHAU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 44:
|
|
PPCRecompilerImlGen_STH(ppcImlGenContext, opcode);
|
|
break;
|
|
case 45:
|
|
PPCRecompilerImlGen_STHU(ppcImlGenContext, opcode);
|
|
break;
|
|
case 46:
|
|
PPCRecompilerImlGen_LMW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 47:
|
|
PPCRecompilerImlGen_STMW(ppcImlGenContext, opcode);
|
|
break;
|
|
case 48:
|
|
if (PPCRecompilerImlGen_LFS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 49:
|
|
if (PPCRecompilerImlGen_LFSU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 50:
|
|
if (PPCRecompilerImlGen_LFD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 51:
|
|
if (PPCRecompilerImlGen_LFDU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 52:
|
|
if (PPCRecompilerImlGen_STFS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 53:
|
|
if (PPCRecompilerImlGen_STFSU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 54:
|
|
if (PPCRecompilerImlGen_STFD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 55:
|
|
if (PPCRecompilerImlGen_STFDU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 56:
|
|
if (PPCRecompilerImlGen_PSQ_L(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 57:
|
|
if (PPCRecompilerImlGen_PSQ_LU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 59: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 18:
|
|
if (PPCRecompilerImlGen_FDIVS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_FSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_FADDS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 24:
|
|
if (PPCRecompilerImlGen_FRES(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 25:
|
|
if (PPCRecompilerImlGen_FMULS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_FMSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 29:
|
|
if (PPCRecompilerImlGen_FMADDS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 30:
|
|
if (PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
case 60:
|
|
if (PPCRecompilerImlGen_PSQ_ST(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 61:
|
|
if (PPCRecompilerImlGen_PSQ_STU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 63: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 0:
|
|
if (PPCRecompilerImlGen_FCMPU(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 12:
|
|
if (PPCRecompilerImlGen_FRSP(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 15:
|
|
if (PPCRecompilerImlGen_FCTIWZ(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 18:
|
|
if (PPCRecompilerImlGen_FDIV(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 20:
|
|
if (PPCRecompilerImlGen_FSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 21:
|
|
if (PPCRecompilerImlGen_FADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 23:
|
|
if (PPCRecompilerImlGen_FSEL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 25:
|
|
if (PPCRecompilerImlGen_FMUL(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 26:
|
|
if (PPCRecompilerImlGen_FRSQRTE(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 28:
|
|
if (PPCRecompilerImlGen_FMSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 29:
|
|
if (PPCRecompilerImlGen_FMADD(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 30:
|
|
if (PPCRecompilerImlGen_FNMSUB(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 32:
|
|
if (PPCRecompilerImlGen_FCMPO(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 40:
|
|
if (PPCRecompilerImlGen_FNEG(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 72:
|
|
if (PPCRecompilerImlGen_FMR(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 136:
|
|
if (PPCRecompilerImlGen_FNABS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
case 264:
|
|
if (PPCRecompilerImlGen_FABS(ppcImlGenContext, opcode) == false)
|
|
unsupportedInstructionFound = true;
|
|
ppcImlGenContext->hasFPUInstruction = true;
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
break;
|
|
}
|
|
break;
|
|
default:
|
|
unsupportedInstructionFound = true;
|
|
break;
|
|
}
|
|
return unsupportedInstructionFound;
|
|
}
|
|
|
|
// 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:
|
|
//{
|
|
// continueDefaultPath = false; // todo - set this to true if this instruction has a condition (including decrementer check)
|
|
// makeNextInstEnterable = Espresso::DecodeLK(opcode);
|
|
// return true;
|
|
//}
|
|
|
|
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))
|
|
{
|
|
//case Espresso::Opcode31::TW:
|
|
// continueDefaultPath = true;
|
|
// return true;
|
|
//case Espresso::Opcode31::MFTB:
|
|
// continueDefaultPath = true;
|
|
// return true;
|
|
//case Espresso::Opcode19::BCLR:
|
|
//case Espresso::Opcode19::BCCTR:
|
|
//{
|
|
// continueDefaultPath = false;
|
|
// makeNextInstEnterable = Espresso::DecodeLK(opcode);
|
|
// return true;
|
|
//}
|
|
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.NewSegment();
|
|
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 (imlSegment->imlList.empty())
|
|
// return;
|
|
//if (imlSegment->imlList[imlSegment->imlList.size() - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlList.size() - 1].op_conditionalJump.jumpmarkAddress > imlSegment->ppcAddrMin)
|
|
// return;
|
|
//if (imlSegment->imlList[imlSegment->imlList.size() - 1].type != PPCREC_IML_TYPE_CJUMP || imlSegment->imlList[imlSegment->imlList.size() - 1].op_conditionalJump.jumpAccordingToSegment)
|
|
// return;
|
|
if (!basicBlockInfo.hasBranchTarget)
|
|
return;
|
|
if (basicBlockInfo.branchTarget >= basicBlockInfo.startAddress)
|
|
return;
|
|
|
|
// exclude non-infinite tight loops
|
|
if (IMLAnalyzer_IsTightFiniteLoop(imlSegment))
|
|
return;
|
|
// 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 contains the ppc_leave instruction
|
|
// 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");
|
|
|
|
|
|
// make the segment enterable so execution can return after checking
|
|
basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress);
|
|
|
|
IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo);
|
|
|
|
// what we know about the crash:
|
|
// It doesnt happen with cycle checks disabled
|
|
// The debugbreak emitted here is only encountered twice before it crashes
|
|
// it doesnt seem to go into the alternative branch (cycles negative) -> tested (debugbreak in exit segment doesnt trigger)
|
|
// Its the enterable segment that causes issues? -> I removed the enterable statement and it still happened
|
|
// Maybe some general issue with getting x64 offsets for enterable segments..
|
|
|
|
// possible explanations:
|
|
// issue with the cycle check / exit logic
|
|
// returning from exit is causing the issue
|
|
// Segments can get marked as jump destination which we no longer do -> Deleted old code and added asserts
|
|
|
|
IMLInstruction* inst = splitSeg->AppendInstruction();
|
|
inst->type = PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK;
|
|
inst->operation = 0;
|
|
inst->crRegister = PPC_REC_INVALID_REGISTER;
|
|
inst->op_conditionalJump.jumpmarkAddress = 0xFFFFFFFF;
|
|
inst->associatedPPCAddress = 0xFFFFFFFF;
|
|
// PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK
|
|
|
|
//splitSeg->AppendInstruction()->make_macro(PPCREC_IML_TYPE_MACRO, )
|
|
|
|
IMLSegment* exitSegment = ppcImlGenContext.NewSegment();
|
|
splitSeg->SetLinkBranchTaken(exitSegment);
|
|
|
|
|
|
//exitSegment->AppendInstruction()->make_debugbreak();
|
|
|
|
inst = exitSegment->AppendInstruction();// ->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress);
|
|
inst->type = PPCREC_IML_TYPE_MACRO;
|
|
inst->operation = PPCREC_IML_MACRO_LEAVE;
|
|
inst->crRegister = PPC_REC_INVALID_REGISTER;
|
|
inst->op_macro.param = basicBlockInfo.startAddress;
|
|
inst->associatedPPCAddress = basicBlockInfo.startAddress;
|
|
|
|
|
|
//debug_printf("----------------------------------------\n");
|
|
//IMLDebug_Dump(&ppcImlGenContext);
|
|
//__debugbreak();
|
|
|
|
//ppcImlGenContext.NewSegment();
|
|
|
|
//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);
|
|
}
|
|
|
|
void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext)
|
|
{
|
|
for (IMLSegment* segIt : ppcImlGenContext.segmentList2)
|
|
{
|
|
bool isLastSegment = segIt == ppcImlGenContext.segmentList2.back();
|
|
//IMLSegment* nextSegment = isLastSegment ? nullptr : ppcImlGenContext->segmentList2[s + 1];
|
|
// 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());
|
|
}
|
|
|
|
//// find destination segment by ppc jump address
|
|
//IMLSegment* jumpDestSegment = PPCRecompiler_getSegmentByPPCJumpAddress(ppcImlGenContext, imlInstruction->op_conditionalJump.jumpmarkAddress);
|
|
//if (jumpDestSegment)
|
|
//{
|
|
// if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE)
|
|
// IMLSegment_SetLinkBranchNotTaken(imlSegment, nextSegment);
|
|
// IMLSegment_SetLinkBranchTaken(imlSegment, jumpDestSegment);
|
|
//}
|
|
//else
|
|
//{
|
|
// imlSegment->nextSegmentIsUncertain = true;
|
|
//}
|
|
}
|
|
else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO)
|
|
{
|
|
auto macroType = imlInstruction->operation;
|
|
switch (macroType)
|
|
{
|
|
case PPCREC_IML_MACRO_BLR:
|
|
case PPCREC_IML_MACRO_BLRL:
|
|
case PPCREC_IML_MACRO_BCTR:
|
|
case PPCREC_IML_MACRO_BCTRL:
|
|
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", segIndex);
|
|
IMLDebug_Dump(&ppcImlGenContext);
|
|
__debugbreak();
|
|
}
|
|
}
|
|
}
|
|
if (seg->nextSegmentBranchTaken)
|
|
{
|
|
if (!seg->HasSuffixInstruction())
|
|
{
|
|
debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", 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", segIndex);
|
|
IMLDebug_Dump(&ppcImlGenContext);
|
|
cemu_assert_error();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// proper error checking for branch-always (or branch-never if invert bit is set)
|
|
}
|
|
}
|
|
}
|
|
//if (seg->list_prevSegments.empty())
|
|
//{
|
|
// cemu_assert_debug(seg->isEnterable);
|
|
//}
|
|
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;
|
|
//}
|
|
} |