| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,72 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "Common.h" | ||
| #include "Thunk.h" | ||
|
|
||
| #include "../../Core.h" | ||
| #include "../PowerPC.h" | ||
| #include "../../ConfigManager.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "ArmEmitter.h" | ||
| #include "../../HW/Memmap.h" | ||
|
|
||
|
|
||
| #include "Jit.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitFPRCache.h" | ||
| #include "JitAsm.h" | ||
|
|
||
| void JitArm::lfs(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(LoadStoreFloating) | ||
| Default(inst); return; | ||
|
|
||
| ARMReg rA = gpr.GetReg(); | ||
| ARMReg rB = gpr.GetReg(); | ||
| LDR(rA, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions)); | ||
| CMP(rA, EXCEPTION_DSI); | ||
| FixupBranch DoNotLoad = B_CC(CC_EQ); | ||
|
|
||
| if (inst.RA) | ||
| { | ||
| MOVI2R(rB, inst.SIMM_16); | ||
| ARMReg RA = gpr.R(inst.RA); | ||
| ADD(rB, rB, RA); | ||
| } | ||
| else | ||
| MOVI2R(rB, (u32)inst.SIMM_16); | ||
|
|
||
| MOVI2R(rA, (u32)&Memory::Read_U32); | ||
| PUSH(4, R0, R1, R2, R3); | ||
| MOV(R0, rB); | ||
| BL(rA); | ||
| MOV(rA, R0); | ||
| POP(4, R0, R1, R2, R3); | ||
|
|
||
| ARMReg v0 = fpr.R0(inst.FD, false); | ||
| ARMReg v1 = fpr.R1(inst.FD, false); | ||
|
|
||
| VMOV(v0, rA, false); | ||
| VMOV(v1, rA, false); | ||
|
|
||
| gpr.Unlock(rA, rB); | ||
| SetJumpTarget(DoNotLoad); | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,111 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
| #include "Common.h" | ||
| #include "Thunk.h" | ||
|
|
||
| #include "../../Core.h" | ||
| #include "../PowerPC.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "../PPCTables.h" | ||
| #include "ArmEmitter.h" | ||
|
|
||
| #include "Jit.h" | ||
| #include "JitRegCache.h" | ||
| #include "JitAsm.h" | ||
|
|
||
| void JitArm::mtspr(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(SystemRegisters) | ||
|
|
||
| u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); | ||
| ARMReg RD = gpr.R(inst.RD); | ||
|
|
||
| switch (iIndex) | ||
| { | ||
| case SPR_LR: | ||
| case SPR_CTR: | ||
| case SPR_XER: | ||
| // These are safe to do the easy way, see the bottom of this function. | ||
| break; | ||
|
|
||
| case SPR_GQR0: | ||
| case SPR_GQR0 + 1: | ||
| case SPR_GQR0 + 2: | ||
| case SPR_GQR0 + 3: | ||
| case SPR_GQR0 + 4: | ||
| case SPR_GQR0 + 5: | ||
| case SPR_GQR0 + 6: | ||
| case SPR_GQR0 + 7: | ||
| // Prevent recompiler from compiling in old quantizer values. | ||
| // If the value changed, destroy all blocks using this quantizer | ||
| // This will create a little bit of block churn, but hopefully not too bad. | ||
| { | ||
| /* | ||
| MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value | ||
| CMP(32, R(EAX), gpr.R(inst.RD)); | ||
| FixupBranch skip_destroy = J_CC(CC_E, false); | ||
| int gqr = iIndex - SPR_GQR0; | ||
| ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr); | ||
| SetJumpTarget(skip_destroy);*/ | ||
| } | ||
| // TODO - break block if quantizers are written to. | ||
| default: | ||
| Default(inst); | ||
| return; | ||
| } | ||
|
|
||
| // OK, this is easy. | ||
| ARMReg rA = gpr.GetReg(false); | ||
| MOVI2R(rA, (u32)&PowerPC::ppcState.spr); | ||
| STR(rA, RD, iIndex * 4); | ||
| } | ||
|
|
||
| void JitArm::mfspr(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| JITDISABLE(SystemRegisters) | ||
|
|
||
| u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); | ||
| ARMReg RD = gpr.R(inst.RD); | ||
| switch (iIndex) | ||
| { | ||
| case SPR_WPAR: | ||
| case SPR_DEC: | ||
| case SPR_TL: | ||
| case SPR_TU: | ||
| Default(inst); | ||
| return; | ||
| default: | ||
| ARMReg rA = gpr.GetReg(false); | ||
| MOVI2R(rA, (u32)&PowerPC::ppcState.spr); | ||
| LDR(RD, rA, iIndex * 4); | ||
| break; | ||
| } | ||
| } | ||
| void JitArm::mtmsr(UGeckoInstruction inst) | ||
| { | ||
| INSTRUCTION_START | ||
| // Don't interpret this, if we do we get thrown out | ||
| //JITDISABLE(SystemRegisters) | ||
|
|
||
| ARMReg rA = gpr.GetReg(); | ||
| MOVI2R(rA, (u32)&MSR); | ||
| STR(rA, gpr.R(inst.RS)); | ||
| gpr.Unlock(rA); | ||
| WriteExit(js.compilerPC + 4, 0); | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #ifndef JITARM_TABLES_H | ||
| #define JITARM_TABLES_H | ||
|
|
||
| #include "../Gekko.h" | ||
| #include "../PPCTables.h" | ||
|
|
||
| namespace JitArmTables | ||
| { | ||
| void CompileInstruction(PPCAnalyst::CodeOp & op); | ||
| void InitTables(); | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,174 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
|
|
||
| #include "../../HW/Memmap.h" | ||
|
|
||
| #include "../PowerPC.h" | ||
| #include "../../CoreTiming.h" | ||
| #include "MemoryUtil.h" | ||
|
|
||
| #include "Jit.h" | ||
| #include "../JitCommon/JitCache.h" | ||
|
|
||
| #include "../../HW/GPFifo.h" | ||
| #include "../../Core.h" | ||
| #include "JitAsm.h" | ||
| #include "ArmEmitter.h" | ||
|
|
||
| using namespace ArmGen; | ||
|
|
||
| //TODO - make an option | ||
| //#if _DEBUG | ||
| // bool enableDebug = false; | ||
| //#else | ||
| // bool enableDebug = false; | ||
| //#endif | ||
|
|
||
| JitArmAsmRoutineManager asm_routines; | ||
|
|
||
| void JitArmAsmRoutineManager::Generate() | ||
| { | ||
| enterCode = GetCodePtr(); | ||
| PUSH(2, R11, _LR); // R11 is frame pointer in Debug. | ||
|
|
||
| MOVI2R(R0, (u32)&CoreTiming::downcount); | ||
| MOVI2R(R9, (u32)&PowerPC::ppcState); | ||
|
|
||
| FixupBranch skipToRealDispatcher = B(); | ||
| dispatcher = GetCodePtr(); | ||
| printf("Dispatcher is %p\n", dispatcher); | ||
|
|
||
| // Downcount Check | ||
| // The result of slice decrementation should be in flags if somebody jumped here | ||
| // IMPORTANT - We jump on negative, not carry!!! | ||
| FixupBranch bail = B_CC(CC_MI); | ||
|
|
||
| SetJumpTarget(skipToRealDispatcher); | ||
| dispatcherNoCheck = GetCodePtr(); | ||
|
|
||
| // This block of code gets the address of the compiled block of code | ||
| // It runs though to the compiling portion if it isn't found | ||
| LDR(R12, R9, STRUCT_OFF(PowerPC::ppcState, pc));// Load the current PC into R12 | ||
|
|
||
| MOVI2R(R14, JIT_ICACHE_MASK); // Potential for optimization | ||
| AND(R12, R12, R14); // R12 contains PC & JIT_ICACHE_MASK here. | ||
| // Confirmed good to this point 08-03-12 | ||
|
|
||
| MOVI2R(R14, (u32)jit->GetBlockCache()->GetICache()); | ||
| // Confirmed That this loads the base iCache Location correctly 08-04-12 | ||
|
|
||
| LDR(R12, R14, R12, true, true); // R12 contains iCache[PC & JIT_ICACHE_MASK] here | ||
| // R12 Confirmed this is the correct iCache Location loaded. | ||
| TST(R12, 0xFC); // Test to see if it is a JIT block. | ||
|
|
||
| SetCC(CC_EQ); // Only run next part if R12 is zero | ||
| // Success, it is our Jitblock. | ||
| MOVI2R(R14, (u32)jit->GetBlockCache()->GetCodePointers()); | ||
| // LDR R14 right here to get CodePointers()[0] pointer. | ||
| REV(R12, R12); // Reversing this gives us our JITblock. | ||
| LSL(R12, R12, 2); // Multiply by four because address locations are u32 in size | ||
| LDR(R14, R14, R12, true, true); // Load the block address in to R14 | ||
|
|
||
| B(R14); | ||
|
|
||
| FixupBranch NextBlock = B(); // Jump to end so we can start a new block | ||
| SetCC(); // Return to always executing codes | ||
|
|
||
| // If we get to this point, that means that we don't have the block cached to execute | ||
| // So call ArmJit to compile the block and then execute it. | ||
| MOVI2R(R14, (u32)&Jit); | ||
| LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc)); | ||
| BL(R14); | ||
|
|
||
| B(dispatcherNoCheck); | ||
|
|
||
| // fpException() | ||
| // Floating Point Exception Check, Jumped to if false | ||
| fpException = GetCodePtr(); | ||
| LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, Exceptions)); | ||
| ORR(R0, R0, EXCEPTION_FPU_UNAVAILABLE); | ||
| STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, Exceptions)); | ||
| QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); | ||
| LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc)); | ||
| STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc)); | ||
| B(dispatcher); | ||
|
|
||
| SetJumpTarget(bail); | ||
| doTiming = GetCodePtr(); | ||
| // XXX: In JIT64, Advance() gets called /after/ the exception checking | ||
| // once it jumps back to the start of outerLoop | ||
| QuickCallFunction(R14, (void*)&CoreTiming::Advance); | ||
|
|
||
| // Does exception checking | ||
| testExceptions = GetCodePtr(); | ||
| LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, pc)); | ||
| STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, npc)); | ||
| QuickCallFunction(R14, (void*)&PowerPC::CheckExceptions); | ||
| LDR(R0, R9, STRUCT_OFF(PowerPC::ppcState, npc)); | ||
| STR(R9, R0, STRUCT_OFF(PowerPC::ppcState, pc)); | ||
| // Check the state pointer to see if we are exiting | ||
| // Gets checked on every exception check | ||
| MOVI2R(R0, (u32)PowerPC::GetStatePtr()); | ||
| MVN(R1, 0); | ||
| LDR(R0, R0); | ||
| TST(R0, R1); | ||
| FixupBranch Exit = B_CC(CC_NEQ); | ||
|
|
||
| SetJumpTarget(NextBlock); | ||
| B(dispatcher); | ||
|
|
||
| SetJumpTarget(Exit); | ||
|
|
||
| POP(2, R11, _PC); | ||
| FlushIcache(); | ||
| } | ||
|
|
||
| void JitArmAsmRoutineManager::GenerateCommon() | ||
| { | ||
| /* fifoDirectWrite8 = AlignCode4(); | ||
| GenFifoWrite(8); | ||
| fifoDirectWrite16 = AlignCode4(); | ||
| GenFifoWrite(16); | ||
| fifoDirectWrite32 = AlignCode4(); | ||
| GenFifoWrite(32); | ||
| fifoDirectWriteFloat = AlignCode4(); | ||
| GenFifoFloatWrite(); | ||
| fifoDirectWriteXmm64 = AlignCode4(); | ||
| GenFifoXmm64Write(); | ||
| GenQuantizedLoads(); | ||
| GenQuantizedStores(); | ||
| GenQuantizedSingleStores(); | ||
| */ | ||
| //CMPSD(R(XMM0), M(&zero), | ||
| // TODO | ||
|
|
||
| // Fast write routines - special case the most common hardware write | ||
| // TODO: use this. | ||
| // Even in x86, the param values will be in the right registers. | ||
| /* | ||
| const u8 *fastMemWrite8 = AlignCode16(); | ||
| CMP(32, R(ABI_PARAM2), Imm32(0xCC008000)); | ||
| FixupBranch skip_fast_write = J_CC(CC_NE, false); | ||
| MOV(32, EAX, M(&m_gatherPipeCount)); | ||
| MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1); | ||
| ADD(32, 1, M(&m_gatherPipeCount)); | ||
| RET(); | ||
| SetJumpTarget(skip_fast_write); | ||
| CALL((void *)&Memory::Write_U8);*/ | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,43 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #ifndef _JITARMASM_H | ||
| #define _JITARMASM_H | ||
| #include "ArmEmitter.h" | ||
| #include "../JitCommon/JitAsmCommon.h" | ||
| using namespace ArmGen; | ||
| class JitArmAsmRoutineManager : public CommonAsmRoutinesBase, public ARMXCodeBlock | ||
| { | ||
| private: | ||
| void Generate(); | ||
| void GenerateCommon(); | ||
|
|
||
| public: | ||
| void Init() { | ||
| AllocCodeSpace(8192); | ||
| Generate(); | ||
| WriteProtect(); | ||
| } | ||
|
|
||
| void Shutdown() { | ||
| FreeCodeSpace(); | ||
| } | ||
| }; | ||
|
|
||
| extern JitArmAsmRoutineManager asm_routines; | ||
|
|
||
| #endif // _JIT64ASM_H |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,168 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "JitFPRCache.h" | ||
|
|
||
| ArmFPRCache::ArmFPRCache() | ||
| { | ||
| emit = 0; | ||
| } | ||
|
|
||
| void ArmFPRCache::Init(ARMXEmitter *emitter) | ||
| { | ||
| emit = emitter; | ||
| ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG); | ||
| ARMReg *Regs = GetAllocationOrder(NUMARMREG); | ||
|
|
||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| { | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].Reg = PPCRegs[a]; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| ArmCRegs[a].PS1 = false; | ||
| } | ||
| for(u8 a = 0; a < NUMARMREG; ++a) | ||
| { | ||
| ArmRegs[a].Reg = Regs[a]; | ||
| ArmRegs[a].free = true; | ||
| } | ||
| } | ||
| void ArmFPRCache::Start(PPCAnalyst::BlockRegStats &stats) | ||
| { | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| { | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| } | ||
| } | ||
| ARMReg *ArmFPRCache::GetPPCAllocationOrder(int &count) | ||
| { | ||
| // This will return us the allocation order of the registers we can use on | ||
| // the ppc side. | ||
| static ARMReg allocationOrder[] = | ||
| { | ||
| D0, D1, D2, D3, D4, D5, D6, D7, D8, D9, D10, | ||
| D11, D12, D13, D14, D15, D16, D17, D18, D19, | ||
| D20, D21, D22, D23, D24, D25, D26, D27 | ||
| }; | ||
| count = sizeof(allocationOrder) / sizeof(const int); | ||
| return allocationOrder; | ||
| } | ||
| ARMReg *ArmFPRCache::GetAllocationOrder(int &count) | ||
| { | ||
| // This will return us the allocation order of the registers we can use on | ||
| // the host side. | ||
| static ARMReg allocationOrder[] = | ||
| { | ||
| D31, D30, D29, D28 | ||
| }; | ||
| count = sizeof(allocationOrder) / sizeof(const int); | ||
| return allocationOrder; | ||
| } | ||
|
|
||
| ARMReg ArmFPRCache::GetReg(bool AutoLock) | ||
| { | ||
| for(u8 a = 0; a < NUMARMREG; ++a) | ||
| if(ArmRegs[a].free) | ||
| { | ||
| // Alright, this one is free | ||
| if (AutoLock) | ||
| ArmRegs[a].free = false; | ||
| return ArmRegs[a].Reg; | ||
| } | ||
| // Uh Oh, we have all them locked.... | ||
| _assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb"); | ||
| return D31; | ||
| } | ||
| void ArmFPRCache::Unlock(ARMReg V0) | ||
| { | ||
| for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum) | ||
| { | ||
| if(ArmRegs[RegNum].Reg == V0) | ||
| { | ||
| _assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked"); | ||
| ArmRegs[RegNum].free = true; | ||
| } | ||
| } | ||
| } | ||
| ARMReg ArmFPRCache::GetPPCReg(u32 preg, bool PS1, bool preLoad) | ||
| { | ||
| u32 HighestUsed = 0; | ||
| u8 Num = 0; | ||
| for(u8 a = 0; a < NUMPPCREG; ++a){ | ||
| ++ArmCRegs[a].LastLoad; | ||
| if (ArmCRegs[a].LastLoad > HighestUsed) | ||
| { | ||
| HighestUsed = ArmCRegs[a].LastLoad; | ||
| Num = a; | ||
| } | ||
| } | ||
| // Check if already Loaded | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg == preg && ArmCRegs[a].PS1 == PS1) | ||
| { | ||
| ArmCRegs[a].LastLoad = 0; | ||
| return ArmCRegs[a].Reg; | ||
| } | ||
| // Check if we have a free register | ||
| for (u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg == 33) | ||
| { | ||
| u16 offset = STRUCT_OFF(PowerPC::ppcState, ps) + (preg * 16) + (PS1 ? 8 : 0); | ||
| if (preLoad) | ||
| emit->VLDR(ArmCRegs[a].Reg, R9, offset); | ||
| ArmCRegs[a].PPCReg = preg; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| ArmCRegs[a].PS1 = PS1; | ||
| return ArmCRegs[a].Reg; | ||
| } | ||
| // Alright, we couldn't get a free space, dump that least used register | ||
| u16 offsetOld = STRUCT_OFF(PowerPC::ppcState, ps) + (ArmCRegs[Num].PPCReg * 16) + (ArmCRegs[Num].PS1 ? 8 : 0); | ||
| emit->VSTR(ArmCRegs[Num].Reg, R9, offsetOld); | ||
|
|
||
| u16 offsetNew = STRUCT_OFF(PowerPC::ppcState, ps) + (preg * 16) + (PS1 ? 8 : 0); | ||
| if (preLoad) | ||
| emit->VLDR(ArmCRegs[Num].Reg, R9, offsetNew); | ||
| ArmCRegs[Num].PPCReg = preg; | ||
| ArmCRegs[Num].LastLoad = 0; | ||
| ArmCRegs[Num].PS1 = PS1; | ||
| return ArmCRegs[Num].Reg; | ||
|
|
||
| } | ||
|
|
||
| ARMReg ArmFPRCache::R0(u32 preg, bool preLoad) | ||
| { | ||
| return GetPPCReg(preg, false, preLoad); | ||
| } | ||
|
|
||
| ARMReg ArmFPRCache::R1(u32 preg, bool preLoad) | ||
| { | ||
| return GetPPCReg(preg, true, preLoad); | ||
| } | ||
|
|
||
| void ArmFPRCache::Flush() | ||
| { | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg != 33) | ||
| { | ||
| u16 offset = STRUCT_OFF(PowerPC::ppcState, ps) + (ArmCRegs[a].PPCReg * 16) + (ArmCRegs[a].PS1 ? 8 : 0); | ||
| emit->VSTR(ArmCRegs[a].Reg, R9, offset); | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,62 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #ifndef _JITARMFPRCACHE_H | ||
| #define _JITARMFPRCACHE_H | ||
|
|
||
| #include "ArmEmitter.h" | ||
| #include "../Gekko.h" | ||
| #include "../PPCAnalyst.h" | ||
| #include "JitRegCache.h" | ||
|
|
||
| #define ARMFPUREGS 32 | ||
| using namespace ArmGen; | ||
|
|
||
| class ArmFPRCache | ||
| { | ||
| private: | ||
| PPCCachedReg regs[32]; | ||
| JRCPPC ArmCRegs[ARMFPUREGS]; | ||
| JRCReg ArmRegs[ARMFPUREGS]; | ||
|
|
||
| int NUMPPCREG; | ||
| int NUMARMREG; | ||
|
|
||
| ARMReg *GetAllocationOrder(int &count); | ||
| ARMReg *GetPPCAllocationOrder(int &count); | ||
|
|
||
| ARMReg GetPPCReg(u32 preg, bool PS1, bool preLoad); | ||
|
|
||
| protected: | ||
| ARMXEmitter *emit; | ||
|
|
||
| public: | ||
| ArmFPRCache(); | ||
| ~ArmFPRCache() {} | ||
|
|
||
| void Init(ARMXEmitter *emitter); | ||
| void Start(PPCAnalyst::BlockRegStats &stats); | ||
|
|
||
| void SetEmitter(ARMXEmitter *emitter) {emit = emitter;} | ||
|
|
||
| ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use. | ||
| void Unlock(ARMReg V0); | ||
| void Flush(); | ||
| ARMReg R0(u32 preg, bool preLoad = true); // Returns a cached register | ||
| ARMReg R1(u32 preg, bool preLoad = true); | ||
| }; | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,167 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "JitRegCache.h" | ||
|
|
||
| ArmRegCache::ArmRegCache() | ||
| { | ||
| emit = 0; | ||
| } | ||
|
|
||
| void ArmRegCache::Init(ARMXEmitter *emitter) | ||
| { | ||
| emit = emitter; | ||
| ARMReg *PPCRegs = GetPPCAllocationOrder(NUMPPCREG); | ||
| ARMReg *Regs = GetAllocationOrder(NUMARMREG); | ||
| for(u8 a = 0; a < 32; ++a) | ||
| { | ||
| // This gives us the memory locations of the gpr registers so we can | ||
| // load them. | ||
| regs[a].location = (u8*)&PowerPC::ppcState.gpr[a]; | ||
| regs[a].UsesLeft = 0; | ||
| } | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| { | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].Reg = PPCRegs[a]; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| } | ||
| for(u8 a = 0; a < NUMARMREG; ++a) | ||
| { | ||
| ArmRegs[a].Reg = Regs[a]; | ||
| ArmRegs[a].free = true; | ||
| } | ||
| } | ||
| void ArmRegCache::Start(PPCAnalyst::BlockRegStats &stats) | ||
| { | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| { | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| } | ||
| for(u8 a = 0; a < 32; ++a) | ||
| regs[a].UsesLeft = stats.GetTotalNumAccesses(a); | ||
| } | ||
| ARMReg *ArmRegCache::GetPPCAllocationOrder(int &count) | ||
| { | ||
| // This will return us the allocation order of the registers we can use on | ||
| // the ppc side. | ||
| static ARMReg allocationOrder[] = | ||
| { | ||
| R0, R1, R2, R3, R4, R5, R6, R7, R8 | ||
| }; | ||
| count = sizeof(allocationOrder) / sizeof(const int); | ||
| return allocationOrder; | ||
| } | ||
| ARMReg *ArmRegCache::GetAllocationOrder(int &count) | ||
| { | ||
| // This will return us the allocation order of the registers we can use on | ||
| // the host side. | ||
| static ARMReg allocationOrder[] = | ||
| { | ||
| R14, R12, R11, R10 | ||
| }; | ||
| count = sizeof(allocationOrder) / sizeof(const int); | ||
| return allocationOrder; | ||
| } | ||
|
|
||
| ARMReg ArmRegCache::GetReg(bool AutoLock) | ||
| { | ||
| for(u8 a = 0; a < NUMARMREG; ++a) | ||
| if(ArmRegs[a].free) | ||
| { | ||
| // Alright, this one is free | ||
| if (AutoLock) | ||
| ArmRegs[a].free = false; | ||
| return ArmRegs[a].Reg; | ||
| } | ||
| // Uh Oh, we have all them locked.... | ||
| _assert_msg_(_DYNA_REC_, false, "All available registers are locked dumb dumb"); | ||
| return R0; | ||
| } | ||
| void ArmRegCache::Lock(ARMReg Reg) | ||
| { | ||
| for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum) | ||
| if(ArmRegs[RegNum].Reg == Reg) | ||
| { | ||
| _assert_msg_(_DYNA_REC, ArmRegs[RegNum].free, "This register is already locked"); | ||
| ArmRegs[RegNum].free = false; | ||
| } | ||
| _assert_msg_(_DYNA_REC, false, "Register %d can't be used with lock", Reg); | ||
| } | ||
| void ArmRegCache::Unlock(ARMReg R0, ARMReg R1, ARMReg R2, ARMReg R3) | ||
| { | ||
| for(u8 RegNum = 0; RegNum < NUMARMREG; ++RegNum) | ||
| { | ||
| if(ArmRegs[RegNum].Reg == R0) | ||
| { | ||
| _assert_msg_(_DYNA_REC, !ArmRegs[RegNum].free, "This register is already unlocked"); | ||
| ArmRegs[RegNum].free = true; | ||
| } | ||
| if( R1 != INVALID_REG && ArmRegs[RegNum].Reg == R1) ArmRegs[RegNum].free = true; | ||
| if( R2 != INVALID_REG && ArmRegs[RegNum].Reg == R2) ArmRegs[RegNum].free = true; | ||
| if( R3 != INVALID_REG && ArmRegs[RegNum].Reg == R3) ArmRegs[RegNum].free = true; | ||
| } | ||
| } | ||
|
|
||
| ARMReg ArmRegCache::R(u32 preg) | ||
| { | ||
| u32 HighestUsed = 0; | ||
| u8 Num = 0; | ||
| for(u8 a = 0; a < NUMPPCREG; ++a){ | ||
| ++ArmCRegs[a].LastLoad; | ||
| if (ArmCRegs[a].LastLoad > HighestUsed) | ||
| { | ||
| HighestUsed = ArmCRegs[a].LastLoad; | ||
| Num = a; | ||
| } | ||
| } | ||
| // Check if already Loaded | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg == preg) | ||
| { | ||
| ArmCRegs[a].LastLoad = 0; | ||
| return ArmCRegs[a].Reg; | ||
| } | ||
| // Check if we have a free register | ||
| for (u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg == 33) | ||
| { | ||
| emit->LDR(ArmCRegs[a].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4); | ||
| ArmCRegs[a].PPCReg = preg; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| return ArmCRegs[a].Reg; | ||
| } | ||
| // Alright, we couldn't get a free space, dump that least used register | ||
| emit->STR(R9, ArmCRegs[Num].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[Num].PPCReg * 4); | ||
| emit->LDR(ArmCRegs[Num].Reg, R9, STRUCT_OFF(PowerPC::ppcState, gpr) + preg * 4); | ||
| ArmCRegs[Num].PPCReg = preg; | ||
| ArmCRegs[Num].LastLoad = 0; | ||
| return ArmCRegs[Num].Reg; | ||
| } | ||
|
|
||
| void ArmRegCache::Flush() | ||
| { | ||
| for(u8 a = 0; a < NUMPPCREG; ++a) | ||
| if (ArmCRegs[a].PPCReg != 33) | ||
| { | ||
| emit->STR(R9, ArmCRegs[a].Reg, STRUCT_OFF(PowerPC::ppcState, gpr) + ArmCRegs[a].PPCReg * 4); | ||
| ArmCRegs[a].PPCReg = 33; | ||
| ArmCRegs[a].LastLoad = 0; | ||
| } | ||
| } | ||
|
|
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,92 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #ifndef _JITARMREGCACHE_H | ||
| #define _JITARMREGCACHE_H | ||
|
|
||
| #include "ArmEmitter.h" | ||
| #include "../Gekko.h" | ||
| #include "../PPCAnalyst.h" | ||
|
|
||
| using namespace ArmGen; | ||
| // This ARM Register cache actually pre loads the most used registers before | ||
| // the block to increase speed since every memory load requires two | ||
| // instructions to load it. We are going to use R0-RMAX as registers for the | ||
| // use of PPC Registers. | ||
| // Allocation order as follows | ||
| #define ARMREGS 16 | ||
| // Allocate R0 to R9 for PPC first. | ||
| // For General registers on the host side, start with R14 and go down as we go | ||
| // R13 is reserved for our stack pointer, don't ever use that. Unless you save | ||
| // it | ||
| // So we have R14, R12, R11, R10 to work with instructions | ||
|
|
||
| struct PPCCachedReg | ||
| { | ||
| const u8 *location; | ||
| u32 UsesLeft; | ||
| }; | ||
| struct JRCPPC | ||
| { | ||
| u32 PPCReg; // Tied to which PPC Register | ||
| bool PS1; | ||
| ARMReg Reg; // Tied to which ARM Register | ||
| u32 LastLoad; | ||
| }; | ||
| struct JRCReg | ||
| { | ||
| ARMReg Reg; // Which reg this is. | ||
| bool free; | ||
| }; | ||
| class ArmRegCache | ||
| { | ||
| private: | ||
| PPCCachedReg regs[32]; | ||
| JRCPPC ArmCRegs[ARMREGS]; | ||
| JRCReg ArmRegs[ARMREGS]; // Four registers remaining | ||
|
|
||
| int NUMPPCREG; | ||
| int NUMARMREG; | ||
|
|
||
| ARMReg *GetAllocationOrder(int &count); | ||
| ARMReg *GetPPCAllocationOrder(int &count); | ||
|
|
||
| protected: | ||
| ARMXEmitter *emit; | ||
|
|
||
| public: | ||
| ArmRegCache(); | ||
| ~ArmRegCache() {} | ||
|
|
||
| void Init(ARMXEmitter *emitter); | ||
| void Start(PPCAnalyst::BlockRegStats &stats); | ||
|
|
||
| void SetEmitter(ARMXEmitter *emitter) {emit = emitter;} | ||
|
|
||
| ARMReg GetReg(bool AutoLock = true); // Return a ARM register we can use. | ||
| void Lock(ARMReg reg); | ||
| void Unlock(ARMReg R0, ARMReg R1 = INVALID_REG, ARMReg R2 = INVALID_REG, ARMReg R3 = | ||
| INVALID_REG); | ||
| void Flush(); | ||
| ARMReg R(u32 preg); // Returns a cached register | ||
|
|
||
| }; | ||
|
|
||
|
|
||
|
|
||
|
|
||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,350 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include <algorithm> | ||
|
|
||
| #ifdef _WIN32 | ||
| #include <windows.h> | ||
| #endif | ||
|
|
||
| #include "JitInterface.h" | ||
| #include "JitCommon/JitBase.h" | ||
|
|
||
| #ifndef _M_GENERIC | ||
| #include "Jit64IL/JitIL.h" | ||
| #include "Jit64/Jit.h" | ||
| #include "Jit64/Jit64_Tables.h" | ||
| #include "Jit64IL/JitIL_Tables.h" | ||
| #endif | ||
|
|
||
| #ifdef _M_ARM | ||
| #include "JitArm32/Jit.h" | ||
| #include "JitArm32/JitArm_Tables.h" | ||
| #endif | ||
|
|
||
| #include "Profiler.h" | ||
| #include "PPCSymbolDB.h" | ||
| #include "HW/Memmap.h" | ||
| #include "ConfigManager.h" | ||
|
|
||
| bool bFakeVMEM = false; | ||
| bool bMMU = false; | ||
|
|
||
| namespace JitInterface | ||
| { | ||
| void DoState(PointerWrap &p) | ||
| { | ||
| if (jit && p.GetMode() == PointerWrap::MODE_READ) | ||
| jit->GetBlockCache()->ClearSafe(); | ||
| } | ||
| CPUCoreBase *InitJitCore(int core) | ||
| { | ||
| bFakeVMEM = SConfig::GetInstance().m_LocalCoreStartupParameter.iTLBHack == 1; | ||
| bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU; | ||
|
|
||
| CPUCoreBase *ptr = NULL; | ||
| switch(core) | ||
| { | ||
| #ifndef _M_GENERIC | ||
| case 1: | ||
| { | ||
| ptr = new Jit64(); | ||
| break; | ||
| } | ||
| case 2: | ||
| { | ||
| ptr = new JitIL(); | ||
| break; | ||
| } | ||
| #endif | ||
| #ifdef _M_ARM | ||
| case 3: | ||
| { | ||
| ptr = new JitArm(); | ||
| break; | ||
| } | ||
| #endif | ||
| default: | ||
| { | ||
| PanicAlert("Unrecognizable cpu_core: %d", core); | ||
| return NULL; | ||
| break; | ||
| } | ||
| } | ||
| jit = static_cast<JitBase*>(ptr); | ||
| jit->Init(); | ||
| return ptr; | ||
| } | ||
| void InitTables(int core) | ||
| { | ||
| switch(core) | ||
| { | ||
| #ifndef _M_GENERIC | ||
| case 1: | ||
| { | ||
| Jit64Tables::InitTables(); | ||
| break; | ||
| } | ||
| case 2: | ||
| { | ||
| JitILTables::InitTables(); | ||
| break; | ||
| } | ||
| #endif | ||
| #ifdef _M_ARM | ||
| case 3: | ||
| { | ||
| JitArmTables::InitTables(); | ||
| break; | ||
| } | ||
| #endif | ||
| default: | ||
| { | ||
| PanicAlert("Unrecognizable cpu_core: %d", core); | ||
| break; | ||
| } | ||
| } | ||
| } | ||
| CPUCoreBase *GetCore() | ||
| { | ||
| return jit; | ||
| } | ||
|
|
||
| void WriteProfileResults(const char *filename) | ||
| { | ||
| // Can't really do this with no jit core available | ||
| #ifndef _M_GENERIC | ||
|
|
||
| std::vector<BlockStat> stats; | ||
| stats.reserve(jit->GetBlockCache()->GetNumBlocks()); | ||
| u64 cost_sum = 0; | ||
| #ifdef _WIN32 | ||
| u64 timecost_sum = 0; | ||
| u64 countsPerSec; | ||
| QueryPerformanceFrequency((LARGE_INTEGER *)&countsPerSec); | ||
| #endif | ||
| for (int i = 0; i < jit->GetBlockCache()->GetNumBlocks(); i++) | ||
| { | ||
| const JitBlock *block = jit->GetBlockCache()->GetBlock(i); | ||
| // Rough heuristic. Mem instructions should cost more. | ||
| u64 cost = block->originalSize * (block->runCount / 4); | ||
| #ifdef _WIN32 | ||
| u64 timecost = block->ticCounter; | ||
| #endif | ||
| // Todo: tweak. | ||
| if (block->runCount >= 1) | ||
| stats.push_back(BlockStat(i, cost)); | ||
| cost_sum += cost; | ||
| #ifdef _WIN32 | ||
| timecost_sum += timecost; | ||
| #endif | ||
| } | ||
|
|
||
| sort(stats.begin(), stats.end()); | ||
| File::IOFile f(filename, "w"); | ||
| if (!f) | ||
| { | ||
| PanicAlert("failed to open %s", filename); | ||
| return; | ||
| } | ||
| fprintf(f.GetHandle(), "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n"); | ||
| for (unsigned int i = 0; i < stats.size(); i++) | ||
| { | ||
| const JitBlock *block = jit->GetBlockCache()->GetBlock(stats[i].blockNum); | ||
| if (block) | ||
| { | ||
| std::string name = g_symbolDB.GetDescription(block->originalAddress); | ||
| double percent = 100.0 * (double)stats[i].cost / (double)cost_sum; | ||
| #ifdef _WIN32 | ||
| double timePercent = 100.0 * (double)block->ticCounter / (double)timecost_sum; | ||
| fprintf(f.GetHandle(), "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n", | ||
| block->originalAddress, name.c_str(), stats[i].cost, | ||
| block->ticCounter, percent, timePercent, | ||
| (double)block->ticCounter*1000.0/(double)countsPerSec, block->codeSize); | ||
| #else | ||
| fprintf(f.GetHandle(), "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n", | ||
| block->originalAddress, name.c_str(), stats[i].cost, percent, block->codeSize); | ||
| #endif | ||
| } | ||
| } | ||
| #endif | ||
| } | ||
| bool IsInCodeSpace(u8 *ptr) | ||
| { | ||
| return jit->IsInCodeSpace(ptr); | ||
| } | ||
| const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx) | ||
| { | ||
| return jit->BackPatch(codePtr, accessType, em_address, ctx); | ||
| } | ||
|
|
||
| void ClearCache() | ||
| { | ||
| if (jit) | ||
| jit->ClearCache(); | ||
| } | ||
| void ClearSafe() | ||
| { | ||
| if (jit) | ||
| jit->GetBlockCache()->ClearSafe(); | ||
| } | ||
|
|
||
| void InvalidateICache(u32 address, u32 size) | ||
| { | ||
| if (jit) | ||
| jit->GetBlockCache()->InvalidateICache(address, size); | ||
| } | ||
|
|
||
|
|
||
| // Memory functions | ||
| u32 Read_Opcode_JIT_Uncached(const u32 _Address) | ||
| { | ||
| u8* iCache; | ||
| u32 addr; | ||
| if (_Address & JIT_ICACHE_VMEM_BIT) | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICacheVMEM(); | ||
| addr = _Address & JIT_ICACHE_MASK; | ||
| } | ||
| else if (_Address & JIT_ICACHE_EXRAM_BIT) | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICacheEx(); | ||
| addr = _Address & JIT_ICACHEEX_MASK; | ||
| } | ||
| else | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICache(); | ||
| addr = _Address & JIT_ICACHE_MASK; | ||
| } | ||
| u32 inst = *(u32*)(iCache + addr); | ||
| if (inst == JIT_ICACHE_INVALID_WORD) | ||
| { | ||
| u32 cache_block_start = addr & ~0x1f; | ||
| u32 mem_block_start = _Address & ~0x1f; | ||
| u8 *pMem = Memory::GetPointer(mem_block_start); | ||
| memcpy(iCache + cache_block_start, pMem, 32); | ||
| inst = *(u32*)(iCache + addr); | ||
| } | ||
| inst = Common::swap32(inst); | ||
|
|
||
| if ((inst & 0xfc000000) == 0) | ||
| { | ||
| inst = jit->GetBlockCache()->GetOriginalFirstOp(inst); | ||
| } | ||
|
|
||
| return inst; | ||
| } | ||
|
|
||
| u32 Read_Opcode_JIT(u32 _Address) | ||
| { | ||
| #ifdef FAST_ICACHE | ||
| if (bMMU && !bFakeVMEM && (_Address & Memory::ADDR_MASK_MEM1)) | ||
| { | ||
| _Address = Memory::TranslateAddress(_Address, Memory::FLAG_OPCODE); | ||
| if (_Address == 0) | ||
| { | ||
| return 0; | ||
| } | ||
| } | ||
| u32 inst = 0; | ||
|
|
||
| // Bypass the icache for the external interrupt exception handler | ||
| if ( (_Address & 0x0FFFFF00) == 0x00000500 ) | ||
| inst = Read_Opcode_JIT_Uncached(_Address); | ||
| else | ||
| inst = PowerPC::ppcState.iCache.ReadInstruction(_Address); | ||
| #else | ||
| u32 inst = Memory::ReadUnchecked_U32(_Address); | ||
| #endif | ||
| return inst; | ||
| } | ||
|
|
||
| // The following function is deprecated in favour of FAST_ICACHE | ||
| u32 Read_Opcode_JIT_LC(const u32 _Address) | ||
| { | ||
| #ifdef JIT_UNLIMITED_ICACHE | ||
| if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 && | ||
| (_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area | ||
| (_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000) | ||
| { | ||
| PanicAlertT("iCacheJIT: Reading Opcode from %x. Please report.", _Address); | ||
| ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address); | ||
| return 0; | ||
| } | ||
| u8* iCache; | ||
| u32 addr; | ||
| if (_Address & JIT_ICACHE_VMEM_BIT) | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICacheVMEM(); | ||
| addr = _Address & JIT_ICACHE_MASK; | ||
| } | ||
| else if (_Address & JIT_ICACHE_EXRAM_BIT) | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICacheEx(); | ||
| addr = _Address & JIT_ICACHEEX_MASK; | ||
| } | ||
| else | ||
| { | ||
| iCache = jit->GetBlockCache()->GetICache(); | ||
| addr = _Address & JIT_ICACHE_MASK; | ||
| } | ||
| u32 inst = *(u32*)(iCache + addr); | ||
| if (inst == JIT_ICACHE_INVALID_WORD) | ||
| inst = Memory::ReadUnchecked_U32(_Address); | ||
| else | ||
| inst = Common::swap32(inst); | ||
| #else | ||
| u32 inst = Memory::ReadUnchecked_U32(_Address); | ||
| #endif | ||
| if ((inst & 0xfc000000) == 0) | ||
| { | ||
| inst = jit->GetBlockCache()->GetOriginalFirstOp(inst); | ||
| } | ||
| return inst; | ||
| } | ||
|
|
||
| // WARNING! No checks! | ||
| // We assume that _Address is cached | ||
| void Write_Opcode_JIT(const u32 _Address, const u32 _Value) | ||
| { | ||
| #ifdef JIT_UNLIMITED_ICACHE | ||
| if (_Address & JIT_ICACHE_VMEM_BIT) | ||
| { | ||
| *(u32*)(jit->GetBlockCache()->GetICacheVMEM() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value); | ||
| } | ||
| else if (_Address & JIT_ICACHE_EXRAM_BIT) | ||
| { | ||
| *(u32*)(jit->GetBlockCache()->GetICacheEx() + (_Address & JIT_ICACHEEX_MASK)) = Common::swap32(_Value); | ||
| } | ||
| else | ||
| *(u32*)(jit->GetBlockCache()->GetICache() + (_Address & JIT_ICACHE_MASK)) = Common::swap32(_Value); | ||
| #else | ||
| Memory::WriteUnchecked_U32(_Value, _Address); | ||
| #endif | ||
| } | ||
|
|
||
|
|
||
| void Shutdown() | ||
| { | ||
| if (jit) | ||
| { | ||
| jit->Shutdown(); | ||
| delete jit; | ||
| jit = NULL; | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,56 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| #include "ChunkFile.h" | ||
| #include "CPUCoreBase.h" | ||
|
|
||
| namespace JitInterface | ||
| { | ||
| void DoState(PointerWrap &p); | ||
|
|
||
| CPUCoreBase *InitJitCore(int core); | ||
| void InitTables(int core); | ||
| CPUCoreBase *GetCore(); | ||
|
|
||
| // Debugging | ||
| void WriteProfileResults(const char *filename); | ||
|
|
||
| // Memory Utilities | ||
| bool IsInCodeSpace(u8 *ptr); | ||
| const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx); | ||
|
|
||
| // used by JIT to read instructions | ||
| u32 Read_Opcode_JIT(const u32 _Address); | ||
| // used by JIT. uses iCacheJIT. Reads in the "Locked cache" mode | ||
| u32 Read_Opcode_JIT_LC(const u32 _Address); | ||
| void Write_Opcode_JIT(const u32 _Address, const u32 _Value); | ||
|
|
||
| // Clearing CodeCache | ||
| void ClearCache(); | ||
|
|
||
| void ClearSafe(); | ||
|
|
||
| void InvalidateICache(u32 address, u32 size); | ||
|
|
||
| void Shutdown(); | ||
| } | ||
| extern bool bFakeVMEM; | ||
| extern bool bMMU; | ||
|
|
||
| #ifdef _M_ARM | ||
| #include "JitArm32/Jit.h" | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,149 @@ | ||
| // Copyright (C) 2003 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
| #include <stdio.h> | ||
| #include <stdlib.h> | ||
| #include <stdarg.h> | ||
|
|
||
| #include "Common.h" | ||
| #include "FileUtil.h" | ||
|
|
||
| #include "Core.h" | ||
| #include "Host.h" | ||
| #include "CPUDetect.h" | ||
| #include "Thread.h" | ||
|
|
||
| #include "PowerPC/PowerPC.h" | ||
| #include "HW/Wiimote.h" | ||
|
|
||
| #include "VideoBackendBase.h" | ||
| #include "ConfigManager.h" | ||
| #include "LogManager.h" | ||
| #include "BootManager.h" | ||
|
|
||
| #include <jni.h> | ||
| #include <android/log.h> | ||
| #define LOGI(...) ((void)__android_log_print(ANDROID_LOG_INFO, "Dolphinemu", __VA_ARGS__)) | ||
|
|
||
| bool rendererHasFocus = true; | ||
| bool running = true; | ||
|
|
||
| void Host_NotifyMapLoaded() {} | ||
| void Host_RefreshDSPDebuggerWindow() {} | ||
|
|
||
| void Host_ShowJitResults(unsigned int address){} | ||
|
|
||
| Common::Event updateMainFrameEvent; | ||
| void Host_Message(int Id) | ||
| { | ||
| } | ||
|
|
||
| void* Host_GetRenderHandle() | ||
| { | ||
| return NULL; | ||
| } | ||
|
|
||
| void* Host_GetInstance() { return NULL; } | ||
|
|
||
| void Host_UpdateTitle(const char* title){}; | ||
|
|
||
| void Host_UpdateLogDisplay(){} | ||
|
|
||
| void Host_UpdateDisasmDialog(){} | ||
|
|
||
| void Host_UpdateMainFrame() | ||
| { | ||
| } | ||
|
|
||
| void Host_UpdateBreakPointView(){} | ||
|
|
||
| bool Host_GetKeyState(int keycode) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| void Host_GetRenderWindowSize(int& x, int& y, int& width, int& height) | ||
| { | ||
| x = SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowXPos; | ||
| y = SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowYPos; | ||
| width = SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowWidth; | ||
| height = SConfig::GetInstance().m_LocalCoreStartupParameter.iRenderWindowHeight; | ||
| } | ||
|
|
||
| void Host_RequestRenderWindowSize(int width, int height) {} | ||
| void Host_SetStartupDebuggingParameters() | ||
| { | ||
| } | ||
|
|
||
| bool Host_RendererHasFocus() | ||
| { | ||
| return true; | ||
| } | ||
|
|
||
| void Host_ConnectWiimote(int wm_idx, bool connect) {} | ||
|
|
||
| void Host_SetWaitCursor(bool enable){} | ||
|
|
||
| void Host_UpdateStatusBar(const char* _pText, int Filed){} | ||
|
|
||
| void Host_SysMessage(const char *fmt, ...) | ||
| { | ||
| va_list list; | ||
| char msg[512]; | ||
|
|
||
| va_start(list, fmt); | ||
| vsprintf(msg, fmt, list); | ||
| va_end(list); | ||
|
|
||
| size_t len = strlen(msg); | ||
| if (msg[len - 1] != '\n') { | ||
| msg[len - 1] = '\n'; | ||
| msg[len] = '\0'; | ||
| } | ||
| LOGI(msg); | ||
| } | ||
|
|
||
| void Host_SetWiiMoteConnectionState(int _State) {} | ||
|
|
||
| #ifdef __cplusplus | ||
| extern "C" | ||
| { | ||
| #endif | ||
| JNIEXPORT void JNICALL Java_org_dolphinemu_dolphinemu_dolphinemuactivity_main(JNIEnv *env, jobject obj) | ||
| { | ||
| LogManager::Init(); | ||
| SConfig::Init(); | ||
| VideoBackend::PopulateList(); | ||
| VideoBackend::ActivateBackend(SConfig::GetInstance(). | ||
| m_LocalCoreStartupParameter.m_strVideoBackend); | ||
| WiimoteReal::LoadSettings(); | ||
|
|
||
| // No use running the loop when booting fails | ||
| if (BootManager::BootCore("")) | ||
| { | ||
| while (PowerPC::GetState() != PowerPC::CPU_POWERDOWN) | ||
| updateMainFrameEvent.Wait(); | ||
| } | ||
|
|
||
| WiimoteReal::Shutdown(); | ||
| VideoBackend::ClearList(); | ||
| SConfig::Shutdown(); | ||
| LogManager::Shutdown(); | ||
| } | ||
|
|
||
| #ifdef __cplusplus | ||
| } | ||
| #endif |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,52 @@ | ||
| // Copyright (C) 2003-2009 Dolphin Project. | ||
|
|
||
| // This program is free software: you can redistribute it and/or modify | ||
| // it under the terms of the GNU General Public License as published by | ||
| // the Free Software Foundation, version 2.0. | ||
|
|
||
| // This program is distributed in the hope that it will be useful, | ||
| // but WITHOUT ANY WARRANTY; without even the implied warranty of | ||
| // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the | ||
| // GNU General Public License 2.0 for more details. | ||
|
|
||
| // A copy of the GPL 2.0 should have been included with the program. | ||
| // If not, see http://www.gnu.org/licenses/ | ||
|
|
||
| // Official SVN repository and contact information can be found at | ||
| // http://code.google.com/p/dolphin-emu/ | ||
|
|
||
| // TODO: Handle cache-is-full condition :p | ||
|
|
||
|
|
||
| #include "Common.h" | ||
| #include "DLCache.h" | ||
|
|
||
| namespace DLCache | ||
| { | ||
|
|
||
| void Init() | ||
| { | ||
| } | ||
|
|
||
| void Shutdown() | ||
| { | ||
| } | ||
|
|
||
| void Clear() | ||
| { | ||
| } | ||
|
|
||
| void ProgressiveCleanup() | ||
| { | ||
| } | ||
| } // namespace | ||
|
|
||
| // NOTE - outside the namespace on purpose. | ||
| bool HandleDisplayList(u32 address, u32 size) | ||
| { | ||
| return false; | ||
| } | ||
|
|
||
| void IncrementCheckContextId() | ||
| { | ||
| } |