mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-04-29 06:49:24 -04:00
528 lines
17 KiB
C++
528 lines
17 KiB
C++
#include "Cafe/HW/MMU/MMU.h"
|
|
#include "Cafe/GraphicPack/GraphicPack2.h"
|
|
#include "util/MemMapper/MemMapper.h"
|
|
#include <wx/msgdlg.h>
|
|
#include "config/ActiveSettings.h"
|
|
|
|
uint8* memory_base = NULL; // base address of the reserved 4GB space
|
|
uint8* memory_elfCodeArena = NULL;
|
|
|
|
void checkMemAlloc(void* result)
|
|
{
|
|
if (result == nullptr)
|
|
assert_dbg();
|
|
}
|
|
|
|
void memory_initPhysicalLayout()
|
|
{
|
|
assert_dbg();
|
|
// todo - rewrite this using new MemMapper and MMU tables
|
|
//memory_base = (uint8*)VirtualAlloc(NULL, 0x100000000ULL, MEM_RESERVE, PAGE_READWRITE);
|
|
//VirtualFree(memory_base, 0, MEM_RELEASE);
|
|
|
|
//// todo - figure out all the ranges and allocate them properly
|
|
|
|
//// allocate memory for the kernel
|
|
////checkMemAlloc(VirtualAlloc(memory_base + 0x08000000, 1024*1024*2, MEM_COMMIT, PAGE_READWRITE));
|
|
//// allocate memory for bootrom
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x00000000, 1024*16, MEM_RESERVE|MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
|
|
//// allocate memory at 0x016FFFFC (is this some sort of register interface or maybe just temporary storage?)
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x016FF000, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
//// temporary storage for bootrom copy
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x016c0000, 0x4000 + 0x4000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
//// 0x016c0000
|
|
|
|
//// L2
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0xE0000000, 1024 * 16, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
//// kernel memory
|
|
//// currently it is unknown if this is it's own physical memory region or if this is mapped somehow
|
|
//// considering the ancast is never copied here and no memory mapping is setup it seems like a hardwired mirror to 0x08000000?
|
|
////checkMemAlloc(VirtualAlloc(memory_base + 0xFFE00000, 0x180000, MEM_COMMIT, PAGE_READWRITE));
|
|
//HANDLE hKernelMem = CreateFileMappingA(
|
|
// INVALID_HANDLE_VALUE, // use paging file
|
|
// NULL, // default security
|
|
// PAGE_READWRITE, // read/write access
|
|
// 0, // maximum object size (high-order DWORD)
|
|
// 1024 * 1024 * 2, // maximum object size (low-order DWORD)
|
|
// "kernelMem08000000"); // name of mapping object
|
|
//
|
|
//checkMemAlloc(MapViewOfFileEx(hKernelMem, FILE_MAP_ALL_ACCESS, 0, 0, 1024 * 1024 * 2, memory_base + 0x08000000));
|
|
//checkMemAlloc(MapViewOfFileEx(hKernelMem, FILE_MAP_ALL_ACCESS, 0, 0, 1024 * 1024 * 2, memory_base + 0xFFE00000));
|
|
|
|
//// IOSU->PPC bootParamBlock
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x01FFF000, 0x1000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
//// used as dynamic kernel memory?
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x1C000000, 0x01000000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
|
|
//// mapped by kernel to FF200000 (loader.elf?)
|
|
//checkMemAlloc(VirtualAlloc(memory_base + 0x1B800000, 0x00800000, MEM_RESERVE | MEM_COMMIT, PAGE_READWRITE));
|
|
}
|
|
|
|
std::vector<struct MMURange*> g_mmuRanges;
|
|
|
|
std::vector<MMURange*> memory_getMMURanges()
|
|
{
|
|
return g_mmuRanges;
|
|
}
|
|
|
|
MMURange* memory_getMMURangeByAddress(MPTR address)
|
|
{
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (address >= itr->getBase() && address < itr->getEnd())
|
|
return itr;
|
|
}
|
|
return nullptr;
|
|
}
|
|
|
|
MMURange::MMURange(const uint32 baseAddress, const uint32 size, MMU_MEM_AREA_ID areaId, const std::string_view name, MFLAG flags) : baseAddress(baseAddress), size(size), initSize(size), areaId(areaId), name(name), flags(flags)
|
|
{
|
|
g_mmuRanges.emplace_back(this);
|
|
}
|
|
|
|
void MMURange::mapMem()
|
|
{
|
|
cemu_assert_debug(!m_isMapped);
|
|
if (MemMapper::AllocateMemory(memory_base + baseAddress, size, MemMapper::PAGE_PERMISSION::P_RW, true) == nullptr)
|
|
{
|
|
std::string errorMsg = fmt::format("Unable to allocate {} memory", name);
|
|
wxMessageBox(errorMsg.c_str(), "Error", wxOK | wxCENTRE | wxICON_ERROR);
|
|
#if BOOST_OS_WINDOWS
|
|
ExitProcess(-1);
|
|
#else
|
|
exit(-1);
|
|
#endif
|
|
}
|
|
m_isMapped = true;
|
|
}
|
|
|
|
void MMURange::unmapMem()
|
|
{
|
|
MemMapper::FreeMemory(memory_base + baseAddress, size, true);
|
|
m_isMapped = false;
|
|
}
|
|
|
|
MMURange mmuRange_LOW0 { 0x00010000, 0x000F0000, MMU_MEM_AREA_ID::CODE_LOW0, "CODE_LOW0" }; // code cave (Cemuhook)
|
|
MMURange mmuRange_TRAMPOLINE_AREA { 0x00E00000, 0x00200000, MMU_MEM_AREA_ID::CODE_TRAMPOLINE, "TRAMPOLINE_AREA" }; // code area for trampolines and imports
|
|
MMURange mmuRange_CODECAVE { 0x01800000, 0x00400000, MMU_MEM_AREA_ID::CODE_CAVE, "CODECAVE" }; // code cave area (4MiB)
|
|
MMURange mmuRange_TEXT_AREA { 0x02000000, 0x0C000000, MMU_MEM_AREA_ID::CODE_MAIN, "TEXT_AREA" }; // module text sections go here (0x02000000 to 0x10000000, 224MiB)
|
|
MMURange mmuRange_CEMU_AREA { 0x0E000000, 0x02000000, MMU_MEM_AREA_ID::CEMU_PRIVATE, "CEMU_AREA", MMURange::MFLAG::FLAG_MAP_EARLY }; // Cemu-only, 32MiB. Should be allocated early for SysAllocator
|
|
MMURange mmuRange_MEM2 { 0x10000000, 0x40000000, MMU_MEM_AREA_ID::MEM2_DATA, "MEM2" }; // main memory area (1GB)
|
|
MMURange mmuRange_OVERLAY_AREA { 0xA0000000, 0x1C000000, MMU_MEM_AREA_ID::OVERLAY, "OVERLAY_AREA", MMURange::MFLAG::FLAG_OPTIONAL }; // has to be requested, 448MiB
|
|
MMURange mmuRange_FGBUCKET { 0xE0000000, 0x04000000, MMU_MEM_AREA_ID::FGBUCKET, "FGBUCKET" }; // foreground bucket (64MiB)
|
|
MMURange mmuRange_TILINGAPERTURE { 0xE8000000, 0x02000000, MMU_MEM_AREA_ID::TILING_APERATURE, "TILINGAPERTURE" }; // tiling aperture
|
|
MMURange mmuRange_MEM1 { 0xF4000000, 0x02000000, MMU_MEM_AREA_ID::MEM1, "MEM1" }; // 32MiB
|
|
MMURange mmuRange_RPLLOADER { 0xF6000000, 0x02000000, MMU_MEM_AREA_ID::RPLLOADER, "RPLLOADER_AREA" }; // shared with RPLLoader
|
|
MMURange mmuRange_SHARED_AREA { 0xF8000000, 0x02000000, MMU_MEM_AREA_ID::SHAREDDATA, "SHARED_AREA", MMURange::MFLAG::FLAG_MAP_EARLY }; // 32MiB, Cemuhook accesses this memory region at boot
|
|
MMURange mmuRange_CORE0_LC { 0xFFC00000, 0x00005000, MMU_MEM_AREA_ID::CPU_LC0, "CORE0_LC" }; // locked L2 cache of core 0
|
|
MMURange mmuRange_CORE1_LC { 0xFFC40000, 0x00005000, MMU_MEM_AREA_ID::CPU_LC1, "CORE1_LC" }; // locked L2 cache of core 1
|
|
MMURange mmuRange_CORE2_LC { 0xFFC80000, 0x00005000, MMU_MEM_AREA_ID::CPU_LC2, "CORE2_LC" }; // locked L2 cache of core 2
|
|
MMURange mmuRange_HIGHMEM { 0xFFFFF000, 0x00001000, MMU_MEM_AREA_ID::CPU_PER_CORE, "PER-CORE" }; // per-core memory? Used by coreinit and PPC kernel to store core context specific data (like current thread ptr). We dont use it but Project Zero has a bug where it writes a byte at 0xfffffffe thus this memory range needs to be writable
|
|
|
|
void memory_init()
|
|
{
|
|
// reserve a continous range of 4GB
|
|
if(!memory_base)
|
|
memory_base = (uint8*)MemMapper::ReserveMemory(nullptr, (size_t)0x100000000, MemMapper::PAGE_PERMISSION::P_RW);
|
|
if( !memory_base )
|
|
{
|
|
debug_printf("memory_init(): Unable to reserve 4GB of memory\n");
|
|
debugBreakpoint();
|
|
wxMessageBox("Unable to reserve 4GB of memory\n", "Error", wxOK | wxCENTRE | wxICON_ERROR);
|
|
exit(-1);
|
|
}
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (itr->isMappedEarly())
|
|
itr->mapMem();
|
|
}
|
|
}
|
|
|
|
void memory_mapForCurrentTitle()
|
|
{
|
|
for (auto& itr : g_mmuRanges)
|
|
if(!itr->isMapped())
|
|
itr->resetConfig();
|
|
// expand ranges
|
|
auto gfxPackMappings = GraphicPack2::GetActiveRAMMappings();
|
|
for (auto& mapping : gfxPackMappings)
|
|
{
|
|
MMURange* mmuRange = nullptr;
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (itr->getBase() == mapping.first)
|
|
{
|
|
mmuRange = itr;
|
|
break;
|
|
}
|
|
}
|
|
if (!mmuRange)
|
|
{
|
|
cemuLog_log(LogType::Force, fmt::format("Graphic pack error: Unable to apply modified RAM mapping {:08x}-{:08x}. Start address must match one of the existing MMU ranges:", mapping.first, mapping.second));
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if(itr->isMapped())
|
|
continue;
|
|
cemuLog_log(LogType::Force, fmt::format("{:08x}-{:08x} ({:})", itr->getBase(), itr->getEnd(), itr->getName()));
|
|
}
|
|
continue;
|
|
}
|
|
// make sure the new range isn't overlapping with anything
|
|
bool isOverlapping = false;
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if(itr == mmuRange)
|
|
continue;
|
|
if (mapping.first < itr->getEnd() && mapping.second > itr->getBase())
|
|
{
|
|
cemuLog_log(LogType::Force, fmt::format("Graphic pack error: Unable to apply modified memory range {:08x}-{:08x} since it is overlapping with {:08x}-{:08x} ({:})", mapping.first, mapping.second, itr->getBase(), itr->getEnd(), itr->getName()));
|
|
isOverlapping = true;
|
|
}
|
|
}
|
|
if(isOverlapping)
|
|
continue;
|
|
mmuRange->setEnd(mapping.second);
|
|
}
|
|
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (!itr->isOptional() && !itr->isMappedEarly())
|
|
itr->mapMem();
|
|
}
|
|
}
|
|
|
|
void memory_unmapForCurrentTitle()
|
|
{
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (itr->isMapped() && !itr->isMappedEarly())
|
|
itr->unmapMem();
|
|
}
|
|
}
|
|
|
|
void memory_logModifiedMemoryRanges()
|
|
{
|
|
auto gfxPackMappings = GraphicPack2::GetActiveRAMMappings();
|
|
for (auto& mapping : gfxPackMappings)
|
|
{
|
|
MMURange* mmuRange = nullptr;
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if (itr->getBase() == mapping.first)
|
|
{
|
|
mmuRange = itr;
|
|
break;
|
|
}
|
|
}
|
|
if (!mmuRange)
|
|
continue;
|
|
sint32 extraMem = (sint32)mapping.second - (sint32)(mmuRange->getBase() + mmuRange->getInitSize());
|
|
extraMem = (extraMem + 1023) / 1024;
|
|
std::string memAmountStr;
|
|
if (extraMem >= 8 * 1024 * 1024)
|
|
memAmountStr = fmt::format("{:+}MiB", (extraMem + 1023) / 1024);
|
|
else
|
|
memAmountStr = fmt::format("{:+}KiB", extraMem);
|
|
cemuLog_log(LogType::Force, fmt::format("Graphic pack: Using modified RAM mapping {:08x}-{:08x} ({})", mapping.first, mapping.second, memAmountStr));
|
|
}
|
|
}
|
|
|
|
void memory_enableOverlayArena()
|
|
{
|
|
if (mmuRange_OVERLAY_AREA.isMapped())
|
|
return;
|
|
mmuRange_OVERLAY_AREA.mapMem();
|
|
}
|
|
|
|
void memory_enableHBLELFCodeArea()
|
|
{
|
|
if (memory_elfCodeArena != NULL)
|
|
return;
|
|
memory_elfCodeArena = (uint8*)MemMapper::AllocateMemory(memory_base + 0x00800000, 0x00800000, MemMapper::PAGE_PERMISSION::P_RW, true);
|
|
if (memory_elfCodeArena == NULL)
|
|
{
|
|
debug_printf("memory_enableHBLELFCodeArea(): Unable to allocate memory for ELF arena\n");
|
|
debugBreakpoint();
|
|
}
|
|
}
|
|
|
|
bool memory_isAddressRangeAccessible(MPTR virtualAddress, uint32 size)
|
|
{
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if(!itr->isMapped())
|
|
continue;
|
|
if (virtualAddress >= itr->getBase() && virtualAddress < itr->getEnd())
|
|
{
|
|
uint32 remainingSize = itr->getEnd() - virtualAddress;
|
|
return size <= remainingSize && itr->isMapped();
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
uint32 memory_virtualToPhysical(uint32 virtualOffset)
|
|
{
|
|
// currently we map virtual to physical space 1:1
|
|
return virtualOffset;
|
|
}
|
|
|
|
uint32 memory_physicalToVirtual(uint32 physicalOffset)
|
|
{
|
|
// currently we map virtual to physical space 1:1
|
|
return physicalOffset;
|
|
}
|
|
|
|
uint8* memory_getPointerFromPhysicalOffset(uint32 physicalOffset)
|
|
{
|
|
return memory_base + physicalOffset;
|
|
}
|
|
|
|
uint32 memory_getVirtualOffsetFromPointer(void* ptr)
|
|
{
|
|
if( !ptr )
|
|
return MPTR_NULL;
|
|
return (uint32)((uint8*)ptr - (uint8*)memory_base);
|
|
}
|
|
|
|
uint8* memory_getPointerFromVirtualOffset(uint32 virtualOffset)
|
|
{
|
|
return memory_base + virtualOffset;
|
|
}
|
|
|
|
uint8* memory_getPointerFromVirtualOffsetAllowNull(uint32 virtualOffset)
|
|
{
|
|
if( virtualOffset == MPTR_NULL )
|
|
return nullptr;
|
|
return memory_getPointerFromVirtualOffset(virtualOffset);
|
|
}
|
|
|
|
// write access
|
|
void memory_writeDouble(uint32 address, double vf)
|
|
{
|
|
uint64 v = *(uint64*)&vf;
|
|
uint32 v1 = v&0xFFFFFFFF;
|
|
uint32 v2 = v>>32;
|
|
uint8* ptr = memory_getPointerFromVirtualOffset(address);
|
|
*(uint32*)(ptr+4) = CPU_swapEndianU32(v1);
|
|
*(uint32*)(ptr+0) = CPU_swapEndianU32(v2);
|
|
}
|
|
|
|
void memory_writeFloat(uint32 address, float vf)
|
|
{
|
|
uint32 v = *(uint32*)&vf;
|
|
*(uint32*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU32(v);
|
|
}
|
|
|
|
void memory_writeU32(uint32 address, uint32 v)
|
|
{
|
|
*(uint32*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU32(v);
|
|
}
|
|
|
|
void memory_writeU64(uint32 address, uint64 v)
|
|
{
|
|
*(uint64*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU64(v);
|
|
}
|
|
|
|
void memory_writeU16(uint32 address, uint16 v)
|
|
{
|
|
*(uint16*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU16(v);
|
|
}
|
|
|
|
void memory_writeU8(uint32 address, uint8 v)
|
|
{
|
|
*(uint8*)(memory_getPointerFromVirtualOffset(address)) = v;
|
|
}
|
|
|
|
// read access
|
|
|
|
double memory_readDouble(uint32 address)
|
|
{
|
|
uint32 v[2];
|
|
v[1] = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
v[0] = *(uint32*)(memory_getPointerFromVirtualOffset(address)+4);
|
|
v[0] = CPU_swapEndianU32(v[0]);
|
|
v[1] = CPU_swapEndianU32(v[1]);
|
|
return *(double*)v;
|
|
}
|
|
|
|
float memory_readFloat(uint32 address)
|
|
{
|
|
uint32 v = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
v = CPU_swapEndianU32(v);
|
|
return *(float*)&v;
|
|
}
|
|
|
|
uint64 memory_readU64(uint32 address)
|
|
{
|
|
uint64 v = *(uint64*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU64(v);
|
|
}
|
|
|
|
uint32 memory_readU32(uint32 address)
|
|
{
|
|
uint32 v = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU32(v);
|
|
}
|
|
|
|
uint16 memory_readU16(uint32 address)
|
|
{
|
|
uint16 v = *(uint16*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU16(v);
|
|
}
|
|
|
|
uint8 memory_readU8(uint32 address)
|
|
{
|
|
return *(uint8*)(memory_getPointerFromVirtualOffset(address));
|
|
}
|
|
|
|
extern "C" DLLEXPORT void* memory_getBase()
|
|
{
|
|
return memory_base;
|
|
}
|
|
|
|
void memory_writeDumpFile(uint32 startAddr, uint32 size, const fs::path& path)
|
|
{
|
|
fs::path filePath = path;
|
|
filePath /= fmt::format("{:08x}.bin", startAddr);
|
|
FileStream* fs = FileStream::createFile2(filePath);
|
|
if (fs)
|
|
{
|
|
fs->writeData(memory_base + startAddr, size);
|
|
delete fs;
|
|
}
|
|
}
|
|
|
|
void memory_createDump()
|
|
{
|
|
const uint32 pageSize = MemMapper::GetPageSize();
|
|
fs::path path = ActiveSettings::GetUserDataPath("dump/ramDump{:}", (uint32)time(nullptr));
|
|
fs::create_directories(path);
|
|
|
|
for (auto& itr : g_mmuRanges)
|
|
{
|
|
if(!itr->isMapped())
|
|
continue;
|
|
memory_writeDumpFile(itr->getBase(), itr->getSize(), path);
|
|
}
|
|
}
|
|
|
|
namespace MMU
|
|
{
|
|
// MMIO access handler
|
|
// located in address region 0x0C000000 - 0x0E000000
|
|
// there seem to be multiple subregions + special meanings for some address bits maybe?
|
|
// Try to figure this out. We know these regions (in Wii U mode):
|
|
// 0x0C000000 (the old GC register interface?)
|
|
// 0x0D000000 (new Wii U stuff?)
|
|
|
|
std::unordered_map<PAddr, MMIOFuncWrite32>* g_mmioHandlerW32{};
|
|
std::unordered_map<PAddr, MMIOFuncWrite16>* g_mmioHandlerW16{};
|
|
std::unordered_map<PAddr, MMIOFuncRead32>* g_mmioHandlerR32{};
|
|
std::unordered_map<PAddr, MMIOFuncRead16>* g_mmioHandlerR16{};
|
|
|
|
void _initHandlers()
|
|
{
|
|
if (g_mmioHandlerW32)
|
|
return;
|
|
g_mmioHandlerW32 = new std::unordered_map<PAddr, MMIOFuncWrite32>();
|
|
g_mmioHandlerW16 = new std::unordered_map<PAddr, MMIOFuncWrite16>();
|
|
g_mmioHandlerR32 = new std::unordered_map<PAddr, MMIOFuncRead32>();
|
|
g_mmioHandlerR16 = new std::unordered_map<PAddr, MMIOFuncRead16>();
|
|
}
|
|
|
|
PAddr _MakeMMIOAddress(MMIOInterface interfaceLocation, uint32 relativeAddress)
|
|
{
|
|
PAddr addr = 0;
|
|
if (interfaceLocation == MMIOInterface::INTERFACE_0C000000)
|
|
addr = 0x0C000000;
|
|
else if (interfaceLocation == MMIOInterface::INTERFACE_0D000000)
|
|
addr = 0x0D000000;
|
|
else
|
|
assert_dbg();
|
|
return addr + relativeAddress;
|
|
}
|
|
|
|
void RegisterMMIO_W32(MMIOInterface interfaceLocation, uint32 relativeAddress, MMIOFuncWrite32 ptr)
|
|
{
|
|
_initHandlers();
|
|
g_mmioHandlerW32->emplace(_MakeMMIOAddress(interfaceLocation, relativeAddress), ptr);
|
|
}
|
|
|
|
void RegisterMMIO_W16(MMIOInterface interfaceLocation, uint32 relativeAddress, MMIOFuncWrite16 ptr)
|
|
{
|
|
_initHandlers();
|
|
g_mmioHandlerW16->emplace(_MakeMMIOAddress(interfaceLocation, relativeAddress), ptr);
|
|
}
|
|
|
|
void RegisterMMIO_R32(MMIOInterface interfaceLocation, uint32 relativeAddress, MMIOFuncRead32 ptr)
|
|
{
|
|
_initHandlers();
|
|
PAddr addr = _MakeMMIOAddress(interfaceLocation, relativeAddress);
|
|
g_mmioHandlerR32->emplace(addr, ptr);
|
|
}
|
|
|
|
void RegisterMMIO_R16(MMIOInterface interfaceLocation, uint32 relativeAddress, MMIOFuncRead16 ptr)
|
|
{
|
|
_initHandlers();
|
|
g_mmioHandlerR16->emplace(_MakeMMIOAddress(interfaceLocation, relativeAddress), ptr);
|
|
}
|
|
|
|
void WriteMMIO_32(PAddr address, uint32 value)
|
|
{
|
|
cemu_assert_debug((address & 0x3) == 0);
|
|
auto itr = g_mmioHandlerW32->find(address);
|
|
if (itr == g_mmioHandlerW32->end())
|
|
{
|
|
//cemuLog_logDebug(LogType::Force, "[MMU] MMIO write u32 0x{:08x} from unhandled address 0x{:08x}", value, address);
|
|
return;
|
|
}
|
|
return itr->second(address, value);
|
|
}
|
|
|
|
void WriteMMIO_16(PAddr address, uint16 value)
|
|
{
|
|
cemu_assert_debug((address & 0x1) == 0);
|
|
auto itr = g_mmioHandlerW16->find(address);
|
|
if (itr == g_mmioHandlerW16->end())
|
|
{
|
|
//cemuLog_logDebug(LogType::Force, "[MMU] MMIO write u16 0x{:04x} from unhandled address 0x{:08x}", (uint32)value, address);
|
|
return;
|
|
}
|
|
return itr->second(address, value);
|
|
}
|
|
|
|
|
|
// todo - instead of passing the physical address to Read/WriteMMIO we should pass an interface id and a relative address? This would allow remapping the hardware address (tho we can just unregister + register at different addresses)
|
|
|
|
uint16 ReadMMIO_32(PAddr address)
|
|
{
|
|
cemu_assert_debug((address & 0x3) == 0);
|
|
auto itr = g_mmioHandlerR32->find(address);
|
|
if(itr == g_mmioHandlerR32->end())
|
|
{
|
|
//cemuLog_logDebug(LogType::Force, "[MMU] MMIO read u32 from unhandled address 0x{:08x}", address);
|
|
return 0;
|
|
}
|
|
return itr->second(address);
|
|
}
|
|
|
|
uint16 ReadMMIO_16(PAddr address)
|
|
{
|
|
cemu_assert_debug((address & 0x1) == 0);
|
|
auto itr = g_mmioHandlerR16->find(address);
|
|
if (itr == g_mmioHandlerR16->end())
|
|
{
|
|
//cemuLog_logDebug(LogType::Force, "[MMU] MMIO read u16 from unhandled address 0x{:08x}", address);
|
|
return 0;
|
|
}
|
|
return itr->second(address);
|
|
}
|
|
|
|
|
|
}
|