1 /******************************************************************************/
2 /* Mednafen Sony PS1 Emulation Module                                         */
3 /******************************************************************************/
4 /* psx.cpp:
5 **  Copyright (C) 2011-2017 Mednafen Team
6 **
7 ** This program is free software; you can redistribute it and/or
8 ** modify it under the terms of the GNU General Public License
9 ** as published by the Free Software Foundation; either version 2
10 ** of the License, or (at your option) any later version.
11 **
12 ** This program is distributed in the hope that it will be useful,
13 ** but WITHOUT ANY WARRANTY; without even the implied warranty of
14 ** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 ** GNU General Public License for more details.
16 **
17 ** You should have received a copy of the GNU General Public License
18 ** along with this program; if not, write to the Free Software Foundation, Inc.,
19 ** 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 */
21 
22 #include "psx.h"
23 #include "mdec.h"
24 #include "frontio.h"
25 #include "timer.h"
26 #include "sio.h"
27 #include "cdc.h"
28 #include "spu.h"
29 #include <mednafen/FileStream.h>
30 #include <mednafen/mempatcher.h>
31 #include <mednafen/PSFLoader.h>
32 #include <mednafen/player.h>
33 #include <mednafen/hash/sha256.h>
34 #include <mednafen/cheat_formats/psx.h>
35 
36 #include <zlib.h>
37 
38 MDFN_HIDE extern MDFNGI EmulatedPSX;
39 
40 namespace MDFN_IEN_PSX
41 {
42 
43 #if PSX_DBGPRINT_ENABLE
44 static unsigned psx_dbg_level = 0;
45 
PSX_DBG_BIOS_PUTC(uint8 c)46 void PSX_DBG_BIOS_PUTC(uint8 c) noexcept
47 {
48  if(psx_dbg_level >= PSX_DBG_BIOS_PRINT)
49  {
50   if(c == 0x1B)
51    return;
52 
53   fputc(c, stdout);
54 
55   //if(c == '\n')
56   //{
57   // fputc('%', stdout);
58   // fputc(' ', stdout);
59   //}
60   fflush(stdout);
61  }
62 }
63 
PSX_DBG(unsigned level,const char * format,...)64 void PSX_DBG(unsigned level, const char *format, ...) noexcept
65 {
66  if(psx_dbg_level >= level)
67  {
68   va_list ap;
69 
70   va_start(ap, format);
71 
72   trio_vprintf(format, ap);
73 
74   va_end(ap);
75  }
76 }
77 #else
78 static unsigned const psx_dbg_level = 0;
79 #endif
80 
81 
82 struct MDFN_PseudoRNG	// Based off(but not the same as) public-domain "JKISS" PRNG.
83 {
MDFN_PseudoRNGMDFN_IEN_PSX::MDFN_PseudoRNG84  MDFN_COLD MDFN_PseudoRNG()
85  {
86   ResetState();
87  }
88 
RandU32MDFN_IEN_PSX::MDFN_PseudoRNG89  uint32 RandU32(void)
90  {
91   uint64 t;
92 
93   x = 314527869 * x + 1234567;
94   y ^= y << 5; y ^= y >> 7; y ^= y << 22;
95   t = 4294584393ULL * z + c; c = t >> 32; z = t;
96   lcgo = (19073486328125ULL * lcgo) + 1;
97 
98   return (x + y + z) ^ (lcgo >> 16);
99  }
100 
RandU32MDFN_IEN_PSX::MDFN_PseudoRNG101  uint32 RandU32(uint32 mina, uint32 maxa)
102  {
103   const uint32 range_m1 = maxa - mina;
104   uint32 range_mask;
105   uint32 tmp;
106 
107   range_mask = range_m1;
108   range_mask |= range_mask >> 1;
109   range_mask |= range_mask >> 2;
110   range_mask |= range_mask >> 4;
111   range_mask |= range_mask >> 8;
112   range_mask |= range_mask >> 16;
113 
114   do
115   {
116    tmp = RandU32() & range_mask;
117   } while(tmp > range_m1);
118 
119   return(mina + tmp);
120  }
121 
ResetStateMDFN_IEN_PSX::MDFN_PseudoRNG122  MDFN_COLD void ResetState(void)	// Must always reset to the same state.
123  {
124   x = 123456789;
125   y = 987654321;
126   z = 43219876;
127   c = 6543217;
128   lcgo = 0xDEADBEEFCAFEBABEULL;
129  }
130 
131  uint32 x,y,z,c;
132  uint64 lcgo;
133 };
134 
135 static MDFN_PseudoRNG PSX_PRNG;
136 
PSX_GetRandU32(uint32 mina,uint32 maxa)137 uint32 PSX_GetRandU32(uint32 mina, uint32 maxa)
138 {
139  return PSX_PRNG.RandU32(mina, maxa);
140 }
141 
142 
143 class PSF1Loader : public PSFLoader
144 {
145  public:
146 
147  PSF1Loader(VirtualFS* vfs, const std::string& dir_path, Stream *fp) MDFN_COLD;
148  virtual ~PSF1Loader() override MDFN_COLD;
149 
150  virtual void HandleEXE(Stream* fp, bool ignore_pcsp = false) override MDFN_COLD;
151 
152  PSFTags tags;
153 };
154 
155 enum
156 {
157  REGION_JP = 0,
158  REGION_NA = 1,
159  REGION_EU = 2,
160 };
161 
162 static const MDFNSetting_EnumList Region_List[] =
163 {
164  { "jp", REGION_JP, gettext_noop("Japan") },
165  { "na", REGION_NA, gettext_noop("North America") },
166  { "eu", REGION_EU, gettext_noop("Europe") },
167  { NULL, 0 },
168 };
169 
170 static const struct
171 {
172  const char* version;
173  char hwrev;		// Ostensibly, not sure where this information originally came from or if it has any use(considering it doesn't reflect all hardware changes).
174  unsigned region;
175  bool bad;
176  sha256_digest sd;
177 } BIOS_DB[] =
178 {
179  // 1.0J, 2.2D, 4.1A(W):
180  //  Tolerant with regards to ISO-9660 system-area contents
181  //
182 
183  { "1.0J", 'A', REGION_JP, false, "cfc1fc38eb442f6f80781452119e931bcae28100c1c97e7e6c5f2725bbb0f8bb"_sha256 },
184 
185  { "1.1J", 'B', REGION_JP, false, "5eb3aee495937558312b83b54323d76a4a015190decd4051214f1b6df06ac34b"_sha256 },
186 
187  { "2.0A", 'X', REGION_NA, false, "42e4124be7623e2e28b1db0d8d426539646faee49d74b71166d8ba5bd7c472ed"_sha256 },	// DTL
188  { "2.0E", 'B', REGION_EU, false, "0af2be3468d30b6018b3c3b0d98b8b64347e255e16d874d55f0363648973dbf0"_sha256 },
189 
190  { "2.1J", 'B', REGION_JP, false, "6f71ca1e716da761dc53187bd39e00c213f566e55090708fd3e2b4b425c8c989"_sha256 },
191  { "2.1A", 'X', REGION_NA, false, "6ad5521d105a6b86741f1af8da2e6ea1c732d34459940618c70305a105e8ec10"_sha256 },	// DTL
192  { "2.1E", 'B', REGION_EU, false, "1efb0cfc5db8a8751a884c5312e9c6265ca1bc580dc0c2663eb2dea3bde9fcf7"_sha256 },
193 
194  { "2.2J", 'B', REGION_JP, false, "0c8359870cbac0ea091f1c87f188cd332dcc709753b91cafd9fd44a4a6188197"_sha256 },
195  { "2.2J", 'B', REGION_JP, true,  "8e0383171e67b33e60d5df6394c58843f3b11c7a0b97f3bfcc4319ac2d1f9d18"_sha256 },	// BAD! ! ! !
196  { "2.2A", 'B', REGION_NA, false, "71af94d1e47a68c11e8fdb9f8368040601514a42a5a399cda48c7d3bff1e99d3"_sha256 },
197  { "2.2E", 'B', REGION_EU, false, "3d06d2c469313c2a2128d24fe2e0c71ff99bc2032be89a829a62337187f500b7"_sha256 },
198  { "2.2D", 'X', REGION_JP, false, "4018749b3698b8694387beebcbabfb48470513066840f9441459ee4c9f0f39bc"_sha256 },	// DTL
199 
200  { "3.0J", 'C', REGION_JP, false, "9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"_sha256 },
201  { "3.0A", 'C', REGION_NA, false, "11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"_sha256 },
202  { "3.0E", 'C', REGION_EU, false, "1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"_sha256 },
203  { "3.0E", 'C', REGION_EU, true,  "9e1f8fb4fa356a5ac29d7c7209626dcc1b3038c0e5a85b0e99d1db96926647ca"_sha256 },	// BAD! ! ! !
204 
205  { "4.0J", 'C', REGION_JP, false, "e900504d1755f021f861b82c8258c5e6658c7b592f800cccd91f5d32ea380d28"_sha256 },
206 
207  { "4.1A(W)",'C',REGION_JP,false, "b3aa63cf30c81e0a40641740f4a43e25fda0b21b792fa9aaef60ce1675761479"_sha256 },	// Weird
208  { "4.1A", 'C', REGION_NA, false, "39dcc1a0717036c9b6ac52fefd1ee7a57d3808e8cfbc755879fa685a0a738278"_sha256 },
209  { "4.1E", 'C', REGION_EU, false, "5e84a94818cf5282f4217591fefd88be36b9b174b3cc7cb0bcd75199beb450f1"_sha256 },
210 
211  { "4.3J", 'C', REGION_JP, false, "b29b4b5fcddef369bd6640acacda0865e0366fcf7ea54e40b2f1a8178004f89a"_sha256},
212 
213  { "4.4E", 'C', REGION_EU, false, "5c0166da24e27deaa82246de8ff0108267fe4bb59f6df0fdec50e05e62448ca4"_sha256 },
214 
215  { "4.5A", 'C', REGION_NA, false, "aca9cbfa974b933646baad6556a867eca9b81ce65d8af343a7843f7775b9ffc8"_sha256 },
216  { "4.5E", 'C', REGION_EU, false, "42244b0c650821519751b7e77ad1d3222a0125e75586df2b4e84ba693b9809dc"_sha256 },
217 };
218 
219 static sha256_digest BIOS_SHA256;	// SHA-256 hash of the currently-loaded BIOS; used for save state sanity checks.
220 static PSF1Loader *psf_loader = NULL;
221 static std::vector<CDInterface*> *cdifs = NULL;
222 static std::vector<const char *> cdifs_scex_ids;
223 
224 static uint64 Memcard_PrevDC[8];
225 static int64 Memcard_SaveDelay[8];
226 
227 PS_CPU *CPU = NULL;
228 PS_SPU *SPU = NULL;
229 PS_CDC *CDC = NULL;
230 static FrontIO *FIO = NULL;
231 
232 static MultiAccessSizeMem<512 * 1024, false> *BIOSROM = NULL;
233 static MultiAccessSizeMem<65536, false> *PIOMem = NULL;
234 
235 MultiAccessSizeMem<2048 * 1024, false> MainRAM;
236 
237 static uint32 TextMem_Start;
238 static std::vector<uint8> TextMem;
239 
240 static const uint32 SysControl_Mask[9] = { 0x00ffffff, 0x00ffffff, 0xffffffff, 0x2f1fffff,
241 					   0xffffffff, 0x2f1fffff, 0x2f1fffff, 0xffffffff,
242 					   0x0003ffff };
243 
244 static const uint32 SysControl_OR[9] = { 0x1f000000, 0x1f000000, 0x00000000, 0x00000000,
245 					 0x00000000, 0x00000000, 0x00000000, 0x00000000,
246 					 0x00000000 };
247 
248 static struct
249 {
250  union
251  {
252   struct
253   {
254    uint32 PIO_Base;	// 0x1f801000	// BIOS Init: 0x1f000000, Writeable bits: 0x00ffffff(assumed, verify), FixedOR = 0x1f000000
255    uint32 Unknown0;	// 0x1f801004	// BIOS Init: 0x1f802000, Writeable bits: 0x00ffffff, FixedOR = 0x1f000000
256    uint32 Unknown1;	// 0x1f801008	// BIOS Init: 0x0013243f, ????
257    uint32 Unknown2;	// 0x1f80100c	// BIOS Init: 0x00003022, Writeable bits: 0x2f1fffff, FixedOR = 0x00000000
258 
259    uint32 BIOS_Mapping;	// 0x1f801010	// BIOS Init: 0x0013243f, ????
260    uint32 SPU_Delay;	// 0x1f801014	// BIOS Init: 0x200931e1, Writeable bits: 0x2f1fffff, FixedOR = 0x00000000 - Affects bus timing on access to SPU
261    uint32 CDC_Delay;	// 0x1f801018	// BIOS Init: 0x00020843, Writeable bits: 0x2f1fffff, FixedOR = 0x00000000
262    uint32 Unknown4;	// 0x1f80101c	// BIOS Init: 0x00070777, ????
263    uint32 Unknown5;	// 0x1f801020	// BIOS Init: 0x00031125(but rewritten with other values often), Writeable bits: 0x0003ffff, FixedOR = 0x00000000 -- Possibly CDC related
264   };
265   uint32 Regs[9];
266  };
267 } SysControl;
268 
269 static unsigned DMACycleSteal = 0;	// Doesn't need to be saved in save states, since it's recalculated in the ForceEventUpdates() call chain.
270 
PSX_SetDMACycleSteal(unsigned stealage)271 void PSX_SetDMACycleSteal(unsigned stealage)
272 {
273  if(stealage > 200)	// Due to 8-bit limitations in the CPU core.
274   stealage = 200;
275 
276  DMACycleSteal = stealage;
277 }
278 
279 
280 //
281 // Event stuff
282 //
283 
284 static pscpu_timestamp_t Running;	// Set to -1 when not desiring exit, and 0 when we are.
285 
286 struct event_list_entry
287 {
288  uint32 which;
289  pscpu_timestamp_t event_time;
290  event_list_entry *prev;
291  event_list_entry *next;
292 };
293 
294 static event_list_entry events[PSX_EVENT__COUNT];
295 
EventReset(void)296 static void EventReset(void)
297 {
298  for(unsigned i = 0; i < PSX_EVENT__COUNT; i++)
299  {
300   events[i].which = i;
301 
302   if(i == PSX_EVENT__SYNFIRST)
303    events[i].event_time = (int32)0x80000000;
304   else if(i == PSX_EVENT__SYNLAST)
305    events[i].event_time = 0x7FFFFFFF;
306   else
307    events[i].event_time = PSX_EVENT_MAXTS;
308 
309   events[i].prev = (i > 0) ? &events[i - 1] : NULL;
310   events[i].next = (i < (PSX_EVENT__COUNT - 1)) ? &events[i + 1] : NULL;
311  }
312 }
313 
314 //static void RemoveEvent(event_list_entry *e)
315 //{
316 // e->prev->next = e->next;
317 // e->next->prev = e->prev;
318 //}
319 
RebaseTS(const pscpu_timestamp_t timestamp)320 static void RebaseTS(const pscpu_timestamp_t timestamp)
321 {
322  for(unsigned i = 0; i < PSX_EVENT__COUNT; i++)
323  {
324   if(i == PSX_EVENT__SYNFIRST || i == PSX_EVENT__SYNLAST)
325    continue;
326 
327   assert(events[i].event_time > timestamp);
328   events[i].event_time -= timestamp;
329  }
330 
331  CPU->SetEventNT(events[PSX_EVENT__SYNFIRST].next->event_time);
332 }
333 
PSX_SetEventNT(const int type,const pscpu_timestamp_t next_timestamp)334 void PSX_SetEventNT(const int type, const pscpu_timestamp_t next_timestamp)
335 {
336  event_list_entry *e = &events[type];
337 
338  if(next_timestamp < e->event_time)
339  {
340   event_list_entry *fe = e;
341 
342   do
343   {
344    fe = fe->prev;
345   }
346   while(next_timestamp < fe->event_time);
347 
348   // Remove this event from the list, temporarily of course.
349   e->prev->next = e->next;
350   e->next->prev = e->prev;
351 
352   // Insert into the list, just after "fe".
353   e->prev = fe;
354   e->next = fe->next;
355   fe->next->prev = e;
356   fe->next = e;
357 
358   e->event_time = next_timestamp;
359  }
360  else if(next_timestamp > e->event_time)
361  {
362   event_list_entry *fe = e;
363 
364   do
365   {
366    fe = fe->next;
367   } while(next_timestamp > fe->event_time);
368 
369   // Remove this event from the list, temporarily of course
370   e->prev->next = e->next;
371   e->next->prev = e->prev;
372 
373   // Insert into the list, just BEFORE "fe".
374   e->prev = fe->prev;
375   e->next = fe;
376   fe->prev->next = e;
377   fe->prev = e;
378 
379   e->event_time = next_timestamp;
380  }
381 
382  CPU->SetEventNT(events[PSX_EVENT__SYNFIRST].next->event_time & Running);
383 }
384 
385 // Called from debug.cpp too.
ForceEventUpdates(const pscpu_timestamp_t timestamp)386 void ForceEventUpdates(const pscpu_timestamp_t timestamp)
387 {
388  PSX_SetEventNT(PSX_EVENT_GPU, GPU_Update(timestamp));
389  PSX_SetEventNT(PSX_EVENT_CDC, CDC->Update(timestamp));
390 
391  PSX_SetEventNT(PSX_EVENT_TIMER, TIMER_Update(timestamp));
392 
393  PSX_SetEventNT(PSX_EVENT_DMA, DMA_Update(timestamp));
394 
395  PSX_SetEventNT(PSX_EVENT_FIO, FIO->Update(timestamp));
396 
397  CPU->SetEventNT(events[PSX_EVENT__SYNFIRST].next->event_time);
398 }
399 
PSX_EventHandler(const pscpu_timestamp_t timestamp)400 bool MDFN_FASTCALL PSX_EventHandler(const pscpu_timestamp_t timestamp)
401 {
402  event_list_entry *e = events[PSX_EVENT__SYNFIRST].next;
403 
404  while(timestamp >= e->event_time)	// If Running = 0, PSX_EventHandler() may be called even if there isn't an event per-se, so while() instead of do { ... } while
405  {
406   event_list_entry *prev = e->prev;
407   pscpu_timestamp_t nt;
408 
409   switch(e->which)
410   {
411    default: abort();
412 
413    case PSX_EVENT_GPU:
414 	nt = GPU_Update(e->event_time);
415 	break;
416 
417    case PSX_EVENT_CDC:
418 	nt = CDC->Update(e->event_time);
419 	break;
420 
421    case PSX_EVENT_TIMER:
422 	nt = TIMER_Update(e->event_time);
423 	break;
424 
425    case PSX_EVENT_DMA:
426 	nt = DMA_Update(e->event_time);
427 	break;
428 
429    case PSX_EVENT_FIO:
430 	nt = FIO->Update(e->event_time);
431 	break;
432   }
433 #if PSX_EVENT_SYSTEM_CHECKS
434   assert(nt > e->event_time);
435 #endif
436 
437   PSX_SetEventNT(e->which, nt);
438 
439   // Order of events can change due to calling PSX_SetEventNT(), this prev business ensures we don't miss an event due to reordering.
440   e = prev->next;
441  }
442 
443  return(Running);
444 }
445 
446 
PSX_RequestMLExit(void)447 void PSX_RequestMLExit(void)
448 {
449  Running = 0;
450  CPU->SetEventNT(0);
451 }
452 
453 
454 //
455 // End event stuff
456 //
457 
458 // Remember to update MemPeek<>() and MemPoke<>() when we change address decoding in MemRW()
MemRW(pscpu_timestamp_t & timestamp,uint32 A,uint32 & V)459 template<typename T, bool IsWrite, bool Access24> static INLINE void MemRW(pscpu_timestamp_t &timestamp, uint32 A, uint32 &V)
460 {
461  #if 0
462  if(IsWrite)
463   printf("Write%d: %08x(orig=%08x), %08x\n", (int)(sizeof(T) * 8), A & mask[A >> 29], A, V);
464  else
465   printf("Read%d: %08x(orig=%08x)\n", (int)(sizeof(T) * 8), A & mask[A >> 29], A);
466  #endif
467 
468  if(!IsWrite)
469   timestamp += DMACycleSteal;
470 
471  if(A < 0x00800000)
472  {
473   if(IsWrite)
474   {
475    //timestamp++;	// Best-case timing.
476   }
477   else
478   {
479    timestamp += 3;
480   }
481 
482   if(Access24)
483   {
484    if(IsWrite)
485     MainRAM.WriteU24(A & 0x1FFFFF, V);
486    else
487     V = MainRAM.ReadU24(A & 0x1FFFFF);
488   }
489   else
490   {
491    if(IsWrite)
492     MainRAM.Write<T>(A & 0x1FFFFF, V);
493    else
494     V = MainRAM.Read<T>(A & 0x1FFFFF);
495   }
496 
497   return;
498  }
499 
500  if(A >= 0x1FC00000 && A <= 0x1FC7FFFF)
501  {
502   if(!IsWrite)
503   {
504    if(Access24)
505     V = BIOSROM->ReadU24(A & 0x7FFFF);
506    else
507     V = BIOSROM->Read<T>(A & 0x7FFFF);
508   }
509 
510   return;
511  }
512 
513  if(timestamp >= events[PSX_EVENT__SYNFIRST].next->event_time)
514   PSX_EventHandler(timestamp);
515 
516  if(A >= 0x1F801000 && A <= 0x1F802FFF)
517  {
518   //if(IsWrite)
519   // printf("HW Write%d: %08x %08x\n", (unsigned int)(sizeof(T)*8), (unsigned int)A, (unsigned int)V);
520   //else
521   // printf("HW Read%d: %08x\n", (unsigned int)(sizeof(T)*8), (unsigned int)A);
522 
523   if(A >= 0x1F801C00 && A <= 0x1F801FFF) // SPU
524   {
525    if(sizeof(T) == 4 && !Access24)
526    {
527     if(IsWrite)
528     {
529      //timestamp += 15;
530 
531      //if(timestamp >= events[PSX_EVENT__SYNFIRST].next->event_time)
532      // PSX_EventHandler(timestamp);
533 
534      SPU->Write(timestamp, A | 0, V);
535      SPU->Write(timestamp, A | 2, V >> 16);
536     }
537     else
538     {
539      timestamp += 36;
540 
541      if(timestamp >= events[PSX_EVENT__SYNFIRST].next->event_time)
542       PSX_EventHandler(timestamp);
543 
544      V = SPU->Read(timestamp, A);
545      V |= SPU->Read(timestamp, A | 2) << 16;
546     }
547    }
548    else
549    {
550     if(IsWrite)
551     {
552      //timestamp += 8;
553 
554      //if(timestamp >= events[PSX_EVENT__SYNFIRST].next->event_time)
555      // PSX_EventHandler(timestamp);
556 
557      SPU->Write(timestamp, A & ~1, V);
558     }
559     else
560     {
561      timestamp += 16; // Just a guess, need to test.
562 
563      if(timestamp >= events[PSX_EVENT__SYNFIRST].next->event_time)
564       PSX_EventHandler(timestamp);
565 
566      V = SPU->Read(timestamp, A & ~1);
567     }
568    }
569    return;
570   }		// End SPU
571 
572 
573   // CDC: TODO - 8-bit access.
574   if(A >= 0x1f801800 && A <= 0x1f80180F)
575   {
576    if(!IsWrite)
577    {
578     timestamp += 6 * sizeof(T); //24;
579    }
580 
581    if(IsWrite)
582     CDC->Write(timestamp, A & 0x3, V);
583    else
584     V = CDC->Read(timestamp, A & 0x3);
585 
586    return;
587   }
588 
589   if(A >= 0x1F801810 && A <= 0x1F801817)
590   {
591    if(!IsWrite)
592     timestamp++;
593 
594    if(IsWrite)
595     GPU_Write(timestamp, A, V);
596    else
597     V = GPU_Read(timestamp, A);
598 
599    return;
600   }
601 
602   if(A >= 0x1F801820 && A <= 0x1F801827)
603   {
604    if(!IsWrite)
605     timestamp++;
606 
607    if(IsWrite)
608     MDEC_Write(timestamp, A, V);
609    else
610     V = MDEC_Read(timestamp, A);
611 
612    return;
613   }
614 
615   if(A >= 0x1F801000 && A <= 0x1F801023)
616   {
617    unsigned index = (A & 0x1F) >> 2;
618 
619    if(!IsWrite)
620     timestamp++;
621 
622    //if(A == 0x1F801014 && IsWrite)
623    // fprintf(stderr, "%08x %08x\n",A,V);
624 
625    if(IsWrite)
626    {
627     V <<= (A & 3) * 8;
628     SysControl.Regs[index] = V & SysControl_Mask[index];
629    }
630    else
631    {
632     V = SysControl.Regs[index] | SysControl_OR[index];
633     V >>= (A & 3) * 8;
634    }
635    return;
636   }
637 
638   if(A >= 0x1F801040 && A <= 0x1F80104F)
639   {
640    if(!IsWrite)
641     timestamp++;
642 
643    if(IsWrite)
644     FIO->Write(timestamp, A, V);
645    else
646     V = FIO->Read(timestamp, A);
647    return;
648   }
649 
650   if(A >= 0x1F801050 && A <= 0x1F80105F)
651   {
652    if(!IsWrite)
653     timestamp++;
654 
655 #if 0
656    if(IsWrite)
657    {
658     PSX_WARNING("[SIO] Write: 0x%08x 0x%08x %u", A, V, (unsigned)sizeof(T));
659    }
660    else
661    {
662     PSX_WARNING("[SIO] Read: 0x%08x", A);
663    }
664 #endif
665 
666    if(IsWrite)
667     SIO_Write(timestamp, A, V);
668    else
669     V = SIO_Read(timestamp, A);
670    return;
671   }
672 
673 #if 0
674   if(A >= 0x1F801060 && A <= 0x1F801063)
675   {
676    if(IsWrite)
677    {
678 
679    }
680    else
681    {
682 
683    }
684 
685    return;
686   }
687 #endif
688 
689   if(A >= 0x1F801070 && A <= 0x1F801077)	// IRQ
690   {
691    if(!IsWrite)
692     timestamp++;
693 
694    if(IsWrite)
695     IRQ_Write(A, V);
696    else
697     V = IRQ_Read(A);
698    return;
699   }
700 
701   if(A >= 0x1F801080 && A <= 0x1F8010FF) 	// DMA
702   {
703    if(!IsWrite)
704     timestamp++;
705 
706    if(IsWrite)
707     DMA_Write(timestamp, A, V);
708    else
709     V = DMA_Read(timestamp, A);
710 
711    return;
712   }
713 
714   if(A >= 0x1F801100 && A <= 0x1F80113F)	// Root counters
715   {
716    if(!IsWrite)
717     timestamp++;
718 
719    if(IsWrite)
720     TIMER_Write(timestamp, A, V);
721    else
722     V = TIMER_Read(timestamp, A);
723 
724    return;
725   }
726  }
727 
728 
729  if(A >= 0x1F000000 && A <= 0x1F7FFFFF)
730  {
731   if(!IsWrite)
732   {
733    //if((A & 0x7FFFFF) <= 0x84)
734    //PSX_WARNING("[PIO] Read%d from 0x%08x at time %d", (int)(sizeof(T) * 8), A, timestamp);
735 
736    V = ~0U;	// A game this affects:  Tetris with Cardcaptor Sakura
737 
738    if(PIOMem)
739    {
740     if((A & 0x7FFFFF) < 65536)
741     {
742      if(Access24)
743       V = PIOMem->ReadU24(A & 0x7FFFFF);
744      else
745       V = PIOMem->Read<T>(A & 0x7FFFFF);
746     }
747     else if((A & 0x7FFFFF) < (65536 + TextMem.size()))
748     {
749      if(Access24)
750       V = MDFN_de24lsb(&TextMem[(A & 0x7FFFFF) - 65536]);
751      else switch(sizeof(T))
752      {
753       case 1: V = TextMem[(A & 0x7FFFFF) - 65536]; break;
754       case 2: V = MDFN_de16lsb(&TextMem[(A & 0x7FFFFF) - 65536]); break;
755       case 4: V = MDFN_de32lsb(&TextMem[(A & 0x7FFFFF) - 65536]); break;
756      }
757     }
758    }
759   }
760   return;
761  }
762 
763  if(A == 0xFFFE0130) // Per tests on PS1, ignores the access(sort of, on reads the value is forced to 0 if not aligned) if not aligned to 4-bytes.
764  {
765   if(!IsWrite)
766    V = CPU->GetBIU();
767   else
768    CPU->SetBIU(V);
769 
770   return;
771  }
772 
773  if(IsWrite)
774  {
775   PSX_WARNING("[MEM] Unknown write%d to %08x at time %d, =%08x(%d)", (int)(sizeof(T) * 8), A, timestamp, V, V);
776  }
777  else
778  {
779   V = 0;
780   PSX_WARNING("[MEM] Unknown read%d from %08x at time %d", (int)(sizeof(T) * 8), A, timestamp);
781  }
782 }
783 
PSX_MemWrite8(pscpu_timestamp_t timestamp,uint32 A,uint32 V)784 void MDFN_FASTCALL PSX_MemWrite8(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
785 {
786  MemRW<uint8, true, false>(timestamp, A, V);
787 }
788 
PSX_MemWrite16(pscpu_timestamp_t timestamp,uint32 A,uint32 V)789 void MDFN_FASTCALL PSX_MemWrite16(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
790 {
791  MemRW<uint16, true, false>(timestamp, A, V);
792 }
793 
PSX_MemWrite24(pscpu_timestamp_t timestamp,uint32 A,uint32 V)794 void MDFN_FASTCALL PSX_MemWrite24(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
795 {
796  MemRW<uint32, true, true>(timestamp, A, V);
797 }
798 
PSX_MemWrite32(pscpu_timestamp_t timestamp,uint32 A,uint32 V)799 void MDFN_FASTCALL PSX_MemWrite32(pscpu_timestamp_t timestamp, uint32 A, uint32 V)
800 {
801  MemRW<uint32, true, false>(timestamp, A, V);
802 }
803 
PSX_MemRead8(pscpu_timestamp_t & timestamp,uint32 A)804 uint8 MDFN_FASTCALL PSX_MemRead8(pscpu_timestamp_t &timestamp, uint32 A)
805 {
806  uint32 V;
807 
808  MemRW<uint8, false, false>(timestamp, A, V);
809 
810  return(V);
811 }
812 
PSX_MemRead16(pscpu_timestamp_t & timestamp,uint32 A)813 uint16 MDFN_FASTCALL PSX_MemRead16(pscpu_timestamp_t &timestamp, uint32 A)
814 {
815  uint32 V;
816 
817  MemRW<uint16, false, false>(timestamp, A, V);
818 
819  return(V);
820 }
821 
PSX_MemRead24(pscpu_timestamp_t & timestamp,uint32 A)822 uint32 MDFN_FASTCALL PSX_MemRead24(pscpu_timestamp_t &timestamp, uint32 A)
823 {
824  uint32 V;
825 
826  MemRW<uint32, false, true>(timestamp, A, V);
827 
828  return(V);
829 }
830 
PSX_MemRead32(pscpu_timestamp_t & timestamp,uint32 A)831 uint32 MDFN_FASTCALL PSX_MemRead32(pscpu_timestamp_t &timestamp, uint32 A)
832 {
833  uint32 V;
834 
835  MemRW<uint32, false, false>(timestamp, A, V);
836 
837  return(V);
838 }
839 
MemPeek(pscpu_timestamp_t timestamp,uint32 A)840 template<typename T, bool Access24> static INLINE uint32 MemPeek(pscpu_timestamp_t timestamp, uint32 A)
841 {
842  if(A < 0x00800000)
843  {
844   if(Access24)
845    return(MainRAM.ReadU24(A & 0x1FFFFF));
846   else
847    return(MainRAM.Read<T>(A & 0x1FFFFF));
848  }
849 
850  if(A >= 0x1FC00000 && A <= 0x1FC7FFFF)
851  {
852   if(Access24)
853    return(BIOSROM->ReadU24(A & 0x7FFFF));
854   else
855    return(BIOSROM->Read<T>(A & 0x7FFFF));
856  }
857 
858  if(A >= 0x1F801000 && A <= 0x1F802FFF)
859  {
860   if(A >= 0x1F801C00 && A <= 0x1F801FFF) // SPU
861   {
862    // TODO
863 
864   }		// End SPU
865 
866 
867   // CDC: TODO - 8-bit access.
868   if(A >= 0x1f801800 && A <= 0x1f80180F)
869   {
870    // TODO
871 
872   }
873 
874   if(A >= 0x1F801810 && A <= 0x1F801817)
875   {
876    // TODO
877 
878   }
879 
880   if(A >= 0x1F801820 && A <= 0x1F801827)
881   {
882    // TODO
883 
884   }
885 
886   if(A >= 0x1F801000 && A <= 0x1F801023)
887   {
888    unsigned index = (A & 0x1F) >> 2;
889    return((SysControl.Regs[index] | SysControl_OR[index]) >> ((A & 3) * 8));
890   }
891 
892   if(A >= 0x1F801040 && A <= 0x1F80104F)
893   {
894    // TODO
895 
896   }
897 
898   if(A >= 0x1F801050 && A <= 0x1F80105F)
899   {
900    // TODO
901 
902   }
903 
904 
905   if(A >= 0x1F801070 && A <= 0x1F801077)	// IRQ
906   {
907    // TODO
908 
909   }
910 
911   if(A >= 0x1F801080 && A <= 0x1F8010FF) 	// DMA
912   {
913    // TODO
914 
915   }
916 
917   if(A >= 0x1F801100 && A <= 0x1F80113F)	// Root counters
918   {
919    // TODO
920 
921   }
922  }
923 
924 
925  if(A >= 0x1F000000 && A <= 0x1F7FFFFF)
926  {
927   if(PIOMem)
928   {
929    if((A & 0x7FFFFF) < 65536)
930    {
931     if(Access24)
932      return(PIOMem->ReadU24(A & 0x7FFFFF));
933     else
934      return(PIOMem->Read<T>(A & 0x7FFFFF));
935    }
936    else if((A & 0x7FFFFF) < (65536 + TextMem.size()))
937    {
938     if(Access24)
939      return(MDFN_de24lsb(&TextMem[(A & 0x7FFFFF) - 65536]));
940     else switch(sizeof(T))
941     {
942      case 1: return(TextMem[(A & 0x7FFFFF) - 65536]); break;
943      case 2: return(MDFN_de16lsb(&TextMem[(A & 0x7FFFFF) - 65536])); break;
944      case 4: return(MDFN_de32lsb(&TextMem[(A & 0x7FFFFF) - 65536])); break;
945     }
946    }
947   }
948   return(~0U);
949  }
950 
951  if(A == 0xFFFE0130)
952   return CPU->GetBIU();
953 
954  return(0);
955 }
956 
PSX_MemPeek8(uint32 A)957 uint8 PSX_MemPeek8(uint32 A)
958 {
959  return MemPeek<uint8, false>(0, A);
960 }
961 
PSX_MemPeek16(uint32 A)962 uint16 PSX_MemPeek16(uint32 A)
963 {
964  return MemPeek<uint16, false>(0, A);
965 }
966 
PSX_MemPeek32(uint32 A)967 uint32 PSX_MemPeek32(uint32 A)
968 {
969  return MemPeek<uint32, false>(0, A);
970 }
971 
MemPoke(pscpu_timestamp_t timestamp,uint32 A,T V)972 template<typename T, bool Access24> static INLINE void MemPoke(pscpu_timestamp_t timestamp, uint32 A, T V)
973 {
974  if(A < 0x00800000)
975  {
976   if(Access24)
977    MainRAM.WriteU24(A & 0x1FFFFF, V);
978   else
979    MainRAM.Write<T>(A & 0x1FFFFF, V);
980 
981   return;
982  }
983 
984  if(A >= 0x1FC00000 && A <= 0x1FC7FFFF)
985  {
986   if(Access24)
987    BIOSROM->WriteU24(A & 0x7FFFF, V);
988   else
989    BIOSROM->Write<T>(A & 0x7FFFF, V);
990 
991   return;
992  }
993 
994  if(A >= 0x1F801000 && A <= 0x1F802FFF)
995  {
996   if(A >= 0x1F801000 && A <= 0x1F801023)
997   {
998    unsigned index = (A & 0x1F) >> 2;
999    SysControl.Regs[index] = (V << ((A & 3) * 8)) & SysControl_Mask[index];
1000    return;
1001   }
1002  }
1003 
1004  if(A == 0xFFFE0130)
1005  {
1006   CPU->SetBIU(V);
1007   return;
1008  }
1009 }
1010 
PSX_MemPoke8(uint32 A,uint8 V)1011 void PSX_MemPoke8(uint32 A, uint8 V)
1012 {
1013  MemPoke<uint8, false>(0, A, V);
1014 }
1015 
PSX_MemPoke16(uint32 A,uint16 V)1016 void PSX_MemPoke16(uint32 A, uint16 V)
1017 {
1018  MemPoke<uint16, false>(0, A, V);
1019 }
1020 
PSX_MemPoke32(uint32 A,uint32 V)1021 void PSX_MemPoke32(uint32 A, uint32 V)
1022 {
1023  MemPoke<uint32, false>(0, A, V);
1024 }
1025 
1026 
PSX_Reset(bool powering_up)1027 static void PSX_Reset(bool powering_up)
1028 {
1029  PSX_PRNG.ResetState();	// Should occur first!
1030 
1031  memset(MainRAM.data8, 0, 2048 * 1024);
1032 
1033  for(unsigned i = 0; i < 9; i++)
1034   SysControl.Regs[i] = 0;
1035 
1036  CPU->Power();
1037 
1038  EventReset();
1039 
1040  TIMER_Power();
1041 
1042  DMA_Power();
1043 
1044  FIO->Reset(powering_up);
1045  SIO_Power();
1046 
1047  MDEC_Power();
1048  CDC->Power();
1049  GPU_Power();
1050  //SPU->Power();	// Called from CDC->Power()
1051  IRQ_Power();
1052 
1053  ForceEventUpdates(0);
1054 }
1055 
1056 
PSX_GPULineHook(const pscpu_timestamp_t timestamp,const pscpu_timestamp_t line_timestamp,bool vsync,uint32 * pixels,const MDFN_PixelFormat * const format,const unsigned width,const unsigned pix_clock_offset,const unsigned pix_clock,const unsigned pix_clock_divider)1057 void PSX_GPULineHook(const pscpu_timestamp_t timestamp, const pscpu_timestamp_t line_timestamp, bool vsync, uint32 *pixels, const MDFN_PixelFormat* const format, const unsigned width, const unsigned pix_clock_offset, const unsigned pix_clock, const unsigned pix_clock_divider)
1058 {
1059  FIO->GPULineHook(timestamp, line_timestamp, vsync, pixels, format, width, pix_clock_offset, pix_clock, pix_clock_divider);
1060 }
1061 
1062 }
1063 
1064 using namespace MDFN_IEN_PSX;
1065 
1066 
Emulate(EmulateSpecStruct * espec)1067 static void Emulate(EmulateSpecStruct *espec)
1068 {
1069  pscpu_timestamp_t timestamp = 0;
1070 
1071  if(FIO->RequireNoFrameskip())
1072  {
1073   //puts("MEOW");
1074   espec->skip = false;	//TODO: Save here, and restore at end of Emulate() ?
1075  }
1076 
1077  MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("psx.input.mouse_sensitivity");
1078 
1079  MDFNMP_ApplyPeriodicCheats();
1080 
1081 
1082  espec->MasterCycles = 0;
1083  espec->SoundBufSize = 0;
1084 
1085  FIO->UpdateInput();
1086  GPU_StartFrame(psf_loader ? NULL : espec);
1087  SPU->StartFrame(espec->SoundRate, MDFN_GetSettingUI("psx.spu.resamp_quality"));
1088 
1089  Running = -1;
1090  timestamp = CPU->Run(timestamp, psf_loader == NULL && psx_dbg_level >= PSX_DBG_BIOS_PRINT, psf_loader != NULL);
1091 
1092  assert(timestamp);
1093 
1094  ForceEventUpdates(timestamp);
1095  if(GPU_GetScanlineNum() < 100)
1096   PSX_DBG(PSX_DBG_ERROR, "[BUUUUUUUG] Frame timing end glitch; scanline=%u, st=%u\n", GPU_GetScanlineNum(), timestamp);
1097 
1098  //printf("scanline=%u, st=%u\n", GPU_GetScanlineNum(), timestamp);
1099 
1100  espec->SoundBufSize = SPU->EndFrame(espec->SoundBuf, espec->NeedSoundReverse);
1101  espec->NeedSoundReverse = false;
1102 
1103  CDC->ResetTS();
1104  TIMER_ResetTS();
1105  DMA_ResetTS();
1106  GPU_ResetTS();
1107  FIO->ResetTS();
1108 
1109  RebaseTS(timestamp);
1110 
1111  espec->MasterCycles = timestamp;
1112 
1113 /*
1114  {
1115   static int frame_counter = -120;
1116   static uint64 cycle_counter = 0;
1117 
1118   frame_counter++;
1119   if(frame_counter > 0)
1120   {
1121    cycle_counter += espec->MasterCycles;
1122    printf("moo: %f %f\n", EmulatedPSX.fps / 65536.0 / 256.0, (double)44100 * 768.0 * frame_counter / cycle_counter);
1123   }
1124  }
1125 */
1126 
1127  if(psf_loader)
1128  {
1129   if(!espec->skip)
1130   {
1131    espec->LineWidths[0] = ~0;
1132    Player_Draw(espec->surface, &espec->DisplayRect, 0, espec->SoundBuf, espec->SoundBufSize);
1133   }
1134  }
1135 
1136  FIO->UpdateOutput();
1137 
1138  // Save memcards if dirty.
1139  if(!psf_loader)
1140  {
1141   for(int i = 0; i < 8; i++)
1142   {
1143    uint64 new_dc = FIO->GetMemcardDirtyCount(i);
1144 
1145    if(new_dc > Memcard_PrevDC[i])
1146    {
1147     Memcard_PrevDC[i] = new_dc;
1148     Memcard_SaveDelay[i] = 0;
1149    }
1150 
1151    if(Memcard_SaveDelay[i] >= 0)
1152    {
1153     Memcard_SaveDelay[i] += timestamp;
1154     if(Memcard_SaveDelay[i] >= (33868800 * 2))	// Wait until about 2 seconds of no new writes.
1155     {
1156      PSX_DBG(PSX_DBG_SPARSE, "Saving memcard %d...\n", i);
1157      try
1158      {
1159       char ext[64];
1160       trio_snprintf(ext, sizeof(ext), "%d.mcr", i);
1161       FIO->SaveMemcard(i, MDFN_MakeFName(MDFNMKF_SAV, 0, ext));
1162       Memcard_SaveDelay[i] = -1;
1163       Memcard_PrevDC[i] = 0;
1164      }
1165      catch(std::exception &e)
1166      {
1167       MDFN_Notify(MDFN_NOTICE_ERROR, _("Memcard %d save error: %s"), i, e.what());
1168       Memcard_SaveDelay[i] = 0; // Delay before trying to save again
1169      }
1170     }
1171    }
1172   }
1173  }
1174 }
1175 
CalcRegion_By_SYSTEMCNF(CDInterface * c,unsigned * rr)1176 static bool CalcRegion_By_SYSTEMCNF(CDInterface *c, unsigned *rr)
1177 {
1178  try
1179  {
1180   uint8 pvd[2048];
1181   unsigned pvd_search_count = 0;
1182   std::unique_ptr<Stream> fp(c->MakeStream(0, ~0U));
1183 
1184   fp->seek(0x8000, SEEK_SET);
1185 
1186   do
1187   {
1188    if((pvd_search_count++) == 32)
1189     throw MDFN_Error(0, "PVD search count limit met.");
1190 
1191    fp->read(pvd, 2048);
1192 
1193    if(memcmp(&pvd[1], "CD001", 5))
1194     throw MDFN_Error(0, "Not ISO-9660");
1195 
1196    if(pvd[0] == 0xFF)
1197     throw MDFN_Error(0, "Missing Primary Volume Descriptor");
1198   } while(pvd[0] != 0x01);
1199   //[156 ... 189], 34 bytes
1200   uint32 rdel = MDFN_de32lsb(&pvd[0x9E]);
1201   uint32 rdel_len = MDFN_de32lsb(&pvd[0xA6]);
1202 
1203   if(rdel_len >= (1024 * 1024 * 10))	// Arbitrary sanity check.
1204    throw MDFN_Error(0, "Root directory table too large");
1205 
1206   fp->seek((int64)rdel * 2048, SEEK_SET);
1207   //printf("%08x, %08x\n", rdel * 2048, rdel_len);
1208   while(fp->tell() < (((uint64)rdel * 2048) + rdel_len))
1209   {
1210    uint8 len_dr = fp->get_u8();
1211    uint8 dr[256 + 1];
1212 
1213    memset(dr, 0xFF, sizeof(dr));
1214 
1215    if(!len_dr)
1216     break;
1217 
1218    memset(dr, 0, sizeof(dr));
1219    dr[0] = len_dr;
1220    fp->read(dr + 1, len_dr - 1);
1221 
1222    uint8 len_fi = dr[0x20];
1223 
1224    if(len_fi == 12 && !memcmp(&dr[0x21], "SYSTEM.CNF;1", 12))
1225    {
1226     uint32 file_lba = MDFN_de32lsb(&dr[0x02]);
1227     //uint32 file_len = MDFN_de32lsb(&dr[0x0A]);
1228     uint8 fb[2048 + 1];
1229     char *bootpos;
1230 
1231     memset(fb, 0, sizeof(fb));
1232     fp->seek(file_lba * 2048, SEEK_SET);
1233     fp->read(fb, 2048);
1234 
1235     if((bootpos = strstr((char*)fb, "BOOT")))
1236     {
1237      bootpos += 4;
1238      while(*bootpos == ' ' || *bootpos == '\t') bootpos++;
1239      if(*bootpos == '=')
1240      {
1241       bootpos++;
1242       while(*bootpos == ' ' || *bootpos == '\t') bootpos++;
1243       if(!MDFN_strazicmp(bootpos, "cdrom:", 6))
1244       {
1245        char* tmp;
1246 
1247        bootpos += 6;
1248 
1249        // strrchr() way will pick up Tekken 3, but only enable if needed due to possibility of regressions.
1250        //if((tmp = strrchr(bootpos, '\\')))
1251        // bootpos = tmp + 1;
1252        while(*bootpos == '\\')
1253         bootpos++;
1254 
1255        if((tmp = strchr(bootpos, '_'))) *tmp = 0;
1256        if((tmp = strchr(bootpos, '.'))) *tmp = 0;
1257        if((tmp = strchr(bootpos, ';'))) *tmp = 0;
1258        //puts(bootpos);
1259 
1260        if(strlen(bootpos) == 4 && MDFN_azupper(bootpos[0]) == 'S' && (MDFN_azupper(bootpos[1]) == 'C' || MDFN_azupper(bootpos[1]) == 'L' || MDFN_azupper(bootpos[1]) == 'I'))
1261        {
1262         switch(MDFN_azupper(bootpos[2]))
1263         {
1264 	 case 'E': *rr = REGION_EU;
1265 	           return(true);
1266 
1267 	 case 'U': *rr = REGION_NA;
1268 		   return(true);
1269 
1270 	 case 'K':	// Korea?
1271 	 case 'B':
1272 	 case 'P': *rr = REGION_JP;
1273 		   return(true);
1274         }
1275        }
1276       }
1277      }
1278     }
1279    }
1280   }
1281  }
1282  catch(std::exception &e)
1283  {
1284   //puts(e.what());
1285  }
1286  catch(...)
1287  {
1288 
1289  }
1290 
1291  return(false);
1292 }
1293 
CalcRegion_By_SA(const uint8 buf[2048* 8],unsigned * region)1294 static bool CalcRegion_By_SA(const uint8 buf[2048 * 8], unsigned* region)
1295 {
1296 	uint8 fbuf[2048 + 1];
1297 	unsigned ipos, opos;
1298 
1299 	memset(fbuf, 0, sizeof(fbuf));
1300 
1301 	for(ipos = 0, opos = 0; ipos < 0x48; ipos++)
1302 	{
1303 	 if(buf[ipos] > 0x20 && buf[ipos] < 0x80)
1304 	 {
1305 	  fbuf[opos++] = MDFN_azlower(buf[ipos]);
1306 	 }
1307 	}
1308 
1309 	fbuf[opos++] = 0;
1310 
1311 	PSX_DBG(PSX_DBG_SPARSE, "License string: %s\n", (char *)fbuf);
1312 
1313 	if(strstr((char *)fbuf, "licensedby") != NULL)
1314 	{
1315 	 if(strstr((char *)fbuf, "america") != NULL)
1316 	 {
1317           *region = REGION_NA;
1318           return(true);
1319          }
1320          else if(strstr((char *)fbuf, "europe") != NULL)
1321          {
1322           *region = REGION_EU;
1323 	  return(true);
1324          }
1325          else if(strstr((char *)fbuf, "japan") != NULL)
1326          {
1327           *region = REGION_JP;
1328           return(true);
1329          }
1330          else if(strstr((char *)fbuf, "sonycomputerentertainmentinc.") != NULL)
1331          {
1332           *region = REGION_JP;
1333           return(true);
1334          }
1335         }
1336 
1337 	return(false);
1338 }
1339 
1340 
1341 //
1342 // Returns true if constraint applied(*region changed), false otherwise.
1343 //
ConstrainRegion_By_SA(const uint8 buf[2048* 8],unsigned * region)1344 static bool ConstrainRegion_By_SA(const uint8 buf[2048 * 8], unsigned* region)
1345 {
1346   //
1347   // If we're going with Japanese region,
1348   // make sure the licensed-by string is correct(Japanese BIOS is kinda strict).
1349   //
1350   if(*region == REGION_JP)
1351   {
1352     static const char tv[2][0x41] = {
1353 			      { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 76, 105, 99, 101, 110, 115,
1354 			        101, 100, 32, 32, 98, 121, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1355 			         83, 111, 110, 121, 32, 67, 111, 109, 112, 117, 116, 101, 114, 32, 69, 110,
1356 			        116, 101, 114, 116, 97, 105, 110, 109, 101, 110, 116, 32, 73, 110, 99, 46,
1357 			        0
1358 			      },
1359 			      { 32, 32, 32, 32, 32, 32, 32, 32, 32, 32, 76, 105, 99, 101, 110, 115,
1360 			        101, 100, 32, 32, 98, 121, 32, 32, 32, 32, 32, 32, 32, 32, 32, 32,
1361 			         83, 111, 110, 121, 32, 67, 111, 109, 112, 117, 116, 101, 114, 32, 69, 110,
1362 			        116, 101, 114, 116, 97, 105, 110, 109, 101, 110, 116, 32, 73, 110, 99, 46,
1363 			        10
1364 			      },
1365 			    };
1366     bool jp_incompatible = false;
1367 
1368     if(memcmp(&buf[0], &tv[0][0], 0x41) && memcmp(&buf[0], &tv[1][0], 0x41))
1369      jp_incompatible = true;
1370 
1371     if(crc32(0, &buf[0x800], 0x3278) != 0x0069c087)
1372      jp_incompatible = true;
1373 
1374     if(jp_incompatible)
1375     {
1376      //
1377      // Doesn't match, so default to most similar region, NA.
1378      //
1379      *region = REGION_NA;
1380      return(true);
1381     }
1382    }
1383 
1384  return(false);
1385 }
1386 
Region_To_SCEx(const unsigned region)1387 static const char* Region_To_SCEx(const unsigned region)
1388 {
1389  switch(region)
1390  {
1391        default:
1392 	  abort();
1393 
1394        case REGION_JP:
1395 	  return("SCEI");
1396 
1397        case REGION_NA:
1398 	  return("SCEA");
1399 
1400        case REGION_EU:
1401 	  return("SCEE");
1402  }
1403 }
1404 
TestMagic(GameFile * gf)1405 static bool TestMagic(GameFile* gf)
1406 {
1407  if(PSFLoader::TestMagic(0x01, gf->stream))
1408   return(true);
1409 
1410  gf->stream->rewind();
1411 
1412  uint8 exe_header[0x800];
1413  if(gf->stream->read(exe_header, 0x800, false) == 0x800 && !memcmp(exe_header, "PS-X EXE", 8))
1414   return true;
1415 
1416  return false;
1417 }
1418 
TestMagicCD(std::vector<CDInterface * > * CDInterfaces)1419 static bool TestMagicCD(std::vector<CDInterface*> *CDInterfaces)
1420 {
1421  std::unique_ptr<uint8[]> buf(new uint8[2048 * 8]);
1422 
1423  if((*CDInterfaces)[0]->ReadSectors(&buf[0], 4, 8) != 0x2)
1424   return(false);
1425 
1426  if(strncmp((char *)&buf[10], "Licensed  by", strlen("Licensed  by")) && crc32(0, &buf[0x800], 0x3278) != 0x0069c087)
1427   return(false);
1428 
1429  return(true);
1430 }
1431 
1432 
CalcDiscSCEx(void)1433 static unsigned CalcDiscSCEx(void)
1434 {
1435  const unsigned region_default = MDFN_GetSettingI("psx.region_default");
1436  bool found_ps1_disc = false;
1437  bool did_constraint = false;
1438  unsigned ret_region = region_default;
1439  unsigned region = region_default;
1440 
1441  cdifs_scex_ids.clear();
1442 
1443  for(unsigned i = 0; i < (cdifs ? cdifs->size() : 0); i++)
1444  {
1445   std::unique_ptr<uint8[]> buf(new uint8[2048 * 8]);
1446   bool is_ps1_disc = true;
1447 
1448   //
1449   // Read the PS1 system area.
1450   //
1451   if((*cdifs)[i]->ReadSectors(buf.get(), 4, 8) == 0x2)
1452   {
1453    if(!CalcRegion_By_SYSTEMCNF((*cdifs)[i], &region))
1454    {
1455     if(!CalcRegion_By_SA(buf.get(), &region))
1456     {
1457      if(strncmp((char *)buf.get() + 10, "Licensed  by", strlen("Licensed  by")) && crc32(0, &buf[0x800], 0x3278) != 0x0069c087)
1458      {
1459       //
1460       // Last-ditch effort before deciding the disc isn't a PS1 disc.
1461       //
1462       uint8 pvd[2048];
1463 
1464       if((*cdifs)[i]->ReadSectors(pvd, 16, 1) != 0x2 || memcmp(&pvd[0], "\x01" "CD001", 6) || memcmp(&pvd[8], "PLAYSTATION", 11))
1465        is_ps1_disc = false;
1466      }
1467     }
1468    }
1469   }
1470   else
1471    is_ps1_disc = false;
1472 
1473   //
1474   // If PS1 disc, apply any constraints and change the region as necessary(e.g. Japanese PS1 BIOS is strict about the structure of the system area);
1475   // especially necessary for homebrew that failed automatic region detection above.
1476   //
1477   if(is_ps1_disc)
1478   {
1479    if(!found_ps1_disc || did_constraint)
1480    {
1481     if(ConstrainRegion_By_SA(buf.get(), &region))
1482      did_constraint = true;
1483    }
1484   }
1485 
1486   //
1487   // Determine what sort of PS1 to emulate based on the first PS1 disc in the set.
1488   //
1489   if(!found_ps1_disc)
1490    ret_region = region;
1491 
1492   found_ps1_disc |= is_ps1_disc;
1493   cdifs_scex_ids.push_back(is_ps1_disc ? Region_To_SCEx(region) : NULL);
1494  }
1495 
1496  if(cdifs_scex_ids.size())
1497  {
1498   MDFN_printf(_("Emulated Disc SCEx IDs:\n"));
1499   {
1500    MDFN_AutoIndent aind(1);
1501 
1502    for(size_t x = 0; x < cdifs_scex_ids.size(); x++)
1503    {
1504     MDFN_printf(_("Disc %zu: %s\n"), x + 1, (cdifs_scex_ids[x] ? cdifs_scex_ids[x] : _("(Not recognized as a PS1 disc)")));
1505    }
1506   }
1507  }
1508 
1509  return ret_region;
1510 }
1511 
DiscSanityChecks(void)1512 static void DiscSanityChecks(void)
1513 {
1514  if(!cdifs)
1515   return;
1516 
1517  assert(cdifs->size() == cdifs_scex_ids.size());
1518 
1519  for(size_t i = 0; i < cdifs_scex_ids.size(); i++)
1520  {
1521   //
1522   // Sanity check to ensure Q subchannel timing data relative to mode1/mode2 header timing data is as we expect it to be for a PS1 disc.
1523   //
1524   if(cdifs_scex_ids[i])
1525   {
1526    bool did_check = false;
1527 
1528    for(int32 lba = -8; lba < 16; lba++)
1529    {
1530     uint8 rawbuf[2352 + 96];
1531 
1532     if((*cdifs)[i]->ReadRawSector(rawbuf, lba))
1533     {
1534      uint8 qbuf[12];
1535 
1536      CDUtility::subq_deinterleave(rawbuf + 2352, qbuf);
1537      if(CDUtility::subq_check_checksum(qbuf) && (qbuf[0] & 0xF) == CDUtility::ADR_CURPOS)
1538      {
1539       uint8 qm = qbuf[7];
1540       uint8 qs = qbuf[8];
1541       uint8 qf = qbuf[9];
1542 
1543       uint8 hm = rawbuf[12];
1544       uint8 hs = rawbuf[13];
1545       uint8 hf = rawbuf[14];
1546 
1547       uint8 lm, ls, lf;
1548 
1549       CDUtility::LBA_to_AMSF(lba, &lm, &ls, &lf);
1550       lm = CDUtility::U8_to_BCD(lm);
1551       ls = CDUtility::U8_to_BCD(ls);
1552       lf = CDUtility::U8_to_BCD(lf);
1553 
1554       if(qm != hm || qs != hs || qf != hf)
1555       {
1556        throw MDFN_Error(0, _("Disc %zu of %zu: Q-subchannel versus sector header absolute time mismatch at lba=%d; Q subchannel: %02x:%02x:%02x, Sector header: %02x:%02x:%02x"),
1557 		i + 1, cdifs->size(),
1558 		lba,
1559 		qm, qs, qf,
1560 		hm, hs, hf);
1561       }
1562 
1563       if(lm != hm || ls != hs || lf != hf)
1564       {
1565        throw MDFN_Error(0, _("Disc %zu of %zu: Sector header absolute time broken at lba=%d(%02x:%02x:%02x); Sector header: %02x:%02x:%02x"),
1566 		i + 1, cdifs->size(),
1567 		lba,
1568 		lm, ls, lf,
1569 		hm, hs, hf);
1570       }
1571 
1572       if(lba >= 0)
1573        did_check = true;
1574      }
1575     }
1576    }
1577 
1578    if(!did_check)
1579    {
1580     throw MDFN_Error(0, _("Disc %zu of %zu: No valid Q subchannel ADR_CURPOS data present at lba 0-15?!"), i + 1, cdifs->size());
1581    }
1582   }
1583  }
1584 }
1585 
InitCommon(std::vector<CDInterface * > * CDInterfaces,const bool IsPSF=false,const bool WantPIOMem=false)1586 static MDFN_COLD void InitCommon(std::vector<CDInterface*> *CDInterfaces, const bool IsPSF = false, const bool WantPIOMem = false)
1587 {
1588  unsigned region;
1589  int sls, sle;
1590  bool correct_aspect;
1591 
1592 #if PSX_DBGPRINT_ENABLE
1593  psx_dbg_level = MDFN_GetSettingUI("psx.dbg_level");
1594 #endif
1595 
1596  cdifs = CDInterfaces;
1597  region = CalcDiscSCEx();
1598  if(MDFN_GetSettingB("psx.cd_sanity"))
1599   DiscSanityChecks();
1600  else
1601   MDFN_printf(_("WARNING: CD (image) sanity checks disabled."));
1602 
1603  if(!MDFN_GetSettingB("psx.region_autodetect"))
1604   region = MDFN_GetSettingI("psx.region_default");
1605 
1606  MDFN_printf("\n");
1607 
1608  for(auto const* rle = Region_List; rle->string; rle++)
1609  {
1610   if((unsigned)rle->number == region)
1611   {
1612    MDFN_printf(_("Region: %s\n"), rle->description);
1613    break;
1614   }
1615  }
1616 
1617  sls = MDFN_GetSettingI((region == REGION_EU) ? "psx.slstartp" : "psx.slstart");
1618  sle = MDFN_GetSettingI((region == REGION_EU) ? "psx.slendp" : "psx.slend");
1619  correct_aspect = MDFN_GetSettingB("psx.correct_aspect");
1620 
1621  if(sls > sle)
1622  {
1623   int tmp = sls;
1624   sls = sle;
1625   sle = tmp;
1626  }
1627 
1628  CPU = new PS_CPU();
1629  SPU = new PS_SPU();
1630  GPU_Init(region == REGION_EU);
1631  CDC = new PS_CDC();
1632  FIO = new FrontIO();
1633 
1634  MDFN_printf("\n");
1635  for(unsigned pp = 0; pp < 2; pp++)
1636  {
1637   char buf[64];
1638   bool sv;
1639 
1640   trio_snprintf(buf, sizeof(buf), "psx.input.pport%u.multitap", pp + 1);
1641   sv = MDFN_GetSettingB(buf);
1642   FIO->SetMultitap(pp, sv);
1643 
1644   MDFN_printf(_("Multitap on PSX Port %u: %s\n"), pp + 1, sv ? _("Enabled") : _("Disabled"));
1645  }
1646 
1647  FIO->SetAMCT(MDFN_GetSettingB("psx.input.analog_mode_ct"), MDFN_GetSettingUI("psx.input.analog_mode_ct.compare"));
1648  for(unsigned i = 0; i < 8; i++)
1649  {
1650   char buf[64];
1651 
1652   trio_snprintf(buf, sizeof(buf), "psx.input.port%u.gun_chairs", i + 1);
1653   FIO->SetCrosshairsColor(i, MDFN_GetSettingUI(buf));
1654 
1655   {
1656    bool mcsv;
1657 
1658    trio_snprintf(buf, sizeof(buf), "psx.input.port%u.memcard", i + 1);
1659    mcsv = !IsPSF && MDFN_GetSettingB(buf);
1660    FIO->SetMemcard(i, mcsv);
1661 
1662    //MDFN_printf(_("Memcard on Virtual Port %u: %s\n"), i + 1, mcsv ? _("Enabled") : _("Disabled"));
1663   }
1664  }
1665 
1666  DMA_Init();
1667 
1668  GPU_SetGetVideoParams(&EmulatedPSX, correct_aspect, sls, sle, MDFN_GetSettingB("psx.h_overscan"));
1669 
1670  CDC->SetDisc(true, NULL, NULL);
1671 
1672  BIOSROM = new MultiAccessSizeMem<512 * 1024, false>();
1673 
1674  if(WantPIOMem)
1675   PIOMem = new MultiAccessSizeMem<65536, false>();
1676  else
1677   PIOMem = NULL;
1678 
1679  for(uint32 ma = 0x00000000; ma < 0x00800000; ma += 2048 * 1024)
1680  {
1681   CPU->SetFastMap(MainRAM.data8, 0x00000000 + ma, 2048 * 1024);
1682   CPU->SetFastMap(MainRAM.data8, 0x80000000 + ma, 2048 * 1024);
1683   CPU->SetFastMap(MainRAM.data8, 0xA0000000 + ma, 2048 * 1024);
1684  }
1685 
1686  CPU->SetFastMap(BIOSROM->data8, 0x1FC00000, 512 * 1024);
1687  CPU->SetFastMap(BIOSROM->data8, 0x9FC00000, 512 * 1024);
1688  CPU->SetFastMap(BIOSROM->data8, 0xBFC00000, 512 * 1024);
1689 
1690  if(PIOMem)
1691  {
1692   CPU->SetFastMap(PIOMem->data8, 0x1F000000, 65536);
1693   CPU->SetFastMap(PIOMem->data8, 0x9F000000, 65536);
1694   CPU->SetFastMap(PIOMem->data8, 0xBF000000, 65536);
1695  }
1696 
1697 
1698  MDFNMP_Init(1024, ((uint64)1 << 29) / 1024);
1699  MDFNMP_AddRAM(2048 * 1024, 0x00000000, MainRAM.data8);
1700  //MDFNMP_AddRAM(1024, 0x1F800000, ScratchRAM.data8);
1701 
1702  //
1703  //
1704  //
1705  const char *biospath_sname;
1706 
1707  if(region == REGION_JP)
1708   biospath_sname = "psx.bios_jp";
1709  else if(region == REGION_EU)
1710   biospath_sname = "psx.bios_eu";
1711  else if(region == REGION_NA)
1712   biospath_sname = "psx.bios_na";
1713  else
1714   abort();
1715 
1716  {
1717   std::string biospath = MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, MDFN_GetSettingS(biospath_sname));
1718   FileStream BIOSFile(biospath, FileStream::MODE_READ);
1719 
1720   if(BIOSFile.size() != 524288)
1721    throw MDFN_Error(0, _("BIOS file \"%s\" is of an incorrect size."), biospath.c_str());
1722 
1723   BIOSFile.read(BIOSROM->data8, 512 * 1024);
1724   BIOS_SHA256 = sha256(BIOSROM->data8, 512 * 1024);
1725 
1726   if(MDFN_GetSettingB("psx.bios_sanity"))
1727   {
1728    bool bios_recognized = false;
1729 
1730    for(auto const& dbe : BIOS_DB)
1731    {
1732     if(BIOS_SHA256 == dbe.sd)
1733     {
1734      if(dbe.bad)
1735       throw MDFN_Error(0, _("BIOS file \"%s\" is a known bad dump."), biospath.c_str());
1736 
1737      if(dbe.region != region)
1738       throw MDFN_Error(0, _("BIOS file \"%s\" is not the proper BIOS for the region of PS1 being emulated."), biospath.c_str());
1739 
1740      bios_recognized = true;
1741      break;
1742     }
1743    }
1744 
1745    if(!bios_recognized)
1746     MDFN_printf(_("Warning: Unrecognized BIOS.\n"));
1747   }
1748   else
1749    MDFN_printf(_("WARNING: BIOS ROM sanity checks disabled.\n"));
1750  }
1751 
1752  if(!IsPSF)
1753  {
1754   for(int i = 0; i < 8; i++)
1755   {
1756    char ext[64];
1757    trio_snprintf(ext, sizeof(ext), "%d.mcr", i);
1758    MDFN_BackupSavFile(5, ext);
1759    FIO->LoadMemcard(i, MDFN_MakeFName(MDFNMKF_SAV, 0, ext));
1760   }
1761 
1762   for(int i = 0; i < 8; i++)
1763   {
1764    Memcard_PrevDC[i] = FIO->GetMemcardDirtyCount(i);
1765    Memcard_SaveDelay[i] = -1;
1766   }
1767  }
1768 
1769  #ifdef WANT_DEBUGGER
1770  DBG_Init();
1771  #endif
1772 
1773  PSX_Reset(true);
1774 }
1775 
LoadEXE(Stream * fp,bool ignore_pcsp=false)1776 static MDFN_COLD void LoadEXE(Stream* fp, bool ignore_pcsp = false)
1777 {
1778  uint8 raw_header[0x800];
1779  uint32 PC;
1780  uint32 SP;
1781  uint32 TextStart;
1782  uint32 TextSize;
1783 
1784  fp->read(raw_header, sizeof(raw_header));
1785 
1786  PC = MDFN_de32lsb(&raw_header[0x10]);
1787  SP = MDFN_de32lsb(&raw_header[0x30]);
1788  TextStart = MDFN_de32lsb(&raw_header[0x18]);
1789  TextSize = MDFN_de32lsb(&raw_header[0x1C]);
1790 
1791  if(ignore_pcsp)
1792   MDFN_printf("TextStart=0x%08x\nTextSize=0x%08x\n", TextStart, TextSize);
1793  else
1794   MDFN_printf("PC=0x%08x\nSP=0x%08x\nTextStart=0x%08x\nTextSize=0x%08x\n", PC, SP, TextStart, TextSize);
1795 
1796  TextStart &= 0x1FFFFF;
1797 
1798  if(TextSize > 2048 * 1024)
1799  {
1800   throw(MDFN_Error(0, "Text section too large"));
1801  }
1802 
1803  //if(TextSize > (size - 0x800))
1804  // throw(MDFN_Error(0, "Text section recorded size is larger than data available in file.  Header=0x%08x, Available=0x%08x", TextSize, size - 0x800));
1805 
1806  //if(TextSize < (size - 0x800))
1807  // throw(MDFN_Error(0, "Text section recorded size is smaller than data available in file.  Header=0x%08x, Available=0x%08x", TextSize, size - 0x800));
1808 
1809  if(!TextMem.size())
1810  {
1811   TextMem_Start = TextStart;
1812   TextMem.resize(TextSize);
1813  }
1814 
1815  if(TextStart < TextMem_Start)
1816  {
1817   uint32 old_size = TextMem.size();
1818 
1819   //printf("RESIZE: 0x%08x\n", TextMem_Start - TextStart);
1820 
1821   TextMem.resize(old_size + TextMem_Start - TextStart);
1822   memmove(&TextMem[TextMem_Start - TextStart], &TextMem[0], old_size);
1823 
1824   TextMem_Start = TextStart;
1825  }
1826 
1827  if(TextMem.size() < (TextStart - TextMem_Start + TextSize))
1828   TextMem.resize(TextStart - TextMem_Start + TextSize);
1829 
1830  fp->read(&TextMem[TextStart - TextMem_Start], TextSize);
1831 
1832  {
1833   uint64 extra_data = fp->read_discard();
1834 
1835   if(extra_data > 0)
1836    throw MDFN_Error(0, _("0x%08llx bytes of extra data after EXE text section."), (unsigned long long)extra_data);
1837  }
1838 
1839  //
1840  //
1841  //
1842 
1843  // BIOS patch
1844  BIOSROM->WriteU32(0x6990, (3 << 26) | ((0xBF001000 >> 2) & ((1 << 26) - 1)));
1845 // BIOSROM->WriteU32(0x691C, (3 << 26) | ((0xBF001000 >> 2) & ((1 << 26) - 1)));
1846 
1847 // printf("INSN: 0x%08x\n", BIOSROM->ReadU32(0x6990));
1848 // exit(1);
1849  uint8 *po;
1850 
1851  po = &PIOMem->data8[0x0800];
1852 
1853  MDFN_en32lsb(po, (0x0 << 26) | (31 << 21) | (0x8 << 0));	// JR
1854  po += 4;
1855  MDFN_en32lsb(po, 0);	// NOP(kinda)
1856  po += 4;
1857 
1858  po = &PIOMem->data8[0x1000];
1859 
1860  // Load cacheable-region target PC into r2
1861  MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (0x9F001010 >> 16));      // LUI
1862  po += 4;
1863  MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (2 << 16) | (0x9F001010 & 0xFFFF));   // ORI
1864  po += 4;
1865 
1866  // Jump to r2
1867  MDFN_en32lsb(po, (0x0 << 26) | (2 << 21) | (0x8 << 0));	// JR
1868  po += 4;
1869  MDFN_en32lsb(po, 0);	// NOP(kinda)
1870  po += 4;
1871 
1872  //
1873  // 0x9F001010:
1874  //
1875 
1876  // Load source address into r8
1877  uint32 sa = 0x9F000000 + 65536;
1878  MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16) | (sa >> 16));	// LUI
1879  po += 4;
1880  MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (8 << 16) | (sa & 0xFFFF)); 	// ORI
1881  po += 4;
1882 
1883  // Load dest address into r9
1884  MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16)  | (TextMem_Start >> 16));	// LUI
1885  po += 4;
1886  MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (9 << 16) | (TextMem_Start & 0xFFFF)); 	// ORI
1887  po += 4;
1888 
1889  // Load size into r10
1890  MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16)  | (TextMem.size() >> 16));	// LUI
1891  po += 4;
1892  MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (10 << 16) | (TextMem.size() & 0xFFFF)); 	// ORI
1893  po += 4;
1894 
1895  //
1896  // Loop begin
1897  //
1898 
1899  MDFN_en32lsb(po, (0x24 << 26) | (8 << 21) | (1 << 16));	// LBU to r1
1900  po += 4;
1901 
1902  MDFN_en32lsb(po, (0x08 << 26) | (10 << 21) | (10 << 16) | 0xFFFF);	// Decrement size
1903  po += 4;
1904 
1905  MDFN_en32lsb(po, (0x28 << 26) | (9 << 21) | (1 << 16));	// SB from r1
1906  po += 4;
1907 
1908  MDFN_en32lsb(po, (0x08 << 26) | (8 << 21) | (8 << 16) | 0x0001);	// Increment source addr
1909  po += 4;
1910 
1911  MDFN_en32lsb(po, (0x05 << 26) | (0 << 21) | (10 << 16) | (-5 & 0xFFFF));
1912  po += 4;
1913  MDFN_en32lsb(po, (0x08 << 26) | (9 << 21) | (9 << 16) | 0x0001);	// Increment dest addr
1914  po += 4;
1915 
1916  //
1917  // Loop end
1918  //
1919 
1920  // Load SP into r29
1921  if(ignore_pcsp)
1922  {
1923   po += 16;
1924  }
1925  else
1926  {
1927   MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16)  | (SP >> 16));	// LUI
1928   po += 4;
1929   MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (29 << 16) | (SP & 0xFFFF)); 	// ORI
1930   po += 4;
1931 
1932   // Load PC into r2
1933   MDFN_en32lsb(po, (0xF << 26) | (0 << 21) | (1 << 16)  | ((PC >> 16) | 0x8000));      // LUI
1934   po += 4;
1935   MDFN_en32lsb(po, (0xD << 26) | (1 << 21) | (2 << 16) | (PC & 0xFFFF));   // ORI
1936   po += 4;
1937  }
1938 
1939  // Half-assed instruction cache flush. ;)
1940  for(unsigned i = 0; i < 1024; i++)
1941  {
1942   MDFN_en32lsb(po, 0);
1943   po += 4;
1944  }
1945 
1946 
1947 
1948  // Jump to r2
1949  MDFN_en32lsb(po, (0x0 << 26) | (2 << 21) | (0x8 << 0));	// JR
1950  po += 4;
1951  MDFN_en32lsb(po, 0);	// NOP(kinda)
1952  po += 4;
1953 }
1954 
PSF1Loader(VirtualFS * vfs,const std::string & dir_path,Stream * fp)1955 PSF1Loader::PSF1Loader(VirtualFS* vfs, const std::string& dir_path, Stream *fp)
1956 {
1957  tags = Load(0x01, 2033664, vfs, dir_path, fp);
1958 }
1959 
~PSF1Loader()1960 PSF1Loader::~PSF1Loader()
1961 {
1962 
1963 }
1964 
HandleEXE(Stream * fp,bool ignore_pcsp)1965 void PSF1Loader::HandleEXE(Stream* fp, bool ignore_pcsp)
1966 {
1967  LoadEXE(fp, ignore_pcsp);
1968 }
1969 
1970 static void Cleanup(void);
Load(GameFile * gf)1971 static MDFN_COLD void Load(GameFile* gf)
1972 {
1973  try
1974  {
1975   const bool IsPSF = PSFLoader::TestMagic(0x01, gf->stream);
1976 
1977   if(!TestMagic(gf))
1978    throw MDFN_Error(0, _("File format is unknown to module \"%s\"."), MDFNGameInfo->shortname);
1979 
1980   gf->stream->rewind();
1981 
1982   if(MDFN_GetSettingS("psx.dbg_exe_cdpath") != "")	// For testing/debug purposes.
1983   {
1984    RMD_Drive dr;
1985    RMD_DriveDefaults drdef;
1986 
1987    dr.Name = std::string("Virtual CD Drive");
1988    dr.PossibleStates.push_back(RMD_State({"Tray Open", false, false, true}));
1989    dr.PossibleStates.push_back(RMD_State({"Tray Closed (Empty)", false, false, false}));
1990    dr.PossibleStates.push_back(RMD_State({"Tray Closed", true, true, false}));
1991    dr.CompatibleMedia.push_back(0);
1992    dr.MediaMtoPDelay = 2000;
1993 
1994    drdef.State = 2; // Tray Closed
1995    drdef.Media = 0;
1996    drdef.Orientation = 0;
1997 
1998    MDFNGameInfo->RMD->Drives.push_back(dr);
1999    MDFNGameInfo->RMD->DrivesDefaults.push_back(drdef);
2000    MDFNGameInfo->RMD->MediaTypes.push_back(RMD_MediaType({"CD"}));
2001    MDFNGameInfo->RMD->Media.push_back(RMD_Media({"Test CD", 0}));
2002 
2003    static std::vector<CDInterface*> CDInterfaces;
2004    CDInterfaces.clear();
2005    CDInterfaces.push_back(CDInterface::Open(&NVFS, MDFN_GetSettingS("psx.dbg_exe_cdpath"), false, MDFN_GetSettingUI("affinity.cd")));
2006    InitCommon(&CDInterfaces, IsPSF, true);
2007   }
2008   else
2009    InitCommon(NULL, IsPSF, true);
2010 
2011   TextMem.resize(0);
2012 
2013   if(IsPSF)
2014   {
2015    psf_loader = new PSF1Loader(gf->vfs, gf->dir, gf->stream);
2016 
2017    std::vector<std::string> SongNames;
2018 
2019    SongNames.push_back(psf_loader->tags.GetTag("title"));
2020 
2021    Player_Init(1, psf_loader->tags.GetTag("game"), psf_loader->tags.GetTag("artist"), psf_loader->tags.GetTag("copyright"), SongNames);
2022   }
2023   else
2024    LoadEXE(gf->stream);
2025  }
2026  catch(std::exception &e)
2027  {
2028   Cleanup();
2029   throw;
2030  }
2031 }
2032 
LoadCD(std::vector<CDInterface * > * CDInterfaces)2033 static MDFN_COLD void LoadCD(std::vector<CDInterface*> *CDInterfaces)
2034 {
2035  try
2036  {
2037   InitCommon(CDInterfaces);
2038  }
2039  catch(std::exception &e)
2040  {
2041   Cleanup();
2042   throw;
2043  }
2044 }
2045 
Cleanup(void)2046 static MDFN_COLD void Cleanup(void)
2047 {
2048  TextMem.resize(0);
2049 
2050  if(psf_loader)
2051  {
2052   delete psf_loader;
2053   psf_loader = NULL;
2054  }
2055 
2056  if(CDC)
2057  {
2058   delete CDC;
2059   CDC = NULL;
2060  }
2061 
2062  if(SPU)
2063  {
2064   delete SPU;
2065   SPU = NULL;
2066  }
2067 
2068  GPU_Kill();
2069 
2070  if(CPU)
2071  {
2072   delete CPU;
2073   CPU = NULL;
2074  }
2075 
2076  if(FIO)
2077  {
2078   delete FIO;
2079   FIO = NULL;
2080  }
2081 
2082  DMA_Kill();
2083 
2084  if(BIOSROM)
2085  {
2086   delete BIOSROM;
2087   BIOSROM = NULL;
2088  }
2089 
2090  if(PIOMem)
2091  {
2092   delete PIOMem;
2093   PIOMem = NULL;
2094  }
2095 
2096  cdifs = NULL;
2097 }
2098 
CloseGame(void)2099 static MDFN_COLD void CloseGame(void)
2100 {
2101  if(!psf_loader)
2102  {
2103   for(int i = 0; i < 8; i++)
2104   {
2105    // If there's an error saving one memcard, don't skip trying to save the other, since it might succeed and
2106    // we can reduce potential data loss!
2107    try
2108    {
2109     char ext[64];
2110     trio_snprintf(ext, sizeof(ext), "%d.mcr", i);
2111 
2112     FIO->SaveMemcard(i, MDFN_MakeFName(MDFNMKF_SAV, 0, ext));
2113    }
2114    catch(std::exception &e)
2115    {
2116     MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
2117    }
2118   }
2119  }
2120 
2121  Cleanup();
2122 }
2123 
2124 
SetInput(unsigned port,const char * type,uint8 * ptr)2125 static void SetInput(unsigned port, const char *type, uint8 *ptr)
2126 {
2127  if(psf_loader)
2128   FIO->SetInput(port, "none", NULL);
2129  else
2130   FIO->SetInput(port, type, ptr);
2131 }
2132 
TransformInput(void)2133 static void TransformInput(void)
2134 {
2135  FIO->TransformInput();
2136 }
2137 
StateAction(StateMem * sm,const unsigned load,const bool data_only)2138 static void StateAction(StateMem *sm, const unsigned load, const bool data_only)
2139 {
2140  if(!data_only)
2141  {
2142   sha256_digest sr_dig = BIOS_SHA256;
2143 
2144   SFORMAT SRDStateRegs[] =
2145   {
2146    SFPTR8(sr_dig.data(), sr_dig.size()),
2147    SFEND
2148   };
2149 
2150   MDFNSS_StateAction(sm, load, data_only, SRDStateRegs, "BIOS_HASH", true);
2151 
2152   if(load && sr_dig != BIOS_SHA256)
2153    throw MDFN_Error(0, _("BIOS hash mismatch(save state created under a different BIOS)!"));
2154  }
2155 
2156 
2157  SFORMAT StateRegs[] =
2158  {
2159   SFPTR8(MainRAM.data8, 1024 * 2048),
2160   SFPTR32(SysControl.Regs, 9),
2161 
2162   SFVAR(PSX_PRNG.lcgo),
2163   SFVAR(PSX_PRNG.x),
2164   SFVAR(PSX_PRNG.y),
2165   SFVAR(PSX_PRNG.z),
2166   SFVAR(PSX_PRNG.c),
2167 
2168   SFEND
2169  };
2170 
2171  MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN");
2172 
2173  CPU->StateAction(sm, load, data_only);
2174  DMA_StateAction(sm, load, data_only);
2175  TIMER_StateAction(sm, load, data_only);
2176  SIO_StateAction(sm, load, data_only);
2177 
2178  CDC->StateAction(sm, load, data_only);
2179  MDEC_StateAction(sm, load, data_only);
2180  GPU_StateAction(sm, load, data_only);
2181  SPU->StateAction(sm, load, data_only);
2182 
2183  FIO->StateAction(sm, load, data_only);
2184 
2185  IRQ_StateAction(sm, load, data_only);	// Do it last.
2186 
2187  if(load)
2188  {
2189   ForceEventUpdates(0);	// FIXME to work with debugger step mode.
2190  }
2191 }
2192 
SetMedia(uint32 drive_idx,uint32 state_idx,uint32 media_idx,uint32 orientation_idx)2193 static void SetMedia(uint32 drive_idx, uint32 state_idx, uint32 media_idx, uint32 orientation_idx)
2194 {
2195  const RMD_Layout* rmd = EmulatedPSX.RMD;
2196  const RMD_Drive* rd = &rmd->Drives[drive_idx];
2197  const RMD_State* rs = &rd->PossibleStates[state_idx];
2198 
2199  //printf("%d %d %d %d\n", drive_idx, state_idx, media_idx, orientation_idx);
2200 
2201  if(rs->MediaPresent && rs->MediaUsable)
2202  {
2203   CDC->SetDisc(false, (*cdifs)[media_idx], cdifs_scex_ids[media_idx]);
2204  }
2205  else
2206  {
2207   CDC->SetDisc(rs->MediaCanChange, NULL, NULL);
2208  }
2209 }
2210 
DoSimpleCommand(int cmd)2211 static void DoSimpleCommand(int cmd)
2212 {
2213  switch(cmd)
2214  {
2215   case MDFN_MSC_RESET: PSX_Reset(false); break;
2216   case MDFN_MSC_POWER: PSX_Reset(true); break;
2217  }
2218 }
2219 
2220 static const CheatInfoStruct CheatInfo =
2221 {
2222  NULL,
2223  NULL,
2224 
2225  NULL,
2226  NULL,
2227 
2228  CheatFormats_PSX
2229 };
2230 
2231 
2232 static const FileExtensionSpecStruct KnownExtensions[] =
2233 {
2234  { ".psf", -20, gettext_noop("PSF1 Rip") },
2235  { ".minipsf", -20, gettext_noop("MiniPSF1 Rip") },
2236  { ".psx",   0, gettext_noop("PS-X Executable") },
2237  { ".exe", -80, gettext_noop("PS-X Executable") },
2238  { NULL, 0, NULL }
2239 };
2240 
2241 static const MDFNSetting PSXSettings[] =
2242 {
2243  { "psx.input.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Emulated mouse sensitivity."), NULL, MDFNST_FLOAT, "1.00", NULL, NULL },
2244 
2245  { "psx.input.analog_mode_ct", MDFNSF_NOFLAGS, gettext_noop("Enable analog mode combo-button alternate toggle."), gettext_noop("When enabled, instead of the configured Analog mode toggle button for the emulated DualShock, use a combination of buttons held down for one emulated second to toggle it instead.  The specific combination is controlled via the \"psx.input.analog_mode_ct.compare\" setting, which by default is Select, Start, and all four shoulder buttons."), MDFNST_BOOL, "0", NULL, NULL },
2246  { "psx.input.analog_mode_ct.compare", MDFNSF_NOFLAGS, gettext_noop("Compare value for analog mode combo-button alternate toggle."), gettext_noop("0x0001=SELECT\n0x0002=L3\n0x0004=R3\n0x0008=START\n0x0010=D-Pad UP\n0x0020=D-Pad Right\n0x0040=D-Pad Down\n0x0080=D-Pad Left\n0x0100=L2\n0x0200=R2\n0x0400=L1\n0x0800=R1\n0x1000=△\n0x2000=○\n0x4000=x\n0x8000=□"), MDFNST_UINT, "0x0F09", "0x0000", "0xFFFF" },
2247 
2248  { "psx.input.pport1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on PSX port 1."), gettext_noop("Makes 3 more virtual ports available.\n\nNOTE: Enabling multitap in games that don't fully support it may cause deleterious effects."), MDFNST_BOOL, "0", NULL, NULL }, //MDFNST_ENUM, "auto", NULL, NULL, NULL, NULL, MultiTap_List },
2249  { "psx.input.pport2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on PSX port 2."), gettext_noop("Makes 3 more virtual ports available.\n\nNOTE: Enabling multitap in games that don't fully support it may cause deleterious effects."), MDFNST_BOOL, "0", NULL, NULL },
2250 
2251  { "psx.input.port1.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 1."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2252  { "psx.input.port2.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 2."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2253  { "psx.input.port3.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 3."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2254  { "psx.input.port4.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 4."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2255  { "psx.input.port5.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 5."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2256  { "psx.input.port6.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 6."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2257  { "psx.input.port7.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 7."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2258  { "psx.input.port8.memcard", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate memory card on virtual port 8."), NULL, MDFNST_BOOL, "1", NULL, NULL, },
2259 
2260 
2261  { "psx.input.port1.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 1."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFF0000", "0x000000", "0x1000000" },
2262  { "psx.input.port2.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 2."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x00FF00", "0x000000", "0x1000000" },
2263  { "psx.input.port3.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 3."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFF00FF", "0x000000", "0x1000000" },
2264  { "psx.input.port4.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 4."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFF8000", "0x000000", "0x1000000" },
2265  { "psx.input.port5.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 5."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFFFF00", "0x000000", "0x1000000" },
2266  { "psx.input.port6.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 6."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x00FFFF", "0x000000", "0x1000000" },
2267  { "psx.input.port7.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 7."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x0080FF", "0x000000", "0x1000000" },
2268  { "psx.input.port8.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 8."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x8000FF", "0x000000", "0x1000000" },
2269 
2270  { "psx.region_autodetect", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Attempt to auto-detect region of game."), NULL, MDFNST_BOOL, "1" },
2271  { "psx.region_default", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Default region to use."), gettext_noop("Used if region autodetection fails or is disabled."), MDFNST_ENUM, "jp", NULL, NULL, NULL, NULL, Region_List },
2272 
2273  { "psx.bios_jp", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the Japan SCPH-5500/v3.0J ROM BIOS"), gettext_noop("SHA-256 9c0421858e217805f4abe18698afea8d5aa36ff0727eb8484944e00eb5e7eadb"), MDFNST_STRING, "scph5500.bin" },
2274  { "psx.bios_na", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the North America SCPH-5501/v3.0A ROM BIOS"), gettext_noop("SHA-256 11052b6499e466bbf0a709b1f9cb6834a9418e66680387912451e971cf8a1fef"), MDFNST_STRING, "scph5501.bin" },
2275  { "psx.bios_eu", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the Europe SCPH-5502/v3.0E ROM BIOS"), gettext_noop("SHA-256 1faaa18fa820a0225e488d9f086296b8e6c46df739666093987ff7d8fd352c09"), MDFNST_STRING, "scph5502.bin" },
2276 
2277  { "psx.bios_sanity", MDFNSF_NOFLAGS, gettext_noop("Enable BIOS ROM image sanity checks."), gettext_noop("Enables blacklisting of known bad BIOS dumps and known BIOS versions that don't match the region of the hardware being emulated.") , MDFNST_BOOL, "1" },
2278  { "psx.cd_sanity", MDFNSF_NOFLAGS, gettext_noop("Enable CD (image) sanity checks."), gettext_noop("Sanity checks are only performed on discs detected(via heuristics) to be PS1 discs.  The checks primarily consist of ensuring that Q subchannel data is as expected for a typical commercially-released PS1 disc."), MDFNST_BOOL, "1" },
2279 
2280  { "psx.spu.resamp_quality", MDFNSF_NOFLAGS, gettext_noop("SPU output resampler quality."),
2281 	gettext_noop("0 is lowest quality and CPU usage, 10 is highest quality and CPU usage.  The resampler that this setting refers to is used for converting from 44.1KHz to the sampling rate of the host audio device Mednafen is using.  Changing Mednafen's output rate, via the \"sound.rate\" setting, to \"44100\" may bypass the resampler, which can decrease CPU usage by Mednafen, and can increase or decrease audio quality, depending on various operating system and hardware factors."), MDFNST_UINT, "5", "0", "10" },
2282 
2283  { "psx.correct_aspect", MDFNSF_NOFLAGS, gettext_noop("Correct aspect ratio."), gettext_noop("Disabling aspect ratio correction with this setting should be considered a hack.\n\nIf disabling it to allow for sharper pixels by also separately disabling interpolation(though using Mednafen's \"autoipsharper\" OpenGL shader is usually a better option), remember to use scale factors that are multiples of 2, or else games that use high-resolution and interlaced modes will have distorted pixels.\n\nDisabling aspect ratio correction with this setting will allow for the QuickTime movie recording feature to produce much smaller files using much less CPU time."), MDFNST_BOOL, "1" },
2284 
2285  { "psx.slstart", MDFNSF_NOFLAGS, gettext_noop("First displayed scanline in NTSC mode."), NULL, MDFNST_INT, "0", "0", "239" },
2286  { "psx.slend", MDFNSF_NOFLAGS, gettext_noop("Last displayed scanline in NTSC mode."), NULL, MDFNST_INT, "239", "0", "239" },
2287 
2288  { "psx.slstartp", MDFNSF_NOFLAGS, gettext_noop("First displayed scanline in PAL mode."), NULL, MDFNST_INT, "0", "0", "287" },	// 14
2289  { "psx.slendp", MDFNSF_NOFLAGS, gettext_noop("Last displayed scanline in PAL mode."), NULL, MDFNST_INT, "287", "0", "287" },	// 275
2290 
2291  { "psx.h_overscan", MDFNSF_NOFLAGS, gettext_noop("Show horizontal overscan area."), NULL, MDFNST_BOOL, "1" },
2292 
2293 #if PSX_DBGPRINT_ENABLE
2294  { "psx.dbg_level", MDFNSF_NOFLAGS, gettext_noop("Debug printf verbosity level."), NULL, MDFNST_UINT, "0", "0", "4" },
2295 #endif
2296 
2297  { "psx.dbg_exe_cdpath", MDFNSF_SUPPRESS_DOC | MDFNSF_CAT_PATH, gettext_noop("CD image to use with .PSX/.EXE loading."), NULL, MDFNST_STRING, "" },
2298 
2299  { NULL },
2300 };
2301 
2302 // Note for the future: If we ever support PSX emulation with non-8-bit RGB color components, or add a new linear RGB colorspace to MDFN_PixelFormat, we'll need
2303 // to buffer the intermediate 24-bit non-linear RGB calculation into an array and pass that into the GPULineHook stuff, otherwise netplay could break when
2304 // an emulated GunCon is used.
2305 MDFNGI EmulatedPSX =
2306 {
2307  "psx",
2308  "Sony PlayStation",
2309  KnownExtensions,
2310  MODPRIO_INTERNAL_HIGH,
2311  #ifdef WANT_DEBUGGER
2312  &PSX_DBGInfo,
2313  #else
2314  NULL,
2315  #endif
2316  FIO_PortInfo,
2317  NULL,
2318  Load,
2319  TestMagic,
2320  LoadCD,
2321  TestMagicCD,
2322  CloseGame,
2323 
2324  NULL,	//ToggleLayer,
2325  "GPU\0",	//"Background Scroll\0Foreground Scroll\0Sprites\0",
2326 
2327  NULL,
2328  NULL,
2329 
2330  NULL,
2331  0,
2332 
2333  CheatInfo,
2334 
2335  false,
2336  StateAction,
2337  Emulate,
2338  TransformInput,
2339  SetInput,
2340  SetMedia,
2341  DoSimpleCommand,
2342  NULL,
2343  PSXSettings,
2344  MDFN_MASTERCLOCK_FIXED(33868800),
2345  0,
2346 
2347  true, // Multires possible?
2348 
2349  //
2350  // Note: Following video settings will be overwritten during game load.
2351  //
2352  0,	// lcm_width
2353  0,	// lcm_height
2354  NULL,  // Dummy
2355 
2356  320,   // Nominal width
2357  240,   // Nominal height
2358 
2359  0,   // Framebuffer width
2360  0,   // Framebuffer height
2361  //
2362  //
2363  //
2364 
2365  2,     // Number of output sound channels
2366 
2367 };
2368