#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& entryAddresses) : startAddress(startAddress), lastAddress(startAddress) { isEnterable = entryAddresses.find(startAddress) != entryAddresses.end(); } uint32 startAddress; uint32 lastAddress; // inclusive bool isEnterable{ false }; //uint32 enterableAddress{}; -> covered by startAddress bool hasContinuedFlow{ true }; // non-branch path goes to next segment (lastAddress+4), assumed by default bool hasBranchTarget{ false }; uint32 branchTarget{}; // associated IML segments IMLSegment* firstSegment{}; // first segment in chain, used as branch target for other segments IMLSegment* appendSegment{}; // last segment in chain, new instructions should be appended to this segment void SetInitialSegment(IMLSegment* seg) { cemu_assert_debug(!firstSegment); cemu_assert_debug(!appendSegment); firstSegment = seg; appendSegment = seg; } IMLSegment* GetFirstSegmentInChain() { return firstSegment; } IMLSegment* GetSegmentForInstructionAppend() { return appendSegment; } }; bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext); uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext); IMLInstruction* PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext_t* ppcImlGenContext) { //if( ppcImlGenContext->imlListCount+1 > ppcImlGenContext->imlListSize ) //{ // sint32 newSize = ppcImlGenContext->imlListCount*2 + 2; // ppcImlGenContext->imlList = (IMLInstruction*)realloc(ppcImlGenContext->imlList, sizeof(IMLInstruction)*newSize); // ppcImlGenContext->imlListSize = newSize; //} //IMLInstruction* imlInstruction = ppcImlGenContext->imlList+ppcImlGenContext->imlListCount; //memset(imlInstruction, 0x00, sizeof(IMLInstruction)); //imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default //imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction; //ppcImlGenContext->imlListCount++; //return imlInstruction; IMLInstruction& inst = ppcImlGenContext->currentOutputSegment->imlList.emplace_back(); memset(&inst, 0x00, sizeof(IMLInstruction)); inst.crRegister = PPC_REC_INVALID_REGISTER; // dont update any cr register by default //imlInstruction->associatedPPCAddress = ppcImlGenContext->ppcAddressOfCurrentInstruction; return &inst; } void PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 operation, uint8 registerResult, uint8 registerA, uint8 crRegister, uint8 crMode) { // operation with two register operands (e.g. "t0 = t1") if(imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_R_R; imlInstruction->operation = operation; imlInstruction->crRegister = crRegister; imlInstruction->crMode = crMode; imlInstruction->op_r_r.registerResult = registerResult; imlInstruction->op_r_r.registerA = registerA; } void PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, uint8 registerB, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0) { // operation with three register operands (e.g. "t0 = t1 + t4") IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_R_R_R; imlInstruction->operation = operation; imlInstruction->crRegister = crRegister; imlInstruction->crMode = crMode; imlInstruction->op_r_r_r.registerResult = registerResult; imlInstruction->op_r_r_r.registerA = registerA; imlInstruction->op_r_r_r.registerB = registerB; } void PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerResult, uint8 registerA, sint32 immS32, uint8 crRegister=PPC_REC_INVALID_REGISTER, uint8 crMode=0) { // operation with two register operands and one signed immediate (e.g. "t0 = t1 + 1234") IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_R_R_S32; imlInstruction->operation = operation; imlInstruction->crRegister = crRegister; imlInstruction->crMode = crMode; imlInstruction->op_r_r_s32.registerResult = registerResult; imlInstruction->op_r_r_s32.registerA = registerA; imlInstruction->op_r_r_s32.immS32 = immS32; } void PPCRecompilerImlGen_generateNewInstruction_name_r(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, uint32 name) { // Store name (e.g. "'r3' = t0" which translates to MOV [ESP+offset_r3], reg32) IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_NAME_R; imlInstruction->operation = operation; imlInstruction->op_r_name.registerIndex = registerIndex; imlInstruction->op_r_name.name = name; } void PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 copyWidth, bool signExtend, bool bigEndian, uint8 crRegister, uint32 crMode) { // two variations: // operation without store (e.g. "'r3' < 123" which has no effect other than updating a condition flags register) // operation with store (e.g. "'r3' = 123") IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_R_S32; imlInstruction->operation = operation; imlInstruction->crRegister = crRegister; imlInstruction->crMode = crMode; imlInstruction->op_r_immS32.registerIndex = registerIndex; imlInstruction->op_r_immS32.immS32 = immS32; } void PPCRecompilerImlGen_generateNewInstruction_conditional_r_s32(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 operation, uint8 registerIndex, sint32 immS32, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) { if(imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); else memset(imlInstruction, 0, sizeof(IMLInstruction)); imlInstruction->type = PPCREC_IML_TYPE_CONDITIONAL_R_S32; imlInstruction->operation = operation; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; // r_s32 operation imlInstruction->op_conditional_r_s32.registerIndex = registerIndex; imlInstruction->op_conditional_r_s32.immS32 = immS32; // condition imlInstruction->op_conditional_r_s32.crRegisterIndex = crRegisterIndex; imlInstruction->op_conditional_r_s32.crBitIndex = crBitIndex; imlInstruction->op_conditional_r_s32.bitMustBeSet = bitMustBeSet; } void PPCRecompilerImlGen_generateNewInstruction_jump(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction, uint32 jumpmarkAddress) { __debugbreak(); //// jump //if (imlInstruction == NULL) // imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); //else // memset(imlInstruction, 0, sizeof(IMLInstruction)); //imlInstruction->type = PPCREC_IML_TYPE_CJUMP; //imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; //imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress; //imlInstruction->op_conditionalJump.jumpAccordingToSegment = false; //imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE; //imlInstruction->op_conditionalJump.crRegisterIndex = 0; //imlInstruction->op_conditionalJump.crBitIndex = 0; //imlInstruction->op_conditionalJump.bitMustBeSet = false; } // jump based on segment branches void PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction) { // jump if (imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_CJUMP; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_conditionalJump.jumpAccordingToSegment = true; imlInstruction->op_conditionalJump.condition = PPCREC_JUMP_CONDITION_NONE; imlInstruction->op_conditionalJump.crRegisterIndex = 0; imlInstruction->op_conditionalJump.crBitIndex = 0; imlInstruction->op_conditionalJump.bitMustBeSet = false; } void PPCRecompilerImlGen_generateNewInstruction_noOp(ppcImlGenContext_t* ppcImlGenContext, IMLInstruction* imlInstruction) { if (imlInstruction == NULL) imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_NO_OP; imlInstruction->operation = 0; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->crMode = 0; } void PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext_t* ppcImlGenContext, uint32 operation, uint8 crD, uint8 crA, uint8 crB) { // multiple variations: // operation involving only one cr bit (like clear crD bit) // operation involving three cr bits (like crD = crA or crB) IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_CR; imlInstruction->operation = operation; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->crMode = 0; imlInstruction->op_cr.crD = crD; imlInstruction->op_cr.crA = crA; imlInstruction->op_cr.crB = crB; } void PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext_t* ppcImlGenContext, uint32 jumpmarkAddress, uint32 jumpCondition, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) { __debugbreak(); //// conditional jump //IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); //imlInstruction->type = PPCREC_IML_TYPE_CJUMP; //imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; //imlInstruction->op_conditionalJump.jumpAccordingToSegment = false; //imlInstruction->op_conditionalJump.jumpmarkAddress = jumpmarkAddress; //imlInstruction->op_conditionalJump.condition = jumpCondition; //imlInstruction->op_conditionalJump.crRegisterIndex = crRegisterIndex; //imlInstruction->op_conditionalJump.crBitIndex = crBitIndex; //imlInstruction->op_conditionalJump.bitMustBeSet = bitMustBeSet; } void PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext_t* ppcImlGenContext, uint32 jumpCondition, uint32 crRegisterIndex, uint32 crBitIndex, bool bitMustBeSet) { // conditional jump IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_CJUMP; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_conditionalJump.jumpAccordingToSegment = true; imlInstruction->op_conditionalJump.condition = jumpCondition; imlInstruction->op_conditionalJump.crRegisterIndex = crRegisterIndex; imlInstruction->op_conditionalJump.crBitIndex = crBitIndex; imlInstruction->op_conditionalJump.bitMustBeSet = bitMustBeSet; } void PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool signExtend, bool switchEndian) { // load from memory IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_LOAD; imlInstruction->operation = 0; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory; imlInstruction->op_storeLoad.immS32 = immS32; imlInstruction->op_storeLoad.copyWidth = copyWidth; //imlInstruction->op_storeLoad.flags = (signExtend ? PPCREC_IML_OP_FLAG_SIGNEXTEND : 0) | (switchEndian ? PPCREC_IML_OP_FLAG_SWITCHENDIAN : 0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } void PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { // load from memory IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_LOAD_INDEXED; imlInstruction->operation = 0; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; //imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } void PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext_t* ppcImlGenContext, uint8 registerSource, uint8 registerMemory, sint32 immS32, uint32 copyWidth, bool switchEndian) { // load from memory IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_STORE; imlInstruction->operation = 0; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerSource; imlInstruction->op_storeLoad.registerMem = registerMemory; imlInstruction->op_storeLoad.immS32 = immS32; imlInstruction->op_storeLoad.copyWidth = copyWidth; //imlInstruction->op_storeLoad.flags = (switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = false; } void PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext_t* ppcImlGenContext, uint8 registerDestination, uint8 registerMemory1, uint8 registerMemory2, uint32 copyWidth, bool signExtend, bool switchEndian) { // load from memory IMLInstruction* imlInstruction = PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext); imlInstruction->type = PPCREC_IML_TYPE_STORE_INDEXED; imlInstruction->operation = 0; imlInstruction->crRegister = PPC_REC_INVALID_REGISTER; imlInstruction->op_storeLoad.registerData = registerDestination; imlInstruction->op_storeLoad.registerMem = registerMemory1; imlInstruction->op_storeLoad.registerMem2 = registerMemory2; imlInstruction->op_storeLoad.copyWidth = copyWidth; //imlInstruction->op_storeLoad.flags = (signExtend?PPCREC_IML_OP_FLAG_SIGNEXTEND:0)|(switchEndian?PPCREC_IML_OP_FLAG_SWITCHENDIAN:0); imlInstruction->op_storeLoad.flags2.swapEndian = switchEndian; imlInstruction->op_storeLoad.flags2.signExtend = signExtend; } uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { if( mappedName == PPCREC_NAME_NONE ) { debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(): Invalid mappedName parameter\n"); return PPC_REC_INVALID_REGISTER; } for(uint32 i=0; i<(PPC_REC_MAX_VIRTUAL_GPR-1); i++) { if( ppcImlGenContext->mappedRegister[i] == PPCREC_NAME_NONE ) { ppcImlGenContext->mappedRegister[i] = mappedName; return i; } } return 0; } uint32 PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { for(uint32 i=0; i< PPC_REC_MAX_VIRTUAL_GPR; i++) { if( ppcImlGenContext->mappedRegister[i] == mappedName ) { return i; } } return PPC_REC_INVALID_REGISTER; } uint32 PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { if( mappedName == PPCREC_NAME_NONE ) { debug_printf("PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(): Invalid mappedName parameter\n"); return PPC_REC_INVALID_REGISTER; } for(uint32 i=0; i<255; i++) { if( ppcImlGenContext->mappedFPRRegister[i] == PPCREC_NAME_NONE ) { ppcImlGenContext->mappedFPRRegister[i] = mappedName; return i; } } return 0; } uint32 PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { for(uint32 i=0; i<255; i++) { if( ppcImlGenContext->mappedFPRRegister[i] == mappedName ) { return i; } } return PPC_REC_INVALID_REGISTER; } /* * Loads a PPC gpr into any of the available IML registers * If loadNew is false, it will reuse already loaded instances */ uint32 PPCRecompilerImlGen_loadRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) { if( loadNew == false ) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return loadedRegisterIndex; } uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName); return registerIndex; } /* * Reuse already loaded register if present * Otherwise create new IML register and map the name. The register contents will be undefined */ uint32 PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return loadedRegisterIndex; uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, mappedName); return registerIndex; } /* * Loads a PPC fpr into any of the available IML FPU registers * If loadNew is false, it will check first if the fpr is already loaded into any IML register */ uint32 PPCRecompilerImlGen_loadFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName, bool loadNew) { if( loadNew == false ) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return loadedRegisterIndex; } uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); return registerIndex; } /* * Checks if a PPC fpr register is already loaded into any IML register * If no, it will create a new undefined temporary IML FPU register and map the name (effectively overwriting the old ppc register) */ uint32 PPCRecompilerImlGen_loadOverwriteFPRRegister(ppcImlGenContext_t* ppcImlGenContext, uint32 mappedName) { uint32 loadedRegisterIndex = PPCRecompilerImlGen_findFPRRegisterByMappedName(ppcImlGenContext, mappedName); if( loadedRegisterIndex != PPC_REC_INVALID_REGISTER ) return loadedRegisterIndex; uint32 registerIndex = PPCRecompilerImlGen_getAndLockFreeTemporaryFPR(ppcImlGenContext, mappedName); return registerIndex; } void PPCRecompilerImlGen_TW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // split before and after to make sure the macro is in an isolated segment that we can make enterable PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); ppcImlGenContext->currentOutputSegment->SetEnterable(ppcImlGenContext->ppcAddressOfCurrentInstruction); PPCRecompilerImlGen_generateNewEmptyInstruction(ppcImlGenContext)->make_macro(PPCREC_IML_MACRO_LEAVE, ppcImlGenContext->ppcAddressOfCurrentInstruction, 0, 0); IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); middleSeg->SetLinkBranchTaken(nullptr); middleSeg->SetLinkBranchNotTaken(nullptr); } bool PPCRecompilerImlGen_MTSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); if (spr == SPR_CTR || spr == SPR_LR) { uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); if (gprReg == PPC_REC_INVALID_REGISTER) gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg); } else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) { uint32 gprReg = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); if (gprReg == PPC_REC_INVALID_REGISTER) gprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); uint32 sprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, sprReg, gprReg); ppcImlGenContext->tracking.modifiesGQR[spr - SPR_UGQR0] = true; } else return false; return true; } bool PPCRecompilerImlGen_MFSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); if (spr == SPR_LR || spr == SPR_CTR) { uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg); } else if (spr >= SPR_UGQR0 && spr <= SPR_UGQR7) { uint32 sprReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + spr); uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprReg, sprReg); } else return false; return true; } bool PPCRecompilerImlGen_MFTB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { printf("PPCRecompilerImlGen_MFTB(): Not supported\n"); return false; uint32 rD, spr1, spr2, spr; PPC_OPC_TEMPL_XO(opcode, rD, spr1, spr2); spr = spr1 | (spr2<<5); if (spr == 268 || spr == 269) { // TBL / TBU uint32 param2 = spr | (rD << 16); ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_MFTB, ppcImlGenContext->ppcAddressOfCurrentInstruction, param2, 0); IMLSegment* middleSeg = PPCIMLGen_CreateSplitSegmentAtEnd(*ppcImlGenContext, *ppcImlGenContext->currentBasicBlock); return true; } return false; } bool PPCRecompilerImlGen_MFCR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rD); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MFCR, gprReg, 0, 0, false, false, PPC_REC_INVALID_REGISTER, 0); return true; } bool PPCRecompilerImlGen_MTCRF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 rS; uint32 crMask; PPC_OPC_TEMPL_XFX(opcode, rS, crMask); uint32 gprReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0 + rS); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_MTCRF, gprReg, crMask, 0, false, false, PPC_REC_INVALID_REGISTER, 0); return true; } void PPCRecompilerImlGen_CMP(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 cr; int rA, rB; PPC_OPC_TEMPL_X(opcode, cr, rA, rB); cr >>= 2; uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_SIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_SIGNED); } void PPCRecompilerImlGen_CMPL(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 cr; int rA, rB; PPC_OPC_TEMPL_X(opcode, cr, rA, rB); cr >>= 2; uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegisterA, gprRegisterB, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED); } void PPCRecompilerImlGen_CMPI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 cr; int rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, cr, rA, imm); cr >>= 2; sint32 b = imm; // load gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_SIGNED, gprRegister, b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_SIGNED); } void PPCRecompilerImlGen_CMPLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 cr; int rA; uint32 imm; PPC_OPC_TEMPL_D_UImm(opcode, cr, rA, imm); cr >>= 2; uint32 b = imm; // load gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_COMPARE_UNSIGNED, gprRegister, (sint32)b, 0, false, false, cr, PPCREC_CR_MODE_COMPARE_UNSIGNED); } bool PPCRecompiler_canInlineFunction(MPTR functionPtr, sint32* functionInstructionCount) { for (sint32 i = 0; i < 6; i++) { uint32 opcode = memory_readU32(functionPtr+i*4); switch ((opcode >> 26)) { case 14: // ADDI case 15: // ADDIS continue; case 19: // opcode category 19 switch (PPC_getBits(opcode, 30, 10)) { case 16: if (opcode == 0x4E800020) { *functionInstructionCount = i; return true; // BLR } return false; } return false; case 32: // LWZ case 33: // LWZU case 34: // LBZ case 35: // LBZU case 36: // STW case 37: // STWU case 38: // STB case 39: // STBU case 40: // LHZ case 41: // LHZU case 42: // LHA case 43: // LHAU case 44: // STH case 45: // STHU case 46: // LMW case 47: // STMW case 48: // LFS case 49: // LFSU case 50: // LFD case 51: // LFDU case 52: // STFS case 53: // STFSU case 54: // STFD case 55: // STFDU continue; default: return false; } } return false; } void PPCRecompiler_generateInlinedCode(ppcImlGenContext_t* ppcImlGenContext, uint32 startAddress, sint32 instructionCount) { for (sint32 i = 0; i < instructionCount; i++) { ppcImlGenContext->ppcAddressOfCurrentInstruction = startAddress + i*4; ppcImlGenContext->cyclesSinceLastBranch++; if (PPCRecompiler_decodePPCInstruction(ppcImlGenContext)) { cemu_assert_suspicious(); } } // add range ppcRecRange_t recRange; recRange.ppcAddress = startAddress; recRange.ppcSize = instructionCount*4 + 4; // + 4 because we have to include the BLR ppcImlGenContext->functionRef->list_ranges.push_back(recRange); } bool PPCRecompilerImlGen_B(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 li; PPC_OPC_TEMPL_I(opcode, li); uint32 jumpAddressDest = li; if( (opcode&PPC_OPC_AA) == 0 ) { jumpAddressDest = li + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction; } if( opcode&PPC_OPC_LK ) { // function call ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); return true; } // is jump destination within recompiled function? if( ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest) ) { // jump to target within same function PPCRecompilerImlGen_generateNewInstruction_jumpSegment(ppcImlGenContext, nullptr); } else { ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); } return true; } bool PPCRecompilerImlGen_BC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); uint32 BO, BI, BD; PPC_OPC_TEMPL_B(opcode, BO, BI, BD); uint32 crRegister = BI/4; uint32 crBit = BI%4; uint32 jumpCondition = 0; bool conditionMustBeTrue = (BO&8)!=0; bool useDecrementer = (BO&4)==0; // bit not set -> decrement bool decrementerMustBeZero = (BO&2)!=0; // bit set -> branch if CTR = 0, bit not set -> branch if CTR != 0 bool ignoreCondition = (BO&16)!=0; uint32 jumpAddressDest = BD; if( (opcode&PPC_OPC_AA) == 0 ) { jumpAddressDest = BD + (unsigned int)ppcImlGenContext->ppcAddressOfCurrentInstruction; } if( opcode&PPC_OPC_LK ) { // conditional function calls are not supported if( ignoreCondition == false ) { // generate jump condition if( conditionMustBeTrue ) { if( crBit == 0 ) jumpCondition = PPCREC_JUMP_CONDITION_GE; else if( crBit == 1 ) jumpCondition = PPCREC_JUMP_CONDITION_LE; else if( crBit == 2 ) jumpCondition = PPCREC_JUMP_CONDITION_NE; else if( crBit == 3 ) jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; } else { if( crBit == 0 ) jumpCondition = PPCREC_JUMP_CONDITION_L; else if( crBit == 1 ) jumpCondition = PPCREC_JUMP_CONDITION_G; else if( crBit == 2 ) jumpCondition = PPCREC_JUMP_CONDITION_E; else if( crBit == 3 ) jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; } PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; IMLSegment* blSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, conditionMustBeTrue); blSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_BL, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); return true; } return false; } // generate iml instructions depending on flags if( useDecrementer ) { if( ignoreCondition == false ) return false; // not supported for the moment uint32 ctrRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0+SPR_CTR, false); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUB, ctrRegister, 1, 0, false, false, PPCREC_CR_REG_TEMP, PPCREC_CR_MODE_ARITHMETIC); if( decrementerMustBeZero ) PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, PPCREC_JUMP_CONDITION_E, PPCREC_CR_REG_TEMP, 0, false); else PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, PPCREC_JUMP_CONDITION_NE, PPCREC_CR_REG_TEMP, 0, false); return true; } else { if( ignoreCondition ) { // branch always, no condition and no decrementer // not supported return false; } else { // generate jump condition if( conditionMustBeTrue ) { if( crBit == 0 ) jumpCondition = PPCREC_JUMP_CONDITION_GE; else if( crBit == 1 ) jumpCondition = PPCREC_JUMP_CONDITION_LE; else if( crBit == 2 ) jumpCondition = PPCREC_JUMP_CONDITION_NE; else if( crBit == 3 ) jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; } else { if( crBit == 0 ) jumpCondition = PPCREC_JUMP_CONDITION_L; else if( crBit == 1 ) jumpCondition = PPCREC_JUMP_CONDITION_G; else if( crBit == 2 ) jumpCondition = PPCREC_JUMP_CONDITION_E; else if( crBit == 3 ) jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; } if (ppcImlGenContext->boundaryTracker->ContainsAddress(jumpAddressDest)) { // near jump PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, conditionMustBeTrue); } else { // far jump debug_printf("PPCRecompilerImlGen_BC(): Far jump not supported yet"); return false; PPCRecompilerImlGen_generateNewInstruction_conditionalJump(ppcImlGenContext, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, jumpCondition, crRegister, crBit, !conditionMustBeTrue); ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_FAR, ppcImlGenContext->ppcAddressOfCurrentInstruction, jumpAddressDest, ppcImlGenContext->cyclesSinceLastBranch); //ppcImlGenContext->emitInst().make_ppcEnter(ppcImlGenContext->ppcAddressOfCurrentInstruction + 4); } } } return true; } // BCCTR or BCLR bool PPCRecompilerImlGen_BCSPR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 sprReg) { PPCIMLGen_AssertIfNotLastSegmentInstruction(*ppcImlGenContext); Espresso::BOField BO; uint32 BI; bool LK; Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); uint32 crRegister = BI/4; uint32 crBit = BI%4; uint32 branchDestReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + sprReg); if (LK) { if (sprReg == SPR_LR) { // if the branch target is LR, then preserve it in a temporary cemu_assert_suspicious(); // this case needs testing uint32 tmpRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_TEMPORARY); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, nullptr, PPCREC_IML_OP_ASSIGN, tmpRegister, branchDestReg); branchDestReg = tmpRegister; } uint32 registerLR = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_SPR0 + SPR_LR); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerLR, ppcImlGenContext->ppcAddressOfCurrentInstruction + 4, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } if (!BO.decrementerIgnore()) { cemu_assert_unimplemented(); return false; } else if (!BO.conditionIgnore()) { // no decrementer but CR check cemu_assert_debug(ppcImlGenContext->currentBasicBlock->hasContinuedFlow); cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); // generate jump condition uint32 jumpCondition = 0; if (!BO.conditionInverted()) { // CR bit must be set if (crBit == 0) jumpCondition = PPCREC_JUMP_CONDITION_L; else if (crBit == 1) jumpCondition = PPCREC_JUMP_CONDITION_G; else if (crBit == 2) jumpCondition = PPCREC_JUMP_CONDITION_E; else if (crBit == 3) jumpCondition = PPCREC_JUMP_CONDITION_SUMMARYOVERFLOW; } else { if (crBit == 0) jumpCondition = PPCREC_JUMP_CONDITION_GE; else if (crBit == 1) jumpCondition = PPCREC_JUMP_CONDITION_LE; else if (crBit == 2) jumpCondition = PPCREC_JUMP_CONDITION_NE; else if (crBit == 3) jumpCondition = PPCREC_JUMP_CONDITION_NSUMMARYOVERFLOW; } // write the dynamic branch instruction to a new segment that is set as a branch target for the current segment PPCBasicBlockInfo* currentBasicBlock = ppcImlGenContext->currentBasicBlock; IMLSegment* bctrSeg = PPCIMLGen_CreateNewSegmentAsBranchTarget(*ppcImlGenContext, *currentBasicBlock); PPCRecompilerImlGen_generateNewInstruction_conditionalJumpSegment(ppcImlGenContext, jumpCondition, crRegister, crBit, !BO.conditionInverted()); bctrSeg->AppendInstruction()->make_macro(PPCREC_IML_MACRO_B_TO_REG, branchDestReg, 0, 0); } else { // branch always, no condition and no decrementer check cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasContinuedFlow); cemu_assert_debug(!ppcImlGenContext->currentBasicBlock->hasBranchTarget); ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_B_TO_REG, branchDestReg, 0, 0); } return true; } bool PPCRecompilerImlGen_ISYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // does not need to be translated return true; } bool PPCRecompilerImlGen_SYNC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // does not need to be translated return true; } bool PPCRecompilerImlGen_ADD(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); //hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, registerRB); } return true; } bool PPCRecompilerImlGen_ADDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); //hCPU->gpr[rD] = (int)hCPU->gpr[rA] + (int)hCPU->gpr[rB]; -> Update carry uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, registerRB); return true; } bool PPCRecompilerImlGen_ADDE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = hCPU->gpr[rA] + hCPU->gpr[rB] + ca; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ADD_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA); return true; } bool PPCRecompilerImlGen_ADDZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); PPC_ASSERT(rB == 0); //uint32 a = hCPU->gpr[rA]; //uint32 ca = hCPU->xer_ca; //hCPU->gpr[rD] = a + ca; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); // move rA to rD if( registerRA != registerRD ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA); } if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY, registerRD, registerRD); } return true; } bool PPCRecompilerImlGen_ADDME(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); PPC_ASSERT(rB == 0); //uint32 a = hCPU->gpr[rA]; //uint32 ca = hCPU->xer_ca; //hCPU->gpr[rD] = a + ca + -1; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); // move rA to rD if( registerRA != registerRD ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRD, registerRA); } if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD_CARRY_ME, registerRD, registerRD); } return true; } bool PPCRecompilerImlGen_ADDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); //hCPU->gpr[rD] = (rA ? (int)hCPU->gpr[rA] : 0) + (int)imm; if( rA != 0 ) { uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if rD is already loaded, else use new temporary register uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, imm); } else { // rA not used, instruction is value assignment // rD = imm uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } // never updates any cr return true; } bool PPCRecompilerImlGen_ADDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rD, rA, imm); if( rA != 0 ) { uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if rD is already loaded, else use new temporary register uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, registerRD, registerRA, (sint32)imm); } else { // rA not used, instruction turns into simple value assignment // rD = imm uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ASSIGN, registerRD, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } // never updates any cr return true; } bool PPCRecompilerImlGen_ADDIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); // rD = rA + imm; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if rD is already loaded, else use new temporary register uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm); // never updates any cr return true; } bool PPCRecompilerImlGen_ADDIC_(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { // this opcode is identical to ADDIC but additionally it updates CR0 sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); // rD = rA + imm; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if rD is already loaded, else use new temporary register uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD_UPDATE_CARRY, registerRD, registerRA, imm, 0, PPCREC_CR_MODE_LOGICAL); return true; } bool PPCRecompilerImlGen_SUBF(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1; // rD = rB - rA uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB, registerRD, registerRB, registerRA); return true; } bool PPCRecompilerImlGen_SUBFE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + ca; uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRB, registerRA); return true; } bool PPCRecompilerImlGen_SUBFZE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); if( rB != 0 ) debugBreakpoint(); uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_SUB_CARRY_UPDATE_CARRY, registerRD, registerRA); return true; } bool PPCRecompilerImlGen_SUBFC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = ~hCPU->gpr[rA] + hCPU->gpr[rB] + 1; // rD = rB - rA uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, registerRB); if (opcode & PPC_OPC_RC) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRD, registerRD, 0, PPCREC_CR_MODE_LOGICAL); return true; } bool PPCRecompilerImlGen_SUBFIC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); //uint32 a = hCPU->gpr[rA]; //hCPU->gpr[rD] = ~a + imm + 1; // cr0 is never affected uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SUBFC, registerRD, registerRA, imm); return true; } bool PPCRecompilerImlGen_MULLI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); // mulli instruction does not modify any flags uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand, (sint32)imm); return true; } bool PPCRecompilerImlGen_MULLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); //hCPU->gpr[rD] = hCPU->gpr[rA] * hCPU->gpr[rB]; uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); if (opcode & PPC_OPC_OE) { return false; } if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_SIGNED, registerResult, registerOperand1, registerOperand2); return true; } bool PPCRecompilerImlGen_MULHW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); //hCPU->gpr[rD] = ((sint64)(sint32)hCPU->gpr[rA] * (sint64)(sint32)hCPU->gpr[rB])>>32; uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_SIGNED, registerResult, registerOperand1, registerOperand2); return true; } bool PPCRecompilerImlGen_MULHWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); //hCPU->gpr[rD] = (hCPU->gpr[rA] * hCPU->gpr[rB])>>32; uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_MULTIPLY_HIGH_UNSIGNED, registerResult, registerOperand1, registerOperand2); return true; } bool PPCRecompilerImlGen_DIVW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = (sint32)a / (sint32)b; uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); if (opcode & PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_SIGNED, registerResult, registerOperand1, registerOperand2); } return true; } bool PPCRecompilerImlGen_DIVWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); // hCPU->gpr[rD] = (uint32)a / (uint32)b; uint32 registerResult = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); uint32 registerOperand1 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerOperand2 = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); if (opcode & PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_DIVIDE_UNSIGNED, registerResult, registerOperand1, registerOperand2); } return true; } bool PPCRecompilerImlGen_RLWINM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); uint32 mask = ppc_mask(MB, ME); //uint32 v = ppc_word_rotl(hCPU->gpr[rS], SH); //hCPU->gpr[rA] = v & mask; uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // handle special forms of RLWINM if( SH == 0 && SH == (ME-SH) && MB == 0 ) { // CLRRWI // todo } else if( ME == (31-SH) && MB == 0 ) { // SLWI if(opcode&PPC_OPC_RC) PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, registerRA, registerRS, SH); return true; } else if( SH == (32-MB) && ME == 31 ) { // SRWI if(opcode&PPC_OPC_RC) PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RIGHT_SHIFT, registerRA, registerRS, MB); return true; } // general handler if( registerRA != registerRS ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, registerRA, registerRS); if( SH != 0 ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, SH, 0, false, false, PPC_REC_INVALID_REGISTER, 0); if(opcode&PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); } else { if( mask != 0xFFFFFFFF ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } return true; } bool PPCRecompilerImlGen_RLWIMI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, SH, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, SH, MB, ME); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // pack RLWIMI parameters into single integer uint32 vImm = MB|(ME<<8)|(SH<<16); PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_RLWIMI, registerRA, registerRS, (sint32)vImm, PPC_REC_INVALID_REGISTER, 0); if (opcode & PPC_OPC_RC) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, registerRA, registerRA, 0, PPCREC_CR_MODE_LOGICAL); return true; } bool PPCRecompilerImlGen_RLWNM(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB, MB, ME; PPC_OPC_TEMPL_M(opcode, rS, rA, rB, MB, ME); // uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]); uint32 mask = ppc_mask(MB, ME); // uint32 v = ppc_word_rotl(hCPU->gpr[rS], hCPU->gpr[rB]); // hCPU->gpr[rA] = v & mask; uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_LEFT_ROTATE, registerRA, registerRS, registerRB); if (opcode & PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, 0, PPCREC_CR_MODE_LOGICAL); } else { if( mask != 0xFFFFFFFF ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, registerRA, (sint32)mask, 32, false, false, PPC_REC_INVALID_REGISTER, 0); } return true; } bool PPCRecompilerImlGen_SRAW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); //uint32 SH = hCPU->gpr[rB] & 0x3f; //hCPU->gpr[rA] = hCPU->gpr[rS]; //hCPU->xer_ca = 0; //if (hCPU->gpr[rA] & 0x80000000) { // uint32 ca = 0; // for (uint32 i=0; i < SH; i++) { // if (hCPU->gpr[rA] & 1) ca = 1; // hCPU->gpr[rA] >>= 1; // hCPU->gpr[rA] |= 0x80000000; // } // if (ca) hCPU->xer_ca = 1; //} else { // if (SH > 31) { // hCPU->gpr[rA] = 0; // } else { // hCPU->gpr[rA] >>= SH; // } //} //if (Opcode & PPC_OPC_RC) { // // update cr0 flags // ppc_update_cr0(hCPU, hCPU->gpr[rA]); //} uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( (opcode&PPC_OPC_RC) != 0 ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, registerRB); return true; } bool PPCRecompilerImlGen_SRAWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA; uint32 SH; PPC_OPC_TEMPL_X(opcode, rS, rA, SH); cemu_assert_debug(SH < 32); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_SRAW, registerRA, registerRS, (sint32)SH); return true; } bool PPCRecompilerImlGen_SLW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if (opcode & PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SLW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0); } return true; } bool PPCRecompilerImlGen_SRW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if (opcode & PPC_OPC_RC) { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_SRW, registerRA, registerRS, registerRB, PPC_REC_INVALID_REGISTER, 0); } return true; } bool PPCRecompilerImlGen_EXTSH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); PPC_ASSERT(rB==0); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if ( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S16_TO_S32, registerRA, registerRS); } return true; } bool PPCRecompilerImlGen_EXTSB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if ( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN_S8_TO_S32, registerRA, registerRS); } return true; } bool PPCRecompilerImlGen_CNTLZW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); PPC_ASSERT(rB==0); if( opcode&PPC_OPC_RC ) { return false; } uint32 registerRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); uint32 registerRA = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_CNTLZW, registerRA, registerRS); //uint32 n=0; //uint32 x=0x80000000; //uint32 v=hCPU->gpr[rS]; //while (!(v & x)) { // n++; // if (n==32) break; // x>>=1; //} //hCPU->gpr[rA] = n; //if (Opcode & PPC_OPC_RC) { // // update cr0 flags // ppc_update_cr0(hCPU, hCPU->gpr[rA]); //} return true; } bool PPCRecompilerImlGen_NEG(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA, rB; PPC_OPC_TEMPL_XO(opcode, rD, rA, rB); PPC_ASSERT(rB == 0); //hCPU->gpr[rD] = -((signed int)hCPU->gpr[rA]); //if (Opcode & PPC_OPC_RC) { // // update cr0 flags // ppc_update_cr0(hCPU, hCPU->gpr[rD]); //} uint32 registerRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 registerRD = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rD); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NEG, registerRD, registerRA); } return true; } void PPCRecompilerImlGen_LWZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 32, false, true); } void PPCRecompilerImlGen_LWZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // add imm to memory register PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true); } void PPCRecompilerImlGen_LHA(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, true, true); } void PPCRecompilerImlGen_LHAU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // add imm to memory register PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, true, true); } void PPCRecompilerImlGen_LHZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used // note: Darksiders 2 has this instruction form but it is never executed. ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 16, false, true); } void PPCRecompilerImlGen_LHZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // add imm to memory register PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new temporary register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true); } void PPCRecompilerImlGen_LBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load byte PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm, 8, false, true); } void PPCRecompilerImlGen_LBZU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // add imm to memory register PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load byte PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 8, false, true); } bool PPCRecompilerImlGen_LWZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { return false; } // hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]); // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load word PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, true); return true; } bool PPCRecompilerImlGen_LWZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { return false; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // add rB to rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); // load word PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 32, false, true); return true; } bool PPCRecompilerImlGen_LWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); // load memory rA and rB into register uint32 gprRegisterA = 0; if( rA ) gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); if (destinationRegister == PPC_REC_INVALID_REGISTER) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register // load word if( rA ) PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false); else PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false, false); return true; } bool PPCRecompilerImlGen_LHAX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return true; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half word PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, true, true); return true; } bool PPCRecompilerImlGen_LHAUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return true; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // add rB to rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); // load half word PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, true, true); return true; } bool PPCRecompilerImlGen_LHZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return true; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half word PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, true); return true; } bool PPCRecompilerImlGen_LHZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return true; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // add rB to rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); // load hald word PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 16, false, true); return true; } void PPCRecompilerImlGen_LHBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); // load memory rA and rB into register uint32 gprRegisterA = rA != 0 ? PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false) : 0; uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); if (destinationRegister == PPC_REC_INVALID_REGISTER) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register // load half word (little-endian) if (rA == 0) PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 16, false, false); else PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 16, false, false); } bool PPCRecompilerImlGen_LBZX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if( rA == 0 ) { // special case where rA is ignored and only rB is used return false; } // hCPU->gpr[rD] = memory_readU8((rA?hCPU->gpr[rA]:0)+hCPU->gpr[rB]); // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load byte PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 8, false, true); return true; } bool PPCRecompilerImlGen_LBZUX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); if (rA == 0) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return true; } // load memory rA and rB into register uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0 + rD); if (destinationRegister == PPC_REC_INVALID_REGISTER) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0 + rD); // else just create new register // add rB to rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); // load byte PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterA, 0, 8, false, true); return true; } bool PPCRecompilerImlGen_LWARX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rD, rB; PPC_OPC_TEMPL_X(opcode, rD, rA, rB); // load memory rA and rB into register uint32 gprRegisterA = rA != 0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load word if( rA != 0 ) PPCRecompilerImlGen_generateNewInstruction_r_memory_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_LOAD_LWARX_MARKER, false, true); else PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_LOAD_LWARX_MARKER, false, true); return true; } void PPCRecompilerImlGen_LMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rD, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); //uint32 ea = (rA ? hCPU->gpr[rA] : 0) + imm; sint32 index = 0; while( rD <= 31 ) { // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load word PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, imm+index*4, 32, false, true); // next rD++; index++; } } void PPCRecompilerImlGen_STW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used // note: Darksiders 2 has this instruction form but it is never executed. //ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 32, true); } void PPCRecompilerImlGen_STWU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // store&update instructions where rD==rA store the register contents without added imm, therefore we need to handle it differently // get memory gpr register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // get source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister // add imm to memory register early if possible if( rD != rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 32, true); // add imm to memory register late if we couldn't do it early if( rD == rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } void PPCRecompilerImlGen_STH(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister // load half PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 16, true); } void PPCRecompilerImlGen_STHU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // get memory gpr register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // get source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister // add imm to memory register early if possible if( rD != rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 16, true); // add imm to memory register late if we couldn't do it early if( rD == rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } void PPCRecompilerImlGen_STB(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rS; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister // store byte PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm, 8, true); } void PPCRecompilerImlGen_STBU(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rD, rA, imm); if( rA == 0 ) { // special form where gpr is ignored and only imm is used ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_DEBUGBREAK, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->ppcAddressOfCurrentInstruction, ppcImlGenContext->cyclesSinceLastBranch); return; } // get memory gpr register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // get source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rD, false); // can be the same as gprRegister // add imm to memory register early if possible if( rD != rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); // store byte PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, (rD==rA)?imm:0, 8, true); // add imm to memory register late if we couldn't do it early if( rD == rA ) PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_ADD, gprRegister, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } // generic indexed store (STWX, STHX, STBX, STWUX. If bitReversed == true -> STHBRX) bool PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth, bool byteReversed = false) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // prepare registers uint32 gprRegisterA; if(rA != 0) gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0 + rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // store word if (rA == 0) { PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, storeBitWidth, !byteReversed); } else PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, !byteReversed); return true; } bool PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode, uint32 storeBitWidth) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); if( rA == 0 ) { // not supported return false; } if( rS == rA || rS == rB ) { // prepare registers uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // store word PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, storeBitWidth, false, true); // update EA after store PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); return true; } // prepare registers uint32 gprRegisterA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 sourceRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // update EA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ADD, gprRegisterA, gprRegisterB); // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegisterA, 0, storeBitWidth, true); return true; } bool PPCRecompilerImlGen_STWCX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // prepare registers uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // store word if( rA != 0 ) PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, PPC_REC_STORE_STWCX_MARKER, false, true); else PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, PPC_REC_STORE_STWCX_MARKER, true); return true; } bool PPCRecompilerImlGen_STWBRX(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rS, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // prepare registers uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); uint32 destinationRegister = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // store word if( rA != 0 ) PPCRecompilerImlGen_generateNewInstruction_memory_r_indexed(ppcImlGenContext, destinationRegister, gprRegisterA, gprRegisterB, 32, false, false); else PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, destinationRegister, gprRegisterB, 0, 32, false); return true; } void PPCRecompilerImlGen_STMW(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_SImm(opcode, rS, rA, imm); sint32 index = 0; while( rS <= 31 ) { // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, imm+index*4, 32, true); // next rS++; index++; } } bool PPCRecompilerImlGen_LSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rD, nb; PPC_OPC_TEMPL_X(opcode, rD, rA, nb); if( nb == 0 ) nb = 32; if( nb == 4 ) { // if nb == 4 this instruction immitates LWZ if( rA == 0 ) { #ifdef CEMU_DEBUG_ASSERT assert_dbg(); // special form where gpr is ignored and only imm is used #endif return false; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 32, false, true); return true; } else if( nb == 2 ) { // if nb == 2 this instruction immitates a LHZ but the result is shifted left by 16 bits if( rA == 0 ) { #ifdef CEMU_DEBUG_ASSERT assert_dbg(); // special form where gpr is ignored and only imm is used #endif return false; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, 16, false, true); // shift PPCRecompilerImlGen_generateNewInstruction_r_r_s32(ppcImlGenContext, PPCREC_IML_OP_LEFT_SHIFT, destinationRegister, destinationRegister, 16); return true; } else if( nb == 3 ) { // if nb == 3 this instruction loads a 3-byte big-endian and the result is shifted left by 8 bits if( rA == 0 ) { #ifdef CEMU_DEBUG_ASSERT assert_dbg(); // special form where gpr is ignored and only imm is used #endif return false; } // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // check if destination register is already loaded uint32 destinationRegister = PPCRecompilerImlGen_findRegisterByMappedName(ppcImlGenContext, PPCREC_NAME_R0+rD); if( destinationRegister == PPC_REC_INVALID_REGISTER ) destinationRegister = PPCRecompilerImlGen_getAndLockFreeTemporaryGPR(ppcImlGenContext, PPCREC_NAME_R0+rD); // else just create new register // load half PPCRecompilerImlGen_generateNewInstruction_r_memory(ppcImlGenContext, destinationRegister, gprRegister, 0, PPC_REC_STORE_LSWI_3, false, true); return true; } debug_printf("PPCRecompilerImlGen_LSWI(): Unsupported nb value %d\n", nb); return false; } bool PPCRecompilerImlGen_STSWI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rA, rS, nb; PPC_OPC_TEMPL_X(opcode, rS, rA, nb); if( nb == 0 ) nb = 32; if( nb == 4 ) { // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister // store word PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, 32, true); return true; } else if( nb == 2 ) { // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister // store half-word (shifted << 16) PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_2, false); return true; } else if( nb == 3 ) { // load memory gpr into register uint32 gprRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false); // load source register uint32 sourceRegister = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS, false); // can be the same as gprRegister // store 3-byte-word (shifted << 8) PPCRecompilerImlGen_generateNewInstruction_memory_r(ppcImlGenContext, sourceRegister, gprRegister, 0, PPC_REC_STORE_STSWI_3, false); return true; } debug_printf("PPCRecompilerImlGen_STSWI(): Unsupported nb value %d\n", nb); return false; } bool PPCRecompilerImlGen_DCBZ(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rA, rB; rA = (opcode>>16)&0x1F; rB = (opcode>>11)&0x1F; // prepare registers uint32 gprRegisterA = rA!=0?PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA, false):0; uint32 gprRegisterB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB, false); // store if( rA != 0 ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterA, gprRegisterB); else PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_DCBZ, gprRegisterB, gprRegisterB); return true; } bool PPCRecompilerImlGen_OR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // check for MR mnemonic if( rS == rB ) { // simple register copy if( rA != rS ) // check if no-op { sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); } } else { if( opcode&PPC_OPC_RC ) { // no effect but CR is updated sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprSourceReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); } else { // no-op } } } else { // rA = rS | rA sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) { // make sure we don't overwrite rS or rA if( gprSource1Reg == gprDestReg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg); } if( opcode&PPC_OPC_RC ) { // fixme: merge CR update into OR instruction above PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } } else { // rA = rS if( gprDestReg != gprSource1Reg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); } // rA |= rB if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); } } } return true; } bool PPCRecompilerImlGen_ORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // hCPU->gpr[rA] = hCPU->gpr[rS] | ~hCPU->gpr[rB]; sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r_r(ppcImlGenContext, PPCREC_IML_OP_ORC, gprDestReg, gprSource1Reg, gprSource2Reg); return true; } bool PPCRecompilerImlGen_NOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); //hCPU->gpr[rA] = ~(hCPU->gpr[rS] | hCPU->gpr[rB]); // check for NOT mnemonic if( rS == rB ) { // simple register copy with NOT sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); } } else { // rA = rS | rA sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) { // make sure we don't overwrite rS or rA if( gprSource1Reg == gprDestReg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource1Reg); } PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); if( opcode&PPC_OPC_RC ) { // fixme: merge CR update into OR instruction above PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } } else { // rA = rS if( gprDestReg != gprSource1Reg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); } // rA |= rB PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprDestReg, gprSource2Reg); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_ARITHMETIC); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); } } } return true; } bool PPCRecompilerImlGen_AND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); // check for MR mnemonic if( rS == rB ) { // simple register copy if( rA != rS ) // check if no-op { sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); } } else { cemu_assert_unimplemented(); // no-op -> verify this case } } else { // rA = rS & rA sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) { // make sure we don't overwrite rS or rA if( gprSource1Reg == gprDestReg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource1Reg); } if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } } else { // rA = rS if( gprDestReg != gprSource1Reg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); } // rA &= rB if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprSource2Reg); } } } return true; } bool PPCRecompilerImlGen_ANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); //hCPU->gpr[rA] = hCPU->gpr[rS] & ~hCPU->gpr[rB]; //if (Opcode & PPC_OPC_RC) { if( rS == rB ) { // result is always 0 -> replace with XOR rA,rA sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); } } else if( rA == rB ) { // rB already in rA, therefore we complement rA first and then AND it with rS sint32 gprRS = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = ~rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); // rA &= rS if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprRA, gprRS); } } else { // a & (~b) is the same as ~((~a) | b) sint32 gprRA = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); sint32 gprRB = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprRS = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); // move rS to rA (if required) if( gprRA != gprRS ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprRA, gprRS); } // rS already in rA, therefore we complement rS first and then OR it with rB // rA = ~rA PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); // rA |= rB PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_OR, gprRA, gprRB); // rA = ~rA if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprRA, gprRA); } } return true; } void PPCRecompilerImlGen_ANDI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); // ANDI. always sets cr0 flags sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA &= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); } void PPCRecompilerImlGen_ANDIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); // ANDI. always sets cr0 flags sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA &= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_AND, gprDestReg, (sint32)imm, 0, false, false, 0, PPCREC_CR_MODE_LOGICAL); } bool PPCRecompilerImlGen_XOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); if( rS == rB ) { // xor register with itself sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); } } else { // rA = rS ^ rA sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) { // make sure we don't overwrite rS or rA if( gprSource1Reg == gprDestReg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg); } if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_AND, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); } } else { // rA = rS if( gprDestReg != gprSource1Reg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); } // rA ^= rB if( opcode&PPC_OPC_RC ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg, 0, PPCREC_CR_MODE_LOGICAL); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); } } } return true; } bool PPCRecompilerImlGen_EQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA, rB; PPC_OPC_TEMPL_X(opcode, rS, rA, rB); if( rS == rB ) { // xor register with itself, then invert sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprDestReg); if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); } else { // rA = ~(rS ^ rA) sint32 gprSource1Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprSource2Reg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rB); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); if( gprSource1Reg == gprDestReg || gprSource2Reg == gprDestReg ) { // make sure we don't overwrite rS or rA if( gprSource1Reg == gprDestReg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); } else { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource1Reg); } } else { // rA = rS if( gprDestReg != gprSource1Reg ) { PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSource1Reg); } // rA ^= rB PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_XOR, gprDestReg, gprSource2Reg); } if( opcode&PPC_OPC_RC ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg, 0, PPCREC_CR_MODE_LOGICAL); else PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_NOT, gprDestReg, gprDestReg); } return true; } void PPCRecompilerImlGen_ORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); // ORI does not set cr0 flags //hCPU->gpr[rA] = hCPU->gpr[rS] | imm; sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA |= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } void PPCRecompilerImlGen_ORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); // ORI does not set cr0 flags //hCPU->gpr[rA] = hCPU->gpr[rS] | imm; sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA |= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_OR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } void PPCRecompilerImlGen_XORI(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_UImm(opcode, rS, rA, imm); //hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm; // XORI does not set cr0 flags sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA |= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } void PPCRecompilerImlGen_XORIS(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { sint32 rS, rA; uint32 imm; PPC_OPC_TEMPL_D_Shift16(opcode, rS, rA, imm); //hCPU->gpr[rA] = hCPU->gpr[rS] ^ imm; // XORIS does not set cr0 flags sint32 gprSourceReg = PPCRecompilerImlGen_loadRegister(ppcImlGenContext, PPCREC_NAME_R0+rS); sint32 gprDestReg = PPCRecompilerImlGen_loadOverwriteRegister(ppcImlGenContext, PPCREC_NAME_R0+rA); // rA = rS if( gprDestReg != gprSourceReg ) PPCRecompilerImlGen_generateNewInstruction_r_r(ppcImlGenContext, NULL, PPCREC_IML_OP_ASSIGN, gprDestReg, gprSourceReg); // rA |= imm32 PPCRecompilerImlGen_generateNewInstruction_r_s32(ppcImlGenContext, PPCREC_IML_OP_XOR, gprDestReg, (sint32)imm, 0, false, false, PPC_REC_INVALID_REGISTER, 0); } bool PPCRecompilerImlGen_CROR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_OR, crD, crA, crB); return true; } bool PPCRecompilerImlGen_CRORC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ORC, crD, crA, crB); return true; } bool PPCRecompilerImlGen_CRAND(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_AND, crD, crA, crB); return true; } bool PPCRecompilerImlGen_CRANDC(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_ANDC, crD, crA, crB); return true; } bool PPCRecompilerImlGen_CRXOR(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); if (crA == crB) { // both operands equal, clear bit in crD // PPC's assert() uses this to pass a parameter to OSPanic PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_CLEAR, crD, 0, 0); return true; } else { return false; } return true; } bool PPCRecompilerImlGen_CREQV(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { int crD, crA, crB; PPC_OPC_TEMPL_X(opcode, crD, crA, crB); if (crA == crB) { // both operands equal, set bit in crD PPCRecompilerImlGen_generateNewInstruction_cr(ppcImlGenContext, PPCREC_IML_OP_CR_SET, crD, 0, 0); return true; } else { return false; } return true; } bool PPCRecompilerImlGen_HLE(ppcImlGenContext_t* ppcImlGenContext, uint32 opcode) { uint32 hleFuncId = opcode&0xFFFF; ppcImlGenContext->emitInst().make_macro(PPCREC_IML_MACRO_HLE, ppcImlGenContext->ppcAddressOfCurrentInstruction, hleFuncId, 0); return true; } uint32 PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction)); ppcImlGenContext->currentInstruction += 1; return v; } uint32 PPCRecompiler_getCurrentInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction)); return v; } uint32 PPCRecompiler_getPreviousInstruction(ppcImlGenContext_t* ppcImlGenContext) { uint32 v = CPU_swapEndianU32(*(ppcImlGenContext->currentInstruction-1)); return v; } void PPCRecompilerIml_setSegmentPoint(IMLSegmentPoint* segmentPoint, IMLSegment* imlSegment, sint32 index) { segmentPoint->imlSegment = imlSegment; segmentPoint->index = index; if (imlSegment->segmentPointList) imlSegment->segmentPointList->prev = segmentPoint; segmentPoint->prev = nullptr; segmentPoint->next = imlSegment->segmentPointList; imlSegment->segmentPointList = segmentPoint; } void PPCRecompilerIml_removeSegmentPoint(IMLSegmentPoint* segmentPoint) { if (segmentPoint->prev) segmentPoint->prev->next = segmentPoint->next; else segmentPoint->imlSegment->segmentPointList = segmentPoint->next; if (segmentPoint->next) segmentPoint->next->prev = segmentPoint->prev; } /* * Insert multiple no-op instructions * Warning: Can invalidate any previous instruction structs from the same segment */ void PPCRecompiler_pushBackIMLInstructions(IMLSegment* imlSegment, sint32 index, sint32 shiftBackCount) { cemu_assert_debug(index >= 0 && index <= imlSegment->imlList.size()); imlSegment->imlList.insert(imlSegment->imlList.begin() + index, shiftBackCount, {}); memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction) * shiftBackCount); // fill empty space with NOP instructions for (sint32 i = 0; i < shiftBackCount; i++) { imlSegment->imlList[index + i].type = PPCREC_IML_TYPE_NONE; } // update position of segment points if (imlSegment->segmentPointList) { IMLSegmentPoint* segmentPoint = imlSegment->segmentPointList; while (segmentPoint) { if (segmentPoint->index != RA_INTER_RANGE_START && segmentPoint->index != RA_INTER_RANGE_END) { if (segmentPoint->index >= index) segmentPoint->index += shiftBackCount; } // next segmentPoint = segmentPoint->next; } } } IMLInstruction* PPCRecompiler_insertInstruction(IMLSegment* imlSegment, sint32 index) { PPCRecompiler_pushBackIMLInstructions(imlSegment, index, 1); return imlSegment->imlList.data() + index; } IMLInstruction* PPCRecompiler_appendInstruction(IMLSegment* imlSegment) { size_t index = imlSegment->imlList.size(); imlSegment->imlList.emplace_back(); memset(imlSegment->imlList.data() + index, 0, sizeof(IMLInstruction)); return imlSegment->imlList.data() + index; } IMLSegment* PPCRecompilerIml_appendSegment(ppcImlGenContext_t* ppcImlGenContext) { IMLSegment* segment = new IMLSegment(); ppcImlGenContext->segmentList2.emplace_back(segment); return segment; } void PPCRecompilerIml_insertSegments(ppcImlGenContext_t* ppcImlGenContext, sint32 index, sint32 count) { ppcImlGenContext->segmentList2.insert(ppcImlGenContext->segmentList2.begin() + index, count, nullptr); for (sint32 i = 0; i < count; i++) ppcImlGenContext->segmentList2[index + i] = new IMLSegment(); } bool PPCRecompiler_decodePPCInstruction(ppcImlGenContext_t* ppcImlGenContext) { bool unsupportedInstructionFound = false; uint32 opcode = PPCRecompiler_iterateCurrentInstruction(ppcImlGenContext); switch ((opcode >> 26)) { case 1: if (PPCRecompilerImlGen_HLE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 4: // opcode category - paired single switch (PPC_getBits(opcode, 30, 5)) { case 0: // subcategory compare switch (PPC_getBits(opcode, 25, 5)) { case 0: PPCRecompilerImlGen_PS_CMPU0(ppcImlGenContext, opcode); ppcImlGenContext->hasFPUInstruction = true; break; case 1: PPCRecompilerImlGen_PS_CMPO0(ppcImlGenContext, opcode); ppcImlGenContext->hasFPUInstruction = true; break; case 2: PPCRecompilerImlGen_PS_CMPU1(ppcImlGenContext, opcode); ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; case 8: //Sub category - move/negate switch (PPC_getBits(opcode, 25, 5)) { case 1: // PS negate if (PPCRecompilerImlGen_PS_NEG(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 2: // PS move register if (PPCRecompilerImlGen_PS_MR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 8: // PS abs if (PPCRecompilerImlGen_PS_ABS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; case 10: if (PPCRecompilerImlGen_PS_SUM0(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 11: if (PPCRecompilerImlGen_PS_SUM1(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 12: // multiply scalar if (PPCRecompilerImlGen_PS_MULS0(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 13: // multiply scalar if (PPCRecompilerImlGen_PS_MULS1(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 14: // multiply add scalar if (PPCRecompilerImlGen_PS_MADDS0(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 15: // multiply add scalar if (PPCRecompilerImlGen_PS_MADDS1(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 16: // sub category - merge switch (PPC_getBits(opcode, 25, 5)) { case 16: if (PPCRecompilerImlGen_PS_MERGE00(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 17: if (PPCRecompilerImlGen_PS_MERGE01(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 18: if (PPCRecompilerImlGen_PS_MERGE10(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 19: if (PPCRecompilerImlGen_PS_MERGE11(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; case 18: // divide paired if (PPCRecompilerImlGen_PS_DIV(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 20: // sub paired if (PPCRecompilerImlGen_PS_SUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 21: // add paired if (PPCRecompilerImlGen_PS_ADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 23: // select paired if (PPCRecompilerImlGen_PS_SEL(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 25: // multiply paired if (PPCRecompilerImlGen_PS_MUL(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 24: // reciprocal paired if (PPCRecompilerImlGen_PS_RES(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 26: // reciprocal squareroot paired if (PPCRecompilerImlGen_PS_RSQRTE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 28: // multiply sub paired if (PPCRecompilerImlGen_PS_MSUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 29: // multiply add paired if (PPCRecompilerImlGen_PS_MADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 30: // negative multiply sub paired if (PPCRecompilerImlGen_PS_NMSUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 31: // negative multiply add paired if (PPCRecompilerImlGen_PS_NMADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; case 7: // MULLI PPCRecompilerImlGen_MULLI(ppcImlGenContext, opcode); break; case 8: // SUBFIC PPCRecompilerImlGen_SUBFIC(ppcImlGenContext, opcode); break; case 10: // CMPLI PPCRecompilerImlGen_CMPLI(ppcImlGenContext, opcode); break; case 11: // CMPI PPCRecompilerImlGen_CMPI(ppcImlGenContext, opcode); break; case 12: // ADDIC if (PPCRecompilerImlGen_ADDIC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 13: // ADDIC. if (PPCRecompilerImlGen_ADDIC_(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 14: // ADDI if (PPCRecompilerImlGen_ADDI(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 15: // ADDIS if (PPCRecompilerImlGen_ADDIS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 16: // BC if (PPCRecompilerImlGen_BC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 17: if (PPC_getBits(opcode, 30, 1) == 1) { // SC -> no-op } else { unsupportedInstructionFound = true; } break; case 18: // B if (PPCRecompilerImlGen_B(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 19: // opcode category 19 switch (PPC_getBits(opcode, 30, 10)) { case 16: // BCLR if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_LR) == false) unsupportedInstructionFound = true; break; case 129: if (PPCRecompilerImlGen_CRANDC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 150: if (PPCRecompilerImlGen_ISYNC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 193: if (PPCRecompilerImlGen_CRXOR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 257: if (PPCRecompilerImlGen_CRAND(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 289: if (PPCRecompilerImlGen_CREQV(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 417: if (PPCRecompilerImlGen_CRORC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 449: if (PPCRecompilerImlGen_CROR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 528: // BCCTR if (PPCRecompilerImlGen_BCSPR(ppcImlGenContext, opcode, SPR_CTR) == false) unsupportedInstructionFound = true; break; default: unsupportedInstructionFound = true; break; } break; case 20: if (PPCRecompilerImlGen_RLWIMI(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 21: if (PPCRecompilerImlGen_RLWINM(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 23: if (PPCRecompilerImlGen_RLWNM(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 24: PPCRecompilerImlGen_ORI(ppcImlGenContext, opcode); break; case 25: PPCRecompilerImlGen_ORIS(ppcImlGenContext, opcode); break; case 26: PPCRecompilerImlGen_XORI(ppcImlGenContext, opcode); break; case 27: PPCRecompilerImlGen_XORIS(ppcImlGenContext, opcode); break; case 28: PPCRecompilerImlGen_ANDI(ppcImlGenContext, opcode); break; case 29: PPCRecompilerImlGen_ANDIS(ppcImlGenContext, opcode); break; case 31: // opcode category switch (PPC_getBits(opcode, 30, 10)) { case 0: PPCRecompilerImlGen_CMP(ppcImlGenContext, opcode); break; case 4: PPCRecompilerImlGen_TW(ppcImlGenContext, opcode); break; case 8: // todo: Check if we can optimize this pattern: // SUBFC + SUBFE // SUBFC if (PPCRecompilerImlGen_SUBFC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 10: if (PPCRecompilerImlGen_ADDC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 11: if (PPCRecompilerImlGen_MULHWU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 19: if (PPCRecompilerImlGen_MFCR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 20: if (PPCRecompilerImlGen_LWARX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 23: if (PPCRecompilerImlGen_LWZX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 24: if (PPCRecompilerImlGen_SLW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 26: if (PPCRecompilerImlGen_CNTLZW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 28: if (PPCRecompilerImlGen_AND(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 32: PPCRecompilerImlGen_CMPL(ppcImlGenContext, opcode); break; case 40: if (PPCRecompilerImlGen_SUBF(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 54: // DBCST - Generates no code break; case 55: if (PPCRecompilerImlGen_LWZUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 60: if (PPCRecompilerImlGen_ANDC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 75: if (PPCRecompilerImlGen_MULHW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 86: // DCBF -> No-Op break; case 87: if (PPCRecompilerImlGen_LBZX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 104: if (PPCRecompilerImlGen_NEG(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 119: if (PPCRecompilerImlGen_LBZUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 124: if (PPCRecompilerImlGen_NOR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 136: if (PPCRecompilerImlGen_SUBFE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 138: if (PPCRecompilerImlGen_ADDE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 144: PPCRecompilerImlGen_MTCRF(ppcImlGenContext, opcode); break; case 150: if (PPCRecompilerImlGen_STWCX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 151: if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 32) == false) unsupportedInstructionFound = true; break; case 183: if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 32) == false) unsupportedInstructionFound = true; break; case 200: if (PPCRecompilerImlGen_SUBFZE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 202: if (PPCRecompilerImlGen_ADDZE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 215: if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 8) == false) unsupportedInstructionFound = true; break; case 234: if (PPCRecompilerImlGen_ADDME(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 235: if (PPCRecompilerImlGen_MULLW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 247: if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 8) == false) unsupportedInstructionFound = true; break; case 266: if (PPCRecompilerImlGen_ADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 279: if (PPCRecompilerImlGen_LHZX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 284: PPCRecompilerImlGen_EQV(ppcImlGenContext, opcode); break; case 311: if (PPCRecompilerImlGen_LHZUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 316: if (PPCRecompilerImlGen_XOR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 339: if (PPCRecompilerImlGen_MFSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 343: if (PPCRecompilerImlGen_LHAX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 371: if (PPCRecompilerImlGen_MFTB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 375: if (PPCRecompilerImlGen_LHAUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 407: if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16) == false) unsupportedInstructionFound = true; break; case 412: if (PPCRecompilerImlGen_ORC(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 439: if (PPCRecompilerImlGen_STORE_INDEXED_UPDATE(ppcImlGenContext, opcode, 16) == false) unsupportedInstructionFound = true; break; case 444: if (PPCRecompilerImlGen_OR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 459: PPCRecompilerImlGen_DIVWU(ppcImlGenContext, opcode); break; case 467: if (PPCRecompilerImlGen_MTSPR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 491: if (PPCRecompilerImlGen_DIVW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 534: if (PPCRecompilerImlGen_LWBRX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 535: if (PPCRecompilerImlGen_LFSX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 536: if (PPCRecompilerImlGen_SRW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 567: if (PPCRecompilerImlGen_LFSUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 597: if (PPCRecompilerImlGen_LSWI(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 598: PPCRecompilerImlGen_SYNC(ppcImlGenContext, opcode); break; case 599: if (PPCRecompilerImlGen_LFDX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 631: if (PPCRecompilerImlGen_LFDUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 662: if (PPCRecompilerImlGen_STWBRX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 663: if (PPCRecompilerImlGen_STFSX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 695: if (PPCRecompilerImlGen_STFSUX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 725: if (PPCRecompilerImlGen_STSWI(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 727: if (PPCRecompilerImlGen_STFDX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 790: PPCRecompilerImlGen_LHBRX(ppcImlGenContext, opcode); break; case 792: if (PPCRecompilerImlGen_SRAW(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 824: if (PPCRecompilerImlGen_SRAWI(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 918: // STHBRX if (PPCRecompilerImlGen_STORE_INDEXED(ppcImlGenContext, opcode, 16, true) == false) unsupportedInstructionFound = true; break; case 922: if (PPCRecompilerImlGen_EXTSH(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 954: if (PPCRecompilerImlGen_EXTSB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 983: if (PPCRecompilerImlGen_STFIWX(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; case 1014: if (PPCRecompilerImlGen_DCBZ(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; break; default: unsupportedInstructionFound = true; break; } break; case 32: PPCRecompilerImlGen_LWZ(ppcImlGenContext, opcode); break; case 33: PPCRecompilerImlGen_LWZU(ppcImlGenContext, opcode); break; case 34: PPCRecompilerImlGen_LBZ(ppcImlGenContext, opcode); break; case 35: PPCRecompilerImlGen_LBZU(ppcImlGenContext, opcode); break; case 36: PPCRecompilerImlGen_STW(ppcImlGenContext, opcode); break; case 37: PPCRecompilerImlGen_STWU(ppcImlGenContext, opcode); break; case 38: PPCRecompilerImlGen_STB(ppcImlGenContext, opcode); break; case 39: PPCRecompilerImlGen_STBU(ppcImlGenContext, opcode); break; case 40: PPCRecompilerImlGen_LHZ(ppcImlGenContext, opcode); break; case 41: PPCRecompilerImlGen_LHZU(ppcImlGenContext, opcode); break; case 42: PPCRecompilerImlGen_LHA(ppcImlGenContext, opcode); break; case 43: PPCRecompilerImlGen_LHAU(ppcImlGenContext, opcode); break; case 44: PPCRecompilerImlGen_STH(ppcImlGenContext, opcode); break; case 45: PPCRecompilerImlGen_STHU(ppcImlGenContext, opcode); break; case 46: PPCRecompilerImlGen_LMW(ppcImlGenContext, opcode); break; case 47: PPCRecompilerImlGen_STMW(ppcImlGenContext, opcode); break; case 48: if (PPCRecompilerImlGen_LFS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 49: if (PPCRecompilerImlGen_LFSU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 50: if (PPCRecompilerImlGen_LFD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 51: if (PPCRecompilerImlGen_LFDU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 52: if (PPCRecompilerImlGen_STFS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 53: if (PPCRecompilerImlGen_STFSU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 54: if (PPCRecompilerImlGen_STFD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 55: if (PPCRecompilerImlGen_STFDU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 56: if (PPCRecompilerImlGen_PSQ_L(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 57: if (PPCRecompilerImlGen_PSQ_LU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 59: // opcode category switch (PPC_getBits(opcode, 30, 5)) { case 18: if (PPCRecompilerImlGen_FDIVS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 20: if (PPCRecompilerImlGen_FSUBS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 21: if (PPCRecompilerImlGen_FADDS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 24: if (PPCRecompilerImlGen_FRES(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 25: if (PPCRecompilerImlGen_FMULS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 28: if (PPCRecompilerImlGen_FMSUBS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 29: if (PPCRecompilerImlGen_FMADDS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 30: if (PPCRecompilerImlGen_FNMSUBS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; case 60: if (PPCRecompilerImlGen_PSQ_ST(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 61: if (PPCRecompilerImlGen_PSQ_STU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 63: // opcode category switch (PPC_getBits(opcode, 30, 5)) { case 0: if (PPCRecompilerImlGen_FCMPU(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 12: if (PPCRecompilerImlGen_FRSP(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 15: if (PPCRecompilerImlGen_FCTIWZ(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 18: if (PPCRecompilerImlGen_FDIV(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 20: if (PPCRecompilerImlGen_FSUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 21: if (PPCRecompilerImlGen_FADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 23: if (PPCRecompilerImlGen_FSEL(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 25: if (PPCRecompilerImlGen_FMUL(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 26: if (PPCRecompilerImlGen_FRSQRTE(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 28: if (PPCRecompilerImlGen_FMSUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 29: if (PPCRecompilerImlGen_FMADD(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 30: if (PPCRecompilerImlGen_FNMSUB(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: switch (PPC_getBits(opcode, 30, 10)) { case 32: if (PPCRecompilerImlGen_FCMPO(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 40: if (PPCRecompilerImlGen_FNEG(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 72: if (PPCRecompilerImlGen_FMR(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 136: if (PPCRecompilerImlGen_FNABS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; case 264: if (PPCRecompilerImlGen_FABS(ppcImlGenContext, opcode) == false) unsupportedInstructionFound = true; ppcImlGenContext->hasFPUInstruction = true; break; default: unsupportedInstructionFound = true; break; } break; } break; default: unsupportedInstructionFound = true; break; } return unsupportedInstructionFound; } // returns false if code flow is not interrupted // continueDefaultPath: Controls if bool PPCRecompiler_CheckIfInstructionEndsSegment(PPCFunctionBoundaryTracker& boundaryTracker, uint32 instructionAddress, uint32 opcode, bool& makeNextInstEnterable, bool& continueDefaultPath, bool& hasBranchTarget, uint32& branchTarget) { hasBranchTarget = false; branchTarget = 0xFFFFFFFF; makeNextInstEnterable = false; continueDefaultPath = false; switch (Espresso::GetPrimaryOpcode(opcode)) { case Espresso::PrimaryOpcode::VIRTUAL_HLE: { makeNextInstEnterable = true; hasBranchTarget = false; continueDefaultPath = false; return true; } case Espresso::PrimaryOpcode::BC: { uint32 BD, BI; Espresso::BOField BO; bool AA, LK; Espresso::decodeOp_BC(opcode, BD, BO, BI, AA, LK); if (!LK) { hasBranchTarget = true; branchTarget = (AA ? BD : BD) + instructionAddress; if (!boundaryTracker.ContainsAddress(branchTarget)) hasBranchTarget = false; // far jump } makeNextInstEnterable = LK; continueDefaultPath = true; return true; } case Espresso::PrimaryOpcode::B: { uint32 LI; bool AA, LK; Espresso::decodeOp_B(opcode, LI, AA, LK); if (!LK) { hasBranchTarget = true; branchTarget = AA ? LI : LI + instructionAddress; if (!boundaryTracker.ContainsAddress(branchTarget)) hasBranchTarget = false; // far jump } makeNextInstEnterable = LK; continueDefaultPath = false; return true; } case Espresso::PrimaryOpcode::GROUP_19: switch (Espresso::GetGroup19Opcode(opcode)) { case Espresso::Opcode19::BCLR: case Espresso::Opcode19::BCCTR: { Espresso::BOField BO; uint32 BI; bool LK; Espresso::decodeOp_BCSPR(opcode, BO, BI, LK); continueDefaultPath = !BO.conditionIgnore() || !BO.decrementerIgnore(); // if branch is always taken then there is no continued path makeNextInstEnterable = Espresso::DecodeLK(opcode); return true; } default: break; } break; case Espresso::PrimaryOpcode::GROUP_31: switch (Espresso::GetGroup31Opcode(opcode)) { default: break; } break; default: break; } return false; } void PPCRecompiler_DetermineBasicBlockRange(std::vector& basicBlockList, PPCFunctionBoundaryTracker& boundaryTracker, uint32 ppcStart, uint32 ppcEnd, const std::set& combinedBranchTargets, const std::set& 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 PPCRecompiler_DetermineBasicBlockRange(PPCFunctionBoundaryTracker& boundaryTracker, const std::set& entryAddresses) { cemu_assert(!entryAddresses.empty()); std::vector basicBlockList; const std::set branchTargets = boundaryTracker.GetBranchTargets(); auto funcRanges = boundaryTracker.GetRanges(); std::set combinedBranchTargets = branchTargets; combinedBranchTargets.insert(entryAddresses.begin(), entryAddresses.end()); for (auto& funcRangeIt : funcRanges) PPCRecompiler_DetermineBasicBlockRange(basicBlockList, boundaryTracker, funcRangeIt.startAddress, funcRangeIt.startAddress + funcRangeIt.length - 4, combinedBranchTargets, entryAddresses); // mark all segments that start at entryAddresses as enterable (debug code for verification, can be removed) size_t numMarkedEnterable = 0; for (auto& basicBlockIt : basicBlockList) { if (entryAddresses.find(basicBlockIt.startAddress) != entryAddresses.end()) { cemu_assert_debug(basicBlockIt.isEnterable); numMarkedEnterable++; } } cemu_assert_debug(numMarkedEnterable == entryAddresses.size()); // todo - inline BL, currently this is done in the instruction handler of BL but this will mean that instruction cycle increasing is ignored return basicBlockList; } bool PPCIMLGen_FillBasicBlock(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { ppcImlGenContext.currentOutputSegment = basicBlockInfo.GetSegmentForInstructionAppend(); ppcImlGenContext.currentInstruction = (uint32*)(memory_base + basicBlockInfo.startAddress); uint32* firstCurrentInstruction = ppcImlGenContext.currentInstruction; uint32* endCurrentInstruction = (uint32*)(memory_base + basicBlockInfo.lastAddress); while (ppcImlGenContext.currentInstruction <= endCurrentInstruction) { uint32 addressOfCurrentInstruction = (uint32)((uint8*)ppcImlGenContext.currentInstruction - memory_base); ppcImlGenContext.ppcAddressOfCurrentInstruction = addressOfCurrentInstruction; if (PPCRecompiler_decodePPCInstruction(&ppcImlGenContext)) { debug_printf("Recompiler encountered unsupported instruction at 0x%08x\n", addressOfCurrentInstruction); ppcImlGenContext.currentOutputSegment = nullptr; return false; } } ppcImlGenContext.currentOutputSegment = nullptr; return true; } // returns split segment from which the continued segment is available via seg->GetBranchNotTaken() IMLSegment* PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); IMLSegment* continuedSegment = ppcImlGenContext.InsertSegment(ppcImlGenContext.GetSegmentIndex(writeSegment) + 1); continuedSegment->SetLinkBranchTaken(writeSegment->GetBranchTaken()); continuedSegment->SetLinkBranchNotTaken(writeSegment->GetBranchNotTaken()); writeSegment->SetLinkBranchNotTaken(continuedSegment); writeSegment->SetLinkBranchTaken(nullptr); if (ppcImlGenContext.currentOutputSegment == writeSegment) ppcImlGenContext.currentOutputSegment = continuedSegment; cemu_assert_debug(basicBlockInfo.appendSegment == writeSegment); basicBlockInfo.appendSegment = continuedSegment; return writeSegment; } // generates a new segment and sets it as branch target for the current write segment. Returns the created segment IMLSegment* PPCIMLGen_CreateNewSegmentAsBranchTarget(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* writeSegment = basicBlockInfo.GetSegmentForInstructionAppend(); IMLSegment* branchTargetSegment = ppcImlGenContext.NewSegment(); cemu_assert_debug(!writeSegment->GetBranchTaken()); // must not have a target already writeSegment->SetLinkBranchTaken(branchTargetSegment); return branchTargetSegment; } // verify that current instruction is the last instruction of the active basic block void PPCIMLGen_AssertIfNotLastSegmentInstruction(ppcImlGenContext_t& ppcImlGenContext) { cemu_assert_debug(ppcImlGenContext.currentBasicBlock->lastAddress == ppcImlGenContext.ppcAddressOfCurrentInstruction); } void PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext_t& ppcImlGenContext, PPCBasicBlockInfo& basicBlockInfo) { IMLSegment* imlSegment = basicBlockInfo.GetFirstSegmentInChain(); if (!basicBlockInfo.hasBranchTarget) return; if (basicBlockInfo.branchTarget > basicBlockInfo.startAddress) return; // exclude non-infinite tight loops if (IMLAnalyzer_IsTightFiniteLoop(imlSegment)) return; // make the segment enterable so execution can return after passing a check basicBlockInfo.GetFirstSegmentInChain()->SetEnterable(basicBlockInfo.startAddress); IMLSegment* splitSeg = PPCIMLGen_CreateSplitSegmentAtEnd(ppcImlGenContext, basicBlockInfo); splitSeg->AppendInstruction()->make_cjump_cycle_check(); IMLSegment* exitSegment = ppcImlGenContext.NewSegment(); splitSeg->SetLinkBranchTaken(exitSegment); exitSegment->AppendInstruction()->make_macro(PPCREC_IML_MACRO_LEAVE, basicBlockInfo.startAddress, 0, 0); } void PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext_t& ppcImlGenContext) { for (IMLSegment* segIt : ppcImlGenContext.segmentList2) { bool isLastSegment = segIt == ppcImlGenContext.segmentList2.back(); // handle empty segment if (segIt->imlList.empty()) { cemu_assert_debug(segIt->GetBranchNotTaken()); continue; } // check last instruction of segment IMLInstruction* imlInstruction = segIt->GetLastInstruction(); if (imlInstruction->type == PPCREC_IML_TYPE_CJUMP || imlInstruction->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) { cemu_assert_debug(segIt->GetBranchTaken()); if (imlInstruction->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE) { cemu_assert_debug(segIt->GetBranchNotTaken()); } } else if (imlInstruction->type == PPCREC_IML_TYPE_MACRO) { auto macroType = imlInstruction->operation; switch (macroType) { case PPCREC_IML_MACRO_B_TO_REG: case PPCREC_IML_MACRO_BL: case PPCREC_IML_MACRO_B_FAR: case PPCREC_IML_MACRO_HLE: case PPCREC_IML_MACRO_LEAVE: segIt->nextSegmentIsUncertain = true; break; case PPCREC_IML_MACRO_DEBUGBREAK: case PPCREC_IML_MACRO_COUNT_CYCLES: case PPCREC_IML_MACRO_MFTB: break; default: cemu_assert_unimplemented(); } } } } bool PPCRecompiler_GenerateIML(ppcImlGenContext_t& ppcImlGenContext, PPCFunctionBoundaryTracker& boundaryTracker, std::set& entryAddresses) { std::vector basicBlockList = PPCRecompiler_DetermineBasicBlockRange(boundaryTracker, entryAddresses); // create segments std::unordered_map addrToBB; ppcImlGenContext.segmentList2.resize(basicBlockList.size()); for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; IMLSegment* seg = new IMLSegment(); seg->ppcAddress = basicBlockInfo.startAddress; if(basicBlockInfo.isEnterable) seg->SetEnterable(basicBlockInfo.startAddress); ppcImlGenContext.segmentList2[i] = seg; cemu_assert_debug(addrToBB.find(basicBlockInfo.startAddress) == addrToBB.end()); basicBlockInfo.SetInitialSegment(seg); addrToBB.emplace(basicBlockInfo.startAddress, &basicBlockInfo); } // link segments for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& bbInfo = basicBlockList[i]; cemu_assert_debug(bbInfo.GetFirstSegmentInChain() == bbInfo.GetSegmentForInstructionAppend()); IMLSegment* seg = ppcImlGenContext.segmentList2[i]; if (bbInfo.hasBranchTarget) { PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.branchTarget]; cemu_assert_debug(targetBB); IMLSegment_SetLinkBranchTaken(seg, targetBB->GetFirstSegmentInChain()); } if (bbInfo.hasContinuedFlow) { PPCBasicBlockInfo* targetBB = addrToBB[bbInfo.lastAddress + 4]; if (!targetBB) { cemuLog_log(LogType::Recompiler, "Recompiler was unable to link segment [0x{:08x}-0x{:08x}] to 0x{:08x}", bbInfo.startAddress, bbInfo.lastAddress, bbInfo.lastAddress + 4); return false; } cemu_assert_debug(targetBB); IMLSegment_SetLinkBranchNotTaken(seg, targetBB->GetFirstSegmentInChain()); } } // we assume that all unreachable segments are potentially enterable // todo - mark them as such // generate cycle counters // in theory we could generate these as part of FillBasicBlock() but in the future we might use more complex logic to emit fewer operations for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; IMLSegment* seg = basicBlockInfo.GetSegmentForInstructionAppend(); uint32 ppcInstructionCount = (basicBlockInfo.lastAddress - basicBlockInfo.startAddress + 4) / 4; cemu_assert_debug(ppcInstructionCount > 0); PPCRecompiler_pushBackIMLInstructions(seg, 0, 1); seg->imlList[0].type = PPCREC_IML_TYPE_MACRO; seg->imlList[0].crRegister = PPC_REC_INVALID_REGISTER; seg->imlList[0].operation = PPCREC_IML_MACRO_COUNT_CYCLES; seg->imlList[0].op_macro.param = ppcInstructionCount; } // generate cycle check instructions // note: Introduces new segments for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; PPCRecompiler_HandleCycleCheckCount(ppcImlGenContext, basicBlockInfo); } // fill in all the basic blocks // note: This step introduces new segments as is necessary for some instructions for (size_t i = 0; i < basicBlockList.size(); i++) { PPCBasicBlockInfo& basicBlockInfo = basicBlockList[i]; ppcImlGenContext.currentBasicBlock = &basicBlockInfo; if (!PPCIMLGen_FillBasicBlock(ppcImlGenContext, basicBlockInfo)) return false; ppcImlGenContext.currentBasicBlock = nullptr; } // mark segments with unknown jump destination (e.g. BLR and most macros) PPCRecompiler_SetSegmentsUncertainFlow(ppcImlGenContext); // debug - check segment graph #ifdef CEMU_DEBUG_ASSERT //for (size_t i = 0; i < basicBlockList.size(); i++) //{ // IMLSegment* seg = ppcImlGenContext.segmentList2[i]; // if (seg->list_prevSegments.empty()) // { // cemu_assert_debug(seg->isEnterable); // } //} // debug - check if suffix instructions are at the end of segments and if they are present for branching segments for (size_t segIndex = 0; segIndex < ppcImlGenContext.segmentList2.size(); segIndex++) { IMLSegment* seg = ppcImlGenContext.segmentList2[segIndex]; IMLSegment* nextSeg = (segIndex+1) < ppcImlGenContext.segmentList2.size() ? ppcImlGenContext.segmentList2[segIndex + 1] : nullptr; if (seg->imlList.size() > 0) { for (size_t f = 0; f < seg->imlList.size() - 1; f++) { if (seg->imlList[f].IsSuffixInstruction()) { debug_printf("---------------- SegmentDump (Suffix instruction at wrong pos in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); __debugbreak(); } } } if (seg->nextSegmentBranchTaken) { if (!seg->HasSuffixInstruction()) { debug_printf("---------------- SegmentDump (NoSuffixInstruction in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); __debugbreak(); } } if (seg->nextSegmentBranchNotTaken) { // if branch not taken, flow must continue to next segment in sequence cemu_assert_debug(seg->nextSegmentBranchNotTaken == nextSeg); } // more detailed checks based on actual suffix instruction if (seg->imlList.size() > 0) { IMLInstruction* inst = seg->GetLastInstruction(); if (inst->type == PPCREC_IML_TYPE_MACRO && inst->op_macro.param == PPCREC_IML_MACRO_B_FAR) { cemu_assert_debug(!seg->GetBranchTaken()); cemu_assert_debug(!seg->GetBranchNotTaken()); } if (inst->type == PPCREC_IML_TYPE_CJUMP_CYCLE_CHECK) { cemu_assert_debug(seg->GetBranchTaken()); cemu_assert_debug(seg->GetBranchNotTaken()); } if (inst->type == PPCREC_IML_TYPE_CJUMP) { if (inst->op_conditionalJump.condition != PPCREC_JUMP_CONDITION_NONE) { if (!seg->GetBranchTaken() || !seg->GetBranchNotTaken()) { debug_printf("---------------- SegmentDump (Missing branch for CJUMP in segment 0x%x):\n", (int)segIndex); IMLDebug_Dump(&ppcImlGenContext); cemu_assert_error(); } } else { // proper error checking for branch-always (or branch-never if invert bit is set) } } } segIndex++; } #endif // todos: // - basic block determination should look for the B(L) B(L) pattern. Or maybe just mark every bb without any input segments as an entry segment return true; } bool PPCRecompiler_generateIntermediateCode(ppcImlGenContext_t& ppcImlGenContext, PPCRecFunction_t* ppcRecFunc, std::set& 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 map_jumpMarks; // for(sint32 i=0; isecond->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 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; //} }