13301992cad5ab9063094597adb5fc8d72a44d8e
[pcsx_rearmed.git] / libpcsxcore / psxmem.c
1 /***************************************************************************
2  *   Copyright (C) 2007 Ryan Schultz, PCSX-df Team, PCSX team              *
3  *                                                                         *
4  *   This program is free software; you can redistribute it and/or modify  *
5  *   it under the terms of the GNU General Public License as published by  *
6  *   the Free Software Foundation; either version 2 of the License, or     *
7  *   (at your option) any later version.                                   *
8  *                                                                         *
9  *   This program is distributed in the hope that it will be useful,       *
10  *   but WITHOUT ANY WARRANTY; without even the implied warranty of        *
11  *   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the         *
12  *   GNU General Public License for more details.                          *
13  *                                                                         *
14  *   You should have received a copy of the GNU General Public License     *
15  *   along with this program; if not, write to the                         *
16  *   Free Software Foundation, Inc.,                                       *
17  *   51 Franklin Street, Fifth Floor, Boston, MA 02111-1307 USA.           *
18  ***************************************************************************/
19
20 /*
21 * PSX memory functions.
22 */
23
24 // TODO: Implement caches & cycle penalty.
25
26 #include "psxmem.h"
27 #include "psxmem_map.h"
28 #include "r3000a.h"
29 #include "psxhw.h"
30 //#include "debug.h"
31 #define DebugCheckBP(...)
32
33 #include "lightrec/mem.h"
34 #include "memmap.h"
35
36 #ifdef USE_LIBRETRO_VFS
37 #include <streams/file_stream_transforms.h>
38 #endif
39
40 #ifndef MAP_ANONYMOUS
41 #define MAP_ANONYMOUS MAP_ANON
42 #endif
43
44 static void * psxMapDefault(unsigned long addr, size_t size,
45                             enum psxMapTag tag, int *can_retry_addr)
46 {
47         void *ptr;
48 #if !P_HAVE_MMAP
49         *can_retry_addr = 0;
50         ptr = calloc(1, size);
51         return ptr ? ptr : MAP_FAILED;
52 #else
53         int flags = MAP_PRIVATE | MAP_ANONYMOUS;
54
55         *can_retry_addr = 1;
56         ptr = mmap((void *)(uintptr_t)addr, size,
57                     PROT_READ | PROT_WRITE, flags, -1, 0);
58 #ifdef MADV_HUGEPAGE
59         if (size >= 2*1024*1024) {
60                 if (ptr != MAP_FAILED && ((uintptr_t)ptr & (2*1024*1024 - 1))) {
61                         // try to manually realign assuming decreasing addr alloc
62                         munmap(ptr, size);
63                         addr = (uintptr_t)ptr & ~(2*1024*1024 - 1);
64                         ptr = mmap((void *)(uintptr_t)addr, size,
65                                 PROT_READ | PROT_WRITE, flags, -1, 0);
66                 }
67                 if (ptr != MAP_FAILED)
68                         madvise(ptr, size, MADV_HUGEPAGE);
69         }
70 #endif
71         return ptr;
72 #endif
73 }
74
75 static void psxUnmapDefault(void *ptr, size_t size, enum psxMapTag tag)
76 {
77 #if !P_HAVE_MMAP
78         free(ptr);
79 #else
80         munmap(ptr, size);
81 #endif
82 }
83
84 void *(*psxMapHook)(unsigned long addr, size_t size,
85                 enum psxMapTag tag, int *can_retry_addr) = psxMapDefault;
86 void (*psxUnmapHook)(void *ptr, size_t size,
87                      enum psxMapTag tag) = psxUnmapDefault;
88
89 void *psxMap(unsigned long addr, size_t size, int is_fixed,
90                 enum psxMapTag tag)
91 {
92         int try_, can_retry_addr = 0;
93         void *ret = MAP_FAILED;
94
95         for (try_ = 0; try_ < 3; try_++)
96         {
97                 if (ret != MAP_FAILED)
98                         psxUnmap(ret, size, tag);
99                 ret = psxMapHook(addr, size, tag, &can_retry_addr);
100                 if (ret == MAP_FAILED)
101                         return MAP_FAILED;
102
103                 if (addr != 0 && ret != (void *)(uintptr_t)addr) {
104                         SysMessage("psxMap: tried to map @%08lx, got %p\n",
105                                 addr, ret);
106                         if (is_fixed) {
107                                 psxUnmap(ret, size, tag);
108                                 return MAP_FAILED;
109                         }
110
111                         if (can_retry_addr && ((addr ^ (uintptr_t)ret) & ~0xff000000l)) {
112                                 unsigned long mask;
113
114                                 // try to use similarly aligned memory instead
115                                 // (recompiler prefers this)
116                                 mask = try_ ? 0xffff : 0xffffff;
117                                 addr = ((uintptr_t)ret + mask) & ~mask;
118                                 continue;
119                         }
120                 }
121                 break;
122         }
123
124         return ret;
125 }
126
127 void psxUnmap(void *ptr, size_t size, enum psxMapTag tag)
128 {
129         psxUnmapHook(ptr, size, tag);
130 }
131
132 s8 *psxM = NULL; // Kernel & User Memory (2 Meg)
133 s8 *psxP = NULL; // Parallel Port (64K)
134 s8 *psxR = NULL; // BIOS ROM (512K)
135 s8 *psxH = NULL; // Scratch Pad (1K) & Hardware Registers (8K)
136
137 u8 **psxMemWLUT = NULL;
138 u8 **psxMemRLUT = NULL;
139
140 /*  Playstation Memory Map (from Playstation doc by Joshua Walker)
141 0x0000_0000-0x0000_ffff         Kernel (64K)
142 0x0001_0000-0x001f_ffff         User Memory (1.9 Meg)
143
144 0x1f00_0000-0x1f00_ffff         Parallel Port (64K)
145
146 0x1f80_0000-0x1f80_03ff         Scratch Pad (1024 bytes)
147
148 0x1f80_1000-0x1f80_2fff         Hardware Registers (8K)
149
150 0x1fc0_0000-0x1fc7_ffff         BIOS (512K)
151
152 0x8000_0000-0x801f_ffff         Kernel and User Memory Mirror (2 Meg) Cached
153 0x9fc0_0000-0x9fc7_ffff         BIOS Mirror (512K) Cached
154
155 0xa000_0000-0xa01f_ffff         Kernel and User Memory Mirror (2 Meg) Uncached
156 0xbfc0_0000-0xbfc7_ffff         BIOS Mirror (512K) Uncached
157 */
158
159 static int psxMemInitMap(void)
160 {
161         psxM = psxMap(0x80000000, 0x00210000, 1, MAP_TAG_RAM);
162         if (psxM == MAP_FAILED)
163                 psxM = psxMap(0x77000000, 0x00210000, 0, MAP_TAG_RAM);
164         if (psxM == MAP_FAILED) {
165                 SysMessage("mapping main RAM failed");
166                 psxM = NULL;
167                 return -1;
168         }
169         psxP = &psxM[0x200000];
170
171         psxH = psxMap(0x1f800000, 0x10000, 0, MAP_TAG_OTHER);
172         if (psxH == MAP_FAILED) {
173                 SysMessage("Error allocating psxH");
174                 psxH = NULL;
175                 return -1;
176         }
177
178         psxR = psxMap(0x1fc00000, 0x80000, 0, MAP_TAG_OTHER);
179         if (psxR == MAP_FAILED) {
180                 SysMessage("Error allocating psxR");
181                 psxR = NULL;
182                 return -1;
183         }
184
185         return 0;
186 }
187
188 static void psxMemFreeMap(void)
189 {
190         if (psxM) psxUnmap(psxM, 0x00210000, MAP_TAG_RAM);
191         if (psxH) psxUnmap(psxH, 0x10000, MAP_TAG_OTHER);
192         if (psxR) psxUnmap(psxR, 0x80000, MAP_TAG_OTHER);
193         psxM = psxH = psxR = NULL;
194         psxP = NULL;
195 }
196
197 int psxMemInit(void)
198 {
199         unsigned int i;
200         int ret;
201
202         if (LIGHTREC_CUSTOM_MAP)
203                 ret = lightrec_init_mmap();
204         else
205                 ret = psxMemInitMap();
206         if (ret) {
207                 if (LIGHTREC_CUSTOM_MAP)
208                         SysMessage("lightrec_init_mmap failed");
209                 psxMemShutdown();
210                 return -1;
211         }
212
213         if (DISABLE_MEM_LUTS)
214                 return 0;
215
216         psxMemRLUT = (u8 **)malloc(0x10000 * sizeof(void *));
217         psxMemWLUT = (u8 **)malloc(0x10000 * sizeof(void *));
218
219         if (psxMemRLUT == NULL || psxMemWLUT == NULL) {
220                 SysMessage("Error allocating psxMem LUTs");
221                 psxMemShutdown();
222                 return -1;
223         }
224
225         memset(psxMemRLUT, (int)(uintptr_t)INVALID_PTR, 0x10000 * sizeof(void *));
226         memset(psxMemWLUT, (int)(uintptr_t)INVALID_PTR, 0x10000 * sizeof(void *));
227
228 // MemR
229         for (i = 0; i < 0x80; i++) psxMemRLUT[i + 0x0000] = (u8 *)&psxM[(i & 0x1f) << 16];
230
231         memcpy(psxMemRLUT + 0x8000, psxMemRLUT, 0x80 * sizeof(void *));
232         memcpy(psxMemRLUT + 0xa000, psxMemRLUT, 0x80 * sizeof(void *));
233
234         psxMemRLUT[0x1f00] = (u8 *)psxP;
235         psxMemRLUT[0x1f80] = (u8 *)psxH;
236
237         for (i = 0; i < 0x08; i++) psxMemRLUT[i + 0x1fc0] = (u8 *)&psxR[i << 16];
238
239         memcpy(psxMemRLUT + 0x9fc0, psxMemRLUT + 0x1fc0, 0x08 * sizeof(void *));
240         memcpy(psxMemRLUT + 0xbfc0, psxMemRLUT + 0x1fc0, 0x08 * sizeof(void *));
241
242 // MemW
243         for (i = 0; i < 0x80; i++) psxMemWLUT[i + 0x0000] = (u8 *)&psxM[(i & 0x1f) << 16];
244
245         memcpy(psxMemWLUT + 0x8000, psxMemWLUT, 0x80 * sizeof(void *));
246         memcpy(psxMemWLUT + 0xa000, psxMemWLUT, 0x80 * sizeof(void *));
247
248         // Don't allow writes to PIO Expansion region (psxP) to take effect.
249         // NOTE: Not sure if this is needed to fix any games but seems wise,
250         //       seeing as some games do read from PIO as part of copy-protection
251         //       check. (See fix in psxMemReset() regarding psxP region reads).
252         psxMemWLUT[0x1f00] = INVALID_PTR;
253         psxMemWLUT[0x1f80] = (u8 *)psxH;
254
255         return 0;
256 }
257
258 void psxMemReset() {
259         FILE *f = NULL;
260         char bios[1024];
261
262         memset(psxM, 0, 0x00200000);
263         memset(psxP, 0xff, 0x00010000);
264
265         Config.HLE = TRUE;
266
267         if (strcmp(Config.Bios, "HLE") != 0) {
268                 sprintf(bios, "%s/%s", Config.BiosDir, Config.Bios);
269                 f = fopen(bios, "rb");
270
271                 if (f == NULL) {
272                         SysMessage(_("Could not open BIOS:\"%s\". Enabling HLE Bios!\n"), bios);
273                 } else {
274                         if (fread(psxR, 1, 0x80000, f) == 0x80000) {
275                                 Config.HLE = FALSE;
276                         } else {
277                                 SysMessage(_("The selected BIOS:\"%s\" is of wrong size. Enabling HLE Bios!\n"), bios);
278                         }
279                         fclose(f);
280                 }
281         }
282         if (Config.HLE)
283                 memset(psxR, 0, 0x80000);
284 }
285
286 void psxMemShutdown() {
287         if (LIGHTREC_CUSTOM_MAP)
288                 lightrec_free_mmap();
289         else
290                 psxMemFreeMap();
291
292         free(psxMemRLUT); psxMemRLUT = NULL;
293         free(psxMemWLUT); psxMemWLUT = NULL;
294 }
295
296 int cache_isolated;
297
298 void psxMemOnIsolate(int enable)
299 {
300         if (!DISABLE_MEM_LUTS) {
301                 if (enable) {
302                         memset(psxMemWLUT + 0x0000, (int)(uintptr_t)INVALID_PTR, 0x80 * sizeof(void *));
303                         memset(psxMemWLUT + 0x8000, (int)(uintptr_t)INVALID_PTR, 0x80 * sizeof(void *));
304                         //memset(psxMemWLUT + 0xa000, (int)(uintptr_t)INVALID_PTR, 0x80 * sizeof(void *));
305                 } else {
306                         int i;
307                         for (i = 0; i < 0x80; i++)
308                                 psxMemWLUT[i + 0x0000] = (void *)&psxM[(i & 0x1f) << 16];
309                         memcpy(psxMemWLUT + 0x8000, psxMemWLUT, 0x80 * sizeof(void *));
310                         memcpy(psxMemWLUT + 0xa000, psxMemWLUT, 0x80 * sizeof(void *));
311                 }
312         }
313
314         cache_isolated = enable;
315         psxCpu->Notify(enable ? R3000ACPU_NOTIFY_CACHE_ISOLATED
316                         : R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
317 }
318
319 u8 psxMemRead8(u32 mem) {
320         char *p;
321         u32 t;
322
323         t = mem >> 16;
324         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
325                 if ((mem & 0xffff) < 0x400)
326                         return psxHu8(mem);
327                 else
328                         return psxHwRead8(mem);
329         } else {
330                 p = psxm(mem, 0);
331                 if (p != INVALID_PTR) {
332                         if (Config.Debug)
333                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R1);
334                         return *(u8 *)p;
335                 } else {
336 #ifdef PSXMEM_LOG
337                         PSXMEM_LOG("err lb %8.8lx\n", mem);
338 #endif
339                         return 0xFF;
340                 }
341         }
342 }
343
344 u16 psxMemRead16(u32 mem) {
345         char *p;
346         u32 t;
347
348         t = mem >> 16;
349         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
350                 if ((mem & 0xffff) < 0x400)
351                         return psxHu16(mem);
352                 else
353                         return psxHwRead16(mem);
354         } else {
355                 p = psxm(mem, 0);
356                 if (p != INVALID_PTR) {
357                         if (Config.Debug)
358                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R2);
359                         return SWAPu16(*(u16 *)p);
360                 } else {
361 #ifdef PSXMEM_LOG
362                         PSXMEM_LOG("err lh %8.8lx\n", mem);
363 #endif
364                         return 0xFFFF;
365                 }
366         }
367 }
368
369 u32 psxMemRead32(u32 mem) {
370         char *p;
371         u32 t;
372
373         t = mem >> 16;
374         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
375                 if ((mem & 0xffff) < 0x400)
376                         return psxHu32(mem);
377                 else
378                         return psxHwRead32(mem);
379         } else {
380                 p = psxm(mem, 0);
381                 if (p != INVALID_PTR) {
382                         if (Config.Debug)
383                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R4);
384                         return SWAPu32(*(u32 *)p);
385                 } else {
386                         if (mem == 0xfffe0130)
387                                 return psxRegs.biuReg;
388 #ifdef PSXMEM_LOG
389                         PSXMEM_LOG("err lw %8.8lx\n", mem);
390 #endif
391                         return 0xFFFFFFFF;
392                 }
393         }
394 }
395
396 void psxMemWrite8(u32 mem, u32 value) {
397         char *p;
398         u32 t;
399
400         t = mem >> 16;
401         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
402                 if ((mem & 0xffff) < 0x400)
403                         psxHu8(mem) = value;
404                 else
405                         psxHwWrite8(mem, value);
406         } else {
407                 p = psxm(mem, 1);
408                 if (p != INVALID_PTR) {
409                         if (Config.Debug)
410                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W1);
411                         *(u8 *)p = value;
412 #ifndef DRC_DISABLE
413                         psxCpu->Clear((mem & (~3)), 1);
414 #endif
415                 } else {
416 #ifdef PSXMEM_LOG
417                         PSXMEM_LOG("err sb %8.8lx\n", mem);
418 #endif
419                 }
420         }
421 }
422
423 void psxMemWrite16(u32 mem, u32 value) {
424         char *p;
425         u32 t;
426
427         t = mem >> 16;
428         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
429                 if ((mem & 0xffff) < 0x400)
430                         psxHu16ref(mem) = SWAPu16(value);
431                 else
432                         psxHwWrite16(mem, value);
433         } else {
434                 p = psxm(mem, 1);
435                 if (p != INVALID_PTR) {
436                         if (Config.Debug)
437                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W2);
438                         *(u16 *)p = SWAPu16(value);
439 #ifndef DRC_DISABLE
440                         psxCpu->Clear((mem & (~3)), 1);
441 #endif
442                 } else {
443 #ifdef PSXMEM_LOG
444                         PSXMEM_LOG("err sh %8.8lx\n", mem);
445 #endif
446                 }
447         }
448 }
449
450 void psxMemWrite32(u32 mem, u32 value) {
451         char *p;
452         u32 t;
453
454 //      if ((mem&0x1fffff) == 0x71E18 || value == 0x48088800) SysPrintf("t2fix!!\n");
455         t = mem >> 16;
456         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
457                 if ((mem & 0xffff) < 0x400)
458                         psxHu32ref(mem) = SWAPu32(value);
459                 else
460                         psxHwWrite32(mem, value);
461         } else {
462                 p = psxm(mem, 1);
463                 if (p != INVALID_PTR) {
464                         if (Config.Debug)
465                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W4);
466                         *(u32 *)p = SWAPu32(value);
467 #ifndef DRC_DISABLE
468                         psxCpu->Clear(mem, 1);
469 #endif
470                 } else {
471                         if (mem == 0xfffe0130) {
472                                 psxRegs.biuReg = value;
473                                 return;
474                         }
475 #ifdef PSXMEM_LOG
476                         PSXMEM_LOG("err sw %8.8lx\n", mem);
477 #endif
478                 }
479         }
480 }
481
482 void *psxMemPointer(u32 mem) {
483         char *p;
484         u32 t;
485
486         t = mem >> 16;
487         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
488                 if ((mem & 0xffff) < 0x400)
489                         return (void *)&psxH[mem];
490                 else
491                         return NULL;
492         } else {
493                 p = psxm(mem, 1);
494                 if (p != INVALID_PTR) {
495                         return (void *)p;
496                 }
497                 return NULL;
498         }
499 }