summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/common/CMakeLists.txt4
-rw-r--r--src/common/hash.cpp524
-rw-r--r--src/common/hash.h17
-rw-r--r--src/common/mem_arena.cpp394
-rw-r--r--src/common/mem_arena.h70
-rw-r--r--src/core/mem_map.cpp113
-rw-r--r--src/core/mem_map.h12
7 files changed, 31 insertions, 1103 deletions
diff --git a/src/common/CMakeLists.txt b/src/common/CMakeLists.txt
index 9a9f1a46b..11659c3c5 100644
--- a/src/common/CMakeLists.txt
+++ b/src/common/CMakeLists.txt
@@ -5,13 +5,11 @@ set(SRCS
5 break_points.cpp 5 break_points.cpp
6 emu_window.cpp 6 emu_window.cpp
7 file_util.cpp 7 file_util.cpp
8 hash.cpp
9 key_map.cpp 8 key_map.cpp
10 logging/filter.cpp 9 logging/filter.cpp
11 logging/text_formatter.cpp 10 logging/text_formatter.cpp
12 logging/backend.cpp 11 logging/backend.cpp
13 math_util.cpp 12 math_util.cpp
14 mem_arena.cpp
15 memory_util.cpp 13 memory_util.cpp
16 misc.cpp 14 misc.cpp
17 profiler.cpp 15 profiler.cpp
@@ -36,7 +34,6 @@ set(HEADERS
36 emu_window.h 34 emu_window.h
37 fifo_queue.h 35 fifo_queue.h
38 file_util.h 36 file_util.h
39 hash.h
40 key_map.h 37 key_map.h
41 linear_disk_cache.h 38 linear_disk_cache.h
42 logging/text_formatter.h 39 logging/text_formatter.h
@@ -45,7 +42,6 @@ set(HEADERS
45 logging/backend.h 42 logging/backend.h
46 make_unique.h 43 make_unique.h
47 math_util.h 44 math_util.h
48 mem_arena.h
49 memory_util.h 45 memory_util.h
50 platform.h 46 platform.h
51 profiler.h 47 profiler.h
diff --git a/src/common/hash.cpp b/src/common/hash.cpp
deleted file mode 100644
index e2364d700..000000000
--- a/src/common/hash.cpp
+++ /dev/null
@@ -1,524 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#include <algorithm>
6
7#include "common/common_funcs.h" // For rotl
8#include "common/hash.h"
9#include "common/platform.h"
10
11#if _M_SSE >= 0x402
12#include "common/cpu_detect.h"
13#include <nmmintrin.h>
14#endif
15
16static u64 (*ptrHashFunction)(const u8 *src, int len, u32 samples) = &GetMurmurHash3;
17
18// uint32_t
19// WARNING - may read one more byte!
20// Implementation from Wikipedia.
21u32 HashFletcher(const u8* data_u8, size_t length)
22{
23 const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */
24 size_t len = (length + 1) / 2; /* Length in 16-bit words */
25 u32 sum1 = 0xffff, sum2 = 0xffff;
26
27 while (len)
28 {
29 size_t tlen = len > 360 ? 360 : len;
30 len -= tlen;
31
32 do {
33 sum1 += *data++;
34 sum2 += sum1;
35 }
36 while (--tlen);
37
38 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
39 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
40 }
41
42 // Second reduction step to reduce sums to 16 bits
43 sum1 = (sum1 & 0xffff) + (sum1 >> 16);
44 sum2 = (sum2 & 0xffff) + (sum2 >> 16);
45 return(sum2 << 16 | sum1);
46}
47
48
49// Implementation from Wikipedia
50// Slightly slower than Fletcher above, but slightly more reliable.
51#define MOD_ADLER 65521
52// data: Pointer to the data to be summed; len is in bytes
53u32 HashAdler32(const u8* data, size_t len)
54{
55 u32 a = 1, b = 0;
56
57 while (len)
58 {
59 size_t tlen = len > 5550 ? 5550 : len;
60 len -= tlen;
61
62 do
63 {
64 a += *data++;
65 b += a;
66 }
67 while (--tlen);
68
69 a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER);
70 b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER);
71 }
72
73 // It can be shown that a <= 0x1013a here, so a single subtract will do.
74 if (a >= MOD_ADLER)
75 {
76 a -= MOD_ADLER;
77 }
78
79 // It can be shown that b can reach 0xfff87 here.
80 b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER);
81
82 if (b >= MOD_ADLER)
83 {
84 b -= MOD_ADLER;
85 }
86
87 return((b << 16) | a);
88}
89
90// Stupid hash - but can't go back now :)
91// Don't use for new things. At least it's reasonably fast.
92u32 HashEctor(const u8* ptr, int length)
93{
94 u32 crc = 0;
95
96 for (int i = 0; i < length; i++)
97 {
98 crc ^= ptr[i];
99 crc = (crc << 3) | (crc >> 29);
100 }
101
102 return(crc);
103}
104
105
106#if EMU_ARCH_BITS == 64
107
108//-----------------------------------------------------------------------------
109// Block read - if your platform needs to do endian-swapping or can only
110// handle aligned reads, do the conversion here
111
112inline u64 getblock(const u64 * p, int i)
113{
114 return p[i];
115}
116
117//----------
118// Block mix - combine the key bits with the hash bits and scramble everything
119
120inline void bmix64(u64 & h1, u64 & h2, u64 & k1, u64 & k2, u64 & c1, u64 & c2)
121{
122 k1 *= c1;
123 k1 = _rotl64(k1,23);
124 k1 *= c2;
125 h1 ^= k1;
126 h1 += h2;
127
128 h2 = _rotl64(h2,41);
129
130 k2 *= c2;
131 k2 = _rotl64(k2,23);
132 k2 *= c1;
133 h2 ^= k2;
134 h2 += h1;
135
136 h1 = h1*3+0x52dce729;
137 h2 = h2*3+0x38495ab5;
138
139 c1 = c1*5+0x7b7d159c;
140 c2 = c2*5+0x6bce6396;
141}
142
143//----------
144// Finalization mix - avalanches all bits to within 0.05% bias
145
146inline u64 fmix64(u64 k)
147{
148 k ^= k >> 33;
149 k *= 0xff51afd7ed558ccd;
150 k ^= k >> 33;
151 k *= 0xc4ceb9fe1a85ec53;
152 k ^= k >> 33;
153
154 return k;
155}
156
157u64 GetMurmurHash3(const u8 *src, int len, u32 samples)
158{
159 const u8 * data = (const u8*)src;
160 const int nblocks = len / 16;
161 u32 Step = (len / 8);
162 if(samples == 0) samples = std::max(Step, 1u);
163 Step = Step / samples;
164 if(Step < 1) Step = 1;
165
166 u64 h1 = 0x9368e53c2f6af274;
167 u64 h2 = 0x586dcd208f7cd3fd;
168
169 u64 c1 = 0x87c37b91114253d5;
170 u64 c2 = 0x4cf5ad432745937f;
171
172
173 //----------
174 // body
175
176 const u64 * blocks = (const u64 *)(data);
177
178 for(int i = 0; i < nblocks; i+=Step)
179 {
180 u64 k1 = getblock(blocks,i*2+0);
181 u64 k2 = getblock(blocks,i*2+1);
182
183 bmix64(h1,h2,k1,k2,c1,c2);
184 }
185
186 //----------
187 // tail
188
189 const u8 * tail = (const u8*)(data + nblocks*16);
190
191 u64 k1 = 0;
192 u64 k2 = 0;
193
194 switch(len & 15)
195 {
196 case 15: k2 ^= u64(tail[14]) << 48;
197 case 14: k2 ^= u64(tail[13]) << 40;
198 case 13: k2 ^= u64(tail[12]) << 32;
199 case 12: k2 ^= u64(tail[11]) << 24;
200 case 11: k2 ^= u64(tail[10]) << 16;
201 case 10: k2 ^= u64(tail[ 9]) << 8;
202 case 9: k2 ^= u64(tail[ 8]) << 0;
203
204 case 8: k1 ^= u64(tail[ 7]) << 56;
205 case 7: k1 ^= u64(tail[ 6]) << 48;
206 case 6: k1 ^= u64(tail[ 5]) << 40;
207 case 5: k1 ^= u64(tail[ 4]) << 32;
208 case 4: k1 ^= u64(tail[ 3]) << 24;
209 case 3: k1 ^= u64(tail[ 2]) << 16;
210 case 2: k1 ^= u64(tail[ 1]) << 8;
211 case 1: k1 ^= u64(tail[ 0]) << 0;
212 bmix64(h1,h2,k1,k2,c1,c2);
213 };
214
215 //----------
216 // finalization
217
218 h2 ^= len;
219
220 h1 += h2;
221 h2 += h1;
222
223 h1 = fmix64(h1);
224 h2 = fmix64(h2);
225
226 h1 += h2;
227
228 return h1;
229}
230
231
232// CRC32 hash using the SSE4.2 instruction
233u64 GetCRC32(const u8 *src, int len, u32 samples)
234{
235#if _M_SSE >= 0x402
236 u64 h = len;
237 u32 Step = (len / 8);
238 const u64 *data = (const u64 *)src;
239 const u64 *end = data + Step;
240 if(samples == 0) samples = std::max(Step, 1u);
241 Step = Step / samples;
242 if(Step < 1) Step = 1;
243 while(data < end)
244 {
245 h = _mm_crc32_u64(h, data[0]);
246 data += Step;
247 }
248
249 const u8 *data2 = (const u8*)end;
250 return _mm_crc32_u64(h, u64(data2[0]));
251#else
252 return 0;
253#endif
254}
255
256
257/*
258 * NOTE: This hash function is used for custom texture loading/dumping, so
259 * it should not be changed, which would require all custom textures to be
260 * recalculated for their new hash values. If the hashing function is
261 * changed, make sure this one is still used when the legacy parameter is
262 * true.
263 */
264u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
265{
266 const u64 m = 0xc6a4a7935bd1e995;
267 u64 h = len * m;
268 const int r = 47;
269 u32 Step = (len / 8);
270 const u64 *data = (const u64 *)src;
271 const u64 *end = data + Step;
272 if(samples == 0) samples = std::max(Step, 1u);
273 Step = Step / samples;
274 if(Step < 1) Step = 1;
275 while(data < end)
276 {
277 u64 k = data[0];
278 data+=Step;
279 k *= m;
280 k ^= k >> r;
281 k *= m;
282 h ^= k;
283 h *= m;
284 }
285
286 const u8 * data2 = (const u8*)end;
287
288 switch(len & 7)
289 {
290 case 7: h ^= u64(data2[6]) << 48;
291 case 6: h ^= u64(data2[5]) << 40;
292 case 5: h ^= u64(data2[4]) << 32;
293 case 4: h ^= u64(data2[3]) << 24;
294 case 3: h ^= u64(data2[2]) << 16;
295 case 2: h ^= u64(data2[1]) << 8;
296 case 1: h ^= u64(data2[0]);
297 h *= m;
298 };
299
300 h ^= h >> r;
301 h *= m;
302 h ^= h >> r;
303
304 return h;
305}
306#else
307// CRC32 hash using the SSE4.2 instruction
308u64 GetCRC32(const u8 *src, int len, u32 samples)
309{
310#if _M_SSE >= 0x402
311 u32 h = len;
312 u32 Step = (len/4);
313 const u32 *data = (const u32 *)src;
314 const u32 *end = data + Step;
315 if(samples == 0) samples = std::max(Step, 1u);
316 Step = Step / samples;
317 if(Step < 1) Step = 1;
318 while(data < end)
319 {
320 h = _mm_crc32_u32(h, data[0]);
321 data += Step;
322 }
323
324 const u8 *data2 = (const u8*)end;
325 return (u64)_mm_crc32_u32(h, u32(data2[0]));
326#else
327 return 0;
328#endif
329}
330
331//-----------------------------------------------------------------------------
332// Block read - if your platform needs to do endian-swapping or can only
333// handle aligned reads, do the conversion here
334
335inline u32 getblock(const u32 * p, int i)
336{
337 return p[i];
338}
339
340//----------
341// Finalization mix - force all bits of a hash block to avalanche
342
343// avalanches all bits to within 0.25% bias
344
345inline u32 fmix32(u32 h)
346{
347 h ^= h >> 16;
348 h *= 0x85ebca6b;
349 h ^= h >> 13;
350 h *= 0xc2b2ae35;
351 h ^= h >> 16;
352
353 return h;
354}
355
356inline void bmix32(u32 & h1, u32 & h2, u32 & k1, u32 & k2, u32 & c1, u32 & c2)
357{
358 k1 *= c1;
359 k1 = _rotl(k1,11);
360 k1 *= c2;
361 h1 ^= k1;
362 h1 += h2;
363
364 h2 = _rotl(h2,17);
365
366 k2 *= c2;
367 k2 = _rotl(k2,11);
368 k2 *= c1;
369 h2 ^= k2;
370 h2 += h1;
371
372 h1 = h1*3+0x52dce729;
373 h2 = h2*3+0x38495ab5;
374
375 c1 = c1*5+0x7b7d159c;
376 c2 = c2*5+0x6bce6396;
377}
378
379//----------
380
381u64 GetMurmurHash3(const u8* src, int len, u32 samples)
382{
383 const u8 * data = (const u8*)src;
384 u32 out[2];
385 const int nblocks = len / 8;
386 u32 Step = (len / 4);
387 if(samples == 0) samples = std::max(Step, 1u);
388 Step = Step / samples;
389 if(Step < 1) Step = 1;
390
391 u32 h1 = 0x8de1c3ac;
392 u32 h2 = 0xbab98226;
393
394 u32 c1 = 0x95543787;
395 u32 c2 = 0x2ad7eb25;
396
397 //----------
398 // body
399
400 const u32 * blocks = (const u32 *)(data + nblocks*8);
401
402 for(int i = -nblocks; i < 0; i+=Step)
403 {
404 u32 k1 = getblock(blocks,i*2+0);
405 u32 k2 = getblock(blocks,i*2+1);
406
407 bmix32(h1,h2,k1,k2,c1,c2);
408 }
409
410 //----------
411 // tail
412
413 const u8 * tail = (const u8*)(data + nblocks*8);
414
415 u32 k1 = 0;
416 u32 k2 = 0;
417
418 switch(len & 7)
419 {
420 case 7: k2 ^= tail[6] << 16;
421 case 6: k2 ^= tail[5] << 8;
422 case 5: k2 ^= tail[4] << 0;
423 case 4: k1 ^= tail[3] << 24;
424 case 3: k1 ^= tail[2] << 16;
425 case 2: k1 ^= tail[1] << 8;
426 case 1: k1 ^= tail[0] << 0;
427 bmix32(h1,h2,k1,k2,c1,c2);
428 };
429
430 //----------
431 // finalization
432
433 h2 ^= len;
434
435 h1 += h2;
436 h2 += h1;
437
438 h1 = fmix32(h1);
439 h2 = fmix32(h2);
440
441 h1 += h2;
442 h2 += h1;
443
444 out[0] = h1;
445 out[1] = h2;
446
447 return *((u64 *)&out);
448}
449
450/*
451 * FIXME: The old 32-bit version of this hash made different hashes than the
452 * 64-bit version. Until someone can make a new version of the 32-bit one that
453 * makes identical hashes, this is just a c/p of the 64-bit one.
454 */
455u64 GetHashHiresTexture(const u8 *src, int len, u32 samples)
456{
457 const u64 m = 0xc6a4a7935bd1e995ULL;
458 u64 h = len * m;
459 const int r = 47;
460 u32 Step = (len / 8);
461 const u64 *data = (const u64 *)src;
462 const u64 *end = data + Step;
463 if(samples == 0) samples = std::max(Step, 1u);
464 Step = Step / samples;
465 if(Step < 1) Step = 1;
466 while(data < end)
467 {
468 u64 k = data[0];
469 data+=Step;
470 k *= m;
471 k ^= k >> r;
472 k *= m;
473 h ^= k;
474 h *= m;
475 }
476
477 const u8 * data2 = (const u8*)end;
478
479 switch(len & 7)
480 {
481 case 7: h ^= u64(data2[6]) << 48;
482 case 6: h ^= u64(data2[5]) << 40;
483 case 5: h ^= u64(data2[4]) << 32;
484 case 4: h ^= u64(data2[3]) << 24;
485 case 3: h ^= u64(data2[2]) << 16;
486 case 2: h ^= u64(data2[1]) << 8;
487 case 1: h ^= u64(data2[0]);
488 h *= m;
489 };
490
491 h ^= h >> r;
492 h *= m;
493 h ^= h >> r;
494
495 return h;
496}
497#endif
498
499u64 GetHash64(const u8 *src, int len, u32 samples)
500{
501 return ptrHashFunction(src, len, samples);
502}
503
504// sets the hash function used for the texture cache
505void SetHash64Function(bool useHiresTextures)
506{
507 if (useHiresTextures)
508 {
509 ptrHashFunction = &GetHashHiresTexture;
510 }
511#if _M_SSE >= 0x402
512 else if (cpu_info.bSSE4_2 && !useHiresTextures) // sse crc32 version
513 {
514 ptrHashFunction = &GetCRC32;
515 }
516#endif
517 else
518 {
519 ptrHashFunction = &GetMurmurHash3;
520 }
521}
522
523
524
diff --git a/src/common/hash.h b/src/common/hash.h
deleted file mode 100644
index 0afaf0e37..000000000
--- a/src/common/hash.h
+++ /dev/null
@@ -1,17 +0,0 @@
1// Copyright 2013 Dolphin Emulator Project / 2014 Citra Emulator Project
2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included.
4
5#pragma once
6
7#include "common/common_types.h"
8
9u32 HashFletcher(const u8* data_u8, size_t length); // FAST. Length & 1 == 0.
10u32 HashAdler32(const u8* data, size_t len); // Fairly accurate, slightly slower
11u32 HashFNV(const u8* ptr, int length); // Another fast and decent hash
12u32 HashEctor(const u8* ptr, int length); // JUNK. DO NOT USE FOR NEW THINGS
13u64 GetCRC32(const u8 *src, int len, u32 samples); // SSE4.2 version of CRC32
14u64 GetHashHiresTexture(const u8 *src, int len, u32 samples);
15u64 GetMurmurHash3(const u8 *src, int len, u32 samples);
16u64 GetHash64(const u8 *src, int len, u32 samples);
17void SetHash64Function(bool useHiresTextures);
diff --git a/src/common/mem_arena.cpp b/src/common/mem_arena.cpp
deleted file mode 100644
index 689fdb92b..000000000
--- a/src/common/mem_arena.cpp
+++ /dev/null
@@ -1,394 +0,0 @@
1// Copyright (C) 2003 Dolphin 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#include <string>
19
20#include "common/logging/log.h"
21#include "common/mem_arena.h"
22#include "common/memory_util.h"
23#include "common/platform.h"
24#include "common/string_util.h"
25
26#ifndef _WIN32
27#include <fcntl.h>
28#include <string.h>
29#include <unistd.h>
30
31#ifdef ANDROID
32#include <sys/ioctl.h>
33#include <linux/ashmem.h>
34#endif
35#endif
36
37#ifdef ANDROID
38
39// Hopefully this ABI will never change...
40
41
42#define ASHMEM_DEVICE "/dev/ashmem"
43
44/*
45* ashmem_create_region - creates a new ashmem region and returns the file
46* descriptor, or <0 on error
47*
48* `name' is an optional label to give the region (visible in /proc/pid/maps)
49* `size' is the size of the region, in page-aligned bytes
50*/
51int ashmem_create_region(const char *name, size_t size)
52{
53 int fd, ret;
54
55 fd = open(ASHMEM_DEVICE, O_RDWR);
56 if (fd < 0)
57 return fd;
58
59 if (name) {
60 char buf[ASHMEM_NAME_LEN];
61
62 strncpy(buf, name, sizeof(buf));
63 ret = ioctl(fd, ASHMEM_SET_NAME, buf);
64 if (ret < 0)
65 goto error;
66 }
67
68 ret = ioctl(fd, ASHMEM_SET_SIZE, size);
69 if (ret < 0)
70 goto error;
71
72 return fd;
73
74error:
75 LOG_ERROR(Common_Memory, "NASTY ASHMEM ERROR: ret = %08x", ret);
76 close(fd);
77 return ret;
78}
79
80int ashmem_set_prot_region(int fd, int prot)
81{
82 return ioctl(fd, ASHMEM_SET_PROT_MASK, prot);
83}
84
85int ashmem_pin_region(int fd, size_t offset, size_t len)
86{
87 struct ashmem_pin pin = { offset, len };
88 return ioctl(fd, ASHMEM_PIN, &pin);
89}
90
91int ashmem_unpin_region(int fd, size_t offset, size_t len)
92{
93 struct ashmem_pin pin = { offset, len };
94 return ioctl(fd, ASHMEM_UNPIN, &pin);
95}
96#endif // Android
97
98
99#if defined(_WIN32)
100SYSTEM_INFO sysInfo;
101#endif
102
103
104// Windows mappings need to be on 64K boundaries, due to Alpha legacy.
105#ifdef _WIN32
106size_t roundup(size_t x) {
107 int gran = sysInfo.dwAllocationGranularity ? sysInfo.dwAllocationGranularity : 0x10000;
108 return (x + gran - 1) & ~(gran - 1);
109}
110#else
111size_t roundup(size_t x) {
112 return x;
113}
114#endif
115
116
117void MemArena::GrabLowMemSpace(size_t size)
118{
119#ifdef _WIN32
120 hMemoryMapping = CreateFileMapping(INVALID_HANDLE_VALUE, nullptr, PAGE_READWRITE, 0, (DWORD)(size), nullptr);
121 GetSystemInfo(&sysInfo);
122#elif defined(ANDROID)
123 // Use ashmem so we don't have to allocate a file on disk!
124 fd = ashmem_create_region("Citra_RAM", size);
125 // Note that it appears that ashmem is pinned by default, so no need to pin.
126 if (fd < 0)
127 {
128 LOG_ERROR(Common_Memory, "Failed to grab ashmem space of size: %08x errno: %d", (int)size, (int)(errno));
129 return;
130 }
131#else
132 // Try to find a non-existing filename for our shared memory.
133 // In most cases the first one will be available, but it's nicer to search
134 // a bit more.
135 for (int i = 0; i < 10000; i++)
136 {
137 std::string file_name = Common::StringFromFormat("/citramem.%d", i);
138 fd = shm_open(file_name.c_str(), O_RDWR | O_CREAT | O_EXCL, 0600);
139 if (fd != -1)
140 {
141 shm_unlink(file_name.c_str());
142 break;
143 }
144 else if (errno != EEXIST)
145 {
146 LOG_ERROR(Common_Memory, "shm_open failed: %s", strerror(errno));
147 return;
148 }
149 }
150 if (ftruncate(fd, size) < 0)
151 LOG_ERROR(Common_Memory, "Failed to allocate low memory space");
152#endif
153}
154
155
156void MemArena::ReleaseSpace()
157{
158#ifdef _WIN32
159 CloseHandle(hMemoryMapping);
160 hMemoryMapping = 0;
161#else
162 close(fd);
163#endif
164}
165
166
167void *MemArena::CreateView(s64 offset, size_t size, void *base)
168{
169#ifdef _WIN32
170 size = roundup(size);
171 void *ptr = MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base);
172 return ptr;
173#else
174 void *retval = mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED |
175 // Do not sync memory to underlying file. Linux has this by default.
176#ifdef __FreeBSD__
177 MAP_NOSYNC |
178#endif
179 ((base == nullptr) ? 0 : MAP_FIXED), fd, offset);
180
181 if (retval == MAP_FAILED)
182 {
183 LOG_ERROR(Common_Memory, "mmap failed");
184 return nullptr;
185 }
186 return retval;
187#endif
188}
189
190
191void MemArena::ReleaseView(void* view, size_t size)
192{
193#ifdef _WIN32
194 UnmapViewOfFile(view);
195#else
196 munmap(view, size);
197#endif
198}
199
200u8* MemArena::Find4GBBase()
201{
202#if EMU_ARCH_BITS == 64
203#ifdef _WIN32
204 // 64 bit
205 u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE);
206 VirtualFree(base, 0, MEM_RELEASE);
207 return base;
208#else
209 // Very precarious - mmap cannot return an error when trying to map already used pages.
210 // This makes the Windows approach above unusable on Linux, so we will simply pray...
211 return reinterpret_cast<u8*>(0x2300000000ULL);
212#endif
213
214#else // 32 bit
215
216#ifdef _WIN32
217 u8* base = (u8*)VirtualAlloc(0, 0x10000000, MEM_RESERVE, PAGE_READWRITE);
218 if (base) {
219 VirtualFree(base, 0, MEM_RELEASE);
220 }
221 return base;
222#else
223 void* base = mmap(0, 0x10000000, PROT_READ | PROT_WRITE,
224 MAP_ANON | MAP_SHARED, -1, 0);
225 if (base == MAP_FAILED) {
226 LOG_ERROR(Common_Memory, "Failed to map 256 MB of memory space: %s", strerror(errno));
227 return 0;
228 }
229 munmap(base, 0x10000000);
230 return static_cast<u8*>(base);
231#endif
232#endif
233}
234
235
236// yeah, this could also be done in like two bitwise ops...
237#define SKIP(a_flags, b_flags)
238//if (!(a_flags & MV_WII_ONLY) && (b_flags & MV_WII_ONLY))
239// continue;
240//if (!(a_flags & MV_FAKE_VMEM) && (b_flags & MV_FAKE_VMEM))
241// continue;
242
243static bool Memory_TryBase(u8 *base, const MemoryView *views, int num_views, u32 flags, MemArena *arena) {
244 // OK, we know where to find free space. Now grab it!
245 // We just mimic the popular BAT setup.
246 size_t position = 0;
247 size_t last_position = 0;
248
249 // Zero all the pointers to be sure.
250 for (int i = 0; i < num_views; i++)
251 {
252 if (views[i].out_ptr_low)
253 *views[i].out_ptr_low = 0;
254 if (views[i].out_ptr)
255 *views[i].out_ptr = 0;
256 }
257
258 int i;
259 for (i = 0; i < num_views; i++)
260 {
261 const MemoryView &view = views[i];
262 if (view.size == 0)
263 continue;
264 SKIP(flags, view.flags);
265 if (view.flags & MV_MIRROR_PREVIOUS) {
266 position = last_position;
267 }
268 else {
269 *(view.out_ptr_low) = (u8*)arena->CreateView(position, view.size);
270 if (!*view.out_ptr_low)
271 goto bail;
272 }
273#if EMU_ARCH_BITS == 64
274 *view.out_ptr = (u8*)arena->CreateView(
275 position, view.size, base + view.virtual_address);
276#else
277 if (view.flags & MV_MIRROR_PREVIOUS) { // TODO: should check if the two & 0x3FFFFFFF are identical.
278 // No need to create multiple identical views.
279 *view.out_ptr = *views[i - 1].out_ptr;
280 }
281 else {
282 *view.out_ptr = (u8*)arena->CreateView(
283 position, view.size, base + (view.virtual_address & 0x3FFFFFFF));
284 if (!*view.out_ptr)
285 goto bail;
286 }
287#endif
288
289 last_position = position;
290 position += roundup(view.size);
291 }
292
293 return true;
294
295bail:
296 // Argh! ERROR! Free what we grabbed so far so we can try again.
297 for (int j = 0; j <= i; j++)
298 {
299 if (views[i].size == 0)
300 continue;
301 SKIP(flags, views[i].flags);
302 if (views[j].out_ptr_low && *views[j].out_ptr_low)
303 {
304 arena->ReleaseView(*views[j].out_ptr_low, views[j].size);
305 *views[j].out_ptr_low = nullptr;
306 }
307 if (*views[j].out_ptr)
308 {
309#if EMU_ARCH_BITS == 64
310 arena->ReleaseView(*views[j].out_ptr, views[j].size);
311#else
312 if (!(views[j].flags & MV_MIRROR_PREVIOUS))
313 {
314 arena->ReleaseView(*views[j].out_ptr, views[j].size);
315 }
316#endif
317 *views[j].out_ptr = nullptr;
318 }
319 }
320 return false;
321}
322
323u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena)
324{
325 size_t total_mem = 0;
326 int base_attempts = 0;
327
328 for (int i = 0; i < num_views; i++)
329 {
330 if (views[i].size == 0)
331 continue;
332 SKIP(flags, views[i].flags);
333 if ((views[i].flags & MV_MIRROR_PREVIOUS) == 0)
334 total_mem += roundup(views[i].size);
335 }
336 // Grab some pagefile backed memory out of the void ...
337 arena->GrabLowMemSpace(total_mem);
338
339 // Now, create views in high memory where there's plenty of space.
340#if EMU_ARCH_BITS == 64
341 u8 *base = MemArena::Find4GBBase();
342 // This really shouldn't fail - in 64-bit, there will always be enough
343 // address space.
344 if (!Memory_TryBase(base, views, num_views, flags, arena))
345 {
346 LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
347 return 0;
348 }
349#elif defined(_WIN32)
350 // Try a whole range of possible bases. Return once we got a valid one.
351 u32 max_base_addr = 0x7FFF0000 - 0x10000000;
352 u8 *base = nullptr;
353
354 for (u32 base_addr = 0x01000000; base_addr < max_base_addr; base_addr += 0x400000)
355 {
356 base_attempts++;
357 base = (u8 *)base_addr;
358 if (Memory_TryBase(base, views, num_views, flags, arena))
359 {
360 LOG_DEBUG(Common_Memory, "Found valid memory base at %p after %i tries.", base, base_attempts);
361 base_attempts = 0;
362 break;
363 }
364 }
365#else
366 // Linux32 is fine with the x64 method, although limited to 32-bit with no automirrors.
367 u8 *base = MemArena::Find4GBBase();
368 if (!Memory_TryBase(base, views, num_views, flags, arena))
369 {
370 LOG_ERROR(Common_Memory, "MemoryMap_Setup: Failed finding a memory base.");
371 return 0;
372 }
373#endif
374 if (base_attempts)
375 LOG_ERROR(Common_Memory, "No possible memory base pointer found!");
376 return base;
377}
378
379void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena)
380{
381 for (int i = 0; i < num_views; i++)
382 {
383 if (views[i].size == 0)
384 continue;
385 SKIP(flags, views[i].flags);
386 if (views[i].out_ptr_low && *views[i].out_ptr_low)
387 arena->ReleaseView(*views[i].out_ptr_low, views[i].size);
388 if (*views[i].out_ptr && (views[i].out_ptr_low && *views[i].out_ptr != *views[i].out_ptr_low))
389 arena->ReleaseView(*views[i].out_ptr, views[i].size);
390 *views[i].out_ptr = nullptr;
391 if (views[i].out_ptr_low)
392 *views[i].out_ptr_low = nullptr;
393 }
394}
diff --git a/src/common/mem_arena.h b/src/common/mem_arena.h
deleted file mode 100644
index d514fe58c..000000000
--- a/src/common/mem_arena.h
+++ /dev/null
@@ -1,70 +0,0 @@
1// Copyright (C) 2003 Dolphin 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#ifdef _WIN32
21#include <windows.h>
22#endif
23
24#include "common/common_types.h"
25
26// This class lets you create a block of anonymous RAM, and then arbitrarily map views into it.
27// Multiple views can mirror the same section of the block, which makes it very convient for emulating
28// memory mirrors.
29
30class MemArena
31{
32public:
33 void GrabLowMemSpace(size_t size);
34 void ReleaseSpace();
35 void *CreateView(s64 offset, size_t size, void *base = 0);
36 void ReleaseView(void *view, size_t size);
37
38 // This only finds 1 GB in 32-bit
39 static u8 *Find4GBBase();
40private:
41
42#ifdef _WIN32
43 HANDLE hMemoryMapping;
44#else
45 int fd;
46#endif
47};
48
49enum {
50 MV_MIRROR_PREVIOUS = 1,
51 // MV_FAKE_VMEM = 2,
52 // MV_WII_ONLY = 4,
53 MV_IS_PRIMARY_RAM = 0x100,
54 MV_IS_EXTRA1_RAM = 0x200,
55 MV_IS_EXTRA2_RAM = 0x400,
56};
57
58struct MemoryView
59{
60 u8 **out_ptr_low;
61 u8 **out_ptr;
62 u32 virtual_address;
63 u32 size;
64 u32 flags;
65};
66
67// Uses a memory arena to set up an emulator-friendly memory map according to
68// a passed-in list of MemoryView structures.
69u8 *MemoryMap_Setup(const MemoryView *views, int num_views, u32 flags, MemArena *arena);
70void MemoryMap_Shutdown(const MemoryView *views, int num_views, u32 flags, MemArena *arena);
diff --git a/src/core/mem_map.cpp b/src/core/mem_map.cpp
index ae88cfb11..46e271c80 100644
--- a/src/core/mem_map.cpp
+++ b/src/core/mem_map.cpp
@@ -2,10 +2,8 @@
2// Licensed under GPLv2 or any later version 2// Licensed under GPLv2 or any later version
3// Refer to the license.txt file included. 3// Refer to the license.txt file included.
4 4
5#include "common/common_funcs.h"
6#include "common/common_types.h" 5#include "common/common_types.h"
7#include "common/logging/log.h" 6#include "common/logging/log.h"
8#include "common/mem_arena.h"
9 7
10#include "core/mem_map.h" 8#include "core/mem_map.h"
11 9
@@ -13,100 +11,51 @@
13 11
14namespace Memory { 12namespace Memory {
15 13
16u8* g_base; ///< The base pointer to the auto-mirrored arena. 14u8* g_exefs_code; ///< ExeFS:/.code is loaded here
15u8* g_system_mem; ///< System memory
16u8* g_heap; ///< Application heap (main memory)
17u8* g_heap_linear; ///< Linear heap
18u8* g_vram; ///< Video memory (VRAM) pointer
19u8* g_shared_mem; ///< Shared memory
20u8* g_dsp_mem; ///< DSP memory
21u8* g_kernel_mem; ///< Kernel memory
17 22
18static MemArena arena; ///< The MemArena class 23namespace {
19 24
20u8* g_exefs_code; ///< ExeFS:/.code is loaded here 25struct MemoryArea {
21u8* g_system_mem; ///< System memory 26 u8** ptr;
22u8* g_heap; ///< Application heap (main memory) 27 size_t size;
23u8* g_heap_linear; ///< Linear heap
24u8* g_vram; ///< Video memory (VRAM) pointer
25u8* g_shared_mem; ///< Shared memory
26u8* g_dsp_mem; ///< DSP memory
27u8* g_kernel_mem; ///< Kernel memory
28
29static u8* physical_bootrom; ///< Bootrom physical memory
30static u8* uncached_bootrom;
31
32static u8* physical_exefs_code; ///< Phsical ExeFS:/.code is loaded here
33static u8* physical_system_mem; ///< System physical memory
34static u8* physical_fcram; ///< Main physical memory (FCRAM)
35static u8* physical_heap_gsp; ///< GSP heap physical memory
36static u8* physical_vram; ///< Video physical memory (VRAM)
37static u8* physical_shared_mem; ///< Physical shared memory
38static u8* physical_dsp_mem; ///< Physical DSP memory
39static u8* physical_kernel_mem; ///< Kernel memory
40
41// We don't declare the IO region in here since its handled by other means.
42static MemoryView g_views[] = {
43 {&g_exefs_code, &physical_exefs_code, EXEFS_CODE_VADDR, EXEFS_CODE_SIZE, 0},
44 {&g_vram, &physical_vram, VRAM_VADDR, VRAM_SIZE, 0},
45 {&g_heap, &physical_fcram, HEAP_VADDR, HEAP_SIZE, MV_IS_PRIMARY_RAM},
46 {&g_shared_mem, &physical_shared_mem, SHARED_MEMORY_VADDR, SHARED_MEMORY_SIZE, 0},
47 {&g_system_mem, &physical_system_mem, SYSTEM_MEMORY_VADDR, SYSTEM_MEMORY_SIZE, 0},
48 {&g_dsp_mem, &physical_dsp_mem, DSP_MEMORY_VADDR, DSP_MEMORY_SIZE, 0},
49 {&g_kernel_mem, &physical_kernel_mem, KERNEL_MEMORY_VADDR, KERNEL_MEMORY_SIZE, 0},
50 {&g_heap_linear, &physical_heap_gsp, HEAP_LINEAR_VADDR, HEAP_LINEAR_SIZE, 0},
51}; 28};
52 29
53/*static MemoryView views[] = 30// We don't declare the IO regions in here since its handled by other means.
54{ 31static MemoryArea memory_areas[] = {
55 {&m_pScratchPad, &m_pPhysicalScratchPad, 0x00010000, SCRATCHPAD_SIZE, 0}, 32 {&g_exefs_code, EXEFS_CODE_SIZE },
56 {NULL, &m_pUncachedScratchPad, 0x40010000, SCRATCHPAD_SIZE, MV_MIRROR_PREVIOUS}, 33 {&g_vram, VRAM_SIZE },
57 {&m_pVRAM, &m_pPhysicalVRAM, 0x04000000, 0x00800000, 0}, 34 {&g_heap, HEAP_SIZE },
58 {NULL, &m_pUncachedVRAM, 0x44000000, 0x00800000, MV_MIRROR_PREVIOUS}, 35 {&g_shared_mem, SHARED_MEMORY_SIZE},
59 {&m_pRAM, &m_pPhysicalRAM, 0x08000000, g_MemorySize, MV_IS_PRIMARY_RAM}, // only from 0x08800000 is it usable (last 24 megs) 36 {&g_system_mem, SYSTEM_MEMORY_SIZE},
60 {NULL, &m_pUncachedRAM, 0x48000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, 37 {&g_dsp_mem, DSP_MEMORY_SIZE },
61 {NULL, &m_pKernelRAM, 0x88000000, g_MemorySize, MV_MIRROR_PREVIOUS | MV_IS_PRIMARY_RAM}, 38 {&g_kernel_mem, KERNEL_MEMORY_SIZE},
62 39 {&g_heap_linear, HEAP_LINEAR_SIZE },
63 // TODO: There are a few swizzled mirrors of VRAM, not sure about the best way to 40};
64 // implement those.
65};*/
66 41
67static const int kNumMemViews = sizeof(g_views) / sizeof(MemoryView); ///< Number of mem views 42}
68 43
69void Init() { 44void Init() {
70 int flags = 0; 45 for (MemoryArea& area : memory_areas) {
71 46 *area.ptr = new u8[area.size];
72 for (size_t i = 0; i < ARRAY_SIZE(g_views); i++) {
73 if (g_views[i].flags & MV_IS_PRIMARY_RAM)
74 g_views[i].size = FCRAM_SIZE;
75 } 47 }
76
77 g_base = MemoryMap_Setup(g_views, kNumMemViews, flags, &arena);
78 MemBlock_Init(); 48 MemBlock_Init();
79 49
80 LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p (mirror at 0 @ %p)", g_heap, 50 LOG_DEBUG(HW_Memory, "initialized OK, RAM at %p", g_heap);
81 physical_fcram);
82} 51}
83 52
84void Shutdown() { 53void Shutdown() {
85 u32 flags = 0;
86 MemoryMap_Shutdown(g_views, kNumMemViews, flags, &arena);
87 arena.ReleaseSpace();
88 MemBlock_Shutdown(); 54 MemBlock_Shutdown();
89 55 for (MemoryArea& area : memory_areas) {
90 g_base = nullptr; 56 delete[] *area.ptr;
91 g_exefs_code = nullptr; 57 *area.ptr = nullptr;
92 g_system_mem = nullptr; 58 }
93 g_heap = nullptr;
94 g_heap_linear = nullptr;
95 g_vram = nullptr;
96 g_shared_mem = nullptr;
97 g_dsp_mem = nullptr;
98 g_kernel_mem = nullptr;
99
100 physical_bootrom = nullptr;
101 uncached_bootrom = nullptr;
102 physical_exefs_code = nullptr;
103 physical_system_mem = nullptr;
104 physical_fcram = nullptr;
105 physical_heap_gsp = nullptr;
106 physical_vram = nullptr;
107 physical_shared_mem = nullptr;
108 physical_dsp_mem = nullptr;
109 physical_kernel_mem = nullptr;
110 59
111 LOG_DEBUG(HW_Memory, "shutdown OK"); 60 LOG_DEBUG(HW_Memory, "shutdown OK");
112} 61}
diff --git a/src/core/mem_map.h b/src/core/mem_map.h
index b11f2ea68..1fb77c94a 100644
--- a/src/core/mem_map.h
+++ b/src/core/mem_map.h
@@ -105,18 +105,6 @@ struct MemoryBlock {
105 105
106//////////////////////////////////////////////////////////////////////////////////////////////////// 106////////////////////////////////////////////////////////////////////////////////////////////////////
107 107
108// Base is a pointer to the base of the memory map. Yes, some MMU tricks
109// are used to set up a full GC or Wii memory map in process memory. on
110// 32-bit, you have to mask your offsets with 0x3FFFFFFF. This means that
111// some things are mirrored too many times, but eh... it works.
112
113// In 64-bit, this might point to "high memory" (above the 32-bit limit),
114// so be sure to load it into a 64-bit register.
115extern u8 *g_base;
116
117// These are guaranteed to point to "low memory" addresses (sub-32-bit).
118// 64-bit: Pointers to low-mem (sub-0x10000000) mirror
119// 32-bit: Same as the corresponding physical/virtual pointers.
120extern u8* g_heap_linear; ///< Linear heap (main memory) 108extern u8* g_heap_linear; ///< Linear heap (main memory)
121extern u8* g_heap; ///< Application heap (main memory) 109extern u8* g_heap; ///< Application heap (main memory)
122extern u8* g_vram; ///< Video memory (VRAM) 110extern u8* g_vram; ///< Video memory (VRAM)