mirror of
https://github.com/izzy2lost/dolphin.git
synced 2026-06-19 01:16:48 -07:00
@@ -157,118 +157,3 @@ u8* MemArena::FindMemoryBase()
|
||||
return static_cast<u8*>(base);
|
||||
#endif
|
||||
}
|
||||
|
||||
// yeah, this could also be done in like two bitwise ops...
|
||||
#define SKIP(a_flags, b_flags) \
|
||||
if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY)) \
|
||||
continue; \
|
||||
if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM)) \
|
||||
continue;
|
||||
|
||||
static bool Memory_TryBase(u8* base, MemoryView* views, int num_views, u32 flags, MemArena* arena)
|
||||
{
|
||||
// OK, we know where to find free space. Now grab it!
|
||||
// We just mimic the popular BAT setup.
|
||||
|
||||
int i;
|
||||
for (i = 0; i < num_views; i++)
|
||||
{
|
||||
MemoryView* view = &views[i];
|
||||
void* view_base;
|
||||
bool use_sw_mirror;
|
||||
|
||||
SKIP(flags, view->flags);
|
||||
|
||||
#if _ARCH_64
|
||||
// On 64-bit, we map the same file position multiple times, so we
|
||||
// don't need the software fallback for the mirrors.
|
||||
view_base = base + view->virtual_address;
|
||||
use_sw_mirror = false;
|
||||
#else
|
||||
// On 32-bit, we don't have the actual address space to store all
|
||||
// the mirrors, so we just map the fallbacks somewhere in our address
|
||||
// space and use the software fallbacks for mirroring.
|
||||
view_base = base + (view->virtual_address & 0x3FFFFFFF);
|
||||
use_sw_mirror = true;
|
||||
#endif
|
||||
|
||||
if (use_sw_mirror && (view->flags & MV_MIRROR_PREVIOUS))
|
||||
{
|
||||
view->view_ptr = views[i - 1].view_ptr;
|
||||
}
|
||||
else
|
||||
{
|
||||
view->mapped_ptr = arena->CreateView(view->shm_position, view->size, view_base);
|
||||
view->view_ptr = view->mapped_ptr;
|
||||
}
|
||||
|
||||
if (!view->view_ptr)
|
||||
{
|
||||
// Argh! ERROR! Free what we grabbed so far so we can try again.
|
||||
MemoryMap_Shutdown(views, i + 1, flags, arena);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (view->out_ptr)
|
||||
*(view->out_ptr) = (u8*)view->view_ptr;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
static u32 MemoryMap_InitializeViews(MemoryView* views, int num_views, u32 flags)
|
||||
{
|
||||
u32 shm_position = 0;
|
||||
u32 last_position = 0;
|
||||
|
||||
for (int i = 0; i < num_views; i++)
|
||||
{
|
||||
// Zero all the pointers to be sure.
|
||||
views[i].mapped_ptr = nullptr;
|
||||
|
||||
SKIP(flags, views[i].flags);
|
||||
|
||||
if (views[i].flags & MV_MIRROR_PREVIOUS)
|
||||
shm_position = last_position;
|
||||
views[i].shm_position = shm_position;
|
||||
last_position = shm_position;
|
||||
shm_position += views[i].size;
|
||||
}
|
||||
|
||||
return shm_position;
|
||||
}
|
||||
|
||||
u8* MemoryMap_Setup(MemoryView* views, int num_views, u32 flags, MemArena* arena)
|
||||
{
|
||||
u32 total_mem = MemoryMap_InitializeViews(views, num_views, flags);
|
||||
|
||||
arena->GrabSHMSegment(total_mem);
|
||||
|
||||
// Now, create views in high memory where there's plenty of space.
|
||||
u8* base = MemArena::FindMemoryBase();
|
||||
// This really shouldn't fail - in 64-bit, there will always be enough
|
||||
// address space.
|
||||
if (!Memory_TryBase(base, views, num_views, flags, arena))
|
||||
{
|
||||
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
|
||||
exit(0);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
return base;
|
||||
}
|
||||
|
||||
void MemoryMap_Shutdown(MemoryView* views, int num_views, u32 flags, MemArena* arena)
|
||||
{
|
||||
std::set<void*> freeset;
|
||||
for (int i = 0; i < num_views; i++)
|
||||
{
|
||||
MemoryView* view = &views[i];
|
||||
if (view->mapped_ptr && !freeset.count(view->mapped_ptr))
|
||||
{
|
||||
arena->ReleaseView(view->mapped_ptr, view->size);
|
||||
freeset.insert(view->mapped_ptr);
|
||||
view->mapped_ptr = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,26 +35,3 @@ private:
|
||||
int fd;
|
||||
#endif
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
MV_MIRROR_PREVIOUS = 1,
|
||||
MV_FAKE_VMEM = 2,
|
||||
MV_WII_ONLY = 4,
|
||||
};
|
||||
|
||||
struct MemoryView
|
||||
{
|
||||
u8** out_ptr;
|
||||
u64 virtual_address;
|
||||
u32 size;
|
||||
u32 flags;
|
||||
void* mapped_ptr;
|
||||
void* view_ptr;
|
||||
u32 shm_position;
|
||||
};
|
||||
|
||||
// Uses a memory arena to set up an emulator-friendly memory map according to
|
||||
// a passed-in list of MemoryView structures.
|
||||
u8* MemoryMap_Setup(MemoryView* views, int num_views, u32 flags, MemArena* arena);
|
||||
void MemoryMap_Shutdown(MemoryView* views, int num_views, u32 flags, MemArena* arena);
|
||||
|
||||
@@ -237,6 +237,8 @@ bool CBoot::Load_BS2(const std::string& _rBootROMFilename)
|
||||
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
|
||||
PowerPC::ppcState.spr[SPR_DBAT3U] = 0xfff0001f;
|
||||
PowerPC::ppcState.spr[SPR_DBAT3L] = 0xfff00001;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
PC = 0x81200150;
|
||||
return true;
|
||||
}
|
||||
@@ -377,6 +379,8 @@ bool CBoot::BootUp()
|
||||
PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
|
||||
dolLoader.Load();
|
||||
PC = dolLoader.GetEntryPoint();
|
||||
|
||||
@@ -52,6 +52,8 @@ bool CBoot::EmulatedBS2_GC(bool skipAppLoader)
|
||||
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
|
||||
// Write necessary values
|
||||
// Here we write values to memory that the apploader does not take care of. Game info goes
|
||||
@@ -298,11 +300,11 @@ bool CBoot::SetupWiiMemory(DiscIO::Country country)
|
||||
Memory::Write_U32(0x8179d500, 0x00003110); // Init
|
||||
Memory::Write_U32(0x04000000, 0x00003118); // Unknown
|
||||
Memory::Write_U32(0x04000000, 0x0000311c); // BAT
|
||||
Memory::Write_U32(0x93400000, 0x00003120); // BAT
|
||||
Memory::Write_U32(0x93600000, 0x00003120); // BAT
|
||||
Memory::Write_U32(0x90000800, 0x00003124); // Init - MEM2 low
|
||||
Memory::Write_U32(0x93ae0000, 0x00003128); // Init - MEM2 high
|
||||
Memory::Write_U32(0x93ae0000, 0x00003130); // IOS MEM2 low
|
||||
Memory::Write_U32(0x93b00000, 0x00003134); // IOS MEM2 high
|
||||
Memory::Write_U32(0x935e0000, 0x00003128); // Init - MEM2 high
|
||||
Memory::Write_U32(0x935e0000, 0x00003130); // IOS MEM2 low
|
||||
Memory::Write_U32(0x93600000, 0x00003134); // IOS MEM2 high
|
||||
Memory::Write_U32(0x00000012, 0x00003138); // Console type
|
||||
// 40 is copied from 88 after running apploader
|
||||
Memory::Write_U32(0x00090204, 0x00003140); // IOS revision (IOS9, v2.4)
|
||||
@@ -371,6 +373,8 @@ bool CBoot::EmulatedBS2_Wii()
|
||||
PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
|
||||
Memory::Write_U32(0x4c000064, 0x00000300); // Write default DSI Handler: rfi
|
||||
Memory::Write_U32(0x4c000064, 0x00000800); // Write default FPU Handler: rfi
|
||||
|
||||
@@ -85,6 +85,8 @@ bool CBoot::Boot_ELF(const std::string& filename)
|
||||
PowerPC::ppcState.spr[SPR_DBAT4L] = 0x10000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5U] = 0xd0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT5L] = 0x1000002a;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
|
||||
if (!reader.LoadSymbols())
|
||||
{
|
||||
|
||||
@@ -410,6 +410,8 @@ void FifoPlayer::LoadMemory()
|
||||
PowerPC::ppcState.spr[SPR_DBAT0L] = 0x00000002;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1U] = 0xc0001fff;
|
||||
PowerPC::ppcState.spr[SPR_DBAT1L] = 0x0000002a;
|
||||
PowerPC::DBATUpdated();
|
||||
PowerPC::IBATUpdated();
|
||||
|
||||
SetupFifo();
|
||||
|
||||
|
||||
+117
-38
@@ -96,6 +96,26 @@ bool IsInitialized()
|
||||
return m_IsInitialized;
|
||||
}
|
||||
|
||||
struct PhysicalMemoryRegion
|
||||
{
|
||||
u8** out_pointer;
|
||||
u32 physical_address;
|
||||
u32 size;
|
||||
enum
|
||||
{
|
||||
ALWAYS = 0,
|
||||
FAKE_VMEM = 1,
|
||||
WII_ONLY = 2,
|
||||
} flags;
|
||||
u32 shm_position;
|
||||
};
|
||||
|
||||
struct LogicalMemoryView
|
||||
{
|
||||
void* mapped_pointer;
|
||||
u32 mapped_size;
|
||||
};
|
||||
|
||||
// Dolphin allocates memory to represent four regions:
|
||||
// - 32MB RAM (actually 24MB on hardware), available on Gamecube and Wii
|
||||
// - 64MB "EXRAM", RAM only available on Wii
|
||||
@@ -116,28 +136,12 @@ bool IsInitialized()
|
||||
// [0x08000000, 0x0C000000) - EFB "mapping" (not handled here)
|
||||
// [0x0C000000, 0x0E000000) - MMIO etc. (not handled here)
|
||||
// [0x10000000, 0x14000000) - 64MB RAM (Wii-only; slightly slower)
|
||||
//
|
||||
// The 4GB starting at logical_base represents access from the CPU
|
||||
// with address translation turned on. Instead of changing the mapping
|
||||
// based on the BAT registers, we approximate the common BAT configuration
|
||||
// used by games:
|
||||
// [0x00000000, 0x02000000) - 32MB RAM, cached access, normally only mapped
|
||||
// during startup by Wii WADs
|
||||
// [0x02000000, 0x08000000) - Mirrors of 32MB RAM (not implemented here)
|
||||
// [0x40000000, 0x50000000) - FakeVMEM
|
||||
// [0x70000000, 0x80000000) - FakeVMEM
|
||||
// [0x80000000, 0x82000000) - 32MB RAM, cached access
|
||||
// [0x82000000, 0x88000000) - Mirrors of 32MB RAM (not implemented here)
|
||||
// [0x90000000, 0x94000000) - 64MB RAM, Wii-only, cached access
|
||||
// [0xC0000000, 0xC2000000) - 32MB RAM, uncached access
|
||||
// [0xC2000000, 0xC8000000) - Mirrors of 32MB RAM (not implemented here)
|
||||
// [0xC8000000, 0xCC000000) - EFB "mapping" (not handled here)
|
||||
// [0xCC000000, 0xCE000000) - MMIO etc. (not handled here)
|
||||
// [0xD0000000, 0xD4000000) - 64MB RAM, Wii-only, uncached access
|
||||
// [0x7E000000, 0x80000000) - FakeVMEM
|
||||
// [0xE0000000, 0xE0040000) - 256KB locked L1
|
||||
//
|
||||
// TODO: We shouldn't hardcode this mapping; we can generate it dynamically
|
||||
// based on the BAT registers.
|
||||
// The 4GB starting at logical_base represents access from the CPU
|
||||
// with address translation turned on. This mapping is computed based
|
||||
// on the BAT registers.
|
||||
//
|
||||
// Each of these 4GB regions is followed by 4GB of empty space so overflows
|
||||
// in address computation in the JIT don't access the wrong memory.
|
||||
@@ -152,18 +156,14 @@ bool IsInitialized()
|
||||
//
|
||||
// TODO: The actual size of RAM is REALRAM_SIZE (24MB); the other 8MB shouldn't
|
||||
// be backed by actual memory.
|
||||
static MemoryView views[] = {
|
||||
{&m_pRAM, 0x00000000, RAM_SIZE, 0},
|
||||
{nullptr, 0x200000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
|
||||
{nullptr, 0x280000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
|
||||
{nullptr, 0x2C0000000, RAM_SIZE, MV_MIRROR_PREVIOUS},
|
||||
{&m_pL1Cache, 0x2E0000000, L1_CACHE_SIZE, 0},
|
||||
{&m_pFakeVMEM, 0x27E000000, FAKEVMEM_SIZE, MV_FAKE_VMEM},
|
||||
{&m_pEXRAM, 0x10000000, EXRAM_SIZE, MV_WII_ONLY},
|
||||
{nullptr, 0x290000000, EXRAM_SIZE, MV_WII_ONLY | MV_MIRROR_PREVIOUS},
|
||||
{nullptr, 0x2D0000000, EXRAM_SIZE, MV_WII_ONLY | MV_MIRROR_PREVIOUS},
|
||||
static PhysicalMemoryRegion physical_regions[] = {
|
||||
{&m_pRAM, 0x00000000, RAM_SIZE, PhysicalMemoryRegion::ALWAYS},
|
||||
{&m_pL1Cache, 0xE0000000, L1_CACHE_SIZE, PhysicalMemoryRegion::ALWAYS},
|
||||
{&m_pFakeVMEM, 0x7E000000, FAKEVMEM_SIZE, PhysicalMemoryRegion::FAKE_VMEM},
|
||||
{&m_pEXRAM, 0x10000000, EXRAM_SIZE, PhysicalMemoryRegion::WII_ONLY},
|
||||
};
|
||||
static const int num_views = sizeof(views) / sizeof(MemoryView);
|
||||
|
||||
static std::vector<LogicalMemoryView> logical_mapped_entries;
|
||||
|
||||
void Init()
|
||||
{
|
||||
@@ -178,10 +178,35 @@ void Init()
|
||||
|
||||
u32 flags = 0;
|
||||
if (wii)
|
||||
flags |= MV_WII_ONLY;
|
||||
flags |= PhysicalMemoryRegion::WII_ONLY;
|
||||
if (bFakeVMEM)
|
||||
flags |= MV_FAKE_VMEM;
|
||||
physical_base = MemoryMap_Setup(views, num_views, flags, &g_arena);
|
||||
flags |= PhysicalMemoryRegion::FAKE_VMEM;
|
||||
u32 mem_size = 0;
|
||||
for (PhysicalMemoryRegion& region : physical_regions)
|
||||
{
|
||||
if ((flags & region.flags) != region.flags)
|
||||
continue;
|
||||
region.shm_position = mem_size;
|
||||
mem_size += region.size;
|
||||
}
|
||||
g_arena.GrabSHMSegment(mem_size);
|
||||
physical_base = MemArena::FindMemoryBase();
|
||||
|
||||
for (PhysicalMemoryRegion& region : physical_regions)
|
||||
{
|
||||
if ((flags & region.flags) != region.flags)
|
||||
continue;
|
||||
|
||||
u8* base = physical_base + region.physical_address;
|
||||
*region.out_pointer = (u8*)g_arena.CreateView(region.shm_position, region.size, base);
|
||||
|
||||
if (!*region.out_pointer)
|
||||
{
|
||||
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
|
||||
exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
#ifndef _ARCH_32
|
||||
logical_base = physical_base + 0x200000000;
|
||||
#endif
|
||||
@@ -197,6 +222,47 @@ void Init()
|
||||
m_IsInitialized = true;
|
||||
}
|
||||
|
||||
void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table)
|
||||
{
|
||||
for (auto& entry : logical_mapped_entries)
|
||||
{
|
||||
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
|
||||
}
|
||||
logical_mapped_entries.clear();
|
||||
for (u32 i = 0; i < (1 << (32 - PowerPC::BAT_INDEX_SHIFT)); ++i)
|
||||
{
|
||||
if (dbat_table[i] & 1)
|
||||
{
|
||||
u32 logical_address = i << PowerPC::BAT_INDEX_SHIFT;
|
||||
// TODO: Merge adjacent mappings to make this faster.
|
||||
u32 logical_size = 1 << PowerPC::BAT_INDEX_SHIFT;
|
||||
u32 translated_address = dbat_table[i] & ~3;
|
||||
for (const auto& physical_region : physical_regions)
|
||||
{
|
||||
u32 mapping_address = physical_region.physical_address;
|
||||
u32 mapping_end = mapping_address + physical_region.size;
|
||||
u32 intersection_start = std::max(mapping_address, translated_address);
|
||||
u32 intersection_end = std::min(mapping_end, translated_address + logical_size);
|
||||
if (intersection_start < intersection_end)
|
||||
{
|
||||
// Found an overlapping region; map it.
|
||||
u32 position = physical_region.shm_position + intersection_start - mapping_address;
|
||||
u8* base = logical_base + logical_address + intersection_start - translated_address;
|
||||
u32 mapped_size = intersection_end - intersection_start;
|
||||
|
||||
void* mapped_pointer = g_arena.CreateView(position, mapped_size, base);
|
||||
if (!mapped_pointer)
|
||||
{
|
||||
PanicAlert("MemoryMap_Setup: Failed finding a memory base.");
|
||||
exit(0);
|
||||
}
|
||||
logical_mapped_entries.push_back({mapped_pointer, mapped_size});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void DoState(PointerWrap& p)
|
||||
{
|
||||
bool wii = SConfig::GetInstance().bWii;
|
||||
@@ -216,10 +282,21 @@ void Shutdown()
|
||||
m_IsInitialized = false;
|
||||
u32 flags = 0;
|
||||
if (SConfig::GetInstance().bWii)
|
||||
flags |= MV_WII_ONLY;
|
||||
flags |= PhysicalMemoryRegion::WII_ONLY;
|
||||
if (bFakeVMEM)
|
||||
flags |= MV_FAKE_VMEM;
|
||||
MemoryMap_Shutdown(views, num_views, flags, &g_arena);
|
||||
flags |= PhysicalMemoryRegion::FAKE_VMEM;
|
||||
for (PhysicalMemoryRegion& region : physical_regions)
|
||||
{
|
||||
if ((flags & region.flags) != region.flags)
|
||||
continue;
|
||||
g_arena.ReleaseView(*region.out_pointer, region.size);
|
||||
*region.out_pointer = 0;
|
||||
}
|
||||
for (auto& entry : logical_mapped_entries)
|
||||
{
|
||||
g_arena.ReleaseView(entry.mapped_pointer, entry.mapped_size);
|
||||
}
|
||||
logical_mapped_entries.clear();
|
||||
g_arena.ReleaseSHMSegment();
|
||||
physical_base = nullptr;
|
||||
logical_base = nullptr;
|
||||
@@ -233,7 +310,9 @@ void Clear()
|
||||
memset(m_pRAM, 0, RAM_SIZE);
|
||||
if (m_pL1Cache)
|
||||
memset(m_pL1Cache, 0, L1_CACHE_SIZE);
|
||||
if (SConfig::GetInstance().bWii && m_pEXRAM)
|
||||
if (m_pFakeVMEM)
|
||||
memset(m_pFakeVMEM, 0, FAKEVMEM_SIZE);
|
||||
if (m_pEXRAM)
|
||||
memset(m_pEXRAM, 0, EXRAM_SIZE);
|
||||
}
|
||||
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
|
||||
#include "Common/CommonFuncs.h"
|
||||
#include "Common/CommonTypes.h"
|
||||
#include "Core/PowerPC/PowerPC.h"
|
||||
|
||||
// Enable memory checks in the Debug/DebugFast builds, but NOT in release
|
||||
#if defined(_DEBUG) || defined(DEBUGFAST)
|
||||
@@ -57,13 +58,6 @@ enum
|
||||
IO_SIZE = 0x00010000,
|
||||
EXRAM_SIZE = 0x04000000,
|
||||
EXRAM_MASK = EXRAM_SIZE - 1,
|
||||
|
||||
ADDR_MASK_HW_ACCESS = 0x0c000000,
|
||||
ADDR_MASK_MEM1 = 0x20000000,
|
||||
|
||||
#if _ARCH_32
|
||||
MEMVIEW32_MASK = 0x3FFFFFFF,
|
||||
#endif
|
||||
};
|
||||
|
||||
// MMIO mapping object.
|
||||
@@ -75,6 +69,8 @@ void Init();
|
||||
void Shutdown();
|
||||
void DoState(PointerWrap& p);
|
||||
|
||||
void UpdateLogicalMemory(const PowerPC::BatTable& dbat_table);
|
||||
|
||||
void Clear();
|
||||
bool AreMemoryBreakpointsActivated();
|
||||
|
||||
|
||||
@@ -962,6 +962,11 @@ IPCCommandResult CWII_IPC_HLE_Device_es::IOCtlV(u32 _CommandAddress)
|
||||
if (pDolLoader->IsValid())
|
||||
{
|
||||
pDolLoader->Load(); // TODO: Check why sysmenu does not load the DOL correctly
|
||||
// WADs start with address translation off at the given entry point.
|
||||
//
|
||||
// The state of other CPU registers (like the BAT registers) doesn't matter much
|
||||
// because the WAD initializes everything itself anyway.
|
||||
MSR = 0;
|
||||
PC = pDolLoader->GetEntryPoint();
|
||||
bSuccess = true;
|
||||
}
|
||||
|
||||
@@ -297,6 +297,15 @@ void Interpreter::mtspr(UGeckoInstruction _inst)
|
||||
// TODO: emulate locked cache and DMA bits.
|
||||
break;
|
||||
|
||||
case SPR_HID4:
|
||||
if (oldValue != rSPR(iIndex))
|
||||
{
|
||||
WARN_LOG(POWERPC, "HID4 updated %x %x", oldValue, rSPR(iIndex));
|
||||
PowerPC::IBATUpdated();
|
||||
PowerPC::DBATUpdated();
|
||||
}
|
||||
break;
|
||||
|
||||
case SPR_WPAR:
|
||||
_assert_msg_(POWERPC, rGPR[_inst.RD] == 0x0C008000, "Gather pipe @ %08x", PC);
|
||||
GPFifo::ResetGatherPipe();
|
||||
@@ -354,6 +363,52 @@ void Interpreter::mtspr(UGeckoInstruction _inst)
|
||||
case SPR_XER:
|
||||
SetXER(rSPR(iIndex));
|
||||
break;
|
||||
|
||||
case SPR_DBAT0L:
|
||||
case SPR_DBAT0U:
|
||||
case SPR_DBAT1L:
|
||||
case SPR_DBAT1U:
|
||||
case SPR_DBAT2L:
|
||||
case SPR_DBAT2U:
|
||||
case SPR_DBAT3L:
|
||||
case SPR_DBAT3U:
|
||||
case SPR_DBAT4L:
|
||||
case SPR_DBAT4U:
|
||||
case SPR_DBAT5L:
|
||||
case SPR_DBAT5U:
|
||||
case SPR_DBAT6L:
|
||||
case SPR_DBAT6U:
|
||||
case SPR_DBAT7L:
|
||||
case SPR_DBAT7U:
|
||||
if (oldValue != rSPR(iIndex))
|
||||
{
|
||||
WARN_LOG(POWERPC, "DBAT updated %d %x %x", iIndex, oldValue, rSPR(iIndex));
|
||||
PowerPC::DBATUpdated();
|
||||
}
|
||||
break;
|
||||
|
||||
case SPR_IBAT0L:
|
||||
case SPR_IBAT0U:
|
||||
case SPR_IBAT1L:
|
||||
case SPR_IBAT1U:
|
||||
case SPR_IBAT2L:
|
||||
case SPR_IBAT2U:
|
||||
case SPR_IBAT3L:
|
||||
case SPR_IBAT3U:
|
||||
case SPR_IBAT4L:
|
||||
case SPR_IBAT4U:
|
||||
case SPR_IBAT5L:
|
||||
case SPR_IBAT5U:
|
||||
case SPR_IBAT6L:
|
||||
case SPR_IBAT6U:
|
||||
case SPR_IBAT7L:
|
||||
case SPR_IBAT7U:
|
||||
if (oldValue != rSPR(iIndex))
|
||||
{
|
||||
WARN_LOG(POWERPC, "IBAT updated %d %x %x", iIndex, oldValue, rSPR(iIndex));
|
||||
PowerPC::IBATUpdated();
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -365,38 +365,40 @@ void Jit64::dcbz(UGeckoInstruction inst)
|
||||
int a = inst.RA;
|
||||
int b = inst.RB;
|
||||
|
||||
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;
|
||||
|
||||
MOV(32, R(RSCRATCH), gpr.R(b));
|
||||
if (a)
|
||||
ADD(32, R(RSCRATCH), gpr.R(a));
|
||||
AND(32, R(RSCRATCH), Imm32(~31));
|
||||
TEST(32, R(RSCRATCH), Imm32(mem_mask));
|
||||
FixupBranch slow = J_CC(CC_NZ, true);
|
||||
|
||||
// Should this code ever run? I can't find any games that use DCBZ on non-physical addresses, but
|
||||
// supposedly there are, at least for some MMU titles. Let's be careful and support it to be sure.
|
||||
SwitchToFarCode();
|
||||
SetJumpTarget(slow);
|
||||
if (UReg_MSR(MSR).DR)
|
||||
{
|
||||
// Perform lookup to see if we can use fast path.
|
||||
MOV(32, R(RSCRATCH2), R(RSCRATCH));
|
||||
SHR(32, R(RSCRATCH2), Imm8(PowerPC::BAT_INDEX_SHIFT));
|
||||
TEST(32, MScaled(RSCRATCH2, SCALE_4, (u32)(u64)&PowerPC::dbat_table[0]), Imm32(2));
|
||||
FixupBranch slow = J_CC(CC_Z, true);
|
||||
|
||||
// Fast path: compute full address, then zero out 32 bytes of memory.
|
||||
PXOR(XMM0, R(XMM0));
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
|
||||
|
||||
// Slow path: call the general-case code.
|
||||
SwitchToFarCode();
|
||||
SetJumpTarget(slow);
|
||||
}
|
||||
MOV(32, M(&PC), Imm32(jit->js.compilerPC));
|
||||
BitSet32 registersInUse = CallerSavedRegistersInUse();
|
||||
ABI_PushRegistersAndAdjustStack(registersInUse, 0);
|
||||
ABI_CallFunctionR(PowerPC::ClearCacheLine, RSCRATCH);
|
||||
ABI_CallFunctionR(&PowerPC::ClearCacheLine, RSCRATCH);
|
||||
ABI_PopRegistersAndAdjustStack(registersInUse, 0);
|
||||
FixupBranch exit = J(true);
|
||||
SwitchToNearCode();
|
||||
|
||||
// Mask out the address so we don't write to MEM1 out of bounds
|
||||
// FIXME: Work out why the AGP disc writes out of bounds
|
||||
if (!SConfig::GetInstance().bWii)
|
||||
AND(32, R(RSCRATCH), Imm32(Memory::RAM_MASK));
|
||||
PXOR(XMM0, R(XMM0));
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 0), XMM0);
|
||||
MOVAPS(MComplex(RMEM, RSCRATCH, SCALE_1, 16), XMM0);
|
||||
SetJumpTarget(exit);
|
||||
if (UReg_MSR(MSR).DR)
|
||||
{
|
||||
FixupBranch end = J(true);
|
||||
SwitchToNearCode();
|
||||
SetJumpTarget(end);
|
||||
}
|
||||
}
|
||||
|
||||
void Jit64::stX(UGeckoInstruction inst)
|
||||
|
||||
@@ -25,6 +25,9 @@ void Jit64::psq_stXX(UGeckoInstruction inst)
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
|
||||
// For performance, the AsmCommon routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
s32 offset = inst.SIMM_12;
|
||||
bool indexed = inst.OPCD == 4;
|
||||
bool update = (inst.OPCD == 61 && offset) || (inst.OPCD == 4 && !!(inst.SUBOP6 & 32));
|
||||
@@ -112,6 +115,9 @@ void Jit64::psq_lXX(UGeckoInstruction inst)
|
||||
INSTRUCTION_START
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
|
||||
// For performance, the AsmCommon routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
s32 offset = inst.SIMM_12;
|
||||
bool indexed = inst.OPCD == 4;
|
||||
bool update = (inst.OPCD == 57 && offset) || (inst.OPCD == 4 && !!(inst.SUBOP6 & 32));
|
||||
|
||||
@@ -401,7 +401,8 @@ void QuantizedMemoryRoutines::GenQuantizedStore(bool single, EQuantizeType type,
|
||||
}
|
||||
}
|
||||
|
||||
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
|
||||
int flags =
|
||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
||||
if (!single)
|
||||
flags |= SAFE_LOADSTORE_NO_SWAP;
|
||||
|
||||
@@ -459,7 +460,8 @@ void QuantizedMemoryRoutines::GenQuantizedLoad(bool single, EQuantizeType type,
|
||||
if (jit->jo.memcheck)
|
||||
{
|
||||
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE_LOAD;
|
||||
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
|
||||
int flags =
|
||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
||||
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
||||
if (!single && (type == QUANTIZE_U8 || type == QUANTIZE_S8))
|
||||
{
|
||||
@@ -582,7 +584,8 @@ void QuantizedMemoryRoutines::GenQuantizedLoadFloat(bool single, bool isInline)
|
||||
if (jit->jo.memcheck)
|
||||
{
|
||||
BitSet32 regsToSave = QUANTIZED_REGS_TO_SAVE;
|
||||
int flags = isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG;
|
||||
int flags =
|
||||
isInline ? 0 : SAFE_LOADSTORE_NO_FASTMEM | SAFE_LOADSTORE_NO_PROLOG | SAFE_LOADSTORE_DR_ON;
|
||||
SafeLoadToReg(RSCRATCH_EXTRA, R(RSCRATCH_EXTRA), size, 0, regsToSave, extend, flags);
|
||||
}
|
||||
|
||||
|
||||
@@ -23,6 +23,9 @@ void JitArm64::psq_l(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
FALLBACK_IF(jo.memcheck || !jo.fastmem);
|
||||
|
||||
// The asm routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
// X30 is LR
|
||||
// X0 contains the scale
|
||||
// X1 is the address
|
||||
@@ -103,6 +106,9 @@ void JitArm64::psq_st(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
FALLBACK_IF(jo.memcheck || !jo.fastmem);
|
||||
|
||||
// The asm routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
// X30 is LR
|
||||
// X0 contains the scale
|
||||
// X1 is the address
|
||||
@@ -179,6 +185,7 @@ void JitArm64::psq_st(UGeckoInstruction inst)
|
||||
UBFM(scale_reg, scale_reg, 8, 13); // Scale
|
||||
|
||||
// Inline address check
|
||||
// FIXME: This doesn't correctly account for the BAT configuration.
|
||||
TST(addr_reg, 6, 1);
|
||||
FixupBranch pass = B(CC_EQ);
|
||||
FixupBranch fail = B();
|
||||
|
||||
@@ -212,7 +212,7 @@ void EmuCodeBlock::MMIOLoadToReg(MMIO::Mapping* mmio, Gen::X64Reg reg_value,
|
||||
}
|
||||
|
||||
FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_addr,
|
||||
BitSet32 registers_in_use, u32 mem_mask)
|
||||
BitSet32 registers_in_use)
|
||||
{
|
||||
registers_in_use[reg_addr] = true;
|
||||
if (reg_value.IsSimpleReg())
|
||||
@@ -227,29 +227,19 @@ FixupBranch EmuCodeBlock::CheckIfSafeAddress(const OpArg& reg_value, X64Reg reg_
|
||||
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->jo.memcheck && !SConfig::GetInstance().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());
|
||||
}
|
||||
if (scratch == reg_addr)
|
||||
PUSH(scratch);
|
||||
else
|
||||
{
|
||||
TEST(32, R(reg_addr), Imm32(mem_mask));
|
||||
return J_CC(CC_NZ, farcode.Enabled());
|
||||
}
|
||||
MOV(32, R(scratch), R(reg_addr));
|
||||
|
||||
// Perform lookup to see if we can use fast path.
|
||||
SHR(32, R(scratch), Imm8(PowerPC::BAT_INDEX_SHIFT));
|
||||
TEST(32, MScaled(scratch, SCALE_4, (u32)(u64)&PowerPC::dbat_table[0]), Imm32(2));
|
||||
|
||||
if (scratch == reg_addr)
|
||||
POP(scratch);
|
||||
|
||||
return J_CC(CC_Z, farcode.Enabled());
|
||||
}
|
||||
|
||||
void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize,
|
||||
@@ -305,14 +295,11 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
|
||||
}
|
||||
|
||||
FixupBranch exit;
|
||||
if (!slowmem)
|
||||
bool dr_set = (flags & SAFE_LOADSTORE_DR_ON) || UReg_MSR(MSR).DR;
|
||||
bool fast_check_address = !slowmem && dr_set;
|
||||
if (fast_check_address)
|
||||
{
|
||||
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;
|
||||
|
||||
FixupBranch slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse, mem_mask);
|
||||
FixupBranch slow = CheckIfSafeAddress(R(reg_value), reg_addr, registersInUse);
|
||||
UnsafeLoadToReg(reg_value, R(reg_addr), accessSize, 0, signExtend);
|
||||
if (farcode.Enabled())
|
||||
SwitchToFarCode();
|
||||
@@ -350,7 +337,7 @@ void EmuCodeBlock::SafeLoadToReg(X64Reg reg_value, const Gen::OpArg& opAddress,
|
||||
MOVZX(64, accessSize, reg_value, R(ABI_RETURN));
|
||||
}
|
||||
|
||||
if (!slowmem)
|
||||
if (fast_check_address)
|
||||
{
|
||||
if (farcode.Enabled())
|
||||
{
|
||||
@@ -575,15 +562,12 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
|
||||
}
|
||||
}
|
||||
|
||||
FixupBranch slow, exit;
|
||||
if (!slowmem)
|
||||
FixupBranch exit;
|
||||
bool dr_set = (flags & SAFE_LOADSTORE_DR_ON) || UReg_MSR(MSR).DR;
|
||||
bool fast_check_address = !slowmem && dr_set;
|
||||
if (fast_check_address)
|
||||
{
|
||||
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;
|
||||
|
||||
slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse, mem_mask);
|
||||
FixupBranch slow = CheckIfSafeAddress(reg_value, reg_addr, registersInUse);
|
||||
UnsafeWriteRegToReg(reg_value, reg_addr, accessSize, 0, swap);
|
||||
if (farcode.Enabled())
|
||||
SwitchToFarCode();
|
||||
@@ -629,7 +613,7 @@ void EmuCodeBlock::SafeWriteRegToReg(OpArg reg_value, X64Reg reg_addr, int acces
|
||||
|
||||
MemoryExceptionCheck();
|
||||
|
||||
if (!slowmem)
|
||||
if (fast_check_address)
|
||||
{
|
||||
if (farcode.Enabled())
|
||||
{
|
||||
|
||||
@@ -123,7 +123,7 @@ public:
|
||||
}
|
||||
|
||||
Gen::FixupBranch CheckIfSafeAddress(const Gen::OpArg& reg_value, Gen::X64Reg reg_addr,
|
||||
BitSet32 registers_in_use, u32 mem_mask);
|
||||
BitSet32 registers_in_use);
|
||||
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,
|
||||
@@ -154,6 +154,7 @@ public:
|
||||
SAFE_LOADSTORE_CLOBBER_RSCRATCH_INSTEAD_OF_ADDR = 8,
|
||||
// Force slowmem (used when generating fallbacks in trampolines)
|
||||
SAFE_LOADSTORE_FORCE_SLOWMEM = 16,
|
||||
SAFE_LOADSTORE_DR_ON = 32,
|
||||
};
|
||||
|
||||
void SafeLoadToReg(Gen::X64Reg reg_value, const Gen::OpArg& opAddress, int accessSize, s32 offset,
|
||||
|
||||
@@ -12,6 +12,9 @@ void JitILBase::psq_st(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
FALLBACK_IF(jo.memcheck || inst.W);
|
||||
|
||||
// For performance, the AsmCommon routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
|
||||
IREmitter::InstLoc val;
|
||||
|
||||
@@ -32,6 +35,9 @@ void JitILBase::psq_l(UGeckoInstruction inst)
|
||||
JITDISABLE(bJITLoadStorePairedOff);
|
||||
FALLBACK_IF(jo.memcheck || inst.W);
|
||||
|
||||
// For performance, the AsmCommon routines assume address translation is on.
|
||||
FALLBACK_IF(!UReg_MSR(MSR).DR);
|
||||
|
||||
IREmitter::InstLoc addr = ibuild.EmitIntConst(inst.SIMM_12);
|
||||
IREmitter::InstLoc val;
|
||||
|
||||
|
||||
+401
-353
File diff suppressed because it is too large
Load Diff
@@ -84,9 +84,6 @@ void PPCSymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const std::string& nam
|
||||
|
||||
Symbol* PPCSymbolDB::GetSymbolFromAddr(u32 addr)
|
||||
{
|
||||
if (!PowerPC::HostIsRAMAddress(addr))
|
||||
return nullptr;
|
||||
|
||||
XFuncMap::iterator it = functions.find(addr);
|
||||
if (it != functions.end())
|
||||
{
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <cstddef>
|
||||
#include <tuple>
|
||||
|
||||
@@ -265,6 +266,8 @@ void ClearCacheLine(const u32 address); // Zeroes 32 bytes; address should be 3
|
||||
// TLB functions
|
||||
void SDRUpdated();
|
||||
void InvalidateTLBEntry(u32 address);
|
||||
void DBATUpdated();
|
||||
void IBATUpdated();
|
||||
|
||||
// Result changes based on the BAT registers and MSR.DR. Returns whether
|
||||
// it's safe to optimize a read or write to this address to an unguarded
|
||||
@@ -280,6 +283,19 @@ struct TranslateResult
|
||||
u32 address;
|
||||
};
|
||||
TranslateResult JitCache_TranslateAddress(u32 address);
|
||||
|
||||
static const int BAT_INDEX_SHIFT = 17;
|
||||
using BatTable = std::array<u32, 1 << (32 - BAT_INDEX_SHIFT)>; // 128 KB
|
||||
extern BatTable ibat_table;
|
||||
extern BatTable dbat_table;
|
||||
inline bool TranslateBatAddess(const BatTable& bat_table, u32* address)
|
||||
{
|
||||
u32 bat_result = bat_table[*address >> BAT_INDEX_SHIFT];
|
||||
if ((bat_result & 1) == 0)
|
||||
return false;
|
||||
*address = (bat_result & ~3) | (*address & 0x0001FFFF);
|
||||
return true;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
enum CRBits
|
||||
|
||||
Reference in New Issue
Block a user