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