Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
hrydgard
GitHub Repository: hrydgard/ppsspp
Path: blob/master/Core/MemMap.h
5654 views
1
// Copyright (C) 2003 Dolphin Project / 2012 PPSSPP Project.
2
3
// This program is free software: you can redistribute it and/or modify
4
// it under the terms of the GNU General Public License as published by
5
// the Free Software Foundation, version 2.0 or later versions.
6
7
// This program is distributed in the hope that it will be useful,
8
// but WITHOUT ANY WARRANTY; without even the implied warranty of
9
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10
// GNU General Public License 2.0 for more details.
11
12
// A copy of the GPL 2.0 should have been included with the program.
13
// If not, see http://www.gnu.org/licenses/
14
15
// Official SVN repository and contact information can be found at
16
// http://code.google.com/p/dolphin-emu/
17
18
#pragma once
19
20
#include "ppsspp_config.h"
21
22
#include <cstring>
23
#include <cstdint>
24
#ifndef offsetof
25
#include <stddef.h>
26
#endif
27
28
#include "Common/Common.h"
29
#include "Common/CommonTypes.h"
30
#include "Common/Swap.h"
31
#include "Core/Opcode.h"
32
33
// PPSSPP is very aggressive about trying to do memory accesses directly, for speed.
34
// This can be a problem when debugging though, as stray memory reads and writes will
35
// crash the whole emulator.
36
// If safe memory is enabled and JIT is disabled, all memory access will go through the proper
37
// memory access functions, and thus won't crash the emu when they go out of bounds.
38
#if defined(_DEBUG)
39
//#define SAFE_MEMORY
40
#endif
41
42
// Global declarations
43
class PointerWrap;
44
45
typedef void (*writeFn8 )(const u8, const u32);
46
typedef void (*writeFn16)(const u16,const u32);
47
typedef void (*writeFn32)(const u32,const u32);
48
typedef void (*writeFn64)(const u64,const u32);
49
50
typedef void (*readFn8 )(u8&, const u32);
51
typedef void (*readFn16)(u16&, const u32);
52
typedef void (*readFn32)(u32&, const u32);
53
typedef void (*readFn64)(u64&, const u32);
54
55
namespace Memory {
56
// Base is a pointer to the base of the memory map. Yes, some MMU tricks
57
// are used to set up a full GC or Wii memory map in process memory. on
58
// 32-bit, you have to mask your offsets with 0x3FFFFFFF. This means that
59
// some things are mirrored too many times, but eh... it works.
60
61
// In 64-bit, this might point to "high memory" (above the 32-bit limit),
62
// so be sure to load it into a 64-bit register.
63
extern u8 *base;
64
65
// This replaces RAM_NORMAL_SIZE at runtime.
66
extern u32 g_MemorySize;
67
extern u32 g_PSPModel;
68
69
// UWP has such limited memory management that we need to mask
70
// even in 64-bit mode. Also, when using the sanitizer, we need to mask as well.
71
#if PPSSPP_ARCH(32BIT) || PPSSPP_PLATFORM(UWP) || USE_ASAN || PPSSPP_PLATFORM(IOS) || defined(__EMSCRIPTEN__)
72
#define MASKED_PSP_MEMORY
73
#endif
74
75
enum {
76
// This may be adjusted by remaster games.
77
RAM_NORMAL_SIZE = 0x02000000,
78
// Used if the PSP model is PSP-2000 (Slim).
79
RAM_DOUBLE_SIZE = RAM_NORMAL_SIZE * 2,
80
81
VRAM_SIZE = 0x00200000,
82
83
SCRATCHPAD_SIZE = 0x00004000,
84
85
#ifdef MASKED_PSP_MEMORY
86
// This wraparound should work for PSP too.
87
MEMVIEW32_MASK = 0x3FFFFFFF,
88
#endif
89
};
90
91
enum {
92
MV_MIRROR_PREVIOUS = 1,
93
MV_IS_PRIMARY_RAM = 0x100,
94
MV_IS_EXTRA1_RAM = 0x200,
95
MV_IS_EXTRA2_RAM = 0x400,
96
MV_KERNEL = 0x800, // Can be skipped on platforms where memory is tight.
97
MV_NULL_PAGE = 0x1000,
98
};
99
100
struct MemoryView {
101
u8 **out_ptr;
102
u32 virtual_address;
103
u32 size;
104
u32 flags;
105
};
106
107
enum class MemMapSetupFlags {
108
Default = 0,
109
AllocNullPage = 1,
110
};
111
ENUM_CLASS_BITOPS(MemMapSetupFlags);
112
113
// Init and Shutdown
114
bool Init(MemMapSetupFlags flags);
115
void Shutdown();
116
void DoState(PointerWrap &p);
117
118
// False when shutdown has already been called.
119
bool IsActive();
120
121
class MemoryInitedLock {
122
public:
123
MemoryInitedLock();
124
~MemoryInitedLock();
125
};
126
127
// This doesn't lock memory access or anything, it just makes sure memory isn't freed.
128
// Use it when accessing PSP memory from external threads.
129
MemoryInitedLock Lock();
130
131
// used by JIT to read instructions. Does not resolve replacements.
132
Opcode Read_Opcode_JIT(const u32 _Address);
133
// used by JIT. Reads in the "Locked cache" mode
134
void Write_Opcode_JIT(const u32 _Address, const Opcode& _Value);
135
136
// Should be used by analyzers, disassemblers etc. Does resolve replacements.
137
Opcode Read_Instruction(const u32 _Address, bool resolveReplacements = false);
138
Opcode ReadUnchecked_Instruction(const u32 _Address, bool resolveReplacements = false);
139
140
u8 Read_U8(const u32 _Address);
141
u16 Read_U16(const u32 _Address);
142
u32 Read_U32(const u32 _Address);
143
u64 Read_U64(const u32 _Address);
144
145
inline u8* GetPointerWriteUnchecked(const u32 address) {
146
#ifdef MASKED_PSP_MEMORY
147
return (u8 *)(base + (address & MEMVIEW32_MASK));
148
#else
149
return (u8 *)(base + address);
150
#endif
151
}
152
153
inline const u8* GetPointerUnchecked(const u32 address) {
154
#ifdef MASKED_PSP_MEMORY
155
return (const u8 *)(base + (address & MEMVIEW32_MASK));
156
#else
157
return (const u8 *)(base + address);
158
#endif
159
}
160
161
inline u32 ReadUnchecked_U32(const u32 address) {
162
#ifdef MASKED_PSP_MEMORY
163
return *(u32_le *)(base + (address & MEMVIEW32_MASK));
164
#else
165
return *(u32_le *)(base + address);
166
#endif
167
}
168
169
inline float ReadUnchecked_Float(const u32 address) {
170
#ifdef MASKED_PSP_MEMORY
171
return *(float_le *)(base + (address & MEMVIEW32_MASK));
172
#else
173
return *(float_le *)(base + address);
174
#endif
175
}
176
177
inline u16 ReadUnchecked_U16(const u32 address) {
178
#ifdef MASKED_PSP_MEMORY
179
return *(u16_le *)(base + (address & MEMVIEW32_MASK));
180
#else
181
return *(u16_le *)(base + address);
182
#endif
183
}
184
185
inline u8 ReadUnchecked_U8(const u32 address) {
186
#ifdef MASKED_PSP_MEMORY
187
return (*(u8 *)(base + (address & MEMVIEW32_MASK)));
188
#else
189
return (*(u8 *)(base + address));
190
#endif
191
}
192
193
inline void WriteUnchecked_U32(u32 data, u32 address) {
194
#ifdef MASKED_PSP_MEMORY
195
*(u32_le *)(base + (address & MEMVIEW32_MASK)) = data;
196
#else
197
*(u32_le *)(base + address) = data;
198
#endif
199
}
200
201
inline void WriteUnchecked_Float(float data, u32 address) {
202
#ifdef MASKED_PSP_MEMORY
203
*(float_le *)(base + (address & MEMVIEW32_MASK)) = data;
204
#else
205
*(float_le *)(base + address) = data;
206
#endif
207
}
208
209
inline void WriteUnchecked_U16(u16 data, u32 address) {
210
#ifdef MASKED_PSP_MEMORY
211
*(u16_le *)(base + (address & MEMVIEW32_MASK)) = data;
212
#else
213
*(u16_le *)(base + address) = data;
214
#endif
215
}
216
217
inline void WriteUnchecked_U8(u8 data, u32 address) {
218
#ifdef MASKED_PSP_MEMORY
219
(*(u8 *)(base + (address & MEMVIEW32_MASK))) = data;
220
#else
221
(*(u8 *)(base + address)) = data;
222
#endif
223
}
224
225
inline float Read_Float(u32 address)
226
{
227
u32 ifloat = Read_U32(address);
228
float f;
229
memcpy(&f, &ifloat, sizeof(float));
230
return f;
231
}
232
233
// used by JIT. Return zero-extended 32bit values
234
u32 Read_U8_ZX(const u32 address);
235
u32 Read_U16_ZX(const u32 address);
236
237
void Write_U8(const u8 data, const u32 address);
238
void Write_U16(const u16 data, const u32 address);
239
void Write_U32(const u32 data, const u32 address);
240
void Write_U64(const u64 data, const u32 address);
241
242
inline void Write_Float(float f, u32 address)
243
{
244
u32 u;
245
memcpy(&u, &f, sizeof(float));
246
Write_U32(u, address);
247
}
248
249
u8* GetPointerWrite(const u32 address);
250
const u8* GetPointer(const u32 address);
251
252
u8 *GetPointerWriteRange(const u32 address, const u32 size);
253
template<typename T>
254
T* GetTypedPointerWriteRange(const u32 address, const u32 size) {
255
return reinterpret_cast<T*>(GetPointerWriteRange(address, size));
256
}
257
258
const u8 *GetPointerRange(const u32 address, const u32 size);
259
template<typename T>
260
const T* GetTypedPointerRange(const u32 address, const u32 size) {
261
return reinterpret_cast<const T*>(GetPointerRange(address, size));
262
}
263
264
bool IsRAMAddress(const u32 address);
265
inline bool IsVRAMAddress(const u32 address) {
266
return ((address & 0x3F800000) == 0x04000000);
267
}
268
inline bool IsDepthTexVRAMAddress(const u32 address) {
269
return ((address & 0x3FE00000) == 0x04200000) || ((address & 0x3FE00000) == 0x04600000);
270
}
271
272
// 0x08000000 -> 0x08800000
273
inline bool IsKernelAddress(const u32 address) {
274
return ((address & 0x3F800000) == 0x08000000);
275
}
276
277
// 0x08000000 -> 0x08400000
278
inline bool IsKernelAndNotVolatileAddress(const u32 address) {
279
return ((address & 0x3FC00000) == 0x08000000);
280
}
281
282
bool IsScratchpadAddress(const u32 address);
283
284
inline void MemcpyUnchecked(void *to_data, const u32 from_address, const u32 len) {
285
memcpy(to_data, GetPointerUnchecked(from_address), len);
286
}
287
288
inline void MemcpyUnchecked(const u32 to_address, const void *from_data, const u32 len) {
289
memcpy(GetPointerWriteUnchecked(to_address), from_data, len);
290
}
291
292
inline void MemcpyUnchecked(const u32 to_address, const u32 from_address, const u32 len) {
293
MemcpyUnchecked(GetPointerWriteUnchecked(to_address), from_address, len);
294
}
295
296
inline bool IsValidAddress(const u32 address) {
297
if ((address & 0x3E000000) == 0x08000000) {
298
return true;
299
} else if ((address & 0x3F800000) == 0x04000000) {
300
return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible.
301
} else if ((address & 0xBFFFC000) == 0x00010000) {
302
return true;
303
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
304
return true;
305
} else {
306
return false;
307
}
308
}
309
310
inline bool IsValid4AlignedAddress(const u32 address) {
311
if ((address & 0x3E000003) == 0x08000000) {
312
return true;
313
} else if ((address & 0x3F800003) == 0x04000000) {
314
return address < 0x80000000; // Let's disallow kernel-flagged VRAM. We don't have it mapped and I am not sure if it's accessible.
315
} else if ((address & 0xBFFFC003) == 0x00010000) {
316
return true;
317
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
318
return (address & 3) == 0;
319
} else {
320
return false;
321
}
322
}
323
324
inline u32 MaxSizeAtAddress(const u32 address){
325
if ((address & 0x3E000000) == 0x08000000) {
326
return 0x08000000 + g_MemorySize - (address & 0x3FFFFFFF);
327
} else if ((address & 0x3F800000) == 0x04000000) {
328
if (address & 0x80000000) {
329
return 0;
330
} else {
331
return 0x04800000 - (address & 0x3FFFFFFF);
332
}
333
} else if ((address & 0xBFFFC000) == 0x00010000) {
334
return 0x00014000 - (address & 0x3FFFFFFF);
335
} else if ((address & 0x3F000000) >= 0x08000000 && (address & 0x3F000000) < 0x08000000 + g_MemorySize) {
336
return 0x08000000 + g_MemorySize - (address & 0x3FFFFFFF);
337
} else {
338
return 0;
339
}
340
}
341
342
inline const char *GetCharPointerUnchecked(const u32 address) {
343
return (const char *)GetPointerUnchecked(address);
344
}
345
346
// NOTE: Unlike the similar IsValidRange/IsValidAddress functions, this one is linear cost vs the size of the string,
347
// for hopefully-obvious reasons.
348
inline bool IsValidNullTerminatedString(const u32 address) {
349
u32 max_size = MaxSizeAtAddress(address);
350
if (max_size == 0) {
351
return false;
352
}
353
354
const char *c = GetCharPointerUnchecked(address);
355
if (memchr(c, '\0', max_size)) {
356
return true;
357
}
358
return false;
359
}
360
361
inline u32 ClampValidSizeAt(const u32 address, const u32 requestedSize) {
362
u32 max_size = MaxSizeAtAddress(address);
363
if (requestedSize > max_size) {
364
return max_size;
365
}
366
return requestedSize;
367
}
368
369
// NOTE: If size == 0, any address will be accepted. This may not be ideal for all cases.
370
inline bool IsValidRange(const u32 address, const u32 size) {
371
return ClampValidSizeAt(address, size) == size;
372
}
373
374
// NOTE: If size == 0, any address will be accepted. This may not be ideal for all cases.
375
// Also, length is not checked for alignment.
376
inline bool IsValid4AlignedRange(const u32 address, const u32 size) {
377
if (address & 3) {
378
return false;
379
}
380
return ClampValidSizeAt(address, size) == size;
381
}
382
383
// Used for auto-converted char * parameters, which can sometimes legitimately be null -
384
// so we don't want to get caught in GetPointer's crash reporting
385
// TODO: This should use IsValidNullTerminatedString, but may be expensive since this is used so much - needs evaluation.
386
inline const char *GetCharPointer(const u32 address) {
387
if (address && IsValidAddress(address)) {
388
return GetCharPointerUnchecked(address);
389
} else {
390
return nullptr;
391
}
392
}
393
394
// Remaps the host pointer (potentially 64bit) into the 32bit virtual pointer, no checks are made
395
inline u32 GetAddressFromHostPointerUnchecked(const void* host_ptr) {
396
auto address = static_cast<const u8*>(host_ptr) - base;
397
return static_cast<u32>(address);
398
}
399
400
// Remaps the host pointer (potentially 64bit) into the 32bit virtual pointer with checks
401
inline u32 GetAddressFromHostPointer(const void* host_ptr) {
402
u32 address = GetAddressFromHostPointerUnchecked(host_ptr);
403
if (!IsValidAddress(address)) {
404
// Somehow report the error?
405
return 0;
406
}
407
return address;
408
}
409
410
// Like GetPointer, but bad values don't result in a memory exception, instead nullptr is returned.
411
inline const u8* GetPointerOrNull(const u32 address) {
412
return IsValidAddress(address) ? GetPointerUnchecked(address) : nullptr;
413
}
414
415
} // namespace Memory
416
417
// Avoiding a global include for NotifyMemInfo.
418
void PSPPointerNotifyRW(int rw, uint32_t ptr, uint32_t bytes, const char *tag, size_t tagLen);
419
420
// TODO: These are actually quite annoying because they can't be followed in the MSVC debugger...
421
// Need to find a solution for that. Can't just change the internal representation though, because
422
// these can be present in PSP-native structs.
423
template <typename T>
424
struct PSPPointer
425
{
426
u32_le ptr;
427
428
inline T &operator*() const
429
{
430
#ifdef MASKED_PSP_MEMORY
431
return *(T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
432
#else
433
return *(T *)(Memory::base + ptr);
434
#endif
435
}
436
437
inline const T &operator[](int i) const
438
{
439
#ifdef MASKED_PSP_MEMORY
440
return *((T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK)) + i);
441
#else
442
return *((const T *)(Memory::base + ptr) + i);
443
#endif
444
}
445
446
inline T &operator[](int i)
447
{
448
#ifdef MASKED_PSP_MEMORY
449
return *((T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK)) + i);
450
#else
451
return *((T *)(Memory::base + ptr) + i);
452
#endif
453
}
454
455
inline const T *operator->() const
456
{
457
#ifdef MASKED_PSP_MEMORY
458
return (T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
459
#else
460
return (const T *)(Memory::base + ptr);
461
#endif
462
}
463
464
inline T *operator->()
465
{
466
#ifdef MASKED_PSP_MEMORY
467
return (T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
468
#else
469
return (T *)(Memory::base + ptr);
470
#endif
471
}
472
473
inline PSPPointer<T> operator+(int i) const
474
{
475
PSPPointer other;
476
other.ptr = ptr + i * sizeof(T);
477
return other;
478
}
479
480
inline PSPPointer<T> &operator=(u32 p)
481
{
482
ptr = p;
483
return *this;
484
}
485
486
inline PSPPointer<T> &operator+=(int i)
487
{
488
ptr = ptr + i * sizeof(T);
489
return *this;
490
}
491
492
inline PSPPointer<T> operator-(int i) const
493
{
494
PSPPointer other;
495
other.ptr = ptr - i * sizeof(T);
496
return other;
497
}
498
499
inline PSPPointer<T> &operator-=(int i)
500
{
501
ptr = ptr - i * sizeof(T);
502
return *this;
503
}
504
505
inline PSPPointer<T> &operator++()
506
{
507
ptr += sizeof(T);
508
return *this;
509
}
510
511
inline PSPPointer<T> operator++(int i)
512
{
513
PSPPointer<T> other;
514
other.ptr = ptr;
515
ptr += sizeof(T);
516
return other;
517
}
518
519
inline PSPPointer<T> &operator--()
520
{
521
ptr -= sizeof(T);
522
return *this;
523
}
524
525
inline PSPPointer<T> operator--(int i)
526
{
527
PSPPointer<T> other;
528
other.ptr = ptr;
529
ptr -= sizeof(T);
530
return other;
531
}
532
533
inline operator T*()
534
{
535
#ifdef MASKED_PSP_MEMORY
536
return (T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
537
#else
538
return (T *)(Memory::base + ptr);
539
#endif
540
}
541
542
inline operator const T*() const
543
{
544
#ifdef MASKED_PSP_MEMORY
545
return (const T *)(Memory::base + (ptr & Memory::MEMVIEW32_MASK));
546
#else
547
return (const T *)(Memory::base + ptr);
548
#endif
549
}
550
551
bool IsValid() const {
552
return Memory::IsValidRange(ptr, (u32)sizeof(T));
553
}
554
555
void FillWithZero() {
556
memset(Memory::GetPointerWrite(ptr), 0, sizeof(T));
557
}
558
559
bool Equals(u32 addr) const {
560
return ptr == addr;
561
}
562
563
T *PtrOrNull() {
564
if (IsValid())
565
return (T *)*this;
566
return nullptr;
567
}
568
569
const T *PtrOrNull() const {
570
if (IsValid())
571
return (const T *)*this;
572
return nullptr;
573
}
574
575
template <size_t tagLen>
576
void NotifyWrite(const char(&tag)[tagLen]) const {
577
PSPPointerNotifyRW(1, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1);
578
}
579
580
template <size_t tagLen>
581
void NotifyRead(const char(&tag)[tagLen]) const {
582
PSPPointerNotifyRW(2, (uint32_t)ptr, (uint32_t)sizeof(T), tag, tagLen - 1);
583
}
584
585
size_t ElementSize() const
586
{
587
return sizeof(T);
588
}
589
590
static PSPPointer<T> Create(u32 ptr) {
591
PSPPointer<T> p;
592
p = ptr;
593
return p;
594
}
595
};
596
597
constexpr u32 PSP_GetScratchpadMemoryBase() { return 0x00010000;}
598
constexpr u32 PSP_GetScratchpadMemoryEnd() { return 0x00014000;}
599
600
constexpr u32 PSP_GetKernelMemoryBase() { return 0x08000000;}
601
inline u32 PSP_GetUserMemoryEnd() { return PSP_GetKernelMemoryBase() + Memory::g_MemorySize;}
602
constexpr u32 PSP_GetKernelMemoryEnd() { return 0x08400000;}
603
604
// "Volatile" RAM is between 0x08400000 and 0x08800000, can be requested by the
605
// game through sceKernelVolatileMemTryLock.
606
constexpr u32 PSP_GetVolatileMemoryStart() { return 0x08400000; }
607
constexpr u32 PSP_GetVolatileMemoryEnd() { return 0x08800000; }
608
609
constexpr u32 PSP_GetUserMemoryBase() { return 0x08800000; }
610
constexpr u32 PSP_GetDefaultLoadAddress() { return 0; }
611
constexpr u32 PSP_GetVidMemBase() { return 0x04000000; }
612
constexpr u32 PSP_GetVidMemEnd() { return 0x04800000; }
613
614
template <typename T>
615
inline bool operator==(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
616
return lhs.ptr == rhs.ptr;
617
}
618
619
template <typename T>
620
inline bool operator!=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
621
return lhs.ptr != rhs.ptr;
622
}
623
624
template <typename T>
625
inline bool operator<(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
626
return lhs.ptr < rhs.ptr;
627
}
628
629
template <typename T>
630
inline bool operator>(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
631
return lhs.ptr > rhs.ptr;
632
}
633
634
template <typename T>
635
inline bool operator<=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
636
return lhs.ptr <= rhs.ptr;
637
}
638
639
template <typename T>
640
inline bool operator>=(const PSPPointer<T> &lhs, const PSPPointer<T> &rhs) {
641
return lhs.ptr >= rhs.ptr;
642
}
643
644