Merge pull request #1063 from FioraAeterna/fastermmufix

MMU: properly check MEM1 range on Gamecube games
This commit is contained in:
comex 2014-09-14 15:17:09 -04:00
commit 74f8a48ee6
4 changed files with 53 additions and 23 deletions

View File

@ -108,6 +108,7 @@ void Jit64::lXXx(UGeckoInstruction inst)
// do our job at first
s32 offset = (s32)(s16)inst.SIMM_16;
gpr.BindToRegister(a, true, false);
gpr.BindToRegister(d, false, true);
SafeLoadToReg(gpr.RX(d), gpr.R(a), accessSize, offset, CallerSavedRegistersInUse(), signExtend);

View File

@ -29,8 +29,7 @@ void Jit64::lfXXX(UGeckoInstruction inst)
FALLBACK_IF((!indexed && !a) || (update && a == d));
if (update)
gpr.BindToRegister(a, true, true);
gpr.BindToRegister(a, true, update);
s32 offset = 0;
OpArg addr = gpr.R(a);

View File

@ -250,6 +250,45 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value,
}
}
FixupBranch EmuCodeBlock::CheckIfSafeAddress(X64Reg reg_value, X64Reg reg_addr, u32 registers_in_use, u32 mem_mask)
{
registers_in_use |= (1 << reg_addr);
registers_in_use |= (1 << reg_value);
// Get ourselves a free register; try to pick one that doesn't involve pushing, if we can.
X64Reg scratch = RSCRATCH;
if (!(registers_in_use & (1 << RSCRATCH)))
scratch = RSCRATCH;
else if (!(registers_in_use & (1 << RSCRATCH_EXTRA)))
scratch = RSCRATCH_EXTRA;
else
scratch = reg_addr;
// On Gamecube games with MMU, do a little bit of extra work to make sure we're not accessing the
// 0x81800000 to 0x83FFFFFF range.
// It's okay to take a shortcut and not check this range on non-MMU games, since we're already
// assuming they'll never do an invalid memory access.
// The slightly more complex check needed for Wii games using the space just above MEM1 isn't
// implemented here yet, since there are no known working Wii MMU games to test it with.
if (jit->js.memcheck && !SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{
if (scratch == reg_addr)
PUSH(scratch);
else
MOV(32, R(scratch), R(reg_addr));
AND(32, R(scratch), Imm32(0x3FFFFFFF));
CMP(32, R(scratch), Imm32(0x01800000));
if (scratch == reg_addr)
POP(scratch);
return J_CC(CC_AE, farcode.Enabled());
}
else
{
TEST(32, R(reg_addr), Imm32(mem_mask));
return J_CC(CC_NZ, farcode.Enabled());
}
}
void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress, int accessSize, s32 offset, u32 registersInUse, bool signExtend, int flags)
{
if (!jit->js.memcheck)
@ -333,46 +372,37 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
}
else
{
OpArg addr_loc = opAddress;
_assert_msg_(DYNA_REC, opAddress.IsSimpleReg(), "Incorrect use of SafeLoadToReg (address isn't register or immediate)");
X64Reg reg_addr = opAddress.GetSimpleReg();
if (offset)
{
addr_loc = R(RSCRATCH);
if (opAddress.IsSimpleReg())
{
LEA(32, RSCRATCH, MDisp(opAddress.GetSimpleReg(), offset));
}
else
{
MOV(32, R(RSCRATCH), opAddress);
ADD(32, R(RSCRATCH), Imm32(offset));
}
reg_addr = RSCRATCH;
LEA(32, RSCRATCH, MDisp(opAddress.GetSimpleReg(), offset));
}
TEST(32, addr_loc, Imm32(mem_mask));
FixupBranch slow, exit;
slow = J_CC(CC_NZ, farcode.Enabled());
UnsafeLoadToReg(reg_value, addr_loc, accessSize, 0, signExtend);
slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask);
UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend);
if (farcode.Enabled())
SwitchToFarCode();
else
exit = J(true);
SetJumpTarget(slow);
size_t rsp_alignment = (flags & SAFE_LOADSTORE_NO_PROLOG) ? 8 : 0;
ABI_PushRegistersAndAdjustStack(registersInUse, rsp_alignment);
switch (accessSize)
{
case 64:
ABI_CallFunctionA((void *)&Memory::Read_U64, addr_loc);
ABI_CallFunctionR((void *)&Memory::Read_U64, reg_addr);
break;
case 32:
ABI_CallFunctionA((void *)&Memory::Read_U32, addr_loc);
ABI_CallFunctionR((void *)&Memory::Read_U32, reg_addr);
break;
case 16:
ABI_CallFunctionA((void *)&Memory::Read_U16_ZX, addr_loc);
ABI_CallFunctionR((void *)&Memory::Read_U16_ZX, reg_addr);
break;
case 8:
ABI_CallFunctionA((void *)&Memory::Read_U8_ZX, addr_loc);
ABI_CallFunctionR((void *)&Memory::Read_U8_ZX, reg_addr);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment);
@ -478,8 +508,7 @@ void EmuCodeBlock::SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int acce
bool swap = !(flags & SAFE_LOADSTORE_NO_SWAP);
FixupBranch slow, exit;
TEST(32, R(reg_addr), Imm32(mem_mask));
slow = J_CC(CC_NZ, farcode.Enabled());
slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask);
UnsafeWriteRegToReg(reg_value, reg_addr, accessSize, 0, swap);
if (farcode.Enabled())
SwitchToFarCode();

View File

@ -76,6 +76,7 @@ public:
void LoadAndSwap(int size, Gen::X64Reg dst, const Gen::OpArg& src);
void SwapAndStore(int size, const Gen::OpArg& dst, Gen::X64Reg src);
Gen::FixupBranch CheckIfSafeAddress(Gen::X64Reg reg_value, Gen::X64Reg reg_addr, u32 registers_in_use, u32 mem_mask);
void UnsafeLoadRegToReg(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset = 0, bool signExtend = false);
void UnsafeLoadRegToRegNoSwap(Gen::X64Reg reg_addr, Gen::X64Reg reg_value, int accessSize, s32 offset, bool signExtend = false);
// these return the address of the MOV, for backpatching