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): // 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) // - 0x0C00xxxx: GameCube MMIOs (CP, PE, VI, PI, MI, DSP, DVD, SI, EI, AI, GP)
// - 0xCD00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI) // - 0x0D00xxxx: Wii MMIOs and GC mirrors (IPC, DVD, SI, EI, AI)
// - 0xCD80xxxx: Mirror of 0xCD00xxxx. // - 0x0D80xxxx: Mirror of 0x0D00xxxx.
// //
// In practice, since the third block is a mirror of the second one, we can // 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. // 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. // interface.
inline bool IsMMIOAddress(u32 address) inline bool IsMMIOAddress(u32 address)
{ {
if (address == 0xCC008000) if (address == 0x0C008000)
return false; // WG Pipe return false; // WG Pipe
if ((address & 0xFFFF0000) == 0xCC000000) if ((address & 0xFFFF0000) == 0x0C000000)
return true; // GameCube MMIOs return true; // GameCube MMIOs
if(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii) if(SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
{ {
return ((address & 0xFFFF0000) == 0xCD000000) || // Wii MMIOs return ((address & 0xFFFF0000) == 0x0D000000) || // Wii MMIOs
((address & 0xFFFF0000) == 0xCD800000); // Mirror of Wii MMIOs ((address & 0xFFFF0000) == 0x0D800000); // Mirror of Wii MMIOs
} }
return false; 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). // The block ID can easily be computed by simply checking bit 24 (CC vs. CD).
inline u32 UniqueID(u32 address) inline u32 UniqueID(u32 address)
{ {
_dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0xCC000000) || _dbg_assert_msg_(MEMMAP, ((address & 0xFFFF0000) == 0x0C000000) ||
((address & 0xFFFF0000) == 0xCD000000) || ((address & 0xFFFF0000) == 0x0D000000) ||
((address & 0xFFFF0000) == 0xCD800000), ((address & 0xFFFF0000) == 0x0D800000),
"Trying to get the ID of a non-existing MMIO address."); "Trying to get the ID of a non-existing MMIO address.");
return (((address >> 24) & 1) << 16) | (address & 0xFFFF); return (((address >> 24) & 1) << 16) | (address & 0xFFFF);

View File

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

View File

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

View File

@ -20,6 +20,21 @@ struct BackPatchInfo
FLAG_EXTEND = (1 << 8), 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_size;
u32 m_fastmem_trouble_inst_offset; u32 m_fastmem_trouble_inst_offset;
u32 m_slowmem_size; u32 m_slowmem_size;

View File

@ -289,116 +289,111 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg & opAddress,
registersInUseAtLoc[mov] = registersInUse; registersInUseAtLoc[mov] = registersInUse;
jit->js.fastmemLoadStore = mov; 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 // If the address is known to be RAM, just load it directly.
mem_mask |= Memory::ADDR_MASK_MEM1; if (PowerPC::IsOptimizableRAMAddress(address))
if (opAddress.IsImm())
{ {
u32 address = (u32)opAddress.offset + offset; UnsafeLoadToReg(reg_value, opAddress, accessSize, offset, signExtend);
return;
// 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));
}
}
} }
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)"); MMIOLoadToReg(Memory::mmio_mapping, reg_value, registersInUse,
X64Reg reg_addr = opAddress.GetSimpleReg(); address, accessSize, signExtend);
if (offset) return;
{
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);
} }
// 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) 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) if (em_address < 0xcc000000)
return EFB_Read(em_address); return EFB_Read(em_address);
else 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) if (segment == 0x0 || segment == 0x8 || segment == 0xC)
{ {
@ -168,7 +168,7 @@ __forceinline static T ReadFromHardware(const u32 em_address)
if (em_address < 0x0c000000) if (em_address < 0x0c000000)
return EFB_Read(em_address); return EFB_Read(em_address);
else 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) if (segment == 0x0)
{ {
@ -258,7 +258,7 @@ __forceinline static void WriteToHardware(u32 em_address, const T data)
} }
else else
{ {
Memory::mmio_mapping->Write(em_address, data); Memory::mmio_mapping->Write(em_address & 0x0FFFFFFF, data);
return; return;
} }
} }
@ -313,7 +313,7 @@ __forceinline static void WriteToHardware(u32 em_address, const T data)
} }
else else
{ {
Memory::mmio_mapping->Write(em_address | 0xC0000000, data); Memory::mmio_mapping->Write(em_address, data);
return; return;
} }
} }
@ -748,6 +748,21 @@ void ClearCacheLine(const u32 address)
Write_U64(0, address + i); 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 // 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 // it's safe to optimize a read or write to this address to an unguarded
// memory access. Does not consider page tables. // memory access. Does not consider page tables.
bool IsOptimizableRAMAddress(const u32 address); bool IsOptimizableRAMAddress(const u32 address);
u32 IsOptimizableMMIOAccess(u32 address, u32 accessSize);
} // namespace } // namespace

View File

@ -9,13 +9,13 @@
TEST(UniqueID, UniqueEnough) TEST(UniqueID, UniqueEnough)
{ {
std::unordered_set<u32> ids; 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); u32 unique_id = MMIO::UniqueID(i);
EXPECT_EQ(ids.end(), ids.find(unique_id)); EXPECT_EQ(ids.end(), ids.find(unique_id));
ids.insert(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); u32 unique_id = MMIO::UniqueID(i);
EXPECT_EQ(ids.end(), ids.find(unique_id)); EXPECT_EQ(ids.end(), ids.find(unique_id));
@ -29,18 +29,22 @@ TEST(IsMMIOAddress, SpecialAddresses)
SConfig::GetInstance().m_LocalCoreStartupParameter.bWii = true; SConfig::GetInstance().m_LocalCoreStartupParameter.bWii = true;
// WG Pipe address, should not be handled by MMIO. // 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)); EXPECT_FALSE(MMIO::IsMMIOAddress(0xE0000000));
// Uncached mirror of MEM1, shouldn't be handled by MMIO // Uncached mirror of MEM1, shouldn't be handled by MMIO
EXPECT_FALSE(MMIO::IsMMIOAddress(0xC0000000)); 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 // And lets check some valid addresses too
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCC0000E0)); // Gamecube MMIOs EXPECT_TRUE(MMIO::IsMMIOAddress(0x0C0000E0)); // Gamecube MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCD00008C)); // Wii MMIOs EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D00008C)); // Wii MMIOs
EXPECT_TRUE(MMIO::IsMMIOAddress(0xCD800F10)); // Mirror of Wii MMIOs EXPECT_TRUE(MMIO::IsMMIOAddress(0x0D800F10)); // Mirror of Wii MMIOs
SConfig::Shutdown(); SConfig::Shutdown();
} }
@ -63,13 +67,13 @@ protected:
TEST_F(MappingTest, ReadConstant) TEST_F(MappingTest, ReadConstant)
{ {
m_mapping->Register(0xCC001234, MMIO::Constant<u8>(0x42), MMIO::Nop<u8>()); m_mapping->Register(0x0C001234, MMIO::Constant<u8>(0x42), MMIO::Nop<u8>());
m_mapping->Register(0xCC001234, MMIO::Constant<u16>(0x1234), MMIO::Nop<u16>()); m_mapping->Register(0x0C001234, MMIO::Constant<u16>(0x1234), MMIO::Nop<u16>());
m_mapping->Register(0xCC001234, MMIO::Constant<u32>(0xdeadbeef), MMIO::Nop<u32>()); m_mapping->Register(0x0C001234, MMIO::Constant<u32>(0xdeadbeef), MMIO::Nop<u32>());
u8 val8 = m_mapping->Read<u8>(0xCC001234); u8 val8 = m_mapping->Read<u8>(0x0C001234);
u16 val16 = m_mapping->Read<u16>(0xCC001234); u16 val16 = m_mapping->Read<u16>(0x0C001234);
u32 val32 = m_mapping->Read<u32>(0xCC001234); u32 val32 = m_mapping->Read<u32>(0x0C001234);
EXPECT_EQ(0x42, val8); EXPECT_EQ(0x42, val8);
EXPECT_EQ(0x1234, val16); EXPECT_EQ(0x1234, val16);
@ -82,19 +86,19 @@ TEST_F(MappingTest, ReadWriteDirect)
u16 target_16 = 0; u16 target_16 = 0;
u32 target_32 = 0; u32 target_32 = 0;
m_mapping->Register(0xCC001234, MMIO::DirectRead<u8>(&target_8), MMIO::DirectWrite<u8>(&target_8)); m_mapping->Register(0x0C001234, 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(0x0C001234, 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<u32>(&target_32), MMIO::DirectWrite<u32>(&target_32));
for (u32 i = 0; i < 100; ++i) for (u32 i = 0; i < 100; ++i)
{ {
u8 val8 = m_mapping->Read<u8>(0xCC001234); EXPECT_EQ(i, val8); u8 val8 = m_mapping->Read<u8>(0x0C001234); EXPECT_EQ(i, val8);
u16 val16 = m_mapping->Read<u16>(0xCC001234); EXPECT_EQ(i, val16); u16 val16 = m_mapping->Read<u16>(0x0C001234); EXPECT_EQ(i, val16);
u32 val32 = m_mapping->Read<u32>(0xCC001234); EXPECT_EQ(i, val32); u32 val32 = m_mapping->Read<u32>(0x0C001234); EXPECT_EQ(i, val32);
val8 += 1; m_mapping->Write(0xCC001234, val8); val8 += 1; m_mapping->Write(0x0C001234, val8);
val16 += 1; m_mapping->Write(0xCC001234, val16); val16 += 1; m_mapping->Write(0x0C001234, val16);
val32 += 1; m_mapping->Write(0xCC001234, val32); val32 += 1; m_mapping->Write(0x0C001234, val32);
} }
} }
@ -102,21 +106,21 @@ TEST_F(MappingTest, ReadWriteComplex)
{ {
bool read_called = false, write_called = false; bool read_called = false, write_called = false;
m_mapping->Register(0xCC001234, m_mapping->Register(0x0C001234,
MMIO::ComplexRead<u8>([&read_called](u32 addr) { MMIO::ComplexRead<u8>([&read_called](u32 addr) {
EXPECT_EQ(0xCC001234, addr); EXPECT_EQ(0x0C001234, addr);
read_called = true; read_called = true;
return 0x12; return 0x12;
}), }),
MMIO::ComplexWrite<u8>([&write_called](u32 addr, u8 val) { MMIO::ComplexWrite<u8>([&write_called](u32 addr, u8 val) {
EXPECT_EQ(0xCC001234, addr); EXPECT_EQ(0x0C001234, addr);
EXPECT_EQ(0x34, val); EXPECT_EQ(0x34, val);
write_called = true; write_called = true;
}) })
); );
u8 val = m_mapping->Read<u8>(0xCC001234); EXPECT_EQ(0x12, val); u8 val = m_mapping->Read<u8>(0x0C001234); EXPECT_EQ(0x12, val);
m_mapping->Write(0xCC001234, (u8)0x34); m_mapping->Write(0x0C001234, (u8)0x34);
EXPECT_TRUE(read_called); EXPECT_TRUE(read_called);
EXPECT_TRUE(write_called); EXPECT_TRUE(write_called);