ffa0dd70d29212d0d5eba04bbc6da880c27e91be
[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
32 #include "memmap.h"
33
34 #ifdef USE_LIBRETRO_VFS
35 #include <streams/file_stream_transforms.h>
36 #endif
37
38 #ifndef MAP_ANONYMOUS
39 #define MAP_ANONYMOUS MAP_ANON
40 #endif
41
42 boolean writeok = TRUE;
43
44 #ifndef NDEBUG
45 #include "debug.h"
46 #else
47 void DebugCheckBP(u32 address, enum breakpoint_types type) {}
48 #endif
49
50 void *(*psxMapHook)(unsigned long addr, size_t size, int is_fixed,
51                 enum psxMapTag tag);
52 void (*psxUnmapHook)(void *ptr, size_t size, enum psxMapTag tag);
53
54 void *psxMap(unsigned long addr, size_t size, int is_fixed,
55                 enum psxMapTag tag)
56 {
57         int flags = MAP_PRIVATE | MAP_ANONYMOUS;
58         int try_ = 0;
59         unsigned long mask;
60         void *req, *ret;
61
62 retry:
63         if (psxMapHook != NULL) {
64                 ret = psxMapHook(addr, size, 0, tag);
65                 if (ret == NULL)
66                         return MAP_FAILED;
67         }
68         else {
69                 /* avoid MAP_FIXED, it overrides existing mappings.. */
70                 /* if (is_fixed)
71                         flags |= MAP_FIXED; */
72
73                 req = (void *)addr;
74                 ret = mmap(req, size, PROT_READ | PROT_WRITE, flags, -1, 0);
75                 if (ret == MAP_FAILED)
76                         return ret;
77         }
78
79         if (addr != 0 && ret != (void *)addr) {
80                 SysMessage("psxMap: warning: wanted to map @%08x, got %p\n",
81                         addr, ret);
82
83                 if (is_fixed) {
84                         psxUnmap(ret, size, tag);
85                         return MAP_FAILED;
86                 }
87
88                 if (((addr ^ (unsigned long)ret) & ~0xff000000l) && try_ < 2)
89                 {
90                         psxUnmap(ret, size, tag);
91
92                         // try to use similarly aligned memory instead
93                         // (recompiler needs this)
94                         mask = try_ ? 0xffff : 0xffffff;
95                         addr = ((unsigned long)ret + mask) & ~mask;
96                         try_++;
97                         goto retry;
98                 }
99         }
100
101         return ret;
102 }
103
104 void psxUnmap(void *ptr, size_t size, enum psxMapTag tag)
105 {
106         if (psxUnmapHook != NULL) {
107                 psxUnmapHook(ptr, size, tag);
108                 return;
109         }
110
111         if (ptr)
112                 munmap(ptr, size);
113 }
114
115 s8 *psxM = NULL; // Kernel & User Memory (2 Meg)
116 s8 *psxP = NULL; // Parallel Port (64K)
117 s8 *psxR = NULL; // BIOS ROM (512K)
118 s8 *psxH = NULL; // Scratch Pad (1K) & Hardware Registers (8K)
119
120 u8 **psxMemWLUT = NULL;
121 u8 **psxMemRLUT = NULL;
122
123 /*  Playstation Memory Map (from Playstation doc by Joshua Walker)
124 0x0000_0000-0x0000_ffff         Kernel (64K)
125 0x0001_0000-0x001f_ffff         User Memory (1.9 Meg)
126
127 0x1f00_0000-0x1f00_ffff         Parallel Port (64K)
128
129 0x1f80_0000-0x1f80_03ff         Scratch Pad (1024 bytes)
130
131 0x1f80_1000-0x1f80_2fff         Hardware Registers (8K)
132
133 0x1fc0_0000-0x1fc7_ffff         BIOS (512K)
134
135 0x8000_0000-0x801f_ffff         Kernel and User Memory Mirror (2 Meg) Cached
136 0x9fc0_0000-0x9fc7_ffff         BIOS Mirror (512K) Cached
137
138 0xa000_0000-0xa01f_ffff         Kernel and User Memory Mirror (2 Meg) Uncached
139 0xbfc0_0000-0xbfc7_ffff         BIOS Mirror (512K) Uncached
140 */
141
142 int psxMemInit() {
143         int i;
144
145         psxM = psxMap(0x80000000, 0x00210000, 1, MAP_TAG_RAM);
146         if (psxM == MAP_FAILED)
147                 psxM = psxMap(0x77000000, 0x00210000, 0, MAP_TAG_RAM);
148         if (psxM == MAP_FAILED) {
149                 SysMessage(_("mapping main RAM failed"));
150                 return -1;
151         }
152
153         psxP = &psxM[0x200000];
154         psxH = psxMap(0x1f800000, 0x10000, 0, MAP_TAG_OTHER);
155         psxR = psxMap(0x1fc00000, 0x80000, 0, MAP_TAG_OTHER);
156
157         if (psxR == MAP_FAILED || psxH == MAP_FAILED) {
158                 SysMessage(_("Error allocating memory!"));
159                 psxMemShutdown();
160                 return -1;
161         }
162
163         psxMemRLUT = (u8 **)malloc(0x10000 * sizeof(void *));
164         psxMemWLUT = (u8 **)malloc(0x10000 * sizeof(void *));
165
166         if (psxMemRLUT == NULL || psxMemWLUT == NULL) {
167                 SysMessage(_("Error allocating memory!"));
168                 psxMemShutdown();
169                 return -1;
170         }
171
172         memset(psxMemRLUT, 0xff, 0x10000 * sizeof(void *));
173         memset(psxMemWLUT, 0xff, 0x10000 * sizeof(void *));
174
175 // MemR
176         for (i = 0; i < 0x80; i++) psxMemRLUT[i + 0x0000] = (u8 *)&psxM[(i & 0x1f) << 16];
177
178         memcpy(psxMemRLUT + 0x8000, psxMemRLUT, 0x80 * sizeof(void *));
179         memcpy(psxMemRLUT + 0xa000, psxMemRLUT, 0x80 * sizeof(void *));
180
181         psxMemRLUT[0x1f00] = (u8 *)psxP;
182         psxMemRLUT[0x1f80] = (u8 *)psxH;
183
184         for (i = 0; i < 0x08; i++) psxMemRLUT[i + 0x1fc0] = (u8 *)&psxR[i << 16];
185
186         memcpy(psxMemRLUT + 0x9fc0, psxMemRLUT + 0x1fc0, 0x08 * sizeof(void *));
187         memcpy(psxMemRLUT + 0xbfc0, psxMemRLUT + 0x1fc0, 0x08 * sizeof(void *));
188
189 // MemW
190         for (i = 0; i < 0x80; i++) psxMemWLUT[i + 0x0000] = (u8 *)&psxM[(i & 0x1f) << 16];
191
192         memcpy(psxMemWLUT + 0x8000, psxMemWLUT, 0x80 * sizeof(void *));
193         memcpy(psxMemWLUT + 0xa000, psxMemWLUT, 0x80 * sizeof(void *));
194
195         // Don't allow writes to PIO Expansion region (psxP) to take effect.
196         // NOTE: Not sure if this is needed to fix any games but seems wise,
197         //       seeing as some games do read from PIO as part of copy-protection
198         //       check. (See fix in psxMemReset() regarding psxP region reads).
199         psxMemWLUT[0x1f00] = INVALID_PTR;
200         psxMemWLUT[0x1f80] = (u8 *)psxH;
201
202         return 0;
203 }
204
205 void psxMemReset() {
206         FILE *f = NULL;
207         char bios[1024];
208
209         memset(psxM, 0, 0x00200000);
210         memset(psxP, 0xff, 0x00010000);
211
212         Config.HLE = TRUE;
213
214         if (strcmp(Config.Bios, "HLE") != 0) {
215                 sprintf(bios, "%s/%s", Config.BiosDir, Config.Bios);
216                 f = fopen(bios, "rb");
217
218                 if (f == NULL) {
219                         SysMessage(_("Could not open BIOS:\"%s\". Enabling HLE Bios!\n"), bios);
220                         memset(psxR, 0, 0x80000);
221                 } else {
222                         if (fread(psxR, 1, 0x80000, f) == 0x80000) {
223                                 Config.HLE = FALSE;
224                         } else {
225                                 SysMessage(_("The selected BIOS:\"%s\" is of wrong size. Enabling HLE Bios!\n"), bios);
226                         }
227                         fclose(f);
228                 }
229         }
230 }
231
232 void psxMemShutdown() {
233         psxUnmap(psxM, 0x00210000, MAP_TAG_RAM); psxM = NULL;
234         psxUnmap(psxH, 0x10000, MAP_TAG_OTHER); psxH = NULL;
235         psxUnmap(psxR, 0x80000, MAP_TAG_OTHER); psxR = NULL;
236
237         free(psxMemRLUT); psxMemRLUT = NULL;
238         free(psxMemWLUT); psxMemWLUT = NULL;
239 }
240
241 u8 psxMemRead8(u32 mem) {
242         char *p;
243         u32 t;
244
245         t = mem >> 16;
246         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
247                 if ((mem & 0xffff) < 0x400)
248                         return psxHu8(mem);
249                 else
250                         return psxHwRead8(mem);
251         } else {
252                 p = (char *)(psxMemRLUT[t]);
253                 if (p != INVALID_PTR) {
254                         if (Config.Debug)
255                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R1);
256                         return *(u8 *)(p + (mem & 0xffff));
257                 } else {
258 #ifdef PSXMEM_LOG
259                         PSXMEM_LOG("err lb %8.8lx\n", mem);
260 #endif
261                         return 0xFF;
262                 }
263         }
264 }
265
266 u16 psxMemRead16(u32 mem) {
267         char *p;
268         u32 t;
269
270         t = mem >> 16;
271         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
272                 if ((mem & 0xffff) < 0x400)
273                         return psxHu16(mem);
274                 else
275                         return psxHwRead16(mem);
276         } else {
277                 p = (char *)(psxMemRLUT[t]);
278                 if (p != INVALID_PTR) {
279                         if (Config.Debug)
280                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R2);
281                         return SWAPu16(*(u16 *)(p + (mem & 0xffff)));
282                 } else {
283 #ifdef PSXMEM_LOG
284                         PSXMEM_LOG("err lh %8.8lx\n", mem);
285 #endif
286                         return 0xFFFF;
287                 }
288         }
289 }
290
291 u32 psxMemRead32(u32 mem) {
292         char *p;
293         u32 t;
294
295         t = mem >> 16;
296         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
297                 if ((mem & 0xffff) < 0x400)
298                         return psxHu32(mem);
299                 else
300                         return psxHwRead32(mem);
301         } else {
302                 p = (char *)(psxMemRLUT[t]);
303                 if (p != INVALID_PTR) {
304                         if (Config.Debug)
305                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, R4);
306                         return SWAPu32(*(u32 *)(p + (mem & 0xffff)));
307                 } else {
308 #ifdef PSXMEM_LOG
309                         if (writeok) { PSXMEM_LOG("err lw %8.8lx\n", mem); }
310 #endif
311                         return 0xFFFFFFFF;
312                 }
313         }
314 }
315
316 void psxMemWrite8(u32 mem, u8 value) {
317         char *p;
318         u32 t;
319
320         t = mem >> 16;
321         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
322                 if ((mem & 0xffff) < 0x400)
323                         psxHu8(mem) = value;
324                 else
325                         psxHwWrite8(mem, value);
326         } else {
327                 p = (char *)(psxMemWLUT[t]);
328                 if (p != INVALID_PTR) {
329                         if (Config.Debug)
330                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W1);
331                         *(u8 *)(p + (mem & 0xffff)) = value;
332 #ifndef DRC_DISABLE
333                         psxCpu->Clear((mem & (~3)), 1);
334 #endif
335                 } else {
336 #ifdef PSXMEM_LOG
337                         PSXMEM_LOG("err sb %8.8lx\n", mem);
338 #endif
339                 }
340         }
341 }
342
343 void psxMemWrite16(u32 mem, u16 value) {
344         char *p;
345         u32 t;
346
347         t = mem >> 16;
348         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
349                 if ((mem & 0xffff) < 0x400)
350                         psxHu16ref(mem) = SWAPu16(value);
351                 else
352                         psxHwWrite16(mem, value);
353         } else {
354                 p = (char *)(psxMemWLUT[t]);
355                 if (p != INVALID_PTR) {
356                         if (Config.Debug)
357                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W2);
358                         *(u16 *)(p + (mem & 0xffff)) = SWAPu16(value);
359 #ifndef DRC_DISABLE
360                         psxCpu->Clear((mem & (~3)), 1);
361 #endif
362                 } else {
363 #ifdef PSXMEM_LOG
364                         PSXMEM_LOG("err sh %8.8lx\n", mem);
365 #endif
366                 }
367         }
368 }
369
370 void psxMemWrite32(u32 mem, u32 value) {
371         char *p;
372         u32 t;
373
374 //      if ((mem&0x1fffff) == 0x71E18 || value == 0x48088800) SysPrintf("t2fix!!\n");
375         t = mem >> 16;
376         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
377                 if ((mem & 0xffff) < 0x400)
378                         psxHu32ref(mem) = SWAPu32(value);
379                 else
380                         psxHwWrite32(mem, value);
381         } else {
382                 p = (char *)(psxMemWLUT[t]);
383                 if (p != INVALID_PTR) {
384                         if (Config.Debug)
385                                 DebugCheckBP((mem & 0xffffff) | 0x80000000, W4);
386                         *(u32 *)(p + (mem & 0xffff)) = SWAPu32(value);
387 #ifndef DRC_DISABLE
388                         psxCpu->Clear(mem, 1);
389 #endif
390                 } else {
391                         if (mem != 0xfffe0130) {
392 #ifndef DRC_DISABLE
393                                 if (!writeok)
394                                         psxCpu->Clear(mem, 1);
395 #endif
396
397 #ifdef PSXMEM_LOG
398                                 if (writeok) { PSXMEM_LOG("err sw %8.8lx\n", mem); }
399 #endif
400                         } else {
401                                 int i;
402
403                                 switch (value) {
404                                         case 0x800: case 0x804:
405                                                 if (writeok == 0) break;
406                                                 writeok = 0;
407                                                 memset(psxMemWLUT + 0x0000, 0xff, 0x80 * sizeof(void *));
408                                                 memset(psxMemWLUT + 0x8000, 0xff, 0x80 * sizeof(void *));
409                                                 memset(psxMemWLUT + 0xa000, 0xff, 0x80 * sizeof(void *));
410                                                 /* Required for icache interpreter otherwise Armored Core won't boot on icache interpreter */
411                                                 psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_ISOLATED, NULL);
412                                                 break;
413                                         case 0x00: case 0x1e988:
414                                                 if (writeok == 1) break;
415                                                 writeok = 1;
416                                                 for (i = 0; i < 0x80; i++) psxMemWLUT[i + 0x0000] = (void *)&psxM[(i & 0x1f) << 16];
417                                                 memcpy(psxMemWLUT + 0x8000, psxMemWLUT, 0x80 * sizeof(void *));
418                                                 memcpy(psxMemWLUT + 0xa000, psxMemWLUT, 0x80 * sizeof(void *));
419                                                 /* Dynarecs might take this opportunity to flush their code cache */
420                                                 psxCpu->Notify(R3000ACPU_NOTIFY_CACHE_UNISOLATED, NULL);
421                                                 break;
422                                         default:
423 #ifdef PSXMEM_LOG
424                                                 PSXMEM_LOG("unk %8.8lx = %x\n", mem, value);
425 #endif
426                                                 break;
427                                 }
428                         }
429                 }
430         }
431 }
432
433 void *psxMemPointer(u32 mem) {
434         char *p;
435         u32 t;
436
437         t = mem >> 16;
438         if (t == 0x1f80 || t == 0x9f80 || t == 0xbf80) {
439                 if ((mem & 0xffff) < 0x400)
440                         return (void *)&psxH[mem];
441                 else
442                         return NULL;
443         } else {
444                 p = (char *)(psxMemWLUT[t]);
445                 if (p != INVALID_PTR) {
446                         return (void *)(p + (mem & 0xffff));
447                 }
448                 return NULL;
449         }
450 }