414 changes: 414 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStore.cpp

Large diffs are not rendered by default.

72 changes: 72 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitArm_LoadStoreFloating.cpp
@@ -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);
}

111 changes: 111 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitArm_SystemRegisters.cpp
@@ -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);
}
502 changes: 502 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Tables.cpp

Large diffs are not rendered by default.

29 changes: 29 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitArm_Tables.h
@@ -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
174 changes: 174 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.cpp
@@ -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);*/
}
43 changes: 43 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitAsm.h
@@ -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
168 changes: 168 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitFPRCache.cpp
@@ -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;
}
}

62 changes: 62 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitFPRCache.h
@@ -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
167 changes: 167 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.cpp
@@ -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;
}
}

92 changes: 92 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitArm32/JitRegCache.h
@@ -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
4 changes: 2 additions & 2 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.cpp
Expand Up @@ -15,7 +15,7 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/

#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "CPUDetect.h"
#include "x64Emitter.h"
Expand All @@ -26,7 +26,7 @@
#include "../../CoreTiming.h"
#include "MemoryUtil.h"

#include "ABI.h"
#include "x64ABI.h"
#include "../JitCommon/JitCache.h"

#include "../../HW/GPFifo.h"
Expand Down
25 changes: 16 additions & 9 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitAsmCommon.h
Expand Up @@ -21,16 +21,8 @@
#include "../JitCommon/Jit_Util.h"
#include "Thunk.h"

class CommonAsmRoutines : public EmuCodeBlock {
protected:
void GenQuantizedLoads();
void GenQuantizedStores();
void GenQuantizedSingleStores();

class CommonAsmRoutinesBase {
public:
void GenFifoWrite(int size);
void GenFifoXmm64Write();
void GenFifoFloatWrite();

const u8 *fifoDirectWrite8;
const u8 *fifoDirectWrite16;
Expand Down Expand Up @@ -72,8 +64,23 @@ class CommonAsmRoutines : public EmuCodeBlock {
// In: XMM0: Bottom 32-bit slot holds the float to be written.
const u8 **singleStoreQuantized;

};

class CommonAsmRoutines : public CommonAsmRoutinesBase, public EmuCodeBlock
{
protected:
void GenQuantizedLoads();
void GenQuantizedStores();
void GenQuantizedSingleStores();

public:
void GenFifoWrite(int size);
void GenFifoXmm64Write();
void GenFifoFloatWrite();

private:
ThunkManager thunks;

};

#endif
4 changes: 2 additions & 2 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitBackpatch.cpp
Expand Up @@ -25,7 +25,7 @@
#include "../../HW/Memmap.h"

#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "Thunk.h"
#include "x64Analyzer.h"

Expand Down Expand Up @@ -160,7 +160,7 @@ const u8 *TrampolineCache::GetWriteTrampoline(const InstructionInfo &info)
// 1) It's really necessary. We don't know anything about the context.
// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be
// that many of them in a typical program/game.
const u8 *JitBase::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
const u8 *Jitx86Base::BackPatch(u8 *codePtr, int accessType, u32 emAddress, void *ctx_void)
{
#ifdef _M_X64
CONTEXT *ctx = (CONTEXT *)ctx_void;
Expand Down
4 changes: 4 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitBackpatch.h
Expand Up @@ -34,13 +34,17 @@
// from the real context.
struct CONTEXT
{
#ifdef _M_ARM
u32 reg_pc;
#else
#ifdef _M_X64
u64 Rip;
u64 Rax;
#else
u32 Eip;
u32 Eax;
#endif
#endif
};

#endif
Expand Down
26 changes: 19 additions & 7 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitBase.h
Expand Up @@ -32,12 +32,9 @@

#define JIT_OPCODE 0

class JitBase : public CPUCoreBase, public EmuCodeBlock
class JitBase : public CPUCoreBase
{
protected:
JitBlockCache blocks;
TrampolineCache trampolines;

struct JitOptions
{
bool optimizeStack;
Expand Down Expand Up @@ -85,14 +82,29 @@ class JitBase : public CPUCoreBase, public EmuCodeBlock
// This should probably be removed from public:
JitOptions jo;
JitState js;

JitBlockCache *GetBlockCache() { return &blocks; }
virtual JitBaseBlockCache *GetBlockCache() = 0;

virtual void Jit(u32 em_address) = 0;

virtual const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx) = 0;

virtual const CommonAsmRoutinesBase *GetAsmRoutines() = 0;

virtual bool IsInCodeSpace(u8 *ptr) = 0;
};

class Jitx86Base : public JitBase, public EmuCodeBlock
{
protected:
JitBlockCache blocks;
TrampolineCache trampolines;
public:
JitBlockCache *GetBlockCache() { return &blocks; }

const u8 *BackPatch(u8 *codePtr, int accessType, u32 em_address, void *ctx);

virtual const CommonAsmRoutines *GetAsmRoutines() = 0;
bool IsInCodeSpace(u8 *ptr) { return IsInSpace(ptr); }
};

extern JitBase *jit;
Expand Down
94 changes: 47 additions & 47 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitCache.cpp
Expand Up @@ -32,15 +32,13 @@
#include "MemoryUtil.h"

#include "../../HW/Memmap.h"
#include "../JitInterface.h"
#include "../../CoreTiming.h"

#include "../PowerPC.h"
#include "../PPCTables.h"
#include "../PPCAnalyst.h"

#include "x64Emitter.h"
#include "x64Analyzer.h"

#include "JitCache.h"
#include "JitBase.h"

Expand Down Expand Up @@ -68,12 +66,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
return (em_address >= originalAddress && em_address < originalAddress + 4 * originalSize);
}

bool JitBlockCache::IsFull() const
bool JitBaseBlockCache::IsFull() const
{
return GetNumBlocks() >= MAX_NUM_BLOCKS - 1;
}

void JitBlockCache::Init()
void JitBaseBlockCache::Init()
{
MAX_NUM_BLOCKS = 65536*2;

Expand All @@ -91,11 +89,11 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
else
{
PanicAlert("JitBlockCache::Init() - iCache is already initialized");
PanicAlert("JitBaseBlockCache::Init() - iCache is already initialized");
}
if (iCache == 0 || iCacheEx == 0 || iCacheVMEM == 0)
{
PanicAlert("JitBlockCache::Init() - unable to allocate iCache");
PanicAlert("JitBaseBlockCache::Init() - unable to allocate iCache");
}
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
memset(iCacheEx, JIT_ICACHE_INVALID_BYTE, JIT_ICACHEEX_SIZE);
Expand All @@ -104,7 +102,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
Clear();
}

void JitBlockCache::Shutdown()
void JitBaseBlockCache::Shutdown()
{
delete[] blocks;
delete[] blockCodePointers;
Expand Down Expand Up @@ -133,7 +131,7 @@ bool JitBlock::ContainsAddress(u32 em_address)

// This clears the JIT cache. It's called from JitCache.cpp when the JIT cache
// is full and when saving and loading states.
void JitBlockCache::Clear()
void JitBaseBlockCache::Clear()
{
if (IsFull())
Core::DisplayMessage("Clearing block cache.", 3000);
Expand All @@ -151,7 +149,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
}

void JitBlockCache::ClearSafe()
void JitBaseBlockCache::ClearSafe()
{
#ifdef JIT_UNLIMITED_ICACHE
memset(iCache, JIT_ICACHE_INVALID_BYTE, JIT_ICACHE_SIZE);
Expand All @@ -160,7 +158,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
#endif
}

/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
/*void JitBaseBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
{
for (int i = 0; i < num_blocks; i++)
{
Expand All @@ -171,23 +169,23 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}*/

void JitBlockCache::Reset()
void JitBaseBlockCache::Reset()
{
Shutdown();
Init();
}

JitBlock *JitBlockCache::GetBlock(int no)
JitBlock *JitBaseBlockCache::GetBlock(int no)
{
return &blocks[no];
}

int JitBlockCache::GetNumBlocks() const
int JitBaseBlockCache::GetNumBlocks() const
{
return num_blocks;
}

bool JitBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
bool JitBaseBlockCache::RangeIntersect(int s1, int e1, int s2, int e2) const
{
// check if any endpoint is inside the other range
if ((s1 >= s2 && s1 <= e2) ||
Expand All @@ -199,7 +197,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
return false;
}

int JitBlockCache::AllocateBlock(u32 em_address)
int JitBaseBlockCache::AllocateBlock(u32 em_address)
{
JitBlock &b = blocks[num_blocks];
b.invalid = false;
Expand All @@ -215,12 +213,12 @@ bool JitBlock::ContainsAddress(u32 em_address)
return num_blocks - 1;
}

void JitBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
void JitBaseBlockCache::FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr)
{
blockCodePointers[block_num] = code_ptr;
JitBlock &b = blocks[block_num];
b.originalFirstOpcode = Memory::Read_Opcode_JIT(b.originalAddress);
Memory::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);
b.originalFirstOpcode = JitInterface::Read_Opcode_JIT(b.originalAddress);
JitInterface::Write_Opcode_JIT(b.originalAddress, (JIT_OPCODE << 26) | block_num);

// Convert the logical address to a physical address for the block map
u32 pAddr = b.originalAddress & 0x1FFFFFFF;
Expand Down Expand Up @@ -264,29 +262,29 @@ bool JitBlock::ContainsAddress(u32 em_address)
#endif
}

const u8 **JitBlockCache::GetCodePointers()
const u8 **JitBaseBlockCache::GetCodePointers()
{
return blockCodePointers;
}

#ifdef JIT_UNLIMITED_ICACHE
u8* JitBlockCache::GetICache()
u8* JitBaseBlockCache::GetICache()
{
return iCache;
}

u8* JitBlockCache::GetICacheEx()
u8* JitBaseBlockCache::GetICacheEx()
{
return iCacheEx;
}

u8* JitBlockCache::GetICacheVMEM()
u8* JitBaseBlockCache::GetICacheVMEM()
{
return iCacheVMEM;
}
#endif

int JitBlockCache::GetBlockNumberFromStartAddress(u32 addr)
int JitBaseBlockCache::GetBlockNumberFromStartAddress(u32 addr)
{
if (!blocks)
return -1;
Expand Down Expand Up @@ -317,24 +315,24 @@ bool JitBlock::ContainsAddress(u32 em_address)
return inst;
}

void JitBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
void JitBaseBlockCache::GetBlockNumbersFromAddress(u32 em_address, std::vector<int> *block_numbers)
{
for (int i = 0; i < num_blocks; i++)
if (blocks[i].ContainsAddress(em_address))
block_numbers->push_back(i);
}

u32 JitBlockCache::GetOriginalFirstOp(int block_num)
u32 JitBaseBlockCache::GetOriginalFirstOp(int block_num)
{
if (block_num >= num_blocks)
{
//PanicAlert("JitBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
//PanicAlert("JitBaseBlockCache::GetOriginalFirstOp - block_num = %u is out of range", block_num);
return block_num;
}
return blocks[block_num].originalFirstOpcode;
}

CompiledCode JitBlockCache::GetCompiledCodeFromBlock(int block_num)
CompiledCode JitBaseBlockCache::GetCompiledCodeFromBlock(int block_num)
{
return (CompiledCode)blockCodePointers[block_num];
}
Expand All @@ -345,7 +343,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
//Can be faster by doing a queue for blocks to link up, and only process those
//Should probably be done

void JitBlockCache::LinkBlockExits(int i)
void JitBaseBlockCache::LinkBlockExits(int i)
{
JitBlock &b = blocks[i];
if (b.invalid)
Expand All @@ -360,8 +358,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
int destinationBlock = GetBlockNumberFromStartAddress(b.exitAddress[e]);
if (destinationBlock != -1)
{
XEmitter emit(b.exitPtrs[e]);
emit.JMP(blocks[destinationBlock].checkedEntry, true);
WriteLinkBlock(b.exitPtrs[e], blocks[destinationBlock].checkedEntry);
b.linkStatus[e] = true;
}
}
Expand All @@ -370,7 +367,7 @@ bool JitBlock::ContainsAddress(u32 em_address)

using namespace std;

void JitBlockCache::LinkBlock(int i)
void JitBaseBlockCache::LinkBlock(int i)
{
LinkBlockExits(i);
JitBlock &b = blocks[i];
Expand All @@ -386,7 +383,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}

void JitBlockCache::UnlinkBlock(int i)
void JitBaseBlockCache::UnlinkBlock(int i)
{
JitBlock &b = blocks[i];
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
Expand All @@ -403,7 +400,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
}

void JitBlockCache::DestroyBlock(int block_num, bool invalidate)
void JitBaseBlockCache::DestroyBlock(int block_num, bool invalidate)
{
if (block_num < 0 || block_num >= num_blocks)
{
Expand All @@ -419,7 +416,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
b.invalid = true;
#ifdef JIT_UNLIMITED_ICACHE
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
JitInterface::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
#else
if (Memory::ReadFast32(b.originalAddress) == block_num)
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
Expand All @@ -430,26 +427,18 @@ bool JitBlock::ContainsAddress(u32 em_address)
// Send anyone who tries to run this block back to the dispatcher.
// Not entirely ideal, but .. pretty good.
// Spurious entrances from previously linked blocks can only come through checkedEntry
XEmitter emit((u8 *)b.checkedEntry);
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
// this is not needed really
/*
emit.SetCodePtr((u8 *)blockCodePointers[blocknum]);
emit.MOV(32, M(&PC), Imm32(b.originalAddress));
emit.JMP(asm_routines.dispatcher, true);
*/
WriteDestroyBlock(b.checkedEntry, b.originalAddress);
}

void JitBlockCache::InvalidateICache(u32 address, const u32 length)
void JitBaseBlockCache::InvalidateICache(u32 address, const u32 length)
{
// Convert the logical address to a physical address for the block map
u32 pAddr = address & 0x1FFFFFFF;

// Optimize the common case of length == 32 which is used by Interpreter::dcb*
bool destroy_block = true;
if (length == 32)
{
{
if (!valid_block[pAddr / 32])
destroy_block = false;
else
Expand All @@ -462,7 +451,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
{
std::map<pair<u32,u32>, u32>::iterator it1 = block_map.lower_bound(std::make_pair(pAddr, 0)), it2 = it1;
while (it2 != block_map.end() && it2->first.second < pAddr + length)
{
{
#ifdef JIT_UNLIMITED_ICACHE
JitBlock &b = blocks[it2->second];
if (b.originalAddress & JIT_ICACHE_VMEM_BIT)
Expand Down Expand Up @@ -516,3 +505,14 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
#endif
}
void JitBlockCache::WriteLinkBlock(u8* location, const u8* address)
{
XEmitter emit(location);
emit.JMP(address, true);
}
void JitBlockCache::WriteDestroyBlock(const u8* location, u32 address)
{
XEmitter emit((u8 *)location);
emit.MOV(32, M(&PC), Imm32(address));
emit.JMP(jit->GetAsmRoutines()->dispatcher, true);
}
16 changes: 14 additions & 2 deletions Source/Core/Core/Src/PowerPC/JitCommon/JitCache.h
Expand Up @@ -78,7 +78,8 @@ struct JitBlock

typedef void (*CompiledCode)();

class JitBlockCache

class JitBaseBlockCache
{
const u8 **blockCodePointers;
JitBlock *blocks;
Expand All @@ -97,9 +98,13 @@ class JitBlockCache
void LinkBlockExits(int i);
void LinkBlock(int i);
void UnlinkBlock(int i);

// Virtual for overloaded
virtual void WriteLinkBlock(u8* location, const u8* address) = 0;
virtual void WriteDestroyBlock(const u8* location, u32 address) = 0;

public:
JitBlockCache() :
JitBaseBlockCache() :
blockCodePointers(0), blocks(0), num_blocks(0),
#ifdef JIT_UNLIMITED_ICACHE
iCache(0), iCacheEx(0), iCacheVMEM(0),
Expand Down Expand Up @@ -146,4 +151,11 @@ class JitBlockCache
//void DestroyBlocksWithFlag(BlockFlag death_flag);
};

// x86 BlockCache
class JitBlockCache : public JitBaseBlockCache
{
private:
void WriteLinkBlock(u8* location, const u8* address);
void WriteDestroyBlock(const u8* location, u32 address);
};
#endif
2 changes: 1 addition & 1 deletion Source/Core/Core/Src/PowerPC/JitCommon/Jit_Util.cpp
Expand Up @@ -25,7 +25,7 @@
#include "../../HW/Memmap.h"
#include "../PPCTables.h"
#include "x64Emitter.h"
#include "ABI.h"
#include "x64ABI.h"
#include "JitBase.h"
#include "Jit_Util.h"

Expand Down
350 changes: 350 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitInterface.cpp
@@ -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;
}
}
}
56 changes: 56 additions & 0 deletions Source/Core/Core/Src/PowerPC/JitInterface.h
@@ -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
3 changes: 2 additions & 1 deletion Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp
Expand Up @@ -21,6 +21,7 @@
#include "StringUtil.h"
#include "Interpreter/Interpreter.h"
#include "../HW/Memmap.h"
#include "JitInterface.h"
#include "PPCTables.h"
#include "PPCSymbolDB.h"
#include "SignatureDB.h"
Expand Down Expand Up @@ -368,7 +369,7 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa,
}
else
{
inst = Memory::Read_Opcode_JIT(address);
inst = JitInterface::Read_Opcode_JIT(address);
}
}

Expand Down
7 changes: 3 additions & 4 deletions Source/Core/Core/Src/PowerPC/PPCCache.cpp
Expand Up @@ -20,6 +20,7 @@
#include "PowerPC.h"
#include "JitCommon/JitBase.h"
#include "JitCommon/JitCache.h"
#include "JitInterface.h"

namespace PowerPC
{
Expand Down Expand Up @@ -76,8 +77,7 @@ namespace PowerPC
memset(lookup_table_ex, 0xff, sizeof(lookup_table_ex));
memset(lookup_table_vmem, 0xff, sizeof(lookup_table_vmem));
#endif
if (jit)
jit->GetBlockCache()->ClearSafe();
JitInterface::ClearSafe();
}

void InstructionCache::Init()
Expand Down Expand Up @@ -109,8 +109,7 @@ namespace PowerPC
}
#endif
valid[set] = 0;
if (jit)
jit->GetBlockCache()->InvalidateICache(addr & ~0x1f, 32);
JitInterface::InvalidateICache(addr & ~0x1f, 32);
}

u32 InstructionCache::ReadInstruction(u32 addr)
Expand Down
18 changes: 2 additions & 16 deletions Source/Core/Core/Src/PowerPC/PPCTables.cpp
Expand Up @@ -24,11 +24,7 @@
#include "FileUtil.h"
#include "Interpreter/Interpreter.h"
#include "Interpreter/Interpreter_Tables.h"
#include "Jit64IL/JitIL_Tables.h"
#include "Jit64/Jit64_Tables.h"

#include "Jit64IL/JitIL.h"
#include "Jit64/Jit.h"
#include "JitInterface.h"

struct op_inf
{
Expand Down Expand Up @@ -165,19 +161,9 @@ void InitTables(int cpu_core)
// Interpreter
break;
}
case 1:
{
Jit64Tables::InitTables();
break;
}
case 2:
{
JitILTables::InitTables();
break;
}
default:
{
PanicAlert("Unrecognizable cpu_core: %d", cpu_core);
JitInterface::InitTables(cpu_core);
break;
}
}
Expand Down
70 changes: 19 additions & 51 deletions Source/Core/Core/Src/PowerPC/PowerPC.cpp
Expand Up @@ -29,12 +29,10 @@
#include "../HW/SystemTimers.h"

#include "Interpreter/Interpreter.h"
#include "JitCommon/JitBase.h"
#include "Jit64IL/JitIL.h"
#include "Jit64/Jit.h"
#include "PowerPC.h"
#include "PPCTables.h"
#include "CPUCoreBase.h"
#include "JitInterface.h"

#include "../Host.h"
#include "HW/EXI.h"
Expand Down Expand Up @@ -87,8 +85,7 @@ void DoState(PointerWrap &p)
// SystemTimers::DecrementerSet();
// SystemTimers::TimeBaseSet();

if (jit && p.GetMode() == PointerWrap::MODE_READ)
jit->GetBlockCache()->ClearSafe();
JitInterface::DoState(p);
}

void ResetRegisters()
Expand Down Expand Up @@ -131,28 +128,18 @@ void ResetRegisters()

void Init(int cpu_core)
{
enum {
FPU_PREC_24 = 0 << 8,
FPU_PREC_53 = 2 << 8,
FPU_PREC_64 = 3 << 8,
FPU_PREC_MASK = 3 << 8,
};
#ifdef _M_IX86
// sets the floating-point lib to 53-bit
// PowerPC has a 53bit floating pipeline only
// eg: sscanf is very sensitive
#ifdef _WIN32
_control87(_PC_53, MCW_PC);
#else
unsigned short _mode;
asm ("fstcw %0" : : "m" (_mode));
_mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53;
asm ("fldcw %0" : : "m" (_mode));
#endif
#else
//x64 doesn't need this - fpu is done with SSE
//but still - set any useful sse options here
#endif
FPURoundMode::SetPrecisionMode(FPURoundMode::PREC_53);

memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
memset(ppcState.sr, 0, sizeof(ppcState.sr));
ppcState.DebugCount = 0;
ppcState.dtlb_last = 0;
ppcState.dtlb_last = 0;
memset(ppcState.dtlb_va, 0, sizeof(ppcState.dtlb_va));
memset(ppcState.dtlb_pa, 0, sizeof(ppcState.dtlb_pa));
ppcState.itlb_last = 0;
memset(ppcState.itlb_va, 0, sizeof(ppcState.itlb_va));
memset(ppcState.itlb_pa, 0, sizeof(ppcState.itlb_pa));

memset(ppcState.mojs, 0, sizeof(ppcState.mojs));
memset(ppcState.sr, 0, sizeof(ppcState.sr));
Expand All @@ -179,27 +166,13 @@ void Init(int cpu_core)
cpu_core_base = interpreter;
break;
}
case 1:
{
cpu_core_base = new Jit64();
break;
}
case 2:
{
cpu_core_base = new JitIL();
break;
}
default:
{
PanicAlert("Unrecognizable cpu_core: %d", cpu_core);
break;
}
default:
cpu_core_base = JitInterface::InitJitCore(cpu_core);
break;
}

if (cpu_core_base != interpreter)
{
jit = static_cast<JitBase*>(cpu_core_base);
jit->Init();
mode = MODE_JIT;
}
else
Expand All @@ -213,12 +186,7 @@ void Init(int cpu_core)

void Shutdown()
{
if (jit)
{
jit->Shutdown();
delete jit;
jit = NULL;
}
JitInterface::Shutdown();
interpreter->Shutdown();
cpu_core_base = NULL;
state = CPU_POWERDOWN;
Expand All @@ -244,7 +212,7 @@ void SetMode(CoreMode new_mode)

case MODE_JIT: // Switching from interpreter to JIT.
// Don't really need to do much. It'll work, the cache will refill itself.
cpu_core_base = jit;
cpu_core_base = JitInterface::GetCore();
break;
}
}
Expand Down
78 changes: 2 additions & 76 deletions Source/Core/Core/Src/PowerPC/Profiler.cpp
Expand Up @@ -15,91 +15,17 @@
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/

#include "JitCommon/JitBase.h"

#include <vector>
#include <algorithm>

#ifdef _WIN32
#include <windows.h>
#endif

#include "PPCSymbolDB.h"
#include "FileUtil.h"
#include "JitInterface.h"

namespace Profiler
{

bool g_ProfileBlocks;
bool g_ProfileInstructions;

struct BlockStat
{
BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {}
int blockNum;
u64 cost;

bool operator <(const BlockStat &other) const
{ return cost > other.cost; }
};

void WriteProfileResults(const char *filename)
{
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);
if (block && !block->invalid)
{
// 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\trunCount\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 && !block->invalid)
{
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%llu\t%.2lf\t%llf\t%lf\t%i\n",
block->originalAddress, name.c_str(), stats[i].cost, block->runCount,
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
}
}
JitInterface::WriteProfileResults(filename);
}

} // namespace
9 changes: 9 additions & 0 deletions Source/Core/Core/Src/PowerPC/Profiler.h
Expand Up @@ -57,6 +57,15 @@
#define PROFILER_VPOP
#endif

struct BlockStat
{
BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {}
int blockNum;
u64 cost;

bool operator <(const BlockStat &other) const
{ return cost > other.cost; }
};

namespace Profiler
{
Expand Down
Expand Up @@ -52,8 +52,10 @@
#include "MemTools.h"
#include "HW/Memmap.h"
#include "PowerPC/PowerPC.h"
#include "PowerPC/JitInterface.h"
#ifndef _M_GENERIC
#include "PowerPC/JitCommon/JitBase.h"
#include "PowerPC/JitCommon/JitBackpatch.h"
#endif
#include "x64Analyzer.h"

namespace EMM
Expand All @@ -77,7 +79,7 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)
PVOID codeAddr = pPtrs->ExceptionRecord->ExceptionAddress;
unsigned char *codePtr = (unsigned char*)codeAddr;

if (!jit->IsInCodeSpace(codePtr)) {
if (!JitInterface::IsInCodeSpace(codePtr)) {
// Let's not prevent debugging.
return (DWORD)EXCEPTION_CONTINUE_SEARCH;
}
Expand Down Expand Up @@ -107,7 +109,7 @@ LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs)

//We could emulate the memory accesses here, but then they would still be around to take up
//execution resources. Instead, we backpatch into a generic memory call and retry.
const u8 *new_rip = jit->BackPatch(codePtr, accessType, emAddress, ctx);
const u8 *new_rip = JitInterface::BackPatch(codePtr, accessType, emAddress, ctx);

// Rip/Eip needs to be updated.
if (new_rip)
Expand Down Expand Up @@ -182,6 +184,7 @@ void print_trace(const char * msg)

void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
{
#ifndef _M_GENERIC
if (signal != SIGSEGV)
{
// We are not interested in other signals - handle it as usual.
Expand All @@ -203,7 +206,7 @@ void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
#else
u8 *fault_instruction_ptr = (u8 *)CREG_EIP(ctx);
#endif
if (!jit->IsInCodeSpace(fault_instruction_ptr)) {
if (!JitInterface::IsInCodeSpace(fault_instruction_ptr)) {
// Let's not prevent debugging.
return;
}
Expand Down Expand Up @@ -240,6 +243,7 @@ void sigsegv_handler(int signal, siginfo_t *info, void *raw_context)
CREG_EIP(ctx) = fake_ctx.Eip;
#endif
}
#endif
}

void InstallExceptionHandler()
Expand Down
6 changes: 6 additions & 0 deletions Source/Core/DiscIO/Src/BannerLoader.cpp
Expand Up @@ -27,7 +27,9 @@
#include <Windows.h>
#else
#include <sys/param.h>
#ifndef ANDROID
#include <iconv.h>
#endif
#include <errno.h>
#endif

Expand Down Expand Up @@ -131,6 +133,9 @@ bool IBannerLoader::CopyBeUnicodeToString( std::string& _rDestination, const u16
delete[] buffer;
}
}
#else
#ifdef ANDROID
return false;
#else
if (_src)
{
Expand Down Expand Up @@ -193,6 +198,7 @@ bool IBannerLoader::CopyBeUnicodeToString( std::string& _rDestination, const u16
delete[] src_buffer_start;
iconv_close(conv_desc);
}
#endif
#endif
return returnCode;
}
Expand Down
175 changes: 96 additions & 79 deletions Source/Core/DolphinWX/CMakeLists.txt
Expand Up @@ -10,21 +10,21 @@ set(LIBS core
${GTK2_LIBRARIES}
${XRANDR_LIBRARIES}
${X11_LIBRARIES})

if(SDL2_FOUND)
# Using shared SDL2
set(LIBS ${LIBS} ${SDL2_LIBRARY})
else(SDL2_FOUND)
if(SDL_FOUND)
# Using shared SDL
set(LIBS ${LIBS} ${SDL_LIBRARY})
else(SDL_FOUND)
# Using static SDL from Externals
set(LIBS ${LIBS} SDL)
if(NOT ANDROID)
if(SDL2_FOUND)
# Using shared SDL2
set(LIBS ${LIBS} ${SDL2_LIBRARY})
else(SDL2_FOUND)
if(SDL_FOUND)
# Using shared SDL
set(LIBS ${LIBS} ${SDL_LIBRARY})
else(SDL_FOUND)
# Using static SDL from Externals
set(LIBS ${LIBS} SDL)
endif()
endif()
endif()


if(LIBAV_FOUND)
set(LIBS ${LIBS} ${LIBAV_LIBRARIES})
endif()
Expand Down Expand Up @@ -79,13 +79,18 @@ if(wxWidgets_FOUND)

set(WXLIBS ${wxWidgets_LIBRARIES})
else()
set(SRCS
Src/MainNoGUI.cpp)
if(ANDROID)
set(SRCS Src/MainAndroid.cpp)
else()
set(SRCS Src/MainNoGUI.cpp)
endif()
endif()

if(USE_EGL)
set(SRCS ${SRCS} Src/GLInterface/EGL.cpp
Src/GLInterface/X11_Util.cpp)
if(NOT ANDROID)
set(SRCS ${SRCS} Src/GLInterface/EGL_X11.cpp
Src/GLInterface/X11_Util.cpp)
endif()
else()
if(WIN32)
set(SRCS ${SRCS} Src/GLInterface/GLW.cpp)
Expand Down Expand Up @@ -127,7 +132,9 @@ elseif(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
set_source_files_properties(${RESOURCES} PROPERTIES
MACOSX_PACKAGE_LOCATION Resources)
else()
set(SRCS ${SRCS} Src/X11Utils.cpp)
if(NOT ANDROID)
set(SRCS ${SRCS} Src/X11Utils.cpp)
endif()
endif()

if(${CMAKE_SYSTEM_NAME} MATCHES "FreeBSD" OR
Expand All @@ -146,75 +153,85 @@ else()
set(DOLPHIN_EXE ${DOLPHIN_EXE_BASE}-nogui)
endif()

add_executable(${DOLPHIN_EXE} ${SRCS})
target_link_libraries(${DOLPHIN_EXE} ${LIBS} ${WXLIBS})

if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
include(BundleUtilities)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${DOLPHIN_EXE}.app)

# Ask for an application bundle.
set_target_properties(${DOLPHIN_EXE} PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)

# Install Cg framework into application bundle.
copy_resolved_framework_into_bundle(
# Our framework in "Externals" does not have "Versions/Current/" in
# its path; work around the missing directory levels using "././".
"${CMAKE_SOURCE_DIR}/Externals/Cg/Cg.framework/././Cg"
"${BUNDLE_PATH}/Contents/Frameworks/Cg.framework/././Cg"
)

# Fix up the bundle after it is finished.
# There does not seem to be an easy way to run CMake commands post-build,
# so we invoke CMake again on a generated script.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/postprocess_bundle.cmake "
if(ANDROID)
add_library(${DOLPHIN_EXE} SHARED ${SRCS})
target_link_libraries(${DOLPHIN_EXE}
log
android
"-Wl,--whole-archive"
${LIBS}
"-Wl,--no-whole-archive"
)
else()
add_executable(${DOLPHIN_EXE} ${SRCS})
target_link_libraries(${DOLPHIN_EXE} ${LIBS} ${WXLIBS})
if(${CMAKE_SYSTEM_NAME} MATCHES "Darwin")
include(BundleUtilities)
message(\"Fixing up application bundle: ${BUNDLE_PATH}\")
set(BU_CHMOD_BUNDLE_ITEMS ON)
fixup_bundle(\"${BUNDLE_PATH}\" \"\" \"\")
")
add_custom_command(TARGET ${DOLPHIN_EXE} POST_BUILD
COMMAND ${CMAKE_COMMAND} -P postprocess_bundle.cmake
)
set(BUNDLE_PATH ${CMAKE_RUNTIME_OUTPUT_DIRECTORY}/${DOLPHIN_EXE}.app)

# Copy data files into application bundle.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/copy_data_into_bundle.cmake "
file(INSTALL ${CMAKE_SOURCE_DIR}/Data/Sys ${CMAKE_SOURCE_DIR}/Data/User
DESTINATION ${BUNDLE_PATH}/Contents/Resources
# Ask for an application bundle.
set_target_properties(${DOLPHIN_EXE} PROPERTIES
MACOSX_BUNDLE true
MACOSX_BUNDLE_INFO_PLIST ${CMAKE_CURRENT_SOURCE_DIR}/Info.plist.in
)
file(GLOB TRANSLATION_FILES RELATIVE ${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/*.gmo

# Install Cg framework into application bundle.
copy_resolved_framework_into_bundle(
# Our framework in "Externals" does not have "Versions/Current/" in
# its path; work around the missing directory levels using "././".
"${CMAKE_SOURCE_DIR}/Externals/Cg/Cg.framework/././Cg"
"${BUNDLE_PATH}/Contents/Frameworks/Cg.framework/././Cg"
)
foreach(TRANSLATION_FILE \${TRANSLATION_FILES})
string(REPLACE \".gmo\" \".lproj\" TRANSLATION_DIR
\${TRANSLATION_FILE}
)
# It would be better to copy to the new name as a single action,
# but I can't figure out a way to let CMake do that.
file(COPY ${CMAKE_BINARY_DIR}/\${TRANSLATION_FILE}
DESTINATION ${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}
NO_SOURCE_PERMISSIONS

# Fix up the bundle after it is finished.
# There does not seem to be an easy way to run CMake commands post-build,
# so we invoke CMake again on a generated script.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/postprocess_bundle.cmake "
include(BundleUtilities)
message(\"Fixing up application bundle: ${BUNDLE_PATH}\")
set(BU_CHMOD_BUNDLE_ITEMS ON)
fixup_bundle(\"${BUNDLE_PATH}\" \"\" \"\")
")
add_custom_command(TARGET ${DOLPHIN_EXE} POST_BUILD
COMMAND ${CMAKE_COMMAND} -P postprocess_bundle.cmake
)

# Copy data files into application bundle.
file(WRITE ${CMAKE_CURRENT_BINARY_DIR}/copy_data_into_bundle.cmake "
file(INSTALL ${CMAKE_SOURCE_DIR}/Data/Sys ${CMAKE_SOURCE_DIR}/Data/User
DESTINATION ${BUNDLE_PATH}/Contents/Resources
)
file(RENAME
${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}/\${TRANSLATION_FILE}
${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}/dolphin-emu.mo
file(GLOB TRANSLATION_FILES RELATIVE ${CMAKE_BINARY_DIR}
${CMAKE_BINARY_DIR}/*.gmo
)
endforeach(TRANSLATION_FILE)
")
add_custom_target(CopyDataIntoBundle ALL
COMMAND ${CMAKE_COMMAND} -P copy_data_into_bundle.cmake
VERBATIM
)
foreach(TRANSLATION_FILE \${TRANSLATION_FILES})
string(REPLACE \".gmo\" \".lproj\" TRANSLATION_DIR
\${TRANSLATION_FILE}
)
# It would be better to copy to the new name as a single action,
# but I can't figure out a way to let CMake do that.
file(COPY ${CMAKE_BINARY_DIR}/\${TRANSLATION_FILE}
DESTINATION ${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}
NO_SOURCE_PERMISSIONS
)
file(RENAME
${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}/\${TRANSLATION_FILE}
${BUNDLE_PATH}/Contents/Resources/\${TRANSLATION_DIR}/dolphin-emu.mo
)
endforeach(TRANSLATION_FILE)
")
add_custom_target(CopyDataIntoBundle ALL
COMMAND ${CMAKE_COMMAND} -P copy_data_into_bundle.cmake
VERBATIM
)

# Install bundle into systemwide /Applications directory.
install(DIRECTORY ${BUNDLE_PATH} DESTINATION /Applications
USE_SOURCE_PERMISSIONS
)
else()
install(TARGETS ${DOLPHIN_EXE} RUNTIME DESTINATION ${bindir})
# Install bundle into systemwide /Applications directory.
install(DIRECTORY ${BUNDLE_PATH} DESTINATION /Applications
USE_SOURCE_PERMISSIONS
)
else()
install(TARGETS ${DOLPHIN_EXE} RUNTIME DESTINATION ${bindir})
endif()
endif()

set(CPACK_PACKAGE_EXECUTABLES ${CPACK_PACKAGE_EXECUTABLES} ${DOLPHIN_EXE})
14 changes: 5 additions & 9 deletions Source/Core/DolphinWX/Src/Debugger/CodeWindow.cpp
Expand Up @@ -38,15 +38,14 @@
#include "LogManager.h"
#include "HW/CPU.h"
#include "PowerPC/PowerPC.h"
#include "PowerPC/JitInterface.h"
#include "Debugger/PPCDebugInterface.h"
#include "Debugger/Debugger_SymbolMap.h"
#include "PowerPC/PPCAnalyst.h"
#include "PowerPC/Profiler.h"
#include "PowerPC/PPCSymbolDB.h"
#include "PowerPC/SignatureDB.h"
#include "PowerPC/PPCTables.h"
#include "PowerPC/JitCommon/JitBase.h"
#include "PowerPC/JitCommon/JitCache.h" // for ClearCache()

#include "ConfigManager.h"

Expand Down Expand Up @@ -260,8 +259,7 @@ void CCodeWindow::SingleStep()
{
if (CCPU::IsStepping())
{
if (jit)
jit->GetBlockCache()->InvalidateICache(PC, 4);
JitInterface::InvalidateICache(PC, 4);
CCPU::StepOpcode(&sync_event);
wxThread::Sleep(20);
// need a short wait here
Expand Down Expand Up @@ -492,10 +490,8 @@ void CCodeWindow::OnCPUMode(wxCommandEvent& event)
}

// Clear the JIT cache to enable these changes
if (jit)
{
jit->ClearCache();
}
JitInterface::ClearCache();

// Update
UpdateButtonStates();
}
Expand All @@ -509,7 +505,7 @@ void CCodeWindow::OnJitMenu(wxCommandEvent& event)
break;

case IDM_CLEARCODECACHE:
jit->ClearCache();
JitInterface::ClearCache();
break;

case IDM_SEARCHINSTRUCTION:
Expand Down
2 changes: 1 addition & 1 deletion Source/Core/DolphinWX/Src/Frame.cpp
Expand Up @@ -704,7 +704,7 @@ bool IsHotkey(wxKeyEvent &event, int Id)
{
return (event.GetKeyCode() != WXK_NONE &&
event.GetKeyCode() == SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkey[Id] &&
event.GetModifiers() == SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkeyModifier[Id]);
event.GetModifiers() == SConfig::GetInstance().m_LocalCoreStartupParameter.iHotkeyModifier[Id]);
}

int GetCmdForHotkey(unsigned int key)
Expand Down
13 changes: 9 additions & 4 deletions Source/Core/DolphinWX/Src/GLInterface.h
Expand Up @@ -18,9 +18,11 @@
#define _GLINTERFACE_H_

#include "Thread.h"

#if defined(USE_EGL) && USE_EGL
#include "GLInterface/EGL.h"
#ifdef ANDROID
#include <GLES2/gl2.h>
#include <GLInterface/InterfaceBase.h>
#elif defined(USE_EGL) && USE_EGL
#include "GLInterface/EGL_X11.h"
#elif defined(USE_WX) && USE_WX
#include "GLInterface/WX.h"
#elif defined(__APPLE__)
Expand All @@ -29,10 +31,13 @@
#include "GLInterface/WGL.h"
#elif defined(HAVE_X11) && HAVE_X11
#include "GLInterface/GLX.h"
#else
#error Platform doesnt have a GLInterface
#endif

typedef struct {
#if defined(USE_EGL) && USE_EGL // This is currently a X11/EGL implementation for desktop
#ifdef ANDROID
#elif defined(USE_EGL) && USE_EGL // This is currently a X11/EGL implementation for desktop
int screen;
Display *dpy;
Display *evdpy;
Expand Down
Expand Up @@ -19,7 +19,7 @@
#include "RenderBase.h"

#include "../GLInterface.h"
#include "EGL.h"
#include "EGL_X11.h"

// Show the current FPS
void cInterfaceEGL::UpdateFPSDisplay(const char *text)
Expand Down
File renamed without changes.
10 changes: 5 additions & 5 deletions Source/Core/DolphinWX/Src/GLInterface/InterfaceBase.h
Expand Up @@ -23,11 +23,11 @@ class cInterfaceBase
u32 s_backbuffer_width;
u32 s_backbuffer_height;
public:
virtual void Swap() = 0;
virtual void UpdateFPSDisplay(const char *Text) = 0;
virtual bool Create(void *&window_handle) = 0;
virtual bool MakeCurrent() = 0;
virtual void Shutdown() = 0;
virtual void Swap() {}
virtual void UpdateFPSDisplay(const char *Text) {}
virtual bool Create(void *&window_handle) { return true; }
virtual bool MakeCurrent() { return true; }
virtual void Shutdown() {}

virtual void SwapInterval(int Interval) { }
virtual u32 GetBackBufferWidth() { return s_backbuffer_width; }
Expand Down
149 changes: 149 additions & 0 deletions Source/Core/DolphinWX/Src/MainAndroid.cpp
@@ -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
10 changes: 8 additions & 2 deletions Source/Core/VideoCommon/CMakeLists.txt
Expand Up @@ -3,7 +3,6 @@ set(SRCS Src/BPFunctions.cpp
Src/BPStructs.cpp
Src/CPMemory.cpp
Src/CommandProcessor.cpp
Src/DLCache.cpp
Src/Debugger.cpp
Src/Fifo.cpp
Src/FPSCounter.cpp
Expand All @@ -24,7 +23,6 @@ set(SRCS Src/BPFunctions.cpp
Src/Statistics.cpp
Src/TextureCacheBase.cpp
Src/TextureConversionShader.cpp
Src/TextureDecoder.cpp
Src/VertexLoader.cpp
Src/VertexLoaderManager.cpp
Src/VertexLoader_Color.cpp
Expand All @@ -41,6 +39,14 @@ set(SRCS Src/BPFunctions.cpp
Src/memcpy_amd.cpp)

set(LIBS core)

if(NOT _M_GENERIC)
set(SRCS ${SRCS} Src/x64TextureDecoder.cpp
Src/x64DLCache.cpp)
else()
set(SRCS ${SRCS} Src/GenericTextureDecoder.cpp
Src/GenericDLCache.cpp)
endif()
if(NOT ${CL} STREQUAL CL-NOTFOUND)
list(APPEND LIBS ${CL})
endif()
Expand Down
6 changes: 3 additions & 3 deletions Source/Core/VideoCommon/Src/Fifo.cpp
Expand Up @@ -222,11 +222,11 @@ void RunGpu()
{
u8 *uData = Memory::GetPointer(fifo.CPReadPointer);

SaveSSEState();
LoadDefaultSSEState();
FPURoundMode::SaveSIMDState();
FPURoundMode::LoadDefaultSIMDState();
ReadDataFromFifo(uData, 32);
OpcodeDecoder_Run(g_bSkipCurrentFrame);
LoadSSEState();
FPURoundMode::LoadSIMDState();

//DEBUG_LOG(COMMANDPROCESSOR, "Fifo wraps to base");

Expand Down
52 changes: 52 additions & 0 deletions Source/Core/VideoCommon/Src/GenericDLCache.cpp
@@ -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()
{
}