Files
ppsspp/Core/MemMap.h
T
Henrik Rydgård b8e8325888 Fix text issue in God Eater Burst. Forgot that games can allocate texture in volatile memory too.
We considered any texture from kernel memory "reliable", which is wrong
since games can allocate out of the upper half of kernel RAM, which is
called "volatile" memory.

Fixes issue #13511
2020-10-14 00:09:08 +02:00

505 lines
13 KiB
C++

// Copyright (C) 2003 Dolphin Project / 2012 PPSSPP Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0 or later versions.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#pragma once
#include "ppsspp_config.h"
#include <cstring>
#include <cstdint>
#ifndef offsetof
#include <stddef.h>
#endif
// Includes
#include "Common/Common.h"
#include "Common/CommonTypes.h"
#include "Common/Swap.h"
#include "Core/Opcode.h"
// PPSSPP is very aggressive about trying to do memory accesses directly, for speed.
// This can be a problem when debugging though, as stray memory reads and writes will
// crash the whole emulator.
// If safe memory is enabled and JIT is disabled, all memory access will go through the proper
// memory access functions, and thus won't crash the emu when they go out of bounds.
#if defined(_DEBUG)
//#define SAFE_MEMORY
#endif
// Global declarations
class PointerWrap;
typedef void (*writeFn8 )(const u8, const u32);
typedef void (*writeFn16)(const u16,const u32);
typedef void (*writeFn32)(const u32,const u32);
typedef void (*writeFn64)(const u64,const u32);
typedef void (*readFn8 )(u8&, const u32);
typedef void (*readFn16)(u16&, const u32);
typedef void (*readFn32)(u32&, const u32);
typedef void (*readFn64)(u64&, const u32);
namespace Memory {
// Base is a pointer to the base of the memory map. Yes, some MMU tricks
// are used to set up a full GC or Wii memory map in process memory. on
// 32-bit, you have to mask your offsets with 0x3FFFFFFF. This means that
// some things are mirrored too many times, but eh... it works.
// In 64-bit, this might point to "high memory" (above the 32-bit limit),
// so be sure to load it into a 64-bit register.
extern u8 *base;
// These are guaranteed to point to "low memory" addresses (sub-32-bit).
// 64-bit: Pointers to low-mem (sub-0x10000000) mirror
// 32-bit: Same as the corresponding physical/virtual pointers.
// Broken into three chunks to workaround 32-bit mmap() limits.
extern u8 *m_pRAM;
extern u8 *m_pRAM2;
extern u8 *m_pRAM3;
extern u8 *m_pScratchPad;
extern u8 *m_pVRAM;
// 64-bit: Pointers to high-mem mirrors
// 32-bit: Same as above
extern u8 *m_pPhysicalRAM;
extern u8 *m_pUncachedRAM;
// This replaces RAM_NORMAL_SIZE at runtime.
extern u32 g_MemorySize;
extern u32 g_PSPModel;
// UWP has such limited memory management that we need to mask
// even in 64-bit mode. Also, when using the sanitizer, we need to mask as well.
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ADDRESS_SANITIZER
#define MASKED_PSP_MEMORY
#endif
enum
{
// This may be adjusted by remaster games.
RAM_NORMAL_SIZE = 0x02000000,
// Used if the PSP model is PSP-2000 (Slim).
RAM_DOUBLE_SIZE = RAM_NORMAL_SIZE * 2,
VRAM_SIZE = 0x00200000,
SCRATCHPAD_SIZE = 0x00004000,
#ifdef MASKED_PSP_MEMORY
// This wraparound should work for PSP too.
MEMVIEW32_MASK = 0x3FFFFFFF,
#endif
};
enum {
MV_MIRROR_PREVIOUS = 1,
MV_IS_PRIMARY_RAM = 0x100,
MV_IS_EXTRA1_RAM = 0x200,
MV_IS_EXTRA2_RAM = 0x400,
MV_KERNEL = 0x800 // Can be skipped on platforms where memory is tight.
};
struct MemoryView
{
u8 **out_ptr;
u32 virtual_address;
u32 size;
u32 flags;
};
// Uses a memory arena to set up an emulator-friendly memory map
bool MemoryMap_Setup(u32 flags);
void MemoryMap_Shutdown(u32 flags);
// Init and Shutdown
bool Init();
void Shutdown();
void DoState(PointerWrap &p);
void Clear();
// False when shutdown has already been called.
bool IsActive();
class MemoryInitedLock {
public:
MemoryInitedLock();
~MemoryInitedLock();
};
// This doesn't lock memory access or anything, it just makes sure memory isn't freed.
// Use it when accessing PSP memory from external threads.
MemoryInitedLock Lock();
// used by JIT to read instructions. Does not resolve replacements.
Opcode Read_Opcode_JIT(const u32 _Address);
// used by JIT. Reads in the "Locked cache" mode
void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value);
// Should be used by analyzers, disassemblers etc. Does resolve replacements.
Opcode Read_Instruction(const u32 _Address, bool resolveReplacements = false);
Opcode ReadUnchecked_Instruction(const u32 _Address, bool resolveReplacements = false);
u8 Read_U8(const u32 _Address);
u16 Read_U16(const u32 _Address);
u32 Read_U32(const u32 _Address);
u64 Read_U64(const u32 _Address);
inline u8* GetPointerUnchecked(const u32 address) {
#ifdef MASKED_PSP_MEMORY
return (u8 *)(base + (address & MEMVIEW32_MASK));
#else
return (u8 *)(base + address);
#endif
}
inline u32 ReadUnchecked_U32(const u32 address) {
#ifdef MASKED_PSP_MEMORY
return *(u32_le *)(base + (address & MEMVIEW32_MASK));
#else
return *(u32_le *)(base + address);
#endif
}
inline float ReadUnchecked_Float(const u32 address) {
#ifdef MASKED_PSP_MEMORY
return *(float *)(base + (address & MEMVIEW32_MASK));
#else
return *(float *)(base + address);
#endif
}
inline u16 ReadUnchecked_U16(const u32 address) {
#ifdef MASKED_PSP_MEMORY
return *(u16_le *)(base + (address & MEMVIEW32_MASK));
#else
return *(u16_le *)(base + address);
#endif
}
inline u8 ReadUnchecked_U8(const u32 address) {
#ifdef MASKED_PSP_MEMORY
return (*(u8 *)(base + (address & MEMVIEW32_MASK)));
#else
return (*(u8 *)(base + address));
#endif
}
inline void WriteUnchecked_U32(u32 data, u32 address) {
#ifdef MASKED_PSP_MEMORY
*(u32_le *)(base + (address & MEMVIEW32_MASK)) = data;
#else
*(u32_le *)(base + address) = data;
#endif
}
inline void WriteUnchecked_Float(float data, u32 address) {
#ifdef MASKED_PSP_MEMORY
*(float *)(base + (address & MEMVIEW32_MASK)) = data;
#else
*(float *)(base + address) = data;
#endif
}
inline void WriteUnchecked_U16(u16 data, u32 address) {
#ifdef MASKED_PSP_MEMORY
*(u16_le *)(base + (address & MEMVIEW32_MASK)) = data;
#else
*(u16_le *)(base + address) = data;
#endif
}
inline void WriteUnchecked_U8(u8 data, u32 address) {
#ifdef MASKED_PSP_MEMORY
(*(u8 *)(base + (address & MEMVIEW32_MASK))) = data;
#else
(*(u8 *)(base + address)) = data;
#endif
}
inline float Read_Float(u32 address)
{
u32 ifloat = Read_U32(address);
float f;
memcpy(&f, &ifloat, sizeof(float));
return f;
}
// used by JIT. Return zero-extended 32bit values
u32 Read_U8_ZX(const u32 address);
u32 Read_U16_ZX(const u32 address);
void Write_U8(const u8 data, const u32 address);
void Write_U16(const u16 data, const u32 address);
void Write_U32(const u32 data, const u32 address);
void Write_U64(const u64 data, const u32 address);
inline void Write_Float(float f, u32 address)
{
u32 u;
memcpy(&u, &f, sizeof(float));
Write_U32(u, address);
}
u8* GetPointer(const u32 address);
bool IsRAMAddress(const u32 address);
inline bool IsVRAMAddress(const u32 address) {
return ((address & 0x3F800000) == 0x04000000);
}
inline bool IsDepthTexVRAMAddress(const u32 address) {
return ((address & 0x3FE00000) == 0x04200000) || ((address & 0x3FE00000) == 0x04600000);
}
// 0x08000000 -> 0x08800000
inline bool IsKernelAddress(const u32 address) {
return ((address & 0x3F800000) == 0x08000000);
}
// 0x08000000 -> 0x08400000
inline bool IsKernelAndNotVolatileAddress(const u32 address) {
return ((address & 0x3FC00000) == 0x08000000);
}
bool IsScratchpadAddress(const u32 address);
// Used for auto-converted char * parameters, which can sometimes legitimately be null -
// so we don't want to get caught in GetPointer's crash reporting.
inline const char* GetCharPointer(const u32 address) {
if (address) {
return (const char *)GetPointer(address);
} else {
return nullptr;
}
}
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len) {
memcpy(to_data, GetPointerUnchecked(from_address), len);
}
inline void MemcpyUnchecked(const u32 to_address, const void *from_data, const u32 len) {
memcpy(GetPointerUnchecked(to_address), from_data, len);
}
inline void MemcpyUnchecked(const u32 to_address, const u32 from_address, const u32 len) {
MemcpyUnchecked(GetPointer(to_address), from_address, len);
}
inline bool IsValidAddress(const u32 address) {
if ((address & 0x3E000000) == 0x08000000) {
return true;
} else if ((address & 0x3F800000) == 0x04000000) {
return true;
} else if ((address & 0xBFFF0000) == 0x00010000) {
return (address & 0x0000FFFF) < SCRATCHPAD_SIZE;
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
return true;
} else {
return false;
}
}
inline u32 ValidSize(const u32 address, const u32 requested_size) {
u32 max_size;
if ((address & 0x3E000000) == 0x08000000) {
max_size = 0x08000000 + g_MemorySize - (address & 0x3E000000);
} else if ((address & 0x3F800000) == 0x04000000) {
max_size = 0x04800000 - (address & 0x3F800000);
} else if ((address & 0xBFFF0000) == 0x00010000) {
max_size = 0x00014000 - (address & 0xBFFF0000);
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
max_size = 0x08000000 + g_MemorySize - (address & 0x3F000000);
} else {
max_size = 0;
}
if (requested_size > max_size) {
return max_size;
}
return requested_size;
}
inline bool IsValidRange(const u32 address, const u32 size) {
return IsValidAddress(address) && ValidSize(address, size) == size;
}
} // namespace Memory
template <typename T>
struct PSPPointer
{
u32_le ptr;
inline T &operator*() const
{
#ifdef MASKED_PSP_MEMORY
return *(T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
#else
return *(T *)(Memory::base + ptr);
#endif
}
inline T &operator[](int i) const
{
#ifdef MASKED_PSP_MEMORY
return *((T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK)) + i);
#else
return *((T *)(Memory::base + ptr) + i);
#endif
}
inline T *operator->() const
{
#ifdef MASKED_PSP_MEMORY
return (T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
#else
return (T *)(Memory::base + ptr);
#endif
}
inline PSPPointer<T> operator+(int i) const
{
PSPPointer other;
other.ptr = ptr + i * sizeof(T);
return other;
}
inline PSPPointer<T> &operator=(u32 p)
{
ptr = p;
return *this;
}
inline PSPPointer<T> &operator+=(int i)
{
ptr = ptr + i * sizeof(T);
return *this;
}
inline PSPPointer<T> operator-(int i) const
{
PSPPointer other;
other.ptr = ptr - i * sizeof(T);
return other;
}
inline PSPPointer<T> &operator-=(int i)
{
ptr = ptr - i * sizeof(T);
return *this;
}
inline PSPPointer<T> &operator++()
{
ptr += sizeof(T);
return *this;
}
inline PSPPointer<T> operator++(int i)
{
PSPPointer<T> other;
other.ptr = ptr;
ptr += sizeof(T);
return other;
}
inline PSPPointer<T> &operator--()
{
ptr -= sizeof(T);
return *this;
}
inline PSPPointer<T> operator--(int i)
{
PSPPointer<T> other;
other.ptr = ptr;
ptr -= sizeof(T);
return other;
}
inline operator T*()
{
#ifdef MASKED_PSP_MEMORY
return (T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
#else
return (T *)(Memory::base + ptr);
#endif
}
inline operator const T*() const
{
#ifdef MASKED_PSP_MEMORY
return (const T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
#else
return (const T *)(Memory::base + ptr);
#endif
}
bool IsValid() const
{
return Memory::IsValidAddress(ptr);
}
static PSPPointer<T> Create(u32 ptr) {
PSPPointer<T> p;
p = ptr;
return p;
}
};
inline u32 PSP_GetScratchpadMemoryBase() { return 0x00010000;}
inline u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;}
inline u32 PSP_GetKernelMemoryBase() { return 0x08000000;}
inline u32 PSP_GetUserMemoryEnd() { return PSP_GetKernelMemoryBase() + Memory::g_MemorySize;}
inline u32 PSP_GetKernelMemoryEnd() { return 0x08400000;}
// "Volatile" RAM is between 0x08400000 and 0x08800000, can be requested by the
// game through sceKernelVolatileMemTryLock.
inline u32 PSP_GetUserMemoryBase() { return 0x08800000;}
inline u32 PSP_GetDefaultLoadAddress() { return 0;}
inline u32 PSP_GetVidMemBase() { return 0x04000000;}
inline u32 PSP_GetVidMemEnd() { return 0x04800000;}
template <typename T>
inline bool operator==(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr == rhs.ptr;
}
template <typename T>
inline bool operator!=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr != rhs.ptr;
}
template <typename T>
inline bool operator<(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr < rhs.ptr;
}
template <typename T>
inline bool operator>(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr > rhs.ptr;
}
template <typename T>
inline bool operator<=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr <= rhs.ptr;
}
template <typename T>
inline bool operator>=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
return lhs.ptr >= rhs.ptr;
}