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