From 11296d32b1eaaf7d6858070f7588536d05b0244c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Henrik=20Rydg=C3=A5rd?= Date: Tue, 20 Oct 2026 14:26:28 +0200 Subject: [PATCH] CPU: Give each MIPSState an address space for its memory accesses Memory::AddressSpace is where a CPU's addresses point (base) and its block shadow, with the unchecked accessors and block shadow helpers as methods. The main CPU's is Memory::g_mainSpace, kept in step with Memory::base and blockShadow, and MIPSState::space points at it. The CPU cores now go through their state's space instead of the globals: the interpreter's and IR interpreter's loads and stores (which also saves the IR interpreter reloading the global base), the IR interpreter's block lookup, the block table's shadow entries, and the native JITs' memory base register, block shadow lookups and pointerified registers. Address validity and everything outside the cores still use the globals. No change for the main CPU; this is so another CPU (the Media Engine) can see memory differently. Benchmarked against master (God of War: Chains of Olympus, Tekken 6): JIT instructions retired within noise, IR interpreter 2% fewer. Co-Authored-By: Claude Opus 5.5 (1M context) --- Core/MIPS/ARM/ArmIRAsm.cpp | 4 +- Core/MIPS/ARM64/Arm64IRAsm.cpp | 5 +- Core/MIPS/ARM64/Arm64IRCompBranch.cpp | 2 +- Core/MIPS/ARM64/Arm64IRRegCache.cpp | 4 +- Core/MIPS/IR/IRInterpreter.cpp | 53 ++++++++++--------- Core/MIPS/IR/IRJit.cpp | 17 +++--- Core/MIPS/IR/IRJit.h | 14 ++++- Core/MIPS/IR/IRRegCache.h | 5 ++ Core/MIPS/Interpreter.cpp | 36 ++++++------- Core/MIPS/InterpreterVFPU.cpp | 32 +++++------ Core/MIPS/LoongArch64/LoongArch64Asm.cpp | 4 +- .../LoongArch64/LoongArch64CompBranch.cpp | 2 +- Core/MIPS/MIPS.cpp | 3 ++ Core/MIPS/MIPS.h | 6 +++ Core/MIPS/RiscV/RiscVAsm.cpp | 4 +- Core/MIPS/RiscV/RiscVCompBranch.cpp | 2 +- Core/MIPS/x86/X64IRAsm.cpp | 11 ++-- Core/MIPS/x86/X64IRCompLoadStore.cpp | 6 +-- Core/MIPS/x86/X64IRCompSystem.cpp | 2 +- Core/MIPS/x86/X64IRRegCache.cpp | 4 +- Core/MemMap.cpp | 20 ++++++- Core/MemMap.h | 37 +++++++++++++ 22 files changed, 177 insertions(+), 96 deletions(-) diff --git a/Core/MIPS/ARM/ArmIRAsm.cpp b/Core/MIPS/ARM/ArmIRAsm.cpp index 696a87fbfc..7023e5b6df 100644 --- a/Core/MIPS/ARM/ArmIRAsm.cpp +++ b/Core/MIPS/ARM/ArmIRAsm.cpp @@ -111,7 +111,7 @@ void ArmJitBackend::GenerateFixedCode(MIPSState *mipsState) { VPUSH(D8, 8); // Fixed registers, these are always kept when in Jit context. - MOVP2R(MEMBASEREG, Memory::base); + MOVP2R(MEMBASEREG, mipsState->space->base); MOVP2R(CTXREG, mipsState); LoadStaticRegisters(); @@ -157,7 +157,7 @@ void ArmJitBackend::GenerateFixedCode(MIPSState *mipsState) { static_assert(Memory::BLOCK_SHADOW_ENTRY_MASK == 0x0FFFFFFC, "BICs below assume the 32-bit shadow mask"); BIC(SCRATCH1, SCRATCH1, Operand2(0xF0, 4)); // &= 0x0FFFFFFF. Rotation is to the right, in 2-bit increments. BIC(SCRATCH1, SCRATCH1, Operand2(3)); - MOVP2R(SCRATCH2, Memory::blockShadow); + MOVP2R(SCRATCH2, mipsState->space->blockShadow); hooks_.dispatchFetch = GetCodePtr(); LDR(SCRATCH1, SCRATCH2, SCRATCH1); // The entry is the block's offset into the code space, or 0 for none. diff --git a/Core/MIPS/ARM64/Arm64IRAsm.cpp b/Core/MIPS/ARM64/Arm64IRAsm.cpp index 0e371d9e2b..fb1311af2b 100644 --- a/Core/MIPS/ARM64/Arm64IRAsm.cpp +++ b/Core/MIPS/ARM64/Arm64IRAsm.cpp @@ -51,6 +51,7 @@ static void ShowPC(void *membase, void *jitbase) { } void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) { + regs_.SetMemoryBase(mipsState->space->base); // This will be used as a writable scratch area, always 32-bit accessible. const u8 *start = AlignCodePage(); if (DebugProfilerEnabled()) { @@ -155,7 +156,7 @@ void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) { fp_.ABI_PushRegisters(regs_to_save, regs_to_save_fp); // Fixed registers, these are always kept when in Jit context. - MOVP2R(MEMBASEREG, Memory::base); + MOVP2R(MEMBASEREG, mipsState->space->base); MOVP2R(CTXREG, mipsState); MOVP2R(JITBASEREG, GetBasePtr()); @@ -205,7 +206,7 @@ void Arm64JitBackend::GenerateFixedCode(MIPSState *mipsState) { MovFromPC(SCRATCH1); WriteDebugPC(SCRATCH1); ANDI2R(SCRATCH1, SCRATCH1, Memory::BLOCK_SHADOW_ENTRY_MASK); - MOVP2R(SCRATCH2_64, Memory::blockShadow); + MOVP2R(SCRATCH2_64, mipsState->space->blockShadow); hooks_.dispatchFetch = GetCodePtr(); LDR(SCRATCH1, SCRATCH2_64, SCRATCH1_64); // The entry is the block's offset into the code space, or 0 for none. diff --git a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp index 223bb4e61a..60d0de1a41 100644 --- a/Core/MIPS/ARM64/Arm64IRCompBranch.cpp +++ b/Core/MIPS/ARM64/Arm64IRCompBranch.cpp @@ -60,7 +60,7 @@ void Arm64JitBackend::CompIR_Exit(IRInst inst) { B(hooks_.dispatcher); SetJumpTarget(inTime); ANDI2R(SCRATCH1, SCRATCH1, Memory::BLOCK_SHADOW_ENTRY_MASK); - MOVP2R(SCRATCH2_64, Memory::blockShadow); + MOVP2R(SCRATCH2_64, mips_->space->blockShadow); LDR(SCRATCH1, SCRATCH2_64, SCRATCH1_64); FixupBranch found = CBNZ(SCRATCH1); B(dispatcherNoCheck_); diff --git a/Core/MIPS/ARM64/Arm64IRRegCache.cpp b/Core/MIPS/ARM64/Arm64IRRegCache.cpp index b6cfb94c60..c09f7f0ce2 100644 --- a/Core/MIPS/ARM64/Arm64IRRegCache.cpp +++ b/Core/MIPS/ARM64/Arm64IRRegCache.cpp @@ -123,7 +123,7 @@ void Arm64IRRegCache::EmitLoadStaticRegisters() { emit_->LDR(INDEX_UNSIGNED, FromNativeReg(allocs[i].nr), CTXREG, offset); if (allocs[i].pointerified && jo_->enablePointerify) { ARM64Reg r64 = FromNativeReg64(allocs[i].nr); - uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32); + uint32_t membaseHigh = (uint32_t)((uint64_t)membase_ >> 32); emit_->MOVK(r64, membaseHigh & 0xFFFF, SHIFT_32); if (membaseHigh & 0xFFFF0000) emit_->MOVK(r64, membaseHigh >> 16, SHIFT_48); @@ -347,7 +347,7 @@ void Arm64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { #endif emit_->ADD(r, r, MEMBASEREG); } else { - uint32_t membaseHigh = (uint32_t)((uint64_t)Memory::base >> 32); + uint32_t membaseHigh = (uint32_t)((uint64_t)membase_ >> 32); emit_->MOVK(r, membaseHigh & 0xFFFF, SHIFT_32); if (membaseHigh & 0xFFFF0000) emit_->MOVK(r, membaseHigh >> 16, SHIFT_48); diff --git a/Core/MIPS/IR/IRInterpreter.cpp b/Core/MIPS/IR/IRInterpreter.cpp index ec38896f3d..29fa94d86d 100644 --- a/Core/MIPS/IR/IRInterpreter.cpp +++ b/Core/MIPS/IR/IRInterpreter.cpp @@ -86,9 +86,9 @@ static u32 RunKernelLoad(MIPSState *mips, u32 addr) { u32 value; if (IsValidAlignedAddress(addr)) { switch (size) { - case 1: value = Memory::ReadUnchecked_U8(addr); break; - case 2: value = Memory::ReadUnchecked_U16(addr); break; - default: value = Memory::ReadUnchecked_U32(addr); break; + case 1: value = mips->space->ReadUnchecked_U8(addr); break; + case 2: value = mips->space->ReadUnchecked_U16(addr); break; + default: value = mips->space->ReadUnchecked_U32(addr); break; } } else if (Memory::IsMMIOAccess(addr)) { value = MMIO::Read(mips, addr, size); @@ -105,9 +105,9 @@ template static void RunKernelStore(MIPSState *mips, u32 addr, u32 value) { if (IsValidAlignedAddress(addr)) { switch (size) { - case 1: Memory::WriteUnchecked_U8((u8)value, addr); break; - case 2: Memory::WriteUnchecked_U16((u16)value, addr); break; - default: Memory::WriteUnchecked_U32(value, addr); break; + case 1: mips->space->WriteUnchecked_U8((u8)value, addr); break; + case 2: mips->space->WriteUnchecked_U16((u16)value, addr); break; + default: mips->space->WriteUnchecked_U32(value, addr); break; } } else if (Memory::IsMMIOAccess(addr)) { MMIO::Write(mips, addr, size, value); @@ -159,6 +159,7 @@ static void RunKernelStore(MIPSState *mips, u32 addr, u32 value) { } while (false) u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { + const Memory::AddressSpace &space = *mips->space; #if IR_THREADED_DISPATCH static const void *dispatch[256]; static bool dispatchReady = false; @@ -423,25 +424,25 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { IR_NEXT; IR_CASE(Load8) - mips->r[inst->dest] = Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = space.ReadUnchecked_U8(mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Load8Ext) - mips->r[inst->dest] = SignExtend8ToU32(Memory::ReadUnchecked_U8(mips->r[inst->src1] + inst->constant)); + mips->r[inst->dest] = SignExtend8ToU32(space.ReadUnchecked_U8(mips->r[inst->src1] + inst->constant)); IR_NEXT; IR_CASE(Load16) - mips->r[inst->dest] = Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = space.ReadUnchecked_U16(mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Load16Ext) - mips->r[inst->dest] = SignExtend16ToU32(Memory::ReadUnchecked_U16(mips->r[inst->src1] + inst->constant)); + mips->r[inst->dest] = SignExtend16ToU32(space.ReadUnchecked_U16(mips->r[inst->src1] + inst->constant)); IR_NEXT; IR_CASE(Load32) - mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = space.ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Load32Left) { u32 addr = mips->r[inst->src1] + inst->constant; u32 shift = (addr & 3) * 8; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc); u32 destMask = 0x00ffffff >> shift; mips->r[inst->dest] = (mips->r[inst->dest] & destMask) | (mem << (24 - shift)); IR_NEXT; @@ -450,52 +451,52 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { { u32 addr = mips->r[inst->src1] + inst->constant; u32 shift = (addr & 3) * 8; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc); u32 destMask = 0xffffff00 << (24 - shift); mips->r[inst->dest] = (mips->r[inst->dest] & destMask) | (mem >> shift); IR_NEXT; } IR_CASE(Load32Linked) if (inst->dest != MIPS_REG_ZERO) - mips->r[inst->dest] = Memory::ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); + mips->r[inst->dest] = space.ReadUnchecked_U32(mips->r[inst->src1] + inst->constant); mips->llBit = 1; IR_NEXT; IR_CASE(LoadFloat) - mips->f[inst->dest] = Memory::ReadUnchecked_Float(mips->r[inst->src1] + inst->constant); + mips->f[inst->dest] = space.ReadUnchecked_Float(mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Store8) - Memory::WriteUnchecked_U8(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); + space.WriteUnchecked_U8(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Store16) - Memory::WriteUnchecked_U16(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); + space.WriteUnchecked_U16(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Store32) - Memory::WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); + space.WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(Store32Left) { u32 addr = mips->r[inst->src1] + inst->constant; u32 shift = (addr & 3) * 8; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc); u32 memMask = 0xffffff00 << shift; u32 result = (mips->r[inst->src3] >> (24 - shift)) | (mem & memMask); - Memory::WriteUnchecked_U32(result, addr & 0xfffffffc); + space.WriteUnchecked_U32(result, addr & 0xfffffffc); IR_NEXT; } IR_CASE(Store32Right) { u32 addr = mips->r[inst->src1] + inst->constant; u32 shift = (addr & 3) * 8; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = space.ReadUnchecked_U32(addr & 0xfffffffc); u32 memMask = 0x00ffffff >> (24 - shift); u32 result = (mips->r[inst->src3] << shift) | (mem & memMask); - Memory::WriteUnchecked_U32(result, addr & 0xfffffffc); + space.WriteUnchecked_U32(result, addr & 0xfffffffc); IR_NEXT; } IR_CASE(Store32Conditional) if (mips->llBit) { - Memory::WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); + space.WriteUnchecked_U32(mips->r[inst->src3], mips->r[inst->src1] + inst->constant); if (inst->dest != MIPS_REG_ZERO) { mips->r[inst->dest] = 1; } @@ -504,20 +505,20 @@ u32 IRInterpret(MIPSState *mips, const IRInst *inst, const IRInst *chainArena) { } IR_NEXT; IR_CASE(StoreFloat) - Memory::WriteUnchecked_Float(mips->f[inst->src3], mips->r[inst->src1] + inst->constant); + space.WriteUnchecked_Float(mips->f[inst->src3], mips->r[inst->src1] + inst->constant); IR_NEXT; IR_CASE(LoadVec4) { u32 base = mips->r[inst->src1] + inst->constant; // This compiles to a nice SSE load/store on x86, and hopefully similar on ARM. - memcpy(&mips->f[inst->dest], Memory::GetPointerUnchecked(base), 4 * 4); + memcpy(&mips->f[inst->dest], space.GetPointerUnchecked(base), 4 * 4); IR_NEXT; } IR_CASE(StoreVec4) { u32 base = mips->r[inst->src1] + inst->constant; - memcpy((float *)Memory::GetPointerUnchecked(base), &mips->f[inst->dest], 4 * 4); + memcpy((float *)space.GetPointerUnchecked(base), &mips->f[inst->dest], 4 * 4); IR_NEXT; } diff --git a/Core/MIPS/IR/IRJit.cpp b/Core/MIPS/IR/IRJit.cpp index cd424d2249..1b9f398189 100644 --- a/Core/MIPS/IR/IRJit.cpp +++ b/Core/MIPS/IR/IRJit.cpp @@ -90,6 +90,7 @@ IRJit::IRJit(MIPSState *mipsState, bool compileToNative) : frontend_(mipsState-> frontend_.SetOptions(opts); profiling_ = !g_Config.sJitProfileFile.empty(); + blocks_.SetAddressSpace(mipsState->space); } IRJit::~IRJit() { @@ -190,7 +191,7 @@ void IRJit::InvalidateBlock(int block_num) { // Before Destroy, which forgets the block's address. Blocks linked to it jump straight in. InvalidateTargetBlock(block_num); blocks_.RemoveBlockFromPageLookup(block_num); - block->Destroy(); + block->Destroy(blocks_.Space()); } void IRJit::InvalidateCacheExcept(const std::vector> &keep) { @@ -358,7 +359,7 @@ void IRInterpreterJit::RunBlocks() { while (mips->downcount >= 0) { // The block's offset into the IR arena, or 0 for none, or a replacement hook (handled by // compiling a block). - const u32 offset = *Memory::GetBlockShadowEntry(mips->pc); + const u32 offset = *mips->space->GetBlockShadowEntry(mips->pc); static_assert(Memory::BLOCK_SHADOW_HOOK_NO_JIT == 1, "IR arena reserves offsets 0 and 1"); if (offset > Memory::BLOCK_SHADOW_HOOK_NO_JIT) { const IRInst *instPtr = arena_.data() + offset; @@ -407,7 +408,7 @@ void IRInterpreterJit::RunBlocks() { void JitBlockTable::Clear() { for (JitBlock &block : blocks_) { - block.Destroy(); + block.Destroy(*space_); } blocks_.clear(); byPage_.clear(); @@ -473,7 +474,7 @@ std::vector JitBlockTable::FindInvalidatedBlockNumbers(u32 address, u32 len void JitBlockTable::FinalizeBlock(int blockIndex) { // TODO: What's different about preload blocks? JitBlock &block = blocks_[blockIndex]; - block.Finalize(); + block.Finalize(*space_); u32 startAddr, size; block.GetRange(&startAddr, &size); @@ -551,24 +552,24 @@ int JitBlockTable::GetBlockNumberFromStartAddress(u32 em_address) const { return best; } -void JitBlock::Finalize() { +void JitBlock::Finalize(const Memory::AddressSpace &space) { // Check it wasn't invalidated, in case this is after preload. // TODO: Allow reusing blocks when the code matches hash_ again, instead. if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) { origFirstOpcode_ = Memory::ReadUnchecked_Instruction(origAddr_); _dbg_assert_(entry_ != 0); - Memory::WriteBlockShadow(origAddr_, entry_); + space.WriteBlockShadow(origAddr_, entry_); } else { WARN_LOG(Log::JIT, "Finalizing invalid block (entry: %d)", entry_); } } -void JitBlock::Destroy() { +void JitBlock::Destroy(const Memory::AddressSpace &space) { if (origAddr_ && Memory::IsValid4AlignedAddress(origAddr_)) { // A block at a mirror may have taken over the entry, leave that alone. One that never got an // entry (the arena or code space filled while compiling it) has nothing in the shadow. if (entry_ != 0) - Memory::ClearBlockShadow(origAddr_, entry_); + space.ClearBlockShadow(origAddr_, entry_); // Let's mark this invalid so we don't try to clear it again. origAddr_ = 0; } diff --git a/Core/MIPS/IR/IRJit.h b/Core/MIPS/IR/IRJit.h index ecfff94791..5fdb92be3d 100644 --- a/Core/MIPS/IR/IRJit.h +++ b/Core/MIPS/IR/IRJit.h @@ -80,8 +80,9 @@ public: return hash_; } - void Finalize(); - void Destroy(); + // Enter the block in the space's block shadow, and take it out. + void Finalize(const Memory::AddressSpace &space); + void Destroy(const Memory::AddressSpace &space); // Only filled in by the IR interpreter, when profiling. JitBlockProfileStats profileStats_{}; @@ -105,6 +106,14 @@ public: Clear(); } + // Whose block shadow the blocks go in. + void SetAddressSpace(const Memory::AddressSpace *space) { + space_ = space; + } + const Memory::AddressSpace &Space() const { + return *space_; + } + void Clear(); std::vector FindInvalidatedBlockNumbers(u32 address, u32 length); void FinalizeBlock(int blockNum); @@ -142,6 +151,7 @@ private: u32 AddressToPage(u32 addr) const; std::vector blocks_; std::unordered_map> byPage_; + const Memory::AddressSpace *space_ = nullptr; }; class IRJit; diff --git a/Core/MIPS/IR/IRRegCache.h b/Core/MIPS/IR/IRRegCache.h index c45137ea72..613e5d6947 100644 --- a/Core/MIPS/IR/IRRegCache.h +++ b/Core/MIPS/IR/IRRegCache.h @@ -163,6 +163,10 @@ public: virtual ~IRNativeRegCacheBase() {} virtual void Start(u32 startPC, const IRInst *instructions, int count); + // The guest memory base of the CPU the code is for, where pointerified registers point. + void SetMemoryBase(const u8 *base) { + membase_ = base; + } void SetIRIndex(int index) { irIndex_ = index; } @@ -259,6 +263,7 @@ protected: bool IsValidFPR(IRReg r) const; MIPSComp::JitOptions *jo_; + const u8 *membase_ = nullptr; // The block being compiled, and its IR. u32 irStartPC_ = 0; const IRInst *irInsts_ = nullptr; diff --git a/Core/MIPS/Interpreter.cpp b/Core/MIPS/Interpreter.cpp index 657f1095dd..41f7eec741 100644 --- a/Core/MIPS/Interpreter.cpp +++ b/Core/MIPS/Interpreter.cpp @@ -379,7 +379,7 @@ namespace MIPSInt { R(rt) = 0; } } else { - R(rt) = Memory::ReadUnchecked_U32(addr); + R(rt) = mips->space->ReadUnchecked_U32(addr); } } mips->llBit = 1; @@ -392,7 +392,7 @@ namespace MIPSInt { else Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sc"); } else { - Memory::WriteUnchecked_U32(R(rt), addr); + mips->space->WriteUnchecked_U32(R(rt), addr); } // Report success even if the store got dropped - reporting failure just makes // the usual retry loop spin on the same bad address forever. @@ -472,7 +472,7 @@ namespace MIPSInt { R(rt) = 0; break; } - R(rt) = SignExtend8ToU32(Memory::ReadUnchecked_U8(addr)); + R(rt) = SignExtend8ToU32(mips->space->ReadUnchecked_U8(addr)); break; //lb case 33: if (!Memory::IsValid2AlignedAddress(addr)) { @@ -485,7 +485,7 @@ namespace MIPSInt { R(rt) = 0; break; } - R(rt) = SignExtend16ToU32(Memory::ReadUnchecked_U16(addr)); + R(rt) = SignExtend16ToU32(mips->space->ReadUnchecked_U16(addr)); break; //lh case 35: if (!Memory::IsValid4AlignedAddress(addr)) { @@ -498,7 +498,7 @@ namespace MIPSInt { R(rt) = 0; break; } - R(rt) = Memory::ReadUnchecked_U32(addr); + R(rt) = mips->space->ReadUnchecked_U32(addr); break; //lw case 36: if (!Memory::IsValidAddress(addr)) { @@ -511,7 +511,7 @@ namespace MIPSInt { R(rt) = 0; break; } - R(rt) = Memory::ReadUnchecked_U8(addr); + R(rt) = mips->space->ReadUnchecked_U8(addr); break; //lbu case 37: if (!Memory::IsValid2AlignedAddress(addr)) { @@ -524,7 +524,7 @@ namespace MIPSInt { R(rt) = 0; break; } - R(rt) = Memory::ReadUnchecked_U16(addr); + R(rt) = mips->space->ReadUnchecked_U16(addr); break; //lhu case 40: if (!Memory::IsValidAddress(addr)) { @@ -536,7 +536,7 @@ namespace MIPSInt { Core_MemoryException(addr, 1, PC, MemoryExceptionType::WRITE_WORD, "sb"); break; } - Memory::WriteUnchecked_U8(R(rt), addr); + mips->space->WriteUnchecked_U8(R(rt), addr); break; //sb case 41: if (!Memory::IsValid2AlignedAddress(addr)) { @@ -548,7 +548,7 @@ namespace MIPSInt { Core_MemoryException(addr, 2, PC, MemoryExceptionType::WRITE_WORD, "sh"); break; } - Memory::WriteUnchecked_U16(R(rt), addr); + mips->space->WriteUnchecked_U16(R(rt), addr); break; //sh case 43: if (!Memory::IsValid4AlignedAddress(addr)) { @@ -560,7 +560,7 @@ namespace MIPSInt { Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sw"); break; } - Memory::WriteUnchecked_U32(R(rt), addr); + mips->space->WriteUnchecked_U32(R(rt), addr); break; //sw // When there's an LWL and an LWR together, we should be able to peephole optimize that @@ -570,7 +570,7 @@ namespace MIPSInt { // Not checking for alignment here - the actual read will be aligned. u32 mem = 0; if (Memory::IsValidAddress(addr)) { - mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc); } else if (Memory::IsMMIOAccess(addr)) { mem = ReadMMIO(mips, addr & 0xfffffffc, 4); } else { @@ -587,7 +587,7 @@ namespace MIPSInt { // Not checking for alignment here - the actual read will be aligned. u32 mem = 0; if (Memory::IsValidAddress(addr)) { - mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc); } else if (Memory::IsMMIOAccess(addr)) { mem = ReadMMIO(mips, addr & 0xfffffffc, 4); } else { @@ -608,9 +608,9 @@ namespace MIPSInt { break; } u32 shift = (addr & 3) * 8; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc); u32 result = ( ( u32(R(rt)) >> (24 - shift) ) ) | ( mem & (0xffffff00 << shift) ); - Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc)); + mips->space->WriteUnchecked_U32(result, (addr & 0xfffffffc)); } break; @@ -622,9 +622,9 @@ namespace MIPSInt { break; } u32 shift = (addr & 3) << 3; - u32 mem = Memory::ReadUnchecked_U32(addr & 0xfffffffc); + u32 mem = mips->space->ReadUnchecked_U32(addr & 0xfffffffc); u32 result = ( ( u32(R(rt)) << shift ) | (mem & (0x00ffffff >> (24 - shift)) ) ); - Memory::WriteUnchecked_U32(result, (addr & 0xfffffffc)); + mips->space->WriteUnchecked_U32(result, (addr & 0xfffffffc)); } break; @@ -652,7 +652,7 @@ namespace MIPSInt { FI(ft) = 0; break; } - FI(ft) = Memory::ReadUnchecked_U32(addr); + FI(ft) = mips->space->ReadUnchecked_U32(addr); break; //lwc1 case 57: if (!Memory::IsValid4AlignedAddress(addr)) { @@ -663,7 +663,7 @@ namespace MIPSInt { Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "swc1"); break; } - Memory::WriteUnchecked_U32(FI(ft), addr); + mips->space->WriteUnchecked_U32(FI(ft), addr); break; //swc1 default: _dbg_assert_msg_(false,"Trying to interpret FPULS instruction that can't be interpreted"); diff --git a/Core/MIPS/InterpreterVFPU.cpp b/Core/MIPS/InterpreterVFPU.cpp index 0d5464396c..ef57c43c3f 100644 --- a/Core/MIPS/InterpreterVFPU.cpp +++ b/Core/MIPS/InterpreterVFPU.cpp @@ -262,7 +262,7 @@ namespace MIPSInt } // It's an LVL for (int i = 0; i < offset + 1; i++) { - d[3 - i] = valid ? Memory::ReadUnchecked_Float(addr - 4 * i) : 0.0f; + d[3 - i] = valid ? mips->space->ReadUnchecked_Float(addr - 4 * i) : 0.0f; } } else { if (!valid) { @@ -270,7 +270,7 @@ namespace MIPSInt } // It's an LVR for (int i = 0; i < (3 - offset) + 1; i++) { - d[i] = valid ? Memory::ReadUnchecked_Float(addr + 4 * i) : 0.0f; + d[i] = valid ? mips->space->ReadUnchecked_Float(addr + 4 * i) : 0.0f; } } WriteLoadedVector(mips, d, vt); @@ -288,15 +288,15 @@ namespace MIPSInt } #ifndef COMMON_BIG_ENDIAN - cf = reinterpret_cast(Memory::GetPointerUnchecked(addr)); + cf = reinterpret_cast(mips->space->GetPointerUnchecked(addr)); WriteLoadedVector(mips, cf, vt); #else float lvqd[4]; - lvqd[0] = Memory::ReadUnchecked_Float(addr); - lvqd[1] = Memory::ReadUnchecked_Float(addr + 4); - lvqd[2] = Memory::ReadUnchecked_Float(addr + 8); - lvqd[3] = Memory::ReadUnchecked_Float(addr + 12); + lvqd[0] = mips->space->ReadUnchecked_Float(addr); + lvqd[1] = mips->space->ReadUnchecked_Float(addr + 4); + lvqd[2] = mips->space->ReadUnchecked_Float(addr + 8); + lvqd[3] = mips->space->ReadUnchecked_Float(addr + 12); WriteLoadedVector(mips, lvqd, vt); #endif @@ -315,7 +315,7 @@ namespace MIPSInt // It's an SVL for (int i = 0; i < offset + 1; i++) { - Memory::WriteUnchecked_Float(d[3 - i], addr - i * 4); + mips->space->WriteUnchecked_Float(d[3 - i], addr - i * 4); } } else { if (!Memory::IsValid4AlignedAddress(addr)) { @@ -324,7 +324,7 @@ namespace MIPSInt } // It's an SVR for (int i = 0; i < (3 - offset) + 1; i++) { - Memory::WriteUnchecked_Float(d[i], addr + 4 * i); + mips->space->WriteUnchecked_Float(d[i], addr + 4 * i); } } break; @@ -337,7 +337,7 @@ namespace MIPSInt break; } #ifndef COMMON_BIG_ENDIAN - f = reinterpret_cast(Memory::GetPointerWriteUnchecked(addr)); + f = reinterpret_cast(mips->space->GetPointerUnchecked(addr)); if (f) { ReadVector(mips, f, V_Quad, vt); } @@ -345,10 +345,10 @@ namespace MIPSInt float svqd[4]; ReadVector(mips, svqd, V_Quad, vt); - Memory::WriteUnchecked_Float(svqd[0], addr); - Memory::WriteUnchecked_Float(svqd[1], addr + 4); - Memory::WriteUnchecked_Float(svqd[2], addr + 8); - Memory::WriteUnchecked_Float(svqd[3], addr + 12); + mips->space->WriteUnchecked_Float(svqd[0], addr); + mips->space->WriteUnchecked_Float(svqd[1], addr + 4); + mips->space->WriteUnchecked_Float(svqd[2], addr + 8); + mips->space->WriteUnchecked_Float(svqd[3], addr + 12); #endif break; @@ -1750,14 +1750,14 @@ namespace MIPSInt VI(vt) = 0; break; } - VI(vt) = Memory::ReadUnchecked_U32(addr); + VI(vt) = mips->space->ReadUnchecked_U32(addr); break; case 58: //sv.s if (!Memory::IsValid4AlignedAddress(addr)) { Core_MemoryException(addr, 4, PC, MemoryExceptionType::WRITE_WORD, "sv.s"); break; } - Memory::WriteUnchecked_U32(VI(vt), addr); + mips->space->WriteUnchecked_U32(VI(vt), addr); break; default: _dbg_assert_msg_(false,"Trying to interpret instruction that can't be interpreted"); diff --git a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp index ecddc3e40f..7c06c60186 100644 --- a/Core/MIPS/LoongArch64/LoongArch64Asm.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64Asm.cpp @@ -120,7 +120,7 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) { _assert_(saveOffset <= saveSize); // Fixed registers, these are always kept when in Jit context. - LI(MEMBASEREG, Memory::base); + LI(MEMBASEREG, mipsState->space->base); LI(CTXREG, mipsState); LI(JITBASEREG, GetBasePtr()); @@ -170,7 +170,7 @@ void LoongArch64JitBackend::GenerateFixedCode(MIPSState *mipsState) { WriteDebugPC(SCRATCH1); LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK); AND(SCRATCH1, SCRATCH1, SCRATCH2); - LI(SCRATCH2, Memory::blockShadow); + LI(SCRATCH2, mipsState->space->blockShadow); ADD_D(SCRATCH1, SCRATCH1, SCRATCH2); hooks_.dispatchFetch = GetCodePtr(); LD_WU(SCRATCH1, SCRATCH1, 0); diff --git a/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp b/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp index 588f42d6a6..dc2b31d10c 100644 --- a/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp +++ b/Core/MIPS/LoongArch64/LoongArch64CompBranch.cpp @@ -57,7 +57,7 @@ void LoongArch64JitBackend::CompIR_Exit(IRInst inst) { SetJumpTarget(inTime); LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK); AND(SCRATCH1, SCRATCH1, SCRATCH2); - LI(SCRATCH2, Memory::blockShadow); + LI(SCRATCH2, mips_->space->blockShadow); LDX_WU(SCRATCH1, SCRATCH2, SCRATCH1); FixupBranch found = BNEZ(SCRATCH1); QuickJ(R_RA, dispatcherNoCheck_); diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index a862ab0da4..0472cda79a 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -33,6 +33,7 @@ #include "Common/Serialize/SerializeFuncs.h" #include "Core/ConfigValues.h" #include "Core/MIPS/MIPS.h" +#include "Core/MemMap.h" #include "Core/MIPS/Interpreter.h" #include "Core/MIPS/MIPSTables.h" #include "Core/MIPS/MIPSDebugInterface.h" @@ -232,6 +233,8 @@ const float cst_constants[32] = { }; MIPSState::MIPSState() { + space = &Memory::g_mainSpace; + // Initialize vorder // This reordering of the VFPU registers in RAM means that instead of being like this: diff --git a/Core/MIPS/MIPS.h b/Core/MIPS/MIPS.h index 68b60cb724..3497ec82fc 100644 --- a/Core/MIPS/MIPS.h +++ b/Core/MIPS/MIPS.h @@ -34,6 +34,10 @@ namespace MIPSComp { class JitInterface; } +namespace Memory { +struct AddressSpace; +} + typedef Memory::Opcode MIPSOpcode; // A fake opcode the CPU cores fetch in place of an instruction with a replacement function (see @@ -317,6 +321,8 @@ public: // Owned. Null on the interpreter. MIPSComp::JitInterface *jit = nullptr; + // What this CPU sees of memory. The main CPU's is Memory::g_mainSpace. + const Memory::AddressSpace *space = nullptr; }; // Access control ('private') break standard layout. Let's not allow it, to avoid excessive diff --git a/Core/MIPS/RiscV/RiscVAsm.cpp b/Core/MIPS/RiscV/RiscVAsm.cpp index 3ab7f23154..37715b7bee 100644 --- a/Core/MIPS/RiscV/RiscVAsm.cpp +++ b/Core/MIPS/RiscV/RiscVAsm.cpp @@ -132,7 +132,7 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) { _assert_(saveOffset <= saveSize); // Fixed registers, these are always kept when in Jit context. - LI(MEMBASEREG, Memory::base, SCRATCH1); + LI(MEMBASEREG, mipsState->space->base, SCRATCH1); LI(CTXREG, mipsState, SCRATCH1); LI(JITBASEREG, GetBasePtr(), SCRATCH1); @@ -182,7 +182,7 @@ void RiscVJitBackend::GenerateFixedCode(MIPSState *mipsState) { WriteDebugPC(SCRATCH1); LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK); AND(SCRATCH1, SCRATCH1, SCRATCH2); - LI(SCRATCH2, Memory::blockShadow, X7); + LI(SCRATCH2, mipsState->space->blockShadow, X7); ADD(SCRATCH1, SCRATCH1, SCRATCH2); hooks_.dispatchFetch = GetCodePtr(); LWU(SCRATCH1, SCRATCH1, 0); diff --git a/Core/MIPS/RiscV/RiscVCompBranch.cpp b/Core/MIPS/RiscV/RiscVCompBranch.cpp index c616104cae..d44db0785c 100644 --- a/Core/MIPS/RiscV/RiscVCompBranch.cpp +++ b/Core/MIPS/RiscV/RiscVCompBranch.cpp @@ -57,7 +57,7 @@ void RiscVJitBackend::CompIR_Exit(IRInst inst) { SetJumpTarget(inTime); LI(SCRATCH2, (u32)Memory::BLOCK_SHADOW_ENTRY_MASK); AND(SCRATCH1, SCRATCH1, SCRATCH2); - LI(SCRATCH2, Memory::blockShadow, X7); + LI(SCRATCH2, mips_->space->blockShadow, X7); ADD(SCRATCH1, SCRATCH1, SCRATCH2); LWU(SCRATCH1, SCRATCH1, 0); FixupBranch found = BNE(SCRATCH1, R_ZERO); diff --git a/Core/MIPS/x86/X64IRAsm.cpp b/Core/MIPS/x86/X64IRAsm.cpp index ddc9d30b50..17912eef8d 100644 --- a/Core/MIPS/x86/X64IRAsm.cpp +++ b/Core/MIPS/x86/X64IRAsm.cpp @@ -50,6 +50,7 @@ static void ShowPC(void *membase, void *jitbase) { } void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { + regs_.SetMemoryBase(mipsState->space->base); // This will be used as a writable scratch area, always 32-bit accessible. const u8 *start = AlignCodePage(); if (DebugProfilerEnabled()) { @@ -150,7 +151,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { ABI_PushAllCalleeSavedRegsAndAdjustStack(); #if PPSSPP_ARCH(AMD64) // Two x64-specific statically allocated registers. - MOV(64, R(MEMBASEREG), ImmPtr(Memory::base)); + MOV(64, R(MEMBASEREG), ImmPtr(mipsState->space->base)); if (jitbaseInR15) MOV(64, R(JITBASEREG), ImmPtr((const void *)jitbase)); #endif @@ -221,7 +222,7 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { ABI_CallFunctionAC(reinterpret_cast(&ShowPC), R(MEMBASEREG), (u32)jitbase); } #else - ABI_CallFunctionCC(reinterpret_cast(&ShowPC), (u32)Memory::base, (u32)GetBasePtr()); + ABI_CallFunctionCC(reinterpret_cast(&ShowPC), (u32)mipsState->space->base, (u32)GetBasePtr()); #endif } @@ -230,11 +231,11 @@ void X64JitBackend::GenerateFixedCode(MIPSState *mipsState) { // Look the block up in the shadow. Its entry is the offset into the code space, or 0 for none. AND(32, R(SCRATCH1), Imm32(Memory::BLOCK_SHADOW_ENTRY_MASK)); #if PPSSPP_ARCH(X86) - _assert_msg_(Memory::blockShadow != 0, "Block shadow bogus"); + _assert_msg_(mipsState->space->blockShadow != 0, "Block shadow bogus"); hooks_.dispatchFetch = GetCodePtr(); - MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)Memory::blockShadow)); + MOV(32, R(SCRATCH1), MDisp(SCRATCH1, (u32)mipsState->space->blockShadow)); #elif PPSSPP_ARCH(AMD64) - MOV(64, R(RDX), ImmPtr(Memory::blockShadow)); + MOV(64, R(RDX), ImmPtr(mipsState->space->blockShadow)); hooks_.dispatchFetch = GetCodePtr(); MOV(32, R(SCRATCH1), MComplex(RDX, SCRATCH1, SCALE_1, 0)); #endif diff --git a/Core/MIPS/x86/X64IRCompLoadStore.cpp b/Core/MIPS/x86/X64IRCompLoadStore.cpp index 7f2f5ac0fe..1a4e51909a 100644 --- a/Core/MIPS/x86/X64IRCompLoadStore.cpp +++ b/Core/MIPS/x86/X64IRCompLoadStore.cpp @@ -64,7 +64,7 @@ Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) { #if PPSSPP_ARCH(AMD64) addrArg = MDisp(MEMBASEREG, disp & 0x7FFFFFFF); #else - addrArg = M(Memory::base + disp); + addrArg = M(mips_->space->base + disp); #endif } else if ((jo.cachePointers || src1IsPointer) && !readsFromSrc1 && (!clobbersSrc1 || src1IsPointer)) { X64Reg src1 = regs_.MapGPRAsPointer(inst.src1); @@ -74,12 +74,12 @@ Gen::OpArg X64JitBackend::PrepareSrc1Address(IRInst inst) { #ifdef MASKED_PSP_MEMORY LEA(PTRBITS, SCRATCH1, MDisp(regs_.RX(inst.src1), disp)); AND(PTRBITS, R(SCRATCH1), Imm32(Memory::MEMVIEW32_MASK)); - addrArg = MDisp(SCRATCH1, (intptr_t)Memory::base); + addrArg = MDisp(SCRATCH1, (intptr_t)mips_->space->base); #else #if PPSSPP_ARCH(AMD64) addrArg = MComplex(MEMBASEREG, regs_.RX(inst.src1), SCALE_1, disp); #else - addrArg = MDisp(regs_.RX(inst.src1), Memory::base + disp); + addrArg = MDisp(regs_.RX(inst.src1), mips_->space->base + disp); #endif #endif } diff --git a/Core/MIPS/x86/X64IRCompSystem.cpp b/Core/MIPS/x86/X64IRCompSystem.cpp index ac0eeec570..5db8e72338 100644 --- a/Core/MIPS/x86/X64IRCompSystem.cpp +++ b/Core/MIPS/x86/X64IRCompSystem.cpp @@ -400,7 +400,7 @@ void X64JitBackend::CompIR_ValidateAddress(IRInst inst) { if (regs_.IsGPRMappedAsPointer(inst.src1)) { LEA(PTRBITS, SCRATCH1, MDisp(regs_.RXPtr(inst.src1), inst.constant)); #if defined(MASKED_PSP_MEMORY) - SUB(PTRBITS, R(SCRATCH1), ImmPtr(Memory::base)); + SUB(PTRBITS, R(SCRATCH1), ImmPtr(mips_->space->base)); #else SUB(PTRBITS, R(SCRATCH1), R(MEMBASEREG)); #endif diff --git a/Core/MIPS/x86/X64IRRegCache.cpp b/Core/MIPS/x86/X64IRRegCache.cpp index a04ee29773..476c0aff8c 100644 --- a/Core/MIPS/x86/X64IRRegCache.cpp +++ b/Core/MIPS/x86/X64IRRegCache.cpp @@ -392,14 +392,14 @@ void X64IRRegCache::AdjustNativeRegAsPtr(IRNativeReg nreg, bool state) { // This destroys the value... _dbg_assert_(!nr[nreg].isDirty); emit_->AND(PTRBITS, ::R(r), Imm32(Memory::MEMVIEW32_MASK)); - emit_->ADD(PTRBITS, ::R(r), ImmPtr(Memory::base)); + emit_->ADD(PTRBITS, ::R(r), ImmPtr(membase_)); #else emit_->ADD(PTRBITS, ::R(r), ::R(MEMBASEREG)); #endif } else { #if defined(MASKED_PSP_MEMORY) _dbg_assert_(!nr[nreg].isDirty); - emit_->SUB(PTRBITS, ::R(r), ImmPtr(Memory::base)); + emit_->SUB(PTRBITS, ::R(r), ImmPtr(membase_)); #else emit_->SUB(PTRBITS, ::R(r), ::R(MEMBASEREG)); #endif diff --git a/Core/MemMap.cpp b/Core/MemMap.cpp index d21f25e41b..ff750a74fe 100644 --- a/Core/MemMap.cpp +++ b/Core/MemMap.cpp @@ -48,6 +48,7 @@ namespace Memory { u8* base = nullptr; u8 *blockShadow = nullptr; +AddressSpace g_mainSpace; static const size_t BLOCK_SHADOW_SIZE = (size_t)BLOCK_SHADOW_MASK + 1; // The MemArena class @@ -430,6 +431,8 @@ bool Init(MemMapSetupFlags flags) { INFO_LOG(Log::MemMap, "Memory system initialized. Base at %p (RAM at @ %p, uncached @ %p)", base, m_pPhysicalRAM, m_pUncachedRAM); + g_mainSpace.base = base; + g_mainSpace.blockShadow = blockShadow; MemFault_Init(); return true; } @@ -609,6 +612,7 @@ void Shutdown() { base = nullptr; FreeMemoryPages(blockShadow, BLOCK_SHADOW_SIZE); blockShadow = nullptr; + g_mainSpace = AddressSpace(); DEBUG_LOG(Log::MemMap, "Memory system shut down."); } @@ -631,18 +635,30 @@ Opcode ReadUnchecked_Instruction(u32 address) { } u32 ReadBlockShadow(u32 address) { + return g_mainSpace.ReadBlockShadow(address); +} + +void WriteBlockShadow(u32 address, u32 value) { + g_mainSpace.WriteBlockShadow(address, value); +} + +bool ClearBlockShadow(u32 address, u32 value) { + return g_mainSpace.ClearBlockShadow(address, value); +} + +u32 AddressSpace::ReadBlockShadow(u32 address) const { if (!blockShadow || !IsValid4AlignedAddress(address)) return 0; return *GetBlockShadowEntry(address); } -void WriteBlockShadow(u32 address, u32 value) { +void AddressSpace::WriteBlockShadow(u32 address, u32 value) const { if (!blockShadow || !IsValid4AlignedAddress(address)) return; *GetBlockShadowEntry(address) = value; } -bool ClearBlockShadow(u32 address, u32 value) { +bool AddressSpace::ClearBlockShadow(u32 address, u32 value) const { if (!blockShadow || !IsValid4AlignedAddress(address)) return false; u32 *entry = GetBlockShadowEntry(address); diff --git a/Core/MemMap.h b/Core/MemMap.h index 3040db0a47..951d897e1a 100644 --- a/Core/MemMap.h +++ b/Core/MemMap.h @@ -311,6 +311,43 @@ inline void WriteUnchecked_U8(u8 data, u32 address) { #endif } +// What one CPU sees of memory: where its addresses point (base) and its block shadow. The main CPU's +// is g_mainSpace, which follows base and blockShadow above. The CPU cores go through their +// MIPSState's, so another CPU (the Media Engine) can have its own. +struct AddressSpace { + u8 *base = nullptr; + u8 *blockShadow = nullptr; + + u8 *GetPointerUnchecked(u32 address) const { +#ifdef MASKED_PSP_MEMORY + return base + (address & MEMVIEW32_MASK); +#else + return base + address; +#endif + } + + u8 ReadUnchecked_U8(u32 address) const { return *GetPointerUnchecked(address); } + u16 ReadUnchecked_U16(u32 address) const { return *(u16_le *)GetPointerUnchecked(DepthMirrored16(address)); } + u32 ReadUnchecked_U32(u32 address) const { return *(u32_le *)GetPointerUnchecked(address); } + u64 ReadUnchecked_U64(u32 address) const { return *(u64_le *)GetPointerUnchecked(address); } + float ReadUnchecked_Float(u32 address) const { return *(float_le *)GetPointerUnchecked(address); } + void WriteUnchecked_U8(u8 data, u32 address) const { *GetPointerUnchecked(address) = data; } + void WriteUnchecked_U16(u16 data, u32 address) const { *(u16_le *)GetPointerUnchecked(DepthMirrored16(address)) = data; } + void WriteUnchecked_U32(u32 data, u32 address) const { *(u32_le *)GetPointerUnchecked(address) = data; } + void WriteUnchecked_U64(u64 data, u32 address) const { *(u64_le *)GetPointerUnchecked(address) = data; } + void WriteUnchecked_Float(float data, u32 address) const { *(float_le *)GetPointerUnchecked(address) = data; } + + u32 *GetBlockShadowEntry(u32 address) const { + return (u32 *)(blockShadow + (address & BLOCK_SHADOW_ENTRY_MASK)); + } + // As the functions of the same name above, for this space's shadow. + u32 ReadBlockShadow(u32 address) const; + void WriteBlockShadow(u32 address, u32 value) const; + bool ClearBlockShadow(u32 address, u32 value) const; +}; + +extern AddressSpace g_mainSpace; + void WriteOrException_U8(const u8 data, const u32 address); void WriteOrException_U16(const u16 data, const u32 address); void WriteOrException_U32(const u32 data, const u32 address);