Merge pull request #2033 from magumagu/mmio-fix-addresses

Fix the addresses of MMIO registers.
This commit is contained in:
magumagu 2015-02-22 10:58:25 -08:00
commit 18ada7a0f5
8 changed files with 202 additions and 163 deletions

View File

@ -16,9 +16,9 @@ namespace MMIO
{
// There are three main MMIO blocks on the Wii (only one on the GameCube):
// - 0xCC00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP)
// - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI)
// - 0xCD80xxxx: Mirror of 0xCD00xxxx.
// - 0x0C00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP)
// - 0x0D00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI)
// - 0x0D80xxxx: Mirror of 0x0D00xxxx.
//
// In practice, since the third block is a mirror of the second one, we can
// assume internally that there are only two blocks: one for GC, one for Wii.
@ -41,15 +41,15 @@ const u32 NUM_MMIOS = NUM_BLOCKS * BLOCK_SIZE;
// interface.
inline bool IsMMIOAddress(u32 address)
{
if (address == 0xCC008000)
if (address == 0x0C008000)
return false; // WG Pipe
if ((address & 0xFFFF0000) == 0xCC000000)
if ((address & 0xFFFF0000) == 0x0C000000)
return true; // GameCube MMIOs
if(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{
return ((address & 0xFFFF0000) == 0xCD000000) || // Wii MMIOs
((address & 0xFFFF0000) == 0xCD800000); // Mirror of Wii MMIOs
return ((address & 0xFFFF0000) == 0x0D000000) || // Wii MMIOs
((address & 0xFFFF0000) == 0x0D800000); // Mirror of Wii MMIOs
}
return false;
@ -61,9 +61,9 @@ inline bool IsMMIOAddress(u32 address)
// The block ID can easily be computed by simply checking bit 24 (CC vs. CD).
inline u32 UniqueID(u32 address)
{
_dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) ||
((address & 0xFFFF0000) == 0xCD000000) ||
((address & 0xFFFF0000) == 0xCD800000),
_dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0x0C000000) ||
((address & 0xFFFF0000) == 0x0D000000) ||
((address & 0xFFFF0000) == 0x0D800000),
"Trying to get the ID of a non-existing MMIO address.");
return (((address >> 24) & 1) << 16) | (address & 0xFFFF);

View File

@ -74,27 +74,27 @@ MMIO::Mapping* mmio_mapping;
static void InitMMIO(MMIO::Mapping* mmio)
{
g_video_backend->RegisterCPMMIO(mmio, 0xCC000000);
PixelEngine::RegisterMMIO(mmio, 0xCC001000);
VideoInterface::RegisterMMIO(mmio, 0xCC002000);
ProcessorInterface::RegisterMMIO(mmio, 0xCC003000);
MemoryInterface::RegisterMMIO(mmio, 0xCC004000);
DSP::RegisterMMIO(mmio, 0xCC005000);
DVDInterface::RegisterMMIO(mmio, 0xCC006000);
SerialInterface::RegisterMMIO(mmio, 0xCC006400);
ExpansionInterface::RegisterMMIO(mmio, 0xCC006800);
AudioInterface::RegisterMMIO(mmio, 0xCC006C00);
g_video_backend->RegisterCPMMIO(mmio, 0x0C000000);
PixelEngine::RegisterMMIO(mmio, 0x0C001000);
VideoInterface::RegisterMMIO(mmio, 0x0C002000);
ProcessorInterface::RegisterMMIO(mmio, 0x0C003000);
MemoryInterface::RegisterMMIO(mmio, 0x0C004000);
DSP::RegisterMMIO(mmio, 0x0C005000);
DVDInterface::RegisterMMIO(mmio, 0x0C006000);
SerialInterface::RegisterMMIO(mmio, 0x0C006400);
ExpansionInterface::RegisterMMIO(mmio, 0x0C006800);
AudioInterface::RegisterMMIO(mmio, 0x0C006C00);
}
static void InitMMIOWii(MMIO::Mapping* mmio)
{
InitMMIO(mmio);
WII_IPCInterface::RegisterMMIO(mmio, 0xCD000000);
DVDInterface::RegisterMMIO(mmio, 0xCD006000);
SerialInterface::RegisterMMIO(mmio, 0xCD006400);
ExpansionInterface::RegisterMMIO(mmio, 0xCD006800);
AudioInterface::RegisterMMIO(mmio, 0xCD006C00);
WII_IPCInterface::RegisterMMIO(mmio, 0x0D000000);
DVDInterface::RegisterMMIO(mmio, 0x0D006000);
SerialInterface::RegisterMMIO(mmio, 0x0D006400);
ExpansionInterface::RegisterMMIO(mmio, 0x0D006800);
AudioInterface::RegisterMMIO(mmio, 0x0D006C00);
}
bool IsInitialized()

View File

@ -147,15 +147,20 @@ void JitArm64::SafeLoadToReg(u32 dest, s32 addr, s32 offsetReg, u32 flags, s32 o
if (update)
MOV(gpr.R(addr), addr_reg);
u32 access_size = BackPatchInfo::GetFlagSize(flags);
u32 mmio_address = 0;
if (is_immediate)
mmio_address = PowerPC::IsOptimizableMMIOAccess(imm_addr, access_size);
if (is_immediate && PowerPC::IsOptimizableRAMAddress(imm_addr))
{
EmitBackpatchRoutine(this, flags, true, false, dest_reg, XA);
}
else if (is_immediate && MMIO::IsMMIOAddress(imm_addr))
else if (mmio_address)
{
MMIOLoadToReg(Memory::mmio_mapping, this,
regs_in_use, fprs_in_use, dest_reg,
imm_addr, flags);
mmio_address, flags);
}
else
{
@ -288,18 +293,22 @@ void JitArm64::SafeStoreFromReg(s32 dest, u32 value, s32 regOffset, u32 flags, s
ARM64Reg XA = EncodeRegTo64(addr_reg);
u32 access_size = BackPatchInfo::GetFlagSize(flags);
u32 mmio_address = 0;
if (is_immediate)
mmio_address = PowerPC::IsOptimizableMMIOAccess(imm_addr, access_size);
if (is_immediate && PowerPC::IsOptimizableRAMAddress(imm_addr))
{
MOVI2R(XA, imm_addr);
EmitBackpatchRoutine(this, flags, true, false, RS, XA);
}
else if (is_immediate && MMIO::IsMMIOAddress(imm_addr) &&
!(flags & BackPatchInfo::FLAG_REVERSE))
else if (mmio_address && !(flags & BackPatchInfo::FLAG_REVERSE))
{
MMIOWriteRegToAddr(Memory::mmio_mapping, this,
regs_in_use, fprs_in_use, RS,
imm_addr, flags);
mmio_address, flags);
}
else
{

View File

@ -20,6 +20,21 @@ struct BackPatchInfo
FLAG_EXTEND = (1 << 8),
};
static u32 GetFlagSize(u32 flags)
{
if (flags & FLAG_SIZE_8)
return 8;
if (flags & FLAG_SIZE_16)
return 16;
if (flags & FLAG_SIZE_32)
return 32;
if (flags & FLAG_SIZE_F32)
return 32;
if (flags & FLAG_SIZE_F64)
return 64;
return 0;
}
u32 m_fastmem_size;
u32 m_fastmem_trouble_inst_offset;
u32 m_slowmem_size;

View File

@ -289,116 +289,111 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
registersInUseAtLoc[mov] = registersInUse;
jit->js.fastmemLoadStore = mov;
return;
}
else
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
if (opAddress.IsImm())
{
u32 mem_mask = Memory::ADDR_MASK_HW_ACCESS;
u32 address = (u32)opAddress.offset + offset;
// The following masks the region used by the GC/Wii virtual memory lib
mem_mask |= Memory::ADDR_MASK_MEM1;
if (opAddress.IsImm())
// If the address is known to be RAM, just load it directly.
if (PowerPC::IsOptimizableRAMAddress(address))
{
u32 address = (u32)opAddress.offset + offset;
// If we know the address, try the following loading methods in
// order:
//
// 1. If the address is in RAM, generate an unsafe load (directly
// access the RAM buffer and load from there).
// 2. If the address is in the MMIO range, find the appropriate
// MMIO handler and generate the code to load using the handler.
// 3. Otherwise, just generate a call to PowerPC::Read_* with the
// address hardcoded.
if (PowerPC::IsOptimizableRAMAddress(address))
{
UnsafeLoadToReg(reg_value, opAddress, accessSize, offset, signExtend);
}
else if (MMIO::IsMMIOAddress(address) && accessSize != 64)
{
MMIOLoadToReg(Memory::mmio_mapping, reg_value, registersInUse,
address, accessSize, signExtend);
}
else
{
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
switch (accessSize)
{
case 64: ABI_CallFunctionC((void *)&PowerPC::Read_U64, address); break;
case 32: ABI_CallFunctionC((void *)&PowerPC::Read_U32, address); break;
case 16: ABI_CallFunctionC((void *)&PowerPC::Read_U16_ZX, address); break;
case 8: ABI_CallFunctionC((void *)&PowerPC::Read_U8_ZX, address); break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
MemoryExceptionCheck();
if (signExtend && accessSize < 32)
{
// Need to sign extend values coming from the Read_U* functions.
MOVSX(32, accessSize, reg_value, R(ABI_RETURN));
}
else if (reg_value != ABI_RETURN)
{
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
}
}
UnsafeLoadToReg(reg_value, opAddress, accessSize, offset, signExtend);
return;
}
else
// If the address maps to an MMIO register, inline MMIO read code.
u32 mmioAddress = PowerPC::IsOptimizableMMIOAccess(address, accessSize);
if (accessSize != 64 && mmioAddress)
{
_assert_msg_(DYNA_REC, opAddress.IsSimpleReg(), "Incorrect use of SafeLoadToReg (address isn't register or immediate)");
X64Reg reg_addr = opAddress.GetSimpleReg();
if (offset)
{
reg_addr = RSCRATCH;
LEA(32, RSCRATCH, MDisp(opAddress.GetSimpleReg(), offset));
}
FixupBranch slow, exit;
slow = CheckIfSafeAddress(R(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_CallFunctionR((void *)&PowerPC::Read_U64, reg_addr);
break;
case 32:
ABI_CallFunctionR((void *)&PowerPC::Read_U32, reg_addr);
break;
case 16:
ABI_CallFunctionR((void *)&PowerPC::Read_U16_ZX, reg_addr);
break;
case 8:
ABI_CallFunctionR((void *)&PowerPC::Read_U8_ZX, reg_addr);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment);
MemoryExceptionCheck();
if (signExtend && accessSize < 32)
{
// Need to sign extend values coming from the Read_U* functions.
MOVSX(32, accessSize, reg_value, R(ABI_RETURN));
}
else if (reg_value != ABI_RETURN)
{
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
}
if (farcode.Enabled())
{
exit = J(true);
SwitchToNearCode();
}
SetJumpTarget(exit);
MMIOLoadToReg(Memory::mmio_mapping, reg_value, registersInUse,
address, accessSize, signExtend);
return;
}
// Fall back to general-case code.
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
switch (accessSize)
{
case 64: ABI_CallFunctionC((void *)&PowerPC::Read_U64, address); break;
case 32: ABI_CallFunctionC((void *)&PowerPC::Read_U32, address); break;
case 16: ABI_CallFunctionC((void *)&PowerPC::Read_U16_ZX, address); break;
case 8: ABI_CallFunctionC((void *)&PowerPC::Read_U8_ZX, address); break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
MemoryExceptionCheck();
if (signExtend && accessSize < 32)
{
// Need to sign extend values coming from the Read_U* functions.
MOVSX(32, accessSize, reg_value, R(ABI_RETURN));
}
else if (reg_value != ABI_RETURN)
{
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
}
return;
}
_assert_msg_(DYNA_REC, opAddress.IsSimpleReg(), "Incorrect use of SafeLoadToReg (address isn't register or immediate)");
X64Reg reg_addr = opAddress.GetSimpleReg();
if (offset)
{
reg_addr = RSCRATCH;
LEA(32, RSCRATCH, MDisp(opAddress.GetSimpleReg(), offset));
}
FixupBranch slow, exit;
slow = CheckIfSafeAddress(R(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_CallFunctionR((void *)&PowerPC::Read_U64, reg_addr);
break;
case 32:
ABI_CallFunctionR((void *)&PowerPC::Read_U32, reg_addr);
break;
case 16:
ABI_CallFunctionR((void *)&PowerPC::Read_U16_ZX, reg_addr);
break;
case 8:
ABI_CallFunctionR((void *)&PowerPC::Read_U8_ZX, reg_addr);
break;
}
ABI_PopRegistersAndAdjustStack(registersInUse, rsp_alignment);
MemoryExceptionCheck();
if (signExtend && accessSize < 32)
{
// Need to sign extend values coming from the Read_U* functions.
MOVSX(32, accessSize, reg_value, R(ABI_RETURN));
}
else if (reg_value != ABI_RETURN)
{
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
}
if (farcode.Enabled())
{
exit = J(true);
SwitchToNearCode();
}
SetJumpTarget(exit);
}
static OpArg SwapImmediate(int accessSize, OpArg reg_value)

View File

@ -134,7 +134,7 @@ __forceinline static T ReadFromHardware(const u32 em_address)
if (em_address < 0xcc000000)
return EFB_Read(em_address);
else
return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address);
return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address & 0x0FFFFFFF);
}
if (segment == 0x0 || segment == 0x8 || segment == 0xC)
{
@ -168,7 +168,7 @@ __forceinline static T ReadFromHardware(const u32 em_address)
if (em_address < 0x0c000000)
return EFB_Read(em_address);
else
return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address | 0xC0000000);
return (T)Memory::mmio_mapping->Read<typename std::make_unsigned<T>::type>(em_address);
}
if (segment == 0x0)
{
@ -258,7 +258,7 @@ __forceinline static void WriteToHardware(u32 em_address, const T data)
}
else
{
Memory::mmio_mapping->Write(em_address, data);
Memory::mmio_mapping->Write(em_address & 0x0FFFFFFF, data);
return;
}
}
@ -313,7 +313,7 @@ __forceinline static void WriteToHardware(u32 em_address, const T data)
}
else
{
Memory::mmio_mapping->Write(em_address | 0xC0000000, data);
Memory::mmio_mapping->Write(em_address, data);
return;
}
}
@ -748,6 +748,21 @@ void ClearCacheLine(const u32 address)
Write_U64(0, address + i);
}
u32 IsOptimizableMMIOAccess(u32 address, u32 accessSize)
{
if (!UReg_MSR(MSR).DR)
return 0;
if ((address & 0xF0000000) != 0xC0000000)
return 0;
unsigned translated = address & 0x0FFFFFFF;
bool aligned = (translated & ((accessSize >> 3) - 1)) == 0;
if (!aligned || !MMIO::IsMMIOAddress(translated))
return 0;
return translated;
}
// *********************************************************************************
// Warning: Test Area
//

View File

@ -269,6 +269,7 @@ void InvalidateTLBEntry(u32 address);
// it's safe to optimize a read or write to this address to an unguarded
// memory access. Does not consider page tables.
bool IsOptimizableRAMAddress(const u32 address);
u32 IsOptimizableMMIOAccess(u32 address, u32 accessSize);
} // namespace

View File

@ -9,13 +9,13 @@
TEST(UniqueID, UniqueEnough)
{
std::unordered_set<u32> ids;
for (u32 i = 0xCC000000; i < 0xCC010000; ++i)
for (u32 i = 0x0C000000; i < 0x0C010000; ++i)
{
u32 unique_id = MMIO::UniqueID(i);
EXPECT_EQ(ids.end(), ids.find(unique_id));
ids.insert(unique_id);
}
for (u32 i = 0xCD000000; i < 0xCD010000; ++i)
for (u32 i = 0x0D000000; i < 0x0D010000; ++i)
{
u32 unique_id = MMIO::UniqueID(i);
EXPECT_EQ(ids.end(), ids.find(unique_id));
@ -29,18 +29,22 @@ TEST(IsMMIOAddress, SpecialAddresses)
SConfig::GetInstance().m_LocalCoreStartupParameter.bWii = true;
// WG Pipe address, should not be handled by MMIO.
EXPECT_FALSE(MMIO::IsMMIOAddress(0xCC008000));
EXPECT_FALSE(MMIO::IsMMIOAddress(0x0C008000));
// Memory zone used by games using the "MMU Speedhack".
// Locked L1 cache allocation.
EXPECT_FALSE(MMIO::IsMMIOAddress(0xE0000000));
// Uncached mirror of MEM1, shouldn't be handled by MMIO
EXPECT_FALSE(MMIO::IsMMIOAddress(0xC0000000));
// Effective address of an MMIO register; MMIO only deals with physical
// addresses.
EXPECT_FALSE(MMIO::IsMMIOAddress(0xCC0000E0));
// And lets check some valid addresses too
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCC0000E0)); // Gamecube MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCD00008C)); // Wii MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCD800F10)); // Mirror of Wii MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0x0C0000E0)); // Gamecube MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D00008C)); // Wii MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D800F10)); // Mirror of Wii MMIOs
SConfig::Shutdown();
}
@ -63,13 +67,13 @@ protected:
TEST_F(MappingTest, ReadConstant)
{
m_mapping->Register(0xCC001234, MMIO::Constant<u8>(0x42), MMIO::Nop<u8>());
m_mapping->Register(0xCC001234, MMIO::Constant<u16>(0x1234), MMIO::Nop<u16>());
m_mapping->Register(0xCC001234, MMIO::Constant<u32>(0xdeadbeef), MMIO::Nop<u32>());
m_mapping->Register(0x0C001234, MMIO::Constant<u8>(0x42), MMIO::Nop<u8>());
m_mapping->Register(0x0C001234, MMIO::Constant<u16>(0x1234), MMIO::Nop<u16>());
m_mapping->Register(0x0C001234, MMIO::Constant<u32>(0xdeadbeef), MMIO::Nop<u32>());
u8 val8 = m_mapping->Read<u8>(0xCC001234);
u16 val16 = m_mapping->Read<u16>(0xCC001234);
u32 val32 = m_mapping->Read<u32>(0xCC001234);
u8 val8 = m_mapping->Read<u8>(0x0C001234);
u16 val16 = m_mapping->Read<u16>(0x0C001234);
u32 val32 = m_mapping->Read<u32>(0x0C001234);
EXPECT_EQ(0x42, val8);
EXPECT_EQ(0x1234, val16);
@ -82,19 +86,19 @@ TEST_F(MappingTest, ReadWriteDirect)
u16 target_16 = 0;
u32 target_32 = 0;
m_mapping->Register(0xCC001234, MMIO::DirectRead<u8>(&target_8), MMIO::DirectWrite<u8>(&target_8));
m_mapping->Register(0xCC001234, MMIO::DirectRead<u16>(&target_16), MMIO::DirectWrite<u16>(&target_16));
m_mapping->Register(0xCC001234, MMIO::DirectRead<u32>(&target_32), MMIO::DirectWrite<u32>(&target_32));
m_mapping->Register(0x0C001234, MMIO::DirectRead<u8>(&target_8), MMIO::DirectWrite<u8>(&target_8));
m_mapping->Register(0x0C001234, MMIO::DirectRead<u16>(&target_16), MMIO::DirectWrite<u16>(&target_16));
m_mapping->Register(0x0C001234, MMIO::DirectRead<u32>(&target_32), MMIO::DirectWrite<u32>(&target_32));
for (u32 i = 0; i < 100; ++i)
{
u8 val8 = m_mapping->Read<u8>(0xCC001234); EXPECT_EQ(i, val8);
u16 val16 = m_mapping->Read<u16>(0xCC001234); EXPECT_EQ(i, val16);
u32 val32 = m_mapping->Read<u32>(0xCC001234); EXPECT_EQ(i, val32);
u8 val8 = m_mapping->Read<u8>(0x0C001234); EXPECT_EQ(i, val8);
u16 val16 = m_mapping->Read<u16>(0x0C001234); EXPECT_EQ(i, val16);
u32 val32 = m_mapping->Read<u32>(0x0C001234); EXPECT_EQ(i, val32);
val8 += 1; m_mapping->Write(0xCC001234, val8);
val16 += 1; m_mapping->Write(0xCC001234, val16);
val32 += 1; m_mapping->Write(0xCC001234, val32);
val8 += 1; m_mapping->Write(0x0C001234, val8);
val16 += 1; m_mapping->Write(0x0C001234, val16);
val32 += 1; m_mapping->Write(0x0C001234, val32);
}
}
@ -102,21 +106,21 @@ TEST_F(MappingTest, ReadWriteComplex)
{
bool read_called = false, write_called = false;
m_mapping->Register(0xCC001234,
m_mapping->Register(0x0C001234,
MMIO::ComplexRead<u8>([&read_called](u32 addr) {
EXPECT_EQ(0xCC001234, addr);
EXPECT_EQ(0x0C001234, addr);
read_called = true;
return 0x12;
}),
MMIO::ComplexWrite<u8>([&write_called](u32 addr, u8 val) {
EXPECT_EQ(0xCC001234, addr);
EXPECT_EQ(0x0C001234, addr);
EXPECT_EQ(0x34, val);
write_called = true;
})
);
u8 val = m_mapping->Read<u8>(0xCC001234); EXPECT_EQ(0x12, val);
m_mapping->Write(0xCC001234, (u8)0x34);
u8 val = m_mapping->Read<u8>(0x0C001234); EXPECT_EQ(0x12, val);
m_mapping->Write(0x0C001234, (u8)0x34);
EXPECT_TRUE(read_called);
EXPECT_TRUE(write_called);