Merge pull request #2033 from magumagu/mmio-fix-addresses
Fix the addresses of MMIO registers.
This commit is contained in:
commit
18ada7a0f5
|
@ -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);
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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
|
||||
{
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
//
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in New Issue