1 /******************************************************************************/
2 /* Mednafen Sega Saturn Emulation Module                                      */
3 /******************************************************************************/
4 /* ss.cpp - Saturn Core Emulation and Support Functions
5 **  Copyright (C) 2015-2020 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 // WARNING: Be careful with 32-bit access to 16-bit space, bus locking, etc. in respect to DMA and event updates(and where they can occur).
23 
24 #include <mednafen/mednafen.h>
25 #include <mednafen/cdrom/CDInterface.h>
26 #include <mednafen/general.h>
27 #include <mednafen/FileStream.h>
28 #include <mednafen/compress/GZFileStream.h>
29 #include <mednafen/mempatcher.h>
30 #include <mednafen/hash/sha256.h>
31 #include <mednafen/hash/md5.h>
32 #include <mednafen/Time.h>
33 
34 #include <bitset>
35 
36 #include <trio/trio.h>
37 
38 #include <zlib.h>
39 
40 #if defined(HAVE_SSE2_INTRINSICS)
41  #include <xmmintrin.h>
42  #include <emmintrin.h>
43 #endif
44 
45 using namespace Mednafen;
46 
47 MDFN_HIDE extern MDFNGI EmulatedSS;
48 
49 #include "ss.h"
50 #include "sound.h"
51 #include "scsp.h"	// For debug.inc
52 #include "smpc.h"
53 #include "cdb.h"
54 #include "vdp1.h"
55 #include "vdp2.h"
56 #include "scu.h"
57 #include "cart.h"
58 #include "db.h"
59 
60 namespace MDFN_IEN_SS
61 {
62 
63 static sscpu_timestamp_t MidSync(const sscpu_timestamp_t timestamp);
64 
65 #ifdef MDFN_ENABLE_DEV_BUILD
66 uint32 ss_dbg_mask;
67 static std::bitset<0x200> BWMIgnoreAddr[2]; // 0=read, 1=write
68 
SS_DBG(uint32 which,const char * format,...)69 void SS_DBG(uint32 which, const char* format, ...)
70 {
71  if(MDFN_LIKELY(!(ss_dbg_mask & which)))
72   return;
73  //
74  va_list ap;
75  va_start(ap, format);
76  trio_vprintf(format, ap);
77  va_end(ap);
78 }
79 
SS_DBGTI(uint32 which,const char * format,...)80 void SS_DBGTI(uint32 which, const char* format, ...)
81 {
82  if(MDFN_LIKELY(!(ss_dbg_mask & which)))
83   return;
84  //
85  va_list ap;
86  va_start(ap, format);
87  trio_vprintf(format, ap);
88  va_end(ap);
89  //
90  trio_printf(" @Line=0x%03x, HPos=0x%03x, memts=%d\n", VDP2::PeekLine(), VDP2::PeekHPos(), SH7095_mem_timestamp);
91 }
92 #endif
93 uint32 ss_horrible_hacks;
94 
95 static bool NeedEmuICache;
96 static const uint8 BRAM_Init_Data[0x10] = { 0x42, 0x61, 0x63, 0x6b, 0x55, 0x70, 0x52, 0x61, 0x6d, 0x20, 0x46, 0x6f, 0x72, 0x6d, 0x61, 0x74 };
97 
98 static void SaveBackupRAM(void);
99 static void LoadBackupRAM(void);
100 static void SaveCartNV(void);
101 static void LoadCartNV(void);
102 static void SaveRTC(void);
103 static void LoadRTC(void);
104 
105 static MDFN_COLD void BackupBackupRAM(void);
106 static MDFN_COLD void BackupCartNV(void);
107 
108 #include "sh7095.h"
109 
110 static uint8 SCU_MSH2VectorFetch(void);
111 static uint8 SCU_SSH2VectorFetch(void);
112 
113 static void INLINE MDFN_HOT CheckEventsByMemTS(void);
114 
115 SH7095 CPU[2]{ {"SH2-M", SS_EVENT_SH2_M_DMA, SCU_MSH2VectorFetch}, {"SH2-S", SS_EVENT_SH2_S_DMA, SCU_SSH2VectorFetch}};
116 static uint16 BIOSROM[524288 / sizeof(uint16)];
117 static uint16 WorkRAML[1024 * 1024 / sizeof(uint16)];
118 static uint16 WorkRAMH[1024 * 1024 / sizeof(uint16)];	// Effectively 32-bit in reality, but 16-bit here because of CPU interpreter design(regarding fastmap).
119 static uint8 BackupRAM[32768];
120 static bool BackupRAM_Dirty;
121 static int64 BackupRAM_SaveDelay;
122 static int64 CartNV_SaveDelay;
123 
124 #define SH7095_EXT_MAP_GRAN_BITS 16
125 static uintptr_t SH7095_FastMap[1U << (32 - SH7095_EXT_MAP_GRAN_BITS)];
126 
127 int32 SH7095_mem_timestamp;
128 static uint32 SH7095_BusLock;
129 static uint32 SH7095_DB;
130 #include "scu.inc"
131 
132 #include "debug.inc"
133 
134 static sha256_digest BIOS_SHA256;	// SHA-256 hash of the currently-loaded BIOS; used for save state sanity checks.
135 static int ActiveCartType;		// Used in save states.
136 static std::vector<CDInterface*> *cdifs = NULL;
137 static std::bitset<1U << (27 - SH7095_EXT_MAP_GRAN_BITS)> FMIsWriteable;
138 static uint16 fmap_dummy[(1U << SH7095_EXT_MAP_GRAN_BITS) / sizeof(uint16)];
139 
140 /*
141  SH-2 external bus address map:
142   CS0: 0x00000000...0x01FFFFFF (16-bit)
143 	0x00000000...0x000FFFFF: BIOS ROM (R)
144 	0x00100000...0x0017FFFF: SMPC (R/W; 8-bit mapped as 16-bit)
145 	0x00180000...0x001FFFFF: Backup RAM(32KiB) (R/W; 8-bit mapped as 16-bit)
146 	0x00200000...0x003FFFFF: Low RAM(1MiB) (R/W)
147 	0x01000000...0x017FFFFF: Slave FRT Input Capture Trigger (W)
148 	0x01800000...0x01FFFFFF: Master FRT Input Capture Trigger (W)
149 
150   CS1: 0x02000000...0x03FFFFFF (SCU managed)
151 	0x02000000...0x03FFFFFF: A-bus CS0 (R/W)
152 
153   CS2: 0x04000000...0x05FFFFFF (SCU managed)
154 	0x04000000...0x04FFFFFF: A-bus CS1 (R/W)
155 	0x05000000...0x057FFFFF: A-bus Dummy
156 	0x05800000...0x058FFFFF: A-bus CS2 (R/W)
157 	0x05A00000...0x05AFFFFF: SCSP RAM (R/W)
158 	0x05B00000...0x05BFFFFF: SCSP Registers (R/W)
159 	0x05C00000...0x05C7FFFF: VDP1 VRAM (R/W)
160 	0x05C80000...0x05CFFFFF: VDP1 FB RAM (R/W; swappable between two framebuffers, but may be temporarily unreadable at swap time)
161 	0x05D00000...0x05D7FFFF: VDP1 Registers (R/W)
162 	0x05E00000...0x05EFFFFF: VDP2 VRAM (R/W)
163 	0x05F00000...0x05F7FFFF: VDP2 CRAM (R/W; 8-bit writes are illegal)
164 	0x05F80000...0x05FBFFFF: VDP2 Registers (R/W; 8-bit writes are illegal)
165 	0x05FE0000...0x05FEFFFF: SCU Registers (R/W)
166 	0x05FF0000...0x05FFFFFF: SCU Debug/Test Registers (R/W)
167 
168   CS3: 0x06000000...0x07FFFFFF
169 	0x06000000...0x07FFFFFF: High RAM/SDRAM(1MiB) (R/W)
170 */
171 //
172 // Never add anything to SH7095_mem_timestamp when DMAHax is true.
173 //
174 // When BurstHax is true and we're accessing high work RAM, don't add anything.
175 //
176 template<typename T, bool IsWrite>
BusRW_DB_CS0(const uint32 A,uint32 & DB,const bool BurstHax,int32 * SH2DMAHax)177 static INLINE void BusRW_DB_CS0(const uint32 A, uint32& DB, const bool BurstHax, int32* SH2DMAHax)
178 {
179  //
180  // Low(and kinda slow) work RAM
181  //
182  if(A >= 0x00200000 && A <= 0x003FFFFF)
183  {
184   if(A & 0x100000)
185   {
186    if(IsWrite)
187     SS_DBG(SS_DBG_WARNING, "[RAM] %zu-byte write of 0x%08x to mirrored address 0x%08x\n", sizeof(T), DB >> (((A & 1) ^ (2 - sizeof(T))) << 3), A);
188    else
189     SS_DBG(SS_DBG_WARNING, "[RAM] %zu-byte read from mirrored address 0x%08x\n", sizeof(T), A);
190   }
191 
192   if(IsWrite)
193    ne16_wbo_be<T>(WorkRAML, A & 0xFFFFF, DB >> (((A & 1) ^ (2 - sizeof(T))) << 3));
194   else
195    DB = (DB & 0xFFFF0000) | ne16_rbo_be<uint16>(WorkRAML, A & 0xFFFFE);
196 
197   if(!SH2DMAHax)
198    SH7095_mem_timestamp += 7;
199   else
200    *SH2DMAHax += 7;
201 
202   return;
203  }
204 
205  //
206  // BIOS ROM
207  //
208  if(A >= 0x00000000 && A <= 0x000FFFFF)
209  {
210   if(!SH2DMAHax)
211    SH7095_mem_timestamp += 8;
212   else
213    *SH2DMAHax += 8;
214 
215   if(!IsWrite)
216    DB = (DB & 0xFFFF0000) | ne16_rbo_be<uint16>(BIOSROM, A & 0x7FFFE);
217 
218   return;
219  }
220 
221  //
222  // SMPC
223  //
224  if(A >= 0x00100000 && A <= 0x0017FFFF)
225  {
226   const uint32 SMPC_A = (A & 0x7F) >> 1;
227 
228   if(!SH2DMAHax)
229   {
230    // SH7095_mem_timestamp += 2;
231    CheckEventsByMemTS();
232   }
233 
234   if(IsWrite)
235   {
236    if(sizeof(T) == 2 || (A & 1))
237     SMPC_Write(SH7095_mem_timestamp, SMPC_A, DB);
238   }
239   else
240    DB = (DB & 0xFFFF0000) | 0xFF00 | SMPC_Read(SH7095_mem_timestamp, SMPC_A);
241 
242   return;
243  }
244 
245  //
246  // Backup RAM
247  //
248  if(A >= 0x00180000 && A <= 0x001FFFFF)
249  {
250   if(!SH2DMAHax)
251    SH7095_mem_timestamp += 8;
252   else
253    *SH2DMAHax += 8;
254 
255   if(IsWrite)
256   {
257    if(sizeof(T) != 1 || (A & 1))
258    {
259     BackupRAM[(A >> 1) & 0x7FFF] = DB;
260     BackupRAM_Dirty = true;
261    }
262   }
263   else
264    DB = (DB & 0xFFFF0000) | 0xFF00 | BackupRAM[(A >> 1) & 0x7FFF];
265 
266   return;
267  }
268 
269  //
270  // FRT trigger region
271  //
272  if(A >= 0x01000000 && A <= 0x01FFFFFF)
273  {
274   if(!SH2DMAHax)
275    SH7095_mem_timestamp += 8;
276   else
277    *SH2DMAHax += 8;
278 
279   if(IsWrite)
280   {
281    if(sizeof(T) != 1)
282    {
283     const unsigned c = ((A >> 23) & 1) ^ 1;
284 
285     CPU[c].SetFTI(true);
286     CPU[c].SetFTI(false);
287    }
288   }
289   return;
290  }
291 
292  //
293  //
294  //
295  if(!SH2DMAHax)
296   SH7095_mem_timestamp += 4;
297  else
298   *SH2DMAHax += 4;
299 
300  if(IsWrite)
301   SS_DBG(SS_DBG_WARNING, "[SH2 BUS] Unknown %zu-byte write of 0x%08x to 0x%08x\n", sizeof(T), DB >> (((A & 1) ^ (2 - sizeof(T))) << 3), A);
302  else
303   SS_DBG(SS_DBG_WARNING, "[SH2 BUS] Unknown %zu-byte read from 0x%08x\n", sizeof(T), A);
304 }
305 
306 template<typename T, bool IsWrite>
BusRW_DB_CS12(const uint32 A,uint32 & DB,const bool BurstHax,int32 * SH2DMAHax)307 static INLINE void BusRW_DB_CS12(const uint32 A, uint32& DB, const bool BurstHax, int32* SH2DMAHax)
308 {
309  //
310  // CS1 and CS2: SCU
311  //
312  if(!IsWrite)
313   DB = 0;
314 
315  SCU_FromSH2_BusRW_DB<T, IsWrite>(A, &DB, SH2DMAHax);
316 }
317 
318 template<typename T, bool IsWrite>
BusRW_DB_CS3(const uint32 A,uint32 & DB,const bool BurstHax,int32 * SH2DMAHax)319 static INLINE void BusRW_DB_CS3(const uint32 A, uint32& DB, const bool BurstHax, int32* SH2DMAHax)
320 {
321  //
322  // CS3: High work RAM/SDRAM, 0x06000000 ... 0x07FFFFFF
323  //
324  //  Timing is handled in BSC_BusWrite() and BSC_BusRead() in sh7095.inc
325  //
326  if(!IsWrite || sizeof(T) == 4)
327   ne16_rwbo_be<uint32, IsWrite>(WorkRAMH, A & 0xFFFFC, &DB);
328  else
329   ne16_wbo_be<T>(WorkRAMH, A & 0xFFFFF, DB >> (((A & 3) ^ (4 - sizeof(T))) << 3));
330 }
331 
332 //
333 //
334 //
CheatMemRead(uint32 A)335 static MDFN_COLD uint8 CheatMemRead(uint32 A)
336 {
337  A &= (1U << 27) - 1;
338 
339  return ne16_rbo_be<uint8>(SH7095_FastMap[A >> SH7095_EXT_MAP_GRAN_BITS], A);
340 }
341 
CheatMemWrite(uint32 A,uint8 V)342 static MDFN_COLD void CheatMemWrite(uint32 A, uint8 V)
343 {
344  A &= (1U << 27) - 1;
345 
346  if(FMIsWriteable[A >> SH7095_EXT_MAP_GRAN_BITS])
347  {
348   ne16_wbo_be<uint8>(SH7095_FastMap[A >> SH7095_EXT_MAP_GRAN_BITS], A, V);
349 
350   for(unsigned c = 0; c < 2; c++)
351   {
352    if(CPU[c].CCR & SH7095::CCR_CE)
353    {
354     for(uint32 Abase = 0x00000000; Abase < 0x20000000; Abase += 0x08000000)
355     {
356      CPU[c].Cache_WriteUpdate<uint8>(Abase + A, V);
357     }
358    }
359   }
360  }
361 }
362 //
363 //
364 //
SetFastMemMap(uint32 Astart,uint32 Aend,uint16 * ptr,uint32 length,bool is_writeable)365 static void SetFastMemMap(uint32 Astart, uint32 Aend, uint16* ptr, uint32 length, bool is_writeable)
366 {
367  const uint64 Abound = (uint64)Aend + 1;
368  assert((Astart & ((1U << SH7095_EXT_MAP_GRAN_BITS) - 1)) == 0);
369  assert((Abound & ((1U << SH7095_EXT_MAP_GRAN_BITS) - 1)) == 0);
370  assert((length & ((1U << SH7095_EXT_MAP_GRAN_BITS) - 1)) == 0);
371  assert(length > 0);
372  assert(length <= (Abound - Astart));
373 
374  for(uint64 A = Astart; A < Abound; A += (1U << SH7095_EXT_MAP_GRAN_BITS))
375  {
376   uintptr_t tmp = (uintptr_t)ptr + ((A - Astart) % length);
377 
378   if(A < (1U << 27))
379    FMIsWriteable[A >> SH7095_EXT_MAP_GRAN_BITS] = is_writeable;
380 
381   SH7095_FastMap[A >> SH7095_EXT_MAP_GRAN_BITS] = tmp - A;
382  }
383 }
384 
InitFastMemMap(void)385 static MDFN_COLD void InitFastMemMap(void)
386 {
387  for(unsigned i = 0; i < sizeof(fmap_dummy) / sizeof(fmap_dummy[0]); i++)
388  {
389   fmap_dummy[i] = 0;
390  }
391 
392  FMIsWriteable.reset();
393  MDFNMP_Init(1ULL << SH7095_EXT_MAP_GRAN_BITS, (1ULL << 27) / (1ULL << SH7095_EXT_MAP_GRAN_BITS));
394 
395  for(uint64 A = 0; A < 1ULL << 32; A += (1U << SH7095_EXT_MAP_GRAN_BITS))
396  {
397   SH7095_FastMap[A >> SH7095_EXT_MAP_GRAN_BITS] = (uintptr_t)fmap_dummy - A;
398  }
399 }
400 
SS_SetPhysMemMap(uint32 Astart,uint32 Aend,uint16 * ptr,uint32 length,bool is_writeable)401 void SS_SetPhysMemMap(uint32 Astart, uint32 Aend, uint16* ptr, uint32 length, bool is_writeable)
402 {
403  assert(Astart < 0x20000000);
404  assert(Aend < 0x20000000);
405 
406  if(!ptr)
407  {
408   ptr = fmap_dummy;
409   length = sizeof(fmap_dummy);
410  }
411 
412  for(uint32 Abase = 0; Abase < 0x40000000; Abase += 0x20000000)
413   SetFastMemMap(Astart + Abase, Aend + Abase, ptr, length, is_writeable);
414 }
415 
416 #include "sh7095.inc"
417 
418 //
419 // Running is:
420 //   0 at end of (emulation) frame
421 //
422 //   1 during normal execution
423 //
424 //  -1 when we need to temporarily break out of the execution loop to
425 //     e.g. handle turning the slave CPU on or off, which we can't
426 //     safely do from an event handler due to the event handler
427 //     potentially being called from deep within the memory read/write
428 //     functions.
429 //
430 static int Running;
431 event_list_entry events[SS_EVENT__COUNT];
432 static sscpu_timestamp_t next_event_ts;
433 
434 template<unsigned c>
SH_DMA_EventHandler(sscpu_timestamp_t et)435 static sscpu_timestamp_t SH_DMA_EventHandler(sscpu_timestamp_t et)
436 {
437  if(et < SH7095_mem_timestamp)
438  {
439   //printf("SH-2 DMA %d reschedule %d->%d\n", c, et, SH7095_mem_timestamp);
440   return SH7095_mem_timestamp;
441  }
442 
443  // Must come after the (et < SH7095_mem_timestamp) check.
444  if(MDFN_UNLIKELY(SH7095_BusLock))
445   return et + 1;
446 
447  return CPU[c].DMA_Update(et);
448 }
449 
450 //
451 //
452 //
453 
InitEvents(void)454 static MDFN_COLD void InitEvents(void)
455 {
456  for(unsigned i = 0; i < SS_EVENT__COUNT; i++)
457  {
458   if(i == SS_EVENT__SYNFIRST)
459    events[i].event_time = 0;
460   else if(i == SS_EVENT__SYNLAST)
461    events[i].event_time = 0x7FFFFFFF;
462   else
463    events[i].event_time = 0; //SS_EVENT_DISABLED_TS;
464 
465   events[i].prev = (i > 0) ? &events[i - 1] : NULL;
466   events[i].next = (i < (SS_EVENT__COUNT - 1)) ? &events[i + 1] : NULL;
467  }
468 
469  events[SS_EVENT_SH2_M_DMA].event_handler = &SH_DMA_EventHandler<0>;
470  events[SS_EVENT_SH2_S_DMA].event_handler = &SH_DMA_EventHandler<1>;
471 
472  events[SS_EVENT_SCU_DMA].event_handler = SCU_UpdateDMA;
473  events[SS_EVENT_SCU_DSP].event_handler = SCU_UpdateDSP;
474 
475  events[SS_EVENT_SMPC].event_handler = SMPC_Update;
476 
477  events[SS_EVENT_VDP1].event_handler = VDP1::Update;
478  events[SS_EVENT_VDP2].event_handler = VDP2::Update;
479 
480  events[SS_EVENT_CDB].event_handler = CDB_Update;
481 
482  events[SS_EVENT_SOUND].event_handler = SOUND_Update;
483 
484  events[SS_EVENT_CART].event_handler = CART_GetEventHandler();
485 
486  events[SS_EVENT_MIDSYNC].event_handler = MidSync;
487  events[SS_EVENT_MIDSYNC].event_time = SS_EVENT_DISABLED_TS;
488 }
489 
RebaseTS(const sscpu_timestamp_t timestamp)490 static void RebaseTS(const sscpu_timestamp_t timestamp)
491 {
492  for(unsigned i = 0; i < SS_EVENT__COUNT; i++)
493  {
494   if(i == SS_EVENT__SYNFIRST || i == SS_EVENT__SYNLAST)
495    continue;
496 
497   assert(events[i].event_time > timestamp);
498 
499   if(events[i].event_time != SS_EVENT_DISABLED_TS)
500    events[i].event_time -= timestamp;
501  }
502 
503  next_event_ts = events[SS_EVENT__SYNFIRST].next->event_time;
504 }
505 
SS_SetEventNT(event_list_entry * e,const sscpu_timestamp_t next_timestamp)506 void SS_SetEventNT(event_list_entry* e, const sscpu_timestamp_t next_timestamp)
507 {
508 #ifdef MDFN_ENABLE_DEV_BUILD
509  if(next_timestamp < SS_EVENT_DISABLED_TS && (next_timestamp < 0 || next_timestamp >= 0x40000000))
510  {
511   fprintf(stderr, "Event %u, bad next timestamp 0x%08x\n", (unsigned)(e - events), next_timestamp);
512   abort();
513  }
514 #endif
515 
516  if(next_timestamp < e->event_time)
517  {
518   event_list_entry *fe = e;
519 
520   do
521   {
522    fe = fe->prev;
523   } while(next_timestamp < fe->event_time);
524 
525   // Remove this event from the list, temporarily of course.
526   e->prev->next = e->next;
527   e->next->prev = e->prev;
528 
529   // Insert into the list, just after "fe".
530   e->prev = fe;
531   e->next = fe->next;
532   fe->next->prev = e;
533   fe->next = e;
534 
535   e->event_time = next_timestamp;
536  }
537  else if(next_timestamp > e->event_time)
538  {
539   event_list_entry *fe = e;
540 
541   do
542   {
543    fe = fe->next;
544   } while(next_timestamp > fe->event_time);
545 
546   // Remove this event from the list, temporarily of course
547   e->prev->next = e->next;
548   e->next->prev = e->prev;
549 
550   // Insert into the list, just BEFORE "fe".
551   e->prev = fe->prev;
552   e->next = fe;
553   fe->prev->next = e;
554   fe->prev = e;
555 
556   e->event_time = next_timestamp;
557  }
558 
559  next_event_ts = ((Running > 0) ? events[SS_EVENT__SYNFIRST].next->event_time : 0);
560 }
561 
562 // Called from debug.cpp too.
ForceEventUpdates(const sscpu_timestamp_t timestamp)563 void ForceEventUpdates(const sscpu_timestamp_t timestamp)
564 {
565  for(unsigned c = 0; c < 2; c++)
566   CPU[c].ForceInternalEventUpdates();
567 
568  for(unsigned evnum = SS_EVENT__SYNFIRST + 1; evnum < SS_EVENT__SYNLAST; evnum++)
569  {
570   if(events[evnum].event_time != SS_EVENT_DISABLED_TS)
571    SS_SetEventNT(&events[evnum], events[evnum].event_handler(timestamp));
572  }
573 
574  next_event_ts = ((Running > 0) ? events[SS_EVENT__SYNFIRST].next->event_time : 0);
575 }
576 
EventHandler(const sscpu_timestamp_t timestamp)577 static INLINE bool EventHandler(const sscpu_timestamp_t timestamp)
578 {
579  event_list_entry *e;
580 
581  while(timestamp >= (e = events[SS_EVENT__SYNFIRST].next)->event_time)	// If Running = 0, EventHandler() may be called even if there isn't an event per-se, so while() instead of do { ... } while
582  {
583 #ifdef MDFN_ENABLE_DEV_BUILD
584   const sscpu_timestamp_t etime = e->event_time;
585 #endif
586   sscpu_timestamp_t nt;
587 
588   nt = e->event_handler(e->event_time);
589 
590 #ifdef MDFN_ENABLE_DEV_BUILD
591   if(MDFN_UNLIKELY(nt <= etime))
592   {
593    fprintf(stderr, "which=%d event_time=%d nt=%d timestamp=%d\n", (int)(e - events), etime, nt, timestamp);
594    assert(nt > etime);
595   }
596 #endif
597 
598   SS_SetEventNT(e, nt);
599  }
600 
601  return Running > 0;
602 }
603 
CheckEventsByMemTS_Sub(void)604 static void NO_INLINE MDFN_HOT CheckEventsByMemTS_Sub(void)
605 {
606  EventHandler(SH7095_mem_timestamp);
607 }
608 
CheckEventsByMemTS(void)609 static void INLINE CheckEventsByMemTS(void)
610 {
611  if(MDFN_UNLIKELY(SH7095_mem_timestamp >= next_event_ts))
612  {
613   //puts("Woot");
614   CheckEventsByMemTS_Sub();
615  }
616 }
617 
SS_RequestEHLExit(void)618 void SS_RequestEHLExit(void)
619 {
620  if(Running)
621  {
622   Running = -1;
623   next_event_ts = 0;
624  }
625 }
626 
SS_RequestMLExit(void)627 void SS_RequestMLExit(void)
628 {
629  Running = 0;
630  next_event_ts = 0;
631 }
632 
633 #pragma GCC push_options
634 #pragma GCC optimize("O2,no-unroll-loops,no-peel-loops,no-crossjumping")
635 template<bool EmulateICache, bool DebugMode>
RunLoop_INLINE(EmulateSpecStruct * espec)636 static INLINE int32 RunLoop_INLINE(EmulateSpecStruct* espec)
637 {
638  sscpu_timestamp_t eff_ts = 0;
639 
640  for(unsigned c = 0; c < 2; c++)
641   CPU[c].SetDebugMode(DebugMode);
642 
643  //printf("%d %d\n", SH7095_mem_timestamp, CPU[0].timestamp);
644  do
645  {
646   SMPC_ProcessSlaveOffOn();
647   //
648   //
649   Running = true;
650   ForceEventUpdates(eff_ts);
651   do
652   {
653    do
654    {
655     if(DebugMode)
656     {
657      DBG_SetEffTS(eff_ts);
658      DBG_CPUHandler<0>();
659     }
660 
661     CPU[0].Step<0, EmulateICache, DebugMode>();
662     CPU[0].DMA_BusTimingKludge();
663 
664     if(EmulateICache)
665     {
666      if(DebugMode)
667       CPU[1].RunSlaveUntil_Debug(CPU[0].timestamp);
668      else
669       CPU[1].RunSlaveUntil(CPU[0].timestamp);
670     }
671     else
672     {
673      while(MDFN_LIKELY(CPU[0].timestamp > CPU[1].timestamp))
674      {
675       if(DebugMode)
676        DBG_CPUHandler<1>();
677 
678       CPU[1].Step<1, false, DebugMode>();
679      }
680     }
681 
682     eff_ts = CPU[0].timestamp;
683     if(SH7095_mem_timestamp > eff_ts)
684      eff_ts = SH7095_mem_timestamp;
685     else
686      SH7095_mem_timestamp = eff_ts;
687    } while(MDFN_LIKELY(eff_ts < next_event_ts));
688   } while(MDFN_LIKELY(EventHandler(eff_ts)));
689  } while(MDFN_LIKELY(Running != 0));
690 
691  //printf(" End: %d %d -- %d\n", SH7095_mem_timestamp, CPU[0].timestamp, eff_ts);
692  return eff_ts;
693 }
694 
695 template<bool EmulateICache>
RunLoop(EmulateSpecStruct * espec)696 static NO_INLINE MDFN_HOT int32 RunLoop(EmulateSpecStruct* espec)
697 {
698  return RunLoop_INLINE<EmulateICache, false>(espec);
699 }
700 
701 template<bool EmulateICache>
RunLoop_Debug(EmulateSpecStruct * espec)702 static NO_INLINE MDFN_COLD int32 RunLoop_Debug(EmulateSpecStruct* espec)
703 {
704  return RunLoop_INLINE<EmulateICache, true>(espec);
705 }
706 
707 #pragma GCC pop_options
708 
709 // Must not be called within an event or read/write handler.
SS_Reset(bool powering_up)710 void SS_Reset(bool powering_up)
711 {
712  SH7095_BusLock = 0;
713 
714  if(powering_up)
715  {
716   memset(WorkRAML, 0x00, sizeof(WorkRAML));	// TODO: Check
717   memset(WorkRAMH, 0x00, sizeof(WorkRAMH));	// TODO: Check
718  }
719 
720  if(powering_up)
721  {
722   CPU[0].TruePowerOn();
723   CPU[1].TruePowerOn();
724  }
725 
726  SCU_Reset(powering_up);
727  CPU[0].Reset(powering_up);
728 
729  SMPC_Reset(powering_up);
730 
731  VDP1::Reset(powering_up);
732  VDP2::Reset(powering_up);
733 
734  CDB_Reset(powering_up);
735 
736  SOUND_Reset(powering_up);
737 
738  CART_Reset(powering_up);
739 }
740 
741 static EmulateSpecStruct* espec;
742 static bool AllowMidSync;
743 static int32 cur_clock_div;
744 
745 static int64 UpdateInputLastBigTS;
UpdateSMPCInput(const sscpu_timestamp_t timestamp)746 static INLINE void UpdateSMPCInput(const sscpu_timestamp_t timestamp)
747 {
748  int32 elapsed_time = (((int64)timestamp * cur_clock_div * 1000 * 1000) - UpdateInputLastBigTS) / (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));
749 
750  UpdateInputLastBigTS += (int64)elapsed_time * (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));
751 
752  SMPC_UpdateInput(elapsed_time);
753 }
754 
MidSync(const sscpu_timestamp_t timestamp)755 static sscpu_timestamp_t MidSync(const sscpu_timestamp_t timestamp)
756 {
757  if(AllowMidSync)
758  {
759   //
760   // Don't call SOUND_Update() here, it's not necessary and will subtly alter emulation behavior from the perspective of the emulated program
761   // (which is not a problem in and of itself, but it's preferable to keep settings from altering emulation behavior when they don't need to).
762   //
763   //printf("MidSync: %d\n", VDP2::PeekLine());
764   if(!espec->NeedSoundReverse)
765   {
766    espec->SoundBufSize += SOUND_FlushOutput(espec->SoundBuf + (espec->SoundBufSize * 2), espec->SoundBufMaxSize - espec->SoundBufSize, espec->NeedSoundReverse);
767    espec->MasterCycles = timestamp * cur_clock_div;
768   }
769   //printf("%d\n", espec->SoundBufSize);
770 
771   SMPC_UpdateOutput();
772   //
773   //
774   MDFN_MidSync(espec);
775   //
776   //
777   UpdateSMPCInput(timestamp);
778 
779   AllowMidSync = false;
780  }
781 
782  return SS_EVENT_DISABLED_TS;
783 }
784 
Emulate(EmulateSpecStruct * espec_arg)785 static void Emulate(EmulateSpecStruct* espec_arg)
786 {
787  int32 end_ts;
788 
789  espec = espec_arg;
790  AllowMidSync = true;
791  MDFNGameInfo->mouse_sensitivity = MDFN_GetSettingF("ss.input.mouse_sensitivity");
792 
793  cur_clock_div = SMPC_StartFrame(espec);
794  UpdateSMPCInput(0);
795  VDP2::StartFrame(espec, cur_clock_div == 61);
796  SOUND_StartFrame(espec->SoundRate / espec->soundmultiplier, MDFN_GetSettingUI("ss.scsp.resamp_quality"));
797  CART_SetCPUClock(EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1), cur_clock_div);
798  espec->SoundBufSize = 0;
799  espec->MasterCycles = 0;
800  espec->soundmultiplier = 1;
801  //
802  //
803  //
804 #ifdef WANT_DEBUGGER
805  #define RLTDAT(eic) RunLoop_Debug<eic>
806 #else
807  #define RLTDAT(eic) RunLoop<eic>
808 #endif
809  static int32 (*const rltab[2][2])(EmulateSpecStruct*) =
810  {
811   //DebugMode=false  DebugMode=true
812   { RunLoop<false>, RLTDAT(false) },	// EmulateICache=false
813   { RunLoop<true>,  RLTDAT(true)  },	// EmulateICache=true
814  };
815 #undef RLTDAT
816  end_ts = rltab[NeedEmuICache][DBG_NeedCPUHooks()](espec);
817  assert(end_ts >= 0);
818  ForceEventUpdates(end_ts);
819  //
820  SMPC_EndFrame(espec, end_ts);
821  //
822  //
823  //
824  RebaseTS(end_ts);
825 
826  CDB_ResetTS();
827  SOUND_AdjustTS(-end_ts);
828  VDP1::AdjustTS(-end_ts);
829  VDP2::AdjustTS(-end_ts);
830  SMPC_ResetTS();
831  SCU_AdjustTS(-end_ts);
832  CART_AdjustTS(-end_ts);
833 
834  UpdateInputLastBigTS -= (int64)end_ts * cur_clock_div * 1000 * 1000;
835  //
836  SH7095_mem_timestamp -= end_ts; // Update before CPU[n].AdjustTS()
837  //
838  for(unsigned c = 0; c < 2; c++)
839   CPU[c].AdjustTS(-end_ts);
840 
841  //printf("B=% 7d M=% 7d S=% 7d\n", SH7095_mem_timestamp, CPU[0].timestamp, CPU[1].timestamp);
842  //
843  //
844  //
845  espec->MasterCycles = end_ts * cur_clock_div;
846  espec->SoundBufSize += SOUND_FlushOutput(espec->SoundBuf + (espec->SoundBufSize * 2), espec->SoundBufMaxSize - espec->SoundBufSize, espec->NeedSoundReverse);
847  espec->NeedSoundReverse = false;
848  //
849  //
850  //
851  SMPC_UpdateOutput();
852  //
853  //
854  //
855  if(BackupRAM_Dirty)
856  {
857   BackupRAM_SaveDelay = (int64)3 * (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));	// 3 second delay
858   BackupRAM_Dirty = false;
859  }
860  else if(BackupRAM_SaveDelay > 0)
861  {
862   BackupRAM_SaveDelay -= espec->MasterCycles;
863 
864   if(BackupRAM_SaveDelay <= 0)
865   {
866    try
867    {
868     SaveBackupRAM();
869    }
870    catch(std::exception& e)
871    {
872     MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
873     BackupRAM_SaveDelay = (int64)60 * (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));	// 60 second retry delay.
874    }
875   }
876  }
877 
878  if(CART_GetClearNVDirty())
879   CartNV_SaveDelay = (int64)3 * (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));	// 3 second delay
880  else if(CartNV_SaveDelay > 0)
881  {
882   CartNV_SaveDelay -= espec->MasterCycles;
883 
884   if(CartNV_SaveDelay <= 0)
885   {
886    try
887    {
888     SaveCartNV();
889    }
890    catch(std::exception& e)
891    {
892     MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
893     CartNV_SaveDelay = (int64)60 * (EmulatedSS.MasterClock / MDFN_MASTERCLOCK_FIXED(1));	// 60 second retry delay.
894    }
895   }
896  }
897 }
898 
899 //
900 //
901 //
902 
Cleanup(void)903 static MDFN_COLD void Cleanup(void)
904 {
905  CART_Kill();
906 
907  DBG_Kill();
908  VDP1::Kill();
909  VDP2::Kill();
910  SOUND_Kill();
911  CDB_Kill();
912 
913  cdifs = NULL;
914 }
915 
IsSaturnDisc(const uint8 * sa32k)916 static MDFN_COLD bool IsSaturnDisc(const uint8* sa32k)
917 {
918  if(sha256(&sa32k[0x100], 0xD00) != "96b8ea48819cfa589f24c40aa149c224c420dccf38b730f00156efe25c9bbc8f"_sha256)
919   return false;
920 
921  if(memcmp(&sa32k[0], "SEGA SEGASATURN ", 16))
922   return false;
923 
924  return true;
925 }
926 
CalcGameID(uint8 * id_out16,uint8 * fd_id_out16,char * sgid,char * sgname,char * sgarea)927 static INLINE void CalcGameID(uint8* id_out16, uint8* fd_id_out16, char* sgid, char* sgname, char* sgarea)
928 {
929  std::unique_ptr<uint8[]> buf(new uint8[2048]);
930  md5_context mctx;
931 
932  mctx.starts();
933 
934  for(size_t x = 0; x < cdifs->size(); x++)
935  {
936   auto* c = (*cdifs)[x];
937   CDUtility::TOC toc;
938 
939   c->ReadTOC(&toc);
940 
941   mctx.update_u32_as_lsb(toc.first_track);
942   mctx.update_u32_as_lsb(toc.last_track);
943   mctx.update_u32_as_lsb(toc.disc_type);
944 
945   for(unsigned i = 1; i <= 100; i++)
946   {
947    const auto& t = toc.tracks[i];
948 
949    mctx.update_u32_as_lsb(t.adr);
950    mctx.update_u32_as_lsb(t.control);
951    mctx.update_u32_as_lsb(t.lba);
952    mctx.update_u32_as_lsb(t.valid);
953   }
954 
955   for(unsigned i = 0; i < 512; i++)
956   {
957    if(c->ReadSectors(&buf[0], i, 1) >= 0x1)
958    {
959     if(i == 0)
960     {
961      char* tmp;
962      memcpy(sgid, &buf[0x20], 16);
963      sgid[16] = 0;
964      if((tmp = strrchr(sgid, 'V')))
965      {
966       do
967       {
968        *tmp = 0;
969       } while(tmp-- != sgid && (signed char)*tmp <= 0x20);
970      }
971      memcpy(sgname, &buf[0x60], 0x70);
972      sgname[0x70] = 0;
973      MDFN_zapctrlchars(sgname);
974      MDFN_trim(sgname);
975 
976      memcpy(sgarea, &buf[0x40], 0x10);
977      sgarea[0x10] = 0;
978      MDFN_zapctrlchars(sgarea);
979      MDFN_trim(sgarea);
980     }
981 
982     mctx.update(&buf[0], 2048);
983    }
984   }
985 
986   if(x == 0)
987   {
988    md5_context fd_mctx = mctx;
989    fd_mctx.finish(fd_id_out16);
990   }
991  }
992 
993  mctx.finish(id_out16);
994 }
995 
996 //
997 // Remember to rebuild region database in db.cpp if changing the order of entries in this table(and be careful about game id collisions, e.g. with some Korean games).
998 //
999 static const struct
1000 {
1001  const char c;
1002  const char* str;	// Community-defined region string that may appear in filename.
1003  unsigned region;
1004 } region_strings[] =
1005 {
1006  // Listed in order of preference for multi-region games.
1007  { 'U', "USA", SMPC_AREA_NA },
1008  { 'J', "Japan", SMPC_AREA_JP },
1009  { 'K', "Korea", SMPC_AREA_KR },
1010 
1011  { 'E', "Europe", SMPC_AREA_EU_PAL },
1012  { 'E', "Germany", SMPC_AREA_EU_PAL },
1013  { 'E', "France", SMPC_AREA_EU_PAL },
1014  { 'E', "Spain", SMPC_AREA_EU_PAL },
1015 
1016  { 'B', "Brazil", SMPC_AREA_CSA_NTSC },
1017 
1018  { 'T', nullptr, SMPC_AREA_ASIA_NTSC },
1019  { 'A', nullptr, SMPC_AREA_ASIA_PAL },
1020  { 'L', nullptr, SMPC_AREA_CSA_PAL },
1021 };
1022 
DetectRegion(unsigned * const region)1023 static INLINE bool DetectRegion(unsigned* const region)
1024 {
1025  std::unique_ptr<uint8[]> buf(new uint8[2048 * 16]);
1026  uint64 possible_regions = 0;
1027 
1028  for(auto& c : *cdifs)
1029  {
1030   if(c->ReadSectors(&buf[0], 0, 16) != 0x1)
1031    continue;
1032 
1033   if(!IsSaturnDisc(&buf[0]))
1034    continue;
1035 
1036   for(unsigned i = 0; i < 16; i++)
1037   {
1038    for(auto const& rs : region_strings)
1039    {
1040     if(rs.c == buf[0x40 + i])
1041     {
1042      possible_regions |= (uint64)1 << rs.region;
1043      break;
1044     }
1045    }
1046   }
1047   break;
1048  }
1049 
1050  for(auto const& rs : region_strings)
1051  {
1052   if(possible_regions & ((uint64)1 << rs.region))
1053   {
1054    *region = rs.region;
1055    return true;
1056   }
1057  }
1058 
1059  return false;
1060 }
1061 #if 0
1062 static MDFN_COLD bool DetectRegionByFN(const std::string& fn, unsigned* const region)
1063 {
1064  std::string ss = fn;
1065  size_t cp_pos;
1066  uint64 possible_regions = 0;
1067 
1068  while((cp_pos = ss.rfind(')')) != std::string::npos && cp_pos > 0)
1069  {
1070   ss.resize(cp_pos);
1071   //
1072   size_t op_pos = ss.rfind('(');
1073 
1074   if(op_pos != std::string::npos)
1075   {
1076    for(auto const& rs : region_strings)
1077    {
1078     if(!rs.str)
1079      continue;
1080 
1081     size_t rs_pos = ss.find(rs.str, op_pos + 1);
1082 
1083     if(rs_pos != std::string::npos)
1084     {
1085      bool leading_ok = true;
1086      bool trailing_ok = true;
1087 
1088      for(size_t i = rs_pos - 1; i > op_pos; i--)
1089      {
1090       if(ss[i] == ',')
1091        break;
1092       else if(ss[i] != ' ')
1093       {
1094        leading_ok = false;
1095        break;
1096       }
1097      }
1098 
1099      for(size_t i = rs_pos + strlen(rs.str); i < ss.size(); i++)
1100      {
1101       if(ss[i] == ',')
1102        break;
1103       else if(ss[i] != ' ')
1104       {
1105        trailing_ok = false;
1106        break;
1107       }
1108      }
1109 
1110      if(leading_ok && trailing_ok)
1111       possible_regions |= (uint64)1 << rs.region;
1112     }
1113    }
1114   }
1115  }
1116 
1117  for(auto const& rs : region_strings)
1118  {
1119   if(possible_regions & ((uint64)1 << rs.region))
1120   {
1121    *region = rs.region;
1122    return true;
1123   }
1124  }
1125 
1126  return false;
1127 }
1128 #endif
InitCommon(const unsigned cpucache_emumode,const unsigned horrible_hacks,const unsigned cart_type,const unsigned smpc_area,Stream * dbg_cart_rom_stream)1129 static void MDFN_COLD InitCommon(const unsigned cpucache_emumode, const unsigned horrible_hacks, const unsigned cart_type, const unsigned smpc_area, Stream* dbg_cart_rom_stream)
1130 {
1131  const char* cart_rom_path_sname = nullptr;
1132 
1133 #ifdef MDFN_ENABLE_DEV_BUILD
1134  ss_dbg_mask = SS_DBG_ERROR;
1135  {
1136   std::vector<uint64> dms = MDFN_GetSettingMultiUI("ss.dbg_mask");
1137 
1138   for(uint64 dmse : dms)
1139    ss_dbg_mask |= dmse;
1140  }
1141 
1142  static const uint32 addrs[] =
1143  {
1144   0x280, 0x300, 0x304, 0x308, 0x30C, 0x310, 0x314, 0x318, 0x31C, 0x320, 0x324, 0x330, 0x334, 0x340, 0x344, 0x348,
1145   0x34C, 0x354, 0x358
1146  };
1147 
1148  static const uint32 wr_addrs[] = { 0x250, 0x348 };
1149 
1150  for(size_t i = 0; i < sizeof(addrs) / sizeof(addrs[0]); i++)
1151   BWMIgnoreAddr[0][addrs[i] & 0x1FF] = true;
1152 
1153  for(size_t i = 0; i < sizeof(wr_addrs) / sizeof(wr_addrs[0]); i++)
1154   BWMIgnoreAddr[1][wr_addrs[i] & 0x1FF] = true;
1155 #endif
1156 
1157  //
1158  {
1159   const struct
1160   {
1161    unsigned mode;
1162    const char* name;
1163   } CPUCacheEmuModes[] =
1164   {
1165    { CPUCACHE_EMUMODE_DATA_CB,	_("Data only, with high-level bypass") },
1166    { CPUCACHE_EMUMODE_DATA,	_("Data only") },
1167    { CPUCACHE_EMUMODE_FULL,	_("Full") },
1168   };
1169   const char* cem = _("Unknown");
1170 
1171   for(auto const& ceme : CPUCacheEmuModes)
1172   {
1173    if(ceme.mode == cpucache_emumode)
1174    {
1175     cem = ceme.name;
1176     break;
1177    }
1178   }
1179   MDFN_printf(_("CPU Cache Emulation Mode: %s\n"), cem);
1180  }
1181  //
1182  if(horrible_hacks)
1183   MDFN_printf(_("Horrible hacks: %s\n"), DB_GetHHDescriptions(horrible_hacks).c_str());
1184  //
1185  {
1186   MDFN_printf(_("Region: 0x%01x\n"), smpc_area);
1187   const struct
1188   {
1189    const unsigned type;
1190    const char* name;
1191    const char* rom_path_sname;
1192   } CartNames[] =
1193   {
1194    { CART_NONE, _("None"), nullptr },
1195    { CART_BACKUP_MEM, _("Backup Memory"), nullptr },
1196    { CART_EXTRAM_1M, _("1MiB Extended RAM"), nullptr },
1197    { CART_EXTRAM_4M, _("4MiB Extended RAM"), nullptr },
1198    { CART_KOF95, _("King of Fighters '95 ROM"), "ss.cart.kof95_path" },
1199    { CART_ULTRAMAN, _("Ultraman ROM"), "ss.cart.ultraman_path" },
1200    { CART_AR4MP, _("Action Replay 4M Plus"), "ss.cart.satar4mp_path" },
1201    { CART_CS1RAM_16M, _("16MiB CS1 RAM"), nullptr },
1202    { CART_NLMODEM, _("Netlink Modem"), nullptr },
1203    { CART_MDFN_DEBUG, _("Mednafen Debug"), nullptr },
1204   };
1205   const char* cn = _("Unknown");
1206 
1207   for(auto const& cne : CartNames)
1208   {
1209    if(cne.type == cart_type)
1210    {
1211     cn = cne.name;
1212     cart_rom_path_sname = cne.rom_path_sname;
1213     break;
1214    }
1215   }
1216   MDFN_printf(_("Cart: %s\n"), cn);
1217  }
1218  //
1219  NeedEmuICache = (cpucache_emumode == CPUCACHE_EMUMODE_FULL);
1220  for(unsigned c = 0; c < 2; c++)
1221  {
1222   CPU[c].Init((cpucache_emumode == CPUCACHE_EMUMODE_FULL), (cpucache_emumode == CPUCACHE_EMUMODE_DATA_CB));
1223   CPU[c].SetMD5((bool)c);
1224  }
1225  SH7095_mem_timestamp = 0;
1226  SH7095_DB = 0;
1227 
1228  ss_horrible_hacks = horrible_hacks;
1229 
1230  //
1231  // Initialize backup memory.
1232  //
1233  memset(BackupRAM, 0x00, sizeof(BackupRAM));
1234  for(unsigned i = 0; i < 0x40; i++)
1235   BackupRAM[i] = BRAM_Init_Data[i & 0x0F];
1236 
1237  // Call InitFastMemMap() before functions like SOUND_Init()
1238  InitFastMemMap();
1239  SS_SetPhysMemMap(0x00000000, 0x000FFFFF, BIOSROM, sizeof(BIOSROM));
1240  SS_SetPhysMemMap(0x00200000, 0x003FFFFF, WorkRAML, sizeof(WorkRAML), true);
1241  SS_SetPhysMemMap(0x06000000, 0x07FFFFFF, WorkRAMH, sizeof(WorkRAMH), true);
1242  MDFNMP_RegSearchable(0x00200000, sizeof(WorkRAML));
1243  MDFNMP_RegSearchable(0x06000000, sizeof(WorkRAMH));
1244 
1245  {
1246   std::unique_ptr<FileStream> cart_rom_stream;
1247 
1248   if(cart_rom_path_sname)
1249   {
1250    const std::string cart_rom_path = MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, MDFN_GetSettingS(cart_rom_path_sname));
1251 
1252    cart_rom_stream.reset(new FileStream(cart_rom_path, FileStream::MODE_READ));
1253   }
1254 
1255   CART_Init(cart_type, cart_rom_stream ? cart_rom_stream.get() : dbg_cart_rom_stream);
1256   ActiveCartType = cart_type;
1257  }
1258  //
1259  //
1260  //
1261  const bool PAL = (smpc_area & SMPC_AREA__PAL_MASK);
1262  const int32 MasterClock = PAL ? 1734687500 : 1746818182;	// NTSC: 1746818181.8181818181, PAL: 1734687500-ish
1263  const char* biospath_sname;
1264  int sls = MDFN_GetSettingI(PAL ? "ss.slstartp" : "ss.slstart");
1265  int sle = MDFN_GetSettingI(PAL ? "ss.slendp" : "ss.slend");
1266  const uint64 vdp2_affinity = MDFN_GetSettingUI("ss.affinity.vdp2");
1267 
1268  if(PAL)
1269  {
1270   sls += 16;
1271   sle += 16;
1272  }
1273 
1274  if(sls > sle)
1275   std::swap(sls, sle);
1276 
1277  if(smpc_area == SMPC_AREA_JP || smpc_area == SMPC_AREA_ASIA_NTSC)
1278   biospath_sname = "ss.bios_jp";
1279  else
1280   biospath_sname = "ss.bios_na_eu";
1281 
1282  {
1283   const std::string biospath = MDFN_MakeFName(MDFNMKF_FIRMWARE, 0, MDFN_GetSettingS(biospath_sname));
1284   FileStream BIOSFile(biospath, FileStream::MODE_READ);
1285 
1286   if(BIOSFile.size() != 524288)
1287    throw MDFN_Error(0, _("BIOS file \"%s\" is of an incorrect size."), biospath.c_str());
1288 
1289   BIOSFile.read(BIOSROM, 512 * 1024);
1290   BIOS_SHA256 = sha256(BIOSROM, 512 * 1024);
1291 
1292   if(MDFN_GetSettingB("ss.bios_sanity"))
1293   {
1294    static const struct
1295    {
1296     const char* fn;
1297     sha256_digest hash;
1298     const uint32 areas;
1299    } BIOSDB[] =
1300    {
1301     { "sega1003.bin",  "cc1e1b7f88f1c6e6fc35994bae2c2292e06fdae258c79eb26a1f1391e72914a8"_sha256, (1U << SMPC_AREA_JP) | (1U << SMPC_AREA_ASIA_NTSC),  },
1302     { "sega_100.bin",  "ae4058627bb5db9be6d8d83c6be95a4aa981acc8a89042e517e73317886c8bc2"_sha256, (1U << SMPC_AREA_JP) | (1U << SMPC_AREA_ASIA_NTSC),  },
1303     { "sega_101.bin",  "dcfef4b99605f872b6c3b6d05c045385cdea3d1b702906a0ed930df7bcb7deac"_sha256, (1U << SMPC_AREA_JP) | (1U << SMPC_AREA_ASIA_NTSC),  },
1304     { "sega_100a.bin", "87293093fad802fcff31fcab427a16caff1acbc5184899b8383b360fd58efb73"_sha256, (~0U) & ~((1U << SMPC_AREA_JP) | (1U << SMPC_AREA_ASIA_NTSC)) },
1305     { "mpr-17933.bin", "96e106f740ab448cf89f0dd49dfbac7fe5391cb6bd6e14ad5e3061c13330266f"_sha256, (~0U) & ~((1U << SMPC_AREA_JP) | (1U << SMPC_AREA_ASIA_NTSC)) },
1306    };
1307    std::string fnbase, fnext;
1308    std::string fn;
1309 
1310    NVFS.get_file_path_components(biospath, nullptr, &fnbase, &fnext);
1311    fn = fnbase + fnext;
1312 
1313    // Discourage people from renaming files instead of changing settings.
1314    for(auto const& dbe : BIOSDB)
1315    {
1316     if(fn == dbe.fn && BIOS_SHA256 != dbe.hash)
1317      throw MDFN_Error(0, _("The BIOS ROM data loaded from \"%s\" does not match what is expected by its filename(possibly due to erroneous file renaming by the user)."), biospath.c_str());
1318    }
1319 
1320    for(auto const& dbe : BIOSDB)
1321    {
1322     if(BIOS_SHA256 == dbe.hash && !(dbe.areas & (1U << smpc_area)))
1323      throw MDFN_Error(0, _("The BIOS loaded from \"%s\" is the wrong BIOS for the region being emulated(possibly due to changing setting \"%s\" to point to the wrong file)."), biospath.c_str(), biospath_sname);
1324    }
1325   }
1326   //
1327   //
1328   for(unsigned i = 0; i < 262144; i++)
1329    BIOSROM[i] = MDFN_de16msb(&BIOSROM[i]);
1330  }
1331 
1332  EmulatedSS.MasterClock = MDFN_MASTERCLOCK_FIXED(MasterClock);
1333 
1334  SCU_Init();
1335  SMPC_Init(smpc_area, MasterClock);
1336  VDP1::Init();
1337  VDP2::Init(PAL, vdp2_affinity);
1338  CDB_Init();
1339  SOUND_Init();
1340 
1341  InitEvents();
1342  UpdateInputLastBigTS = 0;
1343 
1344  DBG_Init();
1345  //
1346  //
1347  //
1348  MDFN_printf("\n");
1349  {
1350   const bool correct_aspect = MDFN_GetSettingB("ss.correct_aspect");
1351   const bool h_overscan = MDFN_GetSettingB("ss.h_overscan");
1352   const bool h_blend = MDFN_GetSettingB("ss.h_blend");
1353 
1354   MDFN_printf(_("Displayed scanlines: [%u,%u]\n"), sls, sle);
1355   MDFN_printf(_("Correct Aspect Ratio: %s\n"), correct_aspect ? _("Enabled") : _("Disabled"));
1356   MDFN_printf(_("Show H Overscan: %s\n"), h_overscan ? _("Enabled") : _("Disabled"));
1357   MDFN_printf(_("H Blend: %s\n"), h_blend ? _("Enabled") : _("Disabled"));
1358 
1359   VDP2::SetGetVideoParams(&EmulatedSS, correct_aspect, sls, sle, h_overscan, h_blend);
1360  }
1361 
1362  MDFN_printf("\n");
1363  for(unsigned sp = 0; sp < 2; sp++)
1364  {
1365   char buf[64];
1366   bool sv;
1367 
1368   trio_snprintf(buf, sizeof(buf), "ss.input.sport%u.multitap", sp + 1);
1369   sv = MDFN_GetSettingB(buf);
1370   SMPC_SetMultitap(sp, sv);
1371 
1372   MDFN_printf(_("Multitap on Saturn Port %u: %s\n"), sp + 1, sv ? _("Enabled") : _("Disabled"));
1373  }
1374 
1375  for(unsigned vp = 0; vp < 12; vp++)
1376  {
1377   char buf[64];
1378   uint32 sv;
1379 
1380   trio_snprintf(buf, sizeof(buf), "ss.input.port%u.gun_chairs", vp + 1);
1381   sv = MDFN_GetSettingUI(buf);
1382   SMPC_SetCrosshairsColor(vp, sv);
1383  }
1384 
1385  //
1386  //
1387  //
1388  try { LoadRTC();       } catch(MDFN_Error& e) { if(e.GetErrno() != ENOENT) throw; }
1389  try { LoadBackupRAM(); } catch(MDFN_Error& e) { if(e.GetErrno() != ENOENT) throw; }
1390  try { LoadCartNV();    } catch(MDFN_Error& e) { if(e.GetErrno() != ENOENT) throw; }
1391 
1392  BackupBackupRAM();
1393  BackupCartNV();
1394 
1395  BackupRAM_Dirty = false;
1396  BackupRAM_SaveDelay = 0;
1397 
1398  CART_GetClearNVDirty();
1399  CartNV_SaveDelay = 0;
1400  //
1401  if(MDFN_GetSettingB("ss.smpc.autortc"))
1402  {
1403   struct tm ht = Time::LocalTime();
1404 
1405   SMPC_SetRTC(&ht, MDFN_GetSettingUI("ss.smpc.autortc.lang"));
1406  }
1407  //
1408  SS_Reset(true);
1409 }
1410 
TestMagic(GameFile * gf)1411 static MDFN_COLD bool TestMagic(GameFile* gf)
1412 {
1413  if(gf->ext == "ss")
1414   return true;
1415 
1416  return false;
1417 }
1418 
Load(GameFile * gf)1419 static MDFN_COLD void Load(GameFile* gf)
1420 {
1421 #if 0
1422  // cat regiondb.inc | sort | uniq --all-repeated=separate -w 102
1423  {
1424   FileStream rdbfp("/tmp/regiondb.inc", FileStream::MODE_WRITE);
1425   Stream* s = fp->stream();
1426   std::string linebuf;
1427   static std::vector<CDInterface*> CDInterfaces;
1428 
1429   cdifs = &CDInterfaces;
1430 
1431   while(s->get_line(linebuf) >= 0)
1432   {
1433    static uint8 sbuf[2048 * 16];
1434    CDInterface* iface = CDInterface::Open(linebuf, false);
1435    int m = iface->ReadSectors(sbuf, 0, 16);
1436    std::string fb;
1437 
1438    assert(m == 0x1);
1439    assert(IsSaturnDisc(&sbuf[0]) == true);
1440    //
1441    uint8 dummytmp[16] = { 0 };
1442    uint8 tmp[16] = { 0 };
1443    const char* regstr;
1444    unsigned region = ~0U;
1445 
1446    NVFS.get_file_path_components(linebuf, nullptr, &fb);
1447 
1448    if(!DetectRegionByFN(fb, &region))
1449     abort();
1450 
1451    switch(region)
1452    {
1453     default: abort(); break;
1454     case SMPC_AREA_NA: regstr = "SMPC_AREA_NA"; break;
1455     case SMPC_AREA_JP: regstr = "SMPC_AREA_JP"; break;
1456     case SMPC_AREA_EU_PAL: regstr = "SMPC_AREA_EU_PAL"; break;
1457     case SMPC_AREA_KR: regstr = "SMPC_AREA_KR"; break;
1458     case SMPC_AREA_CSA_NTSC: regstr = "SMPC_AREA_CSA_NTSC"; break;
1459    }
1460 
1461    CDInterfaces.clear();
1462    CDInterfaces.push_back(iface);
1463 
1464    CalcGameID(dummytmp, tmp);
1465 
1466    unsigned tmpreg;
1467    if(!DetectRegion(&tmpreg) || tmpreg != region)
1468    {
1469     rdbfp.print_format("{ { ");
1470     for(unsigned i = 0; i < 16; i++)
1471      rdbfp.print_format("0x%02x, ", tmp[i]);
1472     rdbfp.print_format("}, %s }, // %s\n", regstr, fb.c_str());
1473    }
1474 
1475    delete iface;
1476   }
1477  }
1478 
1479  return;
1480 #endif
1481 
1482  cdifs = NULL;
1483 
1484  try
1485  {
1486   if(MDFN_GetSettingS("ss.dbg_exe_cdpath") != "")
1487   {
1488    RMD_Drive dr;
1489    RMD_DriveDefaults drdef;
1490 
1491    dr.Name = std::string("Virtual CD Drive");
1492    dr.PossibleStates.push_back(RMD_State({"Tray Open", false, false, true}));
1493    dr.PossibleStates.push_back(RMD_State({"Tray Closed (Empty)", false, false, false}));
1494    dr.PossibleStates.push_back(RMD_State({"Tray Closed", true, true, false}));
1495    dr.CompatibleMedia.push_back(0);
1496    dr.MediaMtoPDelay = 2000;
1497 
1498    drdef.State = 2; // Tray Closed
1499    drdef.Media = 0;
1500    drdef.Orientation = 0;
1501 
1502    MDFNGameInfo->RMD->Drives.push_back(dr);
1503    MDFNGameInfo->RMD->DrivesDefaults.push_back(drdef);
1504    MDFNGameInfo->RMD->MediaTypes.push_back(RMD_MediaType({"CD"}));
1505    MDFNGameInfo->RMD->Media.push_back(RMD_Media({"Test CD", 0}));
1506 
1507    static std::vector<CDInterface*> CDInterfaces;
1508    CDInterfaces.clear();
1509    CDInterfaces.push_back(CDInterface::Open(&NVFS, MDFN_GetSettingS("ss.dbg_exe_cdpath"), false, MDFN_GetSettingUI("affinity.cd")));
1510    cdifs = &CDInterfaces;
1511   }
1512   //
1513   //
1514   uint32 horrible_hacks = 0;
1515   {
1516    std::vector<uint64> dhhs = MDFN_GetSettingMultiUI("ss.dbg_exe_hh");
1517 
1518    for(uint64 dhhse : dhhs)
1519     horrible_hacks |= dhhse;
1520   }
1521 
1522   InitCommon(MDFN_GetSettingUI("ss.dbg_exe_cem"), horrible_hacks, CART_MDFN_DEBUG, MDFN_GetSettingUI("ss.region_default"), gf->stream);
1523  }
1524  catch(...)
1525  {
1526   Cleanup();
1527   throw;
1528  }
1529 }
1530 
TestMagicCD(std::vector<CDInterface * > * CDInterfaces)1531 static MDFN_COLD bool TestMagicCD(std::vector<CDInterface*> *CDInterfaces)
1532 {
1533  std::unique_ptr<uint8[]> buf(new uint8[2048 * 16]);
1534 
1535  if((*CDInterfaces)[0]->ReadSectors(&buf[0], 0, 16) != 0x1)
1536   return false;
1537 
1538  return IsSaturnDisc(&buf[0]);
1539 }
1540 
DiscSanityChecks(void)1541 static MDFN_COLD void DiscSanityChecks(void)
1542 {
1543  for(size_t i = 0; i < cdifs->size(); i++)
1544  {
1545   CDUtility::TOC toc;
1546 
1547   (*cdifs)[i]->ReadTOC(&toc);
1548 
1549   for(int32 track = 1; track <= 99; track++)
1550   {
1551    if(!toc.tracks[track].valid)
1552     continue;
1553 
1554    if(toc.tracks[track].control & CDUtility::SUBQ_CTRLF_DATA)
1555     continue;
1556    //
1557    //
1558    //
1559    const int32 start_lba = toc.tracks[track].lba;
1560    const int32 end_lba = start_lba + 32 - 1;
1561    bool any_subq_curpos = false;
1562 
1563    for(int32 lba = start_lba; lba <= end_lba; lba++)
1564    {
1565     uint8 pwbuf[96];
1566     uint8 qbuf[12];
1567 
1568     if(!(*cdifs)[i]->ReadRawSectorPWOnly(pwbuf, lba, false))
1569      throw MDFN_Error(0, _("Disc %zu of %zu: Error reading sector at lba=%d in DiscSanityChecks()."), i + 1, cdifs->size(), lba);
1570 
1571     CDUtility::subq_deinterleave(pwbuf, qbuf);
1572     if(CDUtility::subq_check_checksum(qbuf) && (qbuf[0] & 0xF) == CDUtility::ADR_CURPOS)
1573     {
1574      const uint8 qm = qbuf[7];
1575      const uint8 qs = qbuf[8];
1576      const uint8 qf = qbuf[9];
1577      uint8 lm, ls, lf;
1578 
1579      any_subq_curpos = true;
1580 
1581      CDUtility::LBA_to_AMSF(lba, &lm, &ls, &lf);
1582      lm = CDUtility::U8_to_BCD(lm);
1583      ls = CDUtility::U8_to_BCD(ls);
1584      lf = CDUtility::U8_to_BCD(lf);
1585 
1586      if(lm != qm || ls != qs || lf != qf)
1587      {
1588       throw MDFN_Error(0, _("Disc %zu of %zu: Time mismatch at lba=%d(%02x:%02x:%02x); Q subchannel: %02x:%02x:%02x"),
1589 		i + 1, cdifs->size(),
1590 		lba,
1591 		lm, ls, lf,
1592 		qm, qs, qf);
1593      }
1594     }
1595    }
1596 
1597    if(!any_subq_curpos)
1598    {
1599     throw MDFN_Error(0, _("Disc %zu of %zu: No valid Q subchannel ADR_CURPOS data present at lba %d-%d?!"), i + 1, cdifs->size(), start_lba, end_lba);
1600    }
1601 
1602    break;
1603   }
1604  }
1605 }
1606 
LoadCD(std::vector<CDInterface * > * CDInterfaces)1607 static MDFN_COLD void LoadCD(std::vector<CDInterface*>* CDInterfaces)
1608 {
1609  try
1610  {
1611   const int ss_cart_setting = MDFN_GetSettingI("ss.cart");
1612   const unsigned region_default = MDFN_GetSettingI("ss.region_default");
1613   unsigned region;
1614   int cart_type;
1615   unsigned cpucache_emumode;
1616   unsigned horrible_hacks;
1617   uint8 fd_id[16];
1618   char sgid[16 + 1] = { 0 };
1619   char sgname[0x70 + 1] = { 0 };
1620   char sgarea[0x10 + 1] = { 0 };
1621   cdifs = CDInterfaces;
1622   CalcGameID(MDFNGameInfo->MD5, fd_id, sgid, sgname, sgarea);
1623 
1624   MDFN_printf("SGID: %s\n", sgid);
1625   MDFN_printf("SGNAME: %s\n", sgname);
1626   MDFN_printf("SGAREA: %s\n", sgarea);
1627 
1628   region = region_default;
1629   cart_type = MDFN_GetSettingI("ss.cart.auto_default");
1630   cpucache_emumode = CPUCACHE_EMUMODE_DATA;
1631 
1632   DetectRegion(&region);
1633   DB_Lookup(nullptr, sgid, sgname, sgarea, fd_id, &region, &cart_type, &cpucache_emumode);
1634   horrible_hacks = DB_LookupHH(sgid, fd_id);
1635   //
1636   if(!MDFN_GetSettingB("ss.region_autodetect"))
1637    region = region_default;
1638 
1639   if(ss_cart_setting != CART__RESERVED)
1640    cart_type = ss_cart_setting;
1641   //
1642   if(MDFN_GetSettingB("ss.cd_sanity"))
1643    DiscSanityChecks();
1644   else
1645    MDFN_printf(_("WARNING: CD (image) sanity checks disabled."));
1646 
1647    // TODO: auth ID calc
1648 
1649   InitCommon(cpucache_emumode, horrible_hacks, cart_type, region, nullptr);
1650  }
1651  catch(...)
1652  {
1653   Cleanup();
1654   throw;
1655  }
1656 }
1657 
CloseGame(void)1658 static MDFN_COLD void CloseGame(void)
1659 {
1660 #ifdef MDFN_ENABLE_DEV_BUILD
1661  VDP1::MakeDump("/tmp/vdp1_dump.h");
1662  VDP2::MakeDump("/tmp/vdp2_dump.h");
1663 #endif
1664  //
1665  //
1666 
1667  try { SaveBackupRAM(); } catch(std::exception& e) { MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what()); }
1668  try { SaveCartNV();    } catch(std::exception& e) { MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what()); }
1669  try { SaveRTC();	} catch(std::exception& e) { MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what()); }
1670 
1671  Cleanup();
1672 }
1673 
SaveBackupRAM(void)1674 static MDFN_COLD void SaveBackupRAM(void)
1675 {
1676  FileStream brs(MDFN_MakeFName(MDFNMKF_SAV, 0, "bkr"), FileStream::MODE_WRITE_INPLACE);
1677 
1678  brs.write(BackupRAM, sizeof(BackupRAM));
1679 
1680  brs.close();
1681 }
1682 
LoadBackupRAM(void)1683 static MDFN_COLD void LoadBackupRAM(void)
1684 {
1685  FileStream brs(MDFN_MakeFName(MDFNMKF_SAV, 0, "bkr"), FileStream::MODE_READ);
1686 
1687  brs.read(BackupRAM, sizeof(BackupRAM));
1688 }
1689 
BackupBackupRAM(void)1690 static MDFN_COLD void BackupBackupRAM(void)
1691 {
1692  MDFN_BackupSavFile(10, "bkr");
1693 }
1694 
BackupCartNV(void)1695 static MDFN_COLD void BackupCartNV(void)
1696 {
1697  const char* ext = nullptr;
1698  void* nv_ptr = nullptr;
1699  bool nv16 = false;
1700  uint64 nv_size = 0;
1701 
1702  CART_GetNVInfo(&ext, &nv_ptr, &nv16, &nv_size);
1703 
1704  if(ext)
1705   MDFN_BackupSavFile(10, ext);
1706 }
1707 
LoadCartNV(void)1708 static MDFN_COLD void LoadCartNV(void)
1709 {
1710  const char* ext = nullptr;
1711  void* nv_ptr = nullptr;
1712  bool nv16 = false;
1713  uint64 nv_size = 0;
1714 
1715  CART_GetNVInfo(&ext, &nv_ptr, &nv16, &nv_size);
1716 
1717  if(ext)
1718  {
1719   //FileStream nvs(MDFN_MakeFName(MDFNMKF_SAV, 0, ext), FileStream::MODE_READ);
1720   GZFileStream nvs(MDFN_MakeFName(MDFNMKF_SAV, 0, ext), GZFileStream::MODE::READ);
1721 
1722   nvs.read(nv_ptr, nv_size);
1723 
1724   if(nv16)
1725   {
1726    for(uint64 i = 0; i < nv_size; i += 2)
1727    {
1728     void* p = (uint8*)nv_ptr + i;
1729 
1730     MDFN_ennsb<uint16>(p, MDFN_de16msb(p));
1731    }
1732   }
1733  }
1734 }
1735 
SaveCartNV(void)1736 static MDFN_COLD void SaveCartNV(void)
1737 {
1738  const char* ext = nullptr;
1739  void* nv_ptr = nullptr;
1740  bool nv16 = false;
1741  uint64 nv_size = 0;
1742 
1743  CART_GetNVInfo(&ext, &nv_ptr, &nv16, &nv_size);
1744 
1745  if(ext)
1746  {
1747   //FileStream nvs(MDFN_MakeFName(MDFNMKF_SAV, 0, ext), FileStream::MODE_WRITE_INPLACE);
1748   GZFileStream nvs(MDFN_MakeFName(MDFNMKF_SAV, 0, ext), GZFileStream::MODE::WRITE);
1749 
1750   if(nv16)
1751   {
1752    // Slow...
1753    for(uint64 i = 0; i < nv_size; i += 2)
1754     nvs.put_BE<uint16>(MDFN_densb<uint16>((uint8*)nv_ptr + i));
1755   }
1756   else
1757    nvs.write(nv_ptr, nv_size);
1758 
1759   nvs.close();
1760  }
1761 }
1762 
SaveRTC(void)1763 static MDFN_COLD void SaveRTC(void)
1764 {
1765  FileStream sds(MDFN_MakeFName(MDFNMKF_SAV, 0, "smpc"), FileStream::MODE_WRITE_INPLACE);
1766 
1767  SMPC_SaveNV(&sds);
1768 
1769  sds.close();
1770 }
1771 
LoadRTC(void)1772 static MDFN_COLD void LoadRTC(void)
1773 {
1774  FileStream sds(MDFN_MakeFName(MDFNMKF_SAV, 0, "smpc"), FileStream::MODE_READ);
1775 
1776  SMPC_LoadNV(&sds);
1777 }
1778 
1779 struct EventsPacker
1780 {
1781  enum : size_t { eventcopy_first = SS_EVENT__SYNFIRST + 1 };
1782  enum : size_t { eventcopy_bound = SS_EVENT__SYNLAST };
1783 
1784  bool Restore(const unsigned state_version);
1785  void Save(void);
1786 
1787  int32 event_times[eventcopy_bound - eventcopy_first];
1788  uint8 event_order[eventcopy_bound - eventcopy_first];
1789 };
1790 
Save(void)1791 INLINE void EventsPacker::Save(void)
1792 {
1793  event_list_entry* evt = events[SS_EVENT__SYNFIRST].next;
1794 
1795  for(size_t i = eventcopy_first; i < eventcopy_bound; i++)
1796  {
1797   event_times[i - eventcopy_first] = events[i].event_time;
1798   event_order[i - eventcopy_first] = evt - events;
1799   assert(event_order[i - eventcopy_first] >= eventcopy_first && event_order[i - eventcopy_first] < eventcopy_bound);
1800   evt = evt->next;
1801  }
1802 }
1803 
Restore(const unsigned state_version)1804 INLINE bool EventsPacker::Restore(const unsigned state_version)
1805 {
1806  bool used[SS_EVENT__COUNT] = { 0 };
1807  event_list_entry* evt = &events[SS_EVENT__SYNFIRST];
1808  for(size_t i = eventcopy_first; i < eventcopy_bound; i++)
1809  {
1810   int32 et = event_times[i - eventcopy_first];
1811   uint8 eo = event_order[i - eventcopy_first];
1812 
1813   if(state_version < 0x00102600 && et >= 0x40000000)
1814   {
1815    et = SS_EVENT_DISABLED_TS;
1816   }
1817 
1818   if(eo < eventcopy_first || eo >= eventcopy_bound)
1819    return false;
1820 
1821   if(used[eo])
1822    return false;
1823 
1824   used[eo] = true;
1825 
1826   if(et < events[SS_EVENT__SYNFIRST].event_time)
1827    return false;
1828 
1829   events[i].event_time = et;
1830 
1831   evt->next = &events[eo];
1832   evt->next->prev = evt;
1833   evt = evt->next;
1834  }
1835  evt->next = &events[SS_EVENT__SYNLAST];
1836  evt->next->prev = evt;
1837 
1838  for(size_t i = 0; i < SS_EVENT__COUNT; i++)
1839  {
1840   if(i == SS_EVENT__SYNLAST)
1841   {
1842    if(events[i].next != NULL)
1843     return false;
1844   }
1845   else
1846   {
1847    if(events[i].next->prev != &events[i])
1848     return false;
1849 
1850    if(events[i].next->event_time < events[i].event_time)
1851     return false;
1852   }
1853 
1854   if(i == SS_EVENT__SYNFIRST)
1855   {
1856    if(events[i].prev != NULL)
1857     return false;
1858   }
1859   else
1860   {
1861    if(events[i].prev->next != &events[i])
1862     return false;
1863 
1864    if(events[i].prev->event_time > events[i].event_time)
1865     return false;
1866   }
1867  }
1868 
1869  return true;
1870 }
1871 
StateAction(StateMem * sm,const unsigned load,const bool data_only)1872 static MDFN_COLD void StateAction(StateMem* sm, const unsigned load, const bool data_only)
1873 {
1874  if(!data_only)
1875  {
1876   sha256_digest sr_dig = BIOS_SHA256;
1877   int cart_type = ActiveCartType;
1878 
1879   if(DBG_InSlaveStep())
1880    throw MDFN_Error(0, _("Slave step mode is incompatible with save states."));
1881 
1882   SFORMAT SRDStateRegs[] =
1883   {
1884    SFPTR8(sr_dig.data(), sr_dig.size()),
1885    SFVAR(cart_type),
1886    SFEND
1887   };
1888 
1889   MDFNSS_StateAction(sm, load, data_only, SRDStateRegs, "BIOS_HASH");
1890 
1891   if(load)
1892   {
1893    if(sr_dig != BIOS_SHA256)
1894     throw MDFN_Error(0, _("BIOS hash mismatch(save state created under a different BIOS)!"));
1895 /*
1896    if(load < 0x00102300)
1897    {
1898     SFORMAT DummyStateRegs[] = { SFEND };
1899 
1900     if(MDFNSS_StateAction(sm, load, data_only, DummyStateRegs, "CART_BACKUP", true))
1901      cart_type = CART_BACKUP_MEM;
1902    }
1903 */
1904    if(cart_type != ActiveCartType)
1905     throw MDFN_Error(0, _("Cart type mismatch(save state created with a different cart)!"));
1906   }
1907  }
1908  //
1909  //
1910  //
1911  bool RecordedNeedEmuICache = load ? false : NeedEmuICache;
1912  EventsPacker ep;
1913  ep.Save();
1914 
1915  SFORMAT StateRegs[] =
1916  {
1917   // cur_clock_div
1918   SFVAR(UpdateInputLastBigTS),
1919 
1920   SFVAR(next_event_ts),
1921   SFVARN(ep.event_times, "event_times"),
1922   SFVARN(ep.event_order, "event_order"),
1923 
1924   SFVAR(SH7095_mem_timestamp),
1925   SFVAR(SH7095_BusLock),
1926   SFVAR(SH7095_DB),
1927 
1928   SFVAR(WorkRAML),
1929   SFVAR(WorkRAMH),
1930   SFVAR(BackupRAM),
1931 
1932   SFVAR(RecordedNeedEmuICache),
1933 
1934   SFEND
1935  };
1936 
1937  CPU[0].StateAction(sm, load, data_only, "SH2-M");
1938  CPU[1].StateAction(sm, load, data_only, "SH2-S");
1939  SCU_StateAction(sm, load, data_only);
1940  SMPC_StateAction(sm, load, data_only);
1941 
1942  CDB_StateAction(sm, load, data_only);
1943  VDP1::StateAction(sm, load, data_only);
1944  VDP2::StateAction(sm, load, data_only);
1945 
1946  SOUND_StateAction(sm, load, data_only);
1947  CART_StateAction(sm, load, data_only);
1948  //
1949  MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN");
1950 
1951  if(load)
1952  {
1953   BackupRAM_Dirty = true;
1954 
1955   if(!ep.Restore(load))
1956   {
1957    printf("Bad state events data.");
1958    InitEvents();
1959   }
1960 
1961   CPU[0].PostStateLoad(load, RecordedNeedEmuICache, NeedEmuICache);
1962   CPU[1].PostStateLoad(load, RecordedNeedEmuICache, NeedEmuICache);
1963  }
1964 }
1965 
SetMedia(uint32 drive_idx,uint32 state_idx,uint32 media_idx,uint32 orientation_idx)1966 static MDFN_COLD void SetMedia(uint32 drive_idx, uint32 state_idx, uint32 media_idx, uint32 orientation_idx)
1967 {
1968  const RMD_Layout* rmd = EmulatedSS.RMD;
1969  const RMD_Drive* rd = &rmd->Drives[drive_idx];
1970  const RMD_State* rs = &rd->PossibleStates[state_idx];
1971 
1972  //printf("%d %d %d\n", rs->MediaPresent, rs->MediaUsable, rs->MediaCanChange);
1973 
1974  if(rs->MediaPresent && rs->MediaUsable)
1975   CDB_SetDisc(false, (*cdifs)[media_idx]);
1976  else
1977   CDB_SetDisc(rs->MediaCanChange, NULL);
1978 }
1979 
DoSimpleCommand(int cmd)1980 static void DoSimpleCommand(int cmd)
1981 {
1982  switch(cmd)
1983  {
1984   case MDFN_MSC_POWER:
1985 	if(DBG_InSlaveStep())
1986 	 MDFN_Notify(MDFN_NOTICE_ERROR, _("Slave step mode is incompatible with hard resets."));
1987 	else
1988 	 SS_Reset(true);
1989 	break;
1990   // MDFN_MSC_RESET is not handled here; special reset button handling in smpc.cpp.
1991  }
1992 }
1993 
1994 static const FileExtensionSpecStruct KnownExtensions[] =
1995 {
1996  { ".ss", 0, gettext_noop("Sega Saturn Debug Cart ROM") },
1997 
1998  { NULL, 0, NULL }
1999 };
2000 
2001 static const MDFNSetting_EnumList Region_List[] =
2002 {
2003  { "jp", SMPC_AREA_JP, gettext_noop("Japan") },
2004  { "na", SMPC_AREA_NA, gettext_noop("North America") },
2005  { "eu", SMPC_AREA_EU_PAL, gettext_noop("Europe") },
2006  { "kr", SMPC_AREA_KR, gettext_noop("South Korea") },
2007 
2008  { "tw", SMPC_AREA_ASIA_NTSC, gettext_noop("Taiwan") },	// Taiwan, Philippines
2009  { "as", SMPC_AREA_ASIA_PAL, gettext_noop("China") },	// China, Middle East
2010 
2011  { "br", SMPC_AREA_CSA_NTSC, gettext_noop("Brazil") },
2012  { "la", SMPC_AREA_CSA_PAL, gettext_noop("Latin America") },
2013 
2014  { NULL, 0 },
2015 };
2016 
2017 static const MDFNSetting_EnumList RTCLang_List[] =
2018 {
2019  { "english", SMPC_RTC_LANG_ENGLISH, gettext_noop("English") },
2020  { "german", SMPC_RTC_LANG_GERMAN, gettext_noop("Deutsch") },
2021  { "french", SMPC_RTC_LANG_FRENCH, gettext_noop("Français") },
2022  { "spanish", SMPC_RTC_LANG_SPANISH, gettext_noop("Español") },
2023  { "italian", SMPC_RTC_LANG_ITALIAN, gettext_noop("Italiano") },
2024  { "japanese", SMPC_RTC_LANG_JAPANESE, gettext_noop("日本語") },
2025 
2026  { "deutsch", SMPC_RTC_LANG_GERMAN, NULL },
2027  { "français", SMPC_RTC_LANG_FRENCH, NULL },
2028  { "español", SMPC_RTC_LANG_SPANISH, NULL },
2029  { "italiano", SMPC_RTC_LANG_ITALIAN, NULL },
2030  { "日本語", SMPC_RTC_LANG_JAPANESE, NULL},
2031 
2032  { NULL, 0 },
2033 };
2034 
2035 #define CART_LIST_BASE											\
2036  { "none", CART_NONE, gettext_noop("None") },								\
2037  { "backup", CART_BACKUP_MEM, gettext_noop("Backup Memory(512KiB)") },					\
2038  { "extram1", CART_EXTRAM_1M, gettext_noop("1MiB Extended RAM") },					\
2039  { "extram4", CART_EXTRAM_4M, gettext_noop("4MiB Extended RAM") },					\
2040  { "cs1ram16", CART_CS1RAM_16M, gettext_noop("16MiB RAM mapped in A-bus CS1") },			\
2041  { "ar4mp", CART_AR4MP, NULL }, /* Undocumented, unfinished. gettext_noop("Action Replay 4M Plus") },*/	\
2042  /* { "nlmodem", CART_NLMODEM, gettext_noop("NetLink Modem") }, */
2043 
2044 static const MDFNSetting_EnumList Cart_List[] =
2045 {
2046  { "auto", CART__RESERVED, gettext_noop("Automatic") },
2047 
2048  CART_LIST_BASE
2049 
2050  { NULL, 0 },
2051 };
2052 
2053 static const MDFNSetting_EnumList CartAD_List[] =
2054 {
2055  CART_LIST_BASE
2056 
2057  { NULL, 0 },
2058 };
2059 
2060 #ifdef MDFN_ENABLE_DEV_BUILD
2061 static const MDFNSetting_EnumList DBGMask_List[] =
2062 {
2063  { "0",		0								},
2064  { "none",	0,			gettext_noop("None")			},
2065 
2066  { "all",	~0,			gettext_noop("All")			},
2067 
2068  { "warning",	SS_DBG_WARNING,		gettext_noop("Warnings")		},
2069 
2070  { "m68k",	SS_DBG_M68K,		gettext_noop("M68K") 			},
2071 
2072  { "sh2",	SS_DBG_SH2,		gettext_noop("SH-2") 			},
2073  { "sh2_regw",	SS_DBG_SH2_REGW,	gettext_noop("SH-2 (peripherals) register writes") },
2074  { "sh2_cache",	SS_DBG_SH2_CACHE,	gettext_noop("SH-2 cache")		},
2075  { "sh2_cache_noisy",SS_DBG_SH2_CACHE_NOISY,	gettext_noop("SH-2 cache, with assoc purge/addr/data array logging")		},
2076  { "sh2_except",SS_DBG_SH2_EXCEPT,	gettext_noop("SH-2 exceptions and interrupts") },
2077  { "sh2_dmarace",SS_DBG_SH2_DMARACE,	gettext_noop("SH-2 DMA/CPU RW races")	},
2078 
2079  { "scu",	SS_DBG_SCU,		gettext_noop("SCU") 			},
2080  { "scu_regw",	SS_DBG_SCU_REGW,	gettext_noop("SCU register writes") 	},
2081  { "scu_int",	SS_DBG_SCU_INT,		gettext_noop("SCU interrupt") 		},
2082  { "scu_dsp",	SS_DBG_SCU_DSP,		gettext_noop("SCU DSP")			},
2083 
2084  { "smpc",	SS_DBG_SMPC,		gettext_noop("SMPC")			},
2085  { "smpc_regw",	SS_DBG_SMPC_REGW,	gettext_noop("SMPC register writes")	},
2086 
2087  { "cdb",	SS_DBG_CDB,		gettext_noop("CDB")			},
2088  { "cdb_regw",	SS_DBG_CDB_REGW,	gettext_noop("CDB register writes")	},
2089 
2090  { "vdp1",	SS_DBG_VDP1,		gettext_noop("VDP1") 			},
2091  { "vdp1_regw", SS_DBG_VDP1_REGW,	gettext_noop("VDP1 register writes")	},
2092  { "vdp1_vramw",SS_DBG_VDP1_VRAMW,	gettext_noop("VDP1 VRAM writes")	},
2093  { "vdp1_fbw",	SS_DBG_VDP1_FBW,	gettext_noop("VDP1 FB writes")		},
2094  { "vdp1_race", SS_DBG_VDP1_RACE,	gettext_noop("VDP1 draw/VRAM write races") },
2095 
2096  { "vdp2",	SS_DBG_VDP2,		gettext_noop("VDP2")			},
2097  { "vdp2_regw", SS_DBG_VDP2_REGW,	gettext_noop("VDP2 register writes")	},
2098 
2099  { "scsp",	SS_DBG_SCSP,		gettext_noop("SCSP")			},
2100  { "scsp_regw", SS_DBG_SCSP_REGW,	gettext_noop("SCSP register writes")	},
2101 
2102  { "bios",	SS_DBG_BIOS,		gettext_noop("BIOS")			},
2103 
2104  { NULL, 0 },
2105 };
2106 #endif
2107 
2108 static const MDFNSetting_EnumList CEM_List[] =
2109 {
2110  { "data_cb",	CPUCACHE_EMUMODE_DATA_CB,	gettext_noop("Data only, with high-level bypass") },
2111  { "data",	CPUCACHE_EMUMODE_DATA, 		gettext_noop("Data only") },
2112  { "full",	CPUCACHE_EMUMODE_FULL,		gettext_noop("Full") },
2113 
2114  { NULL, 0 },
2115 };
2116 
2117 static const MDFNSetting_EnumList HH_List[] =
2118 {
2119  { "0",			0								},
2120  { "none",		0,				gettext_noop("None")		},
2121 
2122  { "nosh2dmaline106",	HORRIBLEHACK_NOSH2DMALINE106,	gettext_noop("nosh2dmaline106") },
2123  { "nosh2dmapenalty",	HORRIBLEHACK_NOSH2DMAPENALTY,	gettext_noop("nosh2dmapenalty")	},
2124  { "vdp1vram5000fix",	HORRIBLEHACK_VDP1VRAM5000FIX,	gettext_noop("vdp1vram5000fix")	},
2125  { "vdp1rwdrawslowdown",HORRIBLEHACK_VDP1RWDRAWSLOWDOWN,gettext_noop("vdp1rwdrawslowdown") },
2126  { "vdp1instant",	HORRIBLEHACK_VDP1INSTANT,	gettext_noop("vdp1instant") },
2127 
2128  { NULL, 0 },
2129 };
2130 
2131 static const MDFNSetting SSSettings[] =
2132 {
2133  { "ss.bios_jp", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the Japan ROM BIOS"), NULL, MDFNST_STRING, "sega_101.bin" },
2134  { "ss.bios_na_eu", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the North America and Europe ROM BIOS"), NULL, MDFNST_STRING, "mpr-17933.bin" },
2135 
2136  { "ss.scsp.resamp_quality", MDFNSF_NOFLAGS, gettext_noop("SCSP output resampler quality."),
2137 	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, "4", "0", "10" },
2138 
2139  { "ss.region_autodetect", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Attempt to auto-detect region of game."), NULL, MDFNST_BOOL, "1" },
2140  { "ss.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 },
2141 
2142  { "ss.input.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Emulated mouse sensitivity."), NULL, MDFNST_FLOAT, "0.50", NULL, NULL },
2143  { "ss.input.sport1.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on Saturn port 1."), NULL, MDFNST_BOOL, "0", NULL, NULL },
2144  { "ss.input.sport2.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap on Saturn port 2."), NULL, MDFNST_BOOL, "0", NULL, NULL },
2145 
2146  { "ss.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" },
2147  { "ss.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" },
2148  { "ss.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" },
2149  { "ss.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" },
2150  { "ss.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" },
2151  { "ss.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" },
2152  { "ss.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" },
2153  { "ss.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" },
2154  { "ss.input.port9.gun_chairs",  MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 9."),  gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFF80FF", "0x000000", "0x1000000" },
2155  { "ss.input.port10.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 10."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x00FF80", "0x000000", "0x1000000" },
2156  { "ss.input.port11.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 11."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0x8080FF", "0x000000", "0x1000000" },
2157  { "ss.input.port12.gun_chairs", MDFNSF_NOFLAGS, gettext_noop("Crosshairs color for lightgun on virtual port 12."), gettext_noop("A value of 0x1000000 disables crosshair drawing."), MDFNST_UINT, "0xFF8080", "0x000000", "0x1000000" },
2158 
2159  { "ss.smpc.autortc", MDFNSF_NOFLAGS, gettext_noop("Automatically set RTC on game load."), gettext_noop("Automatically set the SMPC's emulated Real-Time Clock to the host system's current time and date upon game load."), MDFNST_BOOL, "1" },
2160  { "ss.smpc.autortc.lang", MDFNSF_NOFLAGS, gettext_noop("BIOS language."), gettext_noop("Also affects language used in some games(e.g. the European release of \"Panzer Dragoon\")."), MDFNST_ENUM, "english", NULL, NULL, NULL, NULL, RTCLang_List },
2161 
2162  { "ss.cart", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Expansion cart."), NULL, MDFNST_ENUM, "auto", NULL, NULL, NULL, NULL, Cart_List },
2163  { "ss.cart.auto_default", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Default expansion cart when autodetection fails."), gettext_noop("Expansion cart to emulate when \"ss.cart\" is set to \"auto\", but the game wasn't found in the internal database for carts."), MDFNST_ENUM, "backup", NULL, NULL, NULL, NULL, CartAD_List },
2164 
2165  { "ss.cart.kof95_path", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to KoF 95 ROM image."), NULL, MDFNST_STRING, "mpr-18811-mx.ic1" },
2166  { "ss.cart.ultraman_path", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to Ultraman ROM image."), NULL, MDFNST_STRING, "mpr-19367-mx.ic1" },
2167  { "ss.cart.satar4mp_path", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH | MDFNSF_SUPPRESS_DOC | MDFNSF_NONPERSISTENT, gettext_noop("Path to Action Replay 4M Plus firmware image."), NULL, MDFNST_STRING, "satar4mp.bin" },
2168 // { "ss.cart.modem_port", MDFNSF_NOFLAGS, gettext_noop("TCP/IP port to use for modem emulation."), gettext_noop("A value of \"0\" disables network access."), MDFNST_UINT, "4920", "0", "65535" },
2169 
2170  { "ss.bios_sanity", MDFNSF_NOFLAGS, gettext_noop("Enable BIOS ROM image sanity checks."), NULL, MDFNST_BOOL, "1" },
2171 
2172  { "ss.cd_sanity", MDFNSF_NOFLAGS, gettext_noop("Enable CD (image) sanity checks."), NULL, MDFNST_BOOL, "1" },
2173 
2174  { "ss.slstart", MDFNSF_NOFLAGS, gettext_noop("First displayed scanline in NTSC mode."), NULL, MDFNST_INT, "0", "0", "239" },
2175  { "ss.slend", MDFNSF_NOFLAGS, gettext_noop("Last displayed scanline in NTSC mode."), NULL, MDFNST_INT, "239", "0", "239" },
2176 
2177  { "ss.h_overscan", MDFNSF_NOFLAGS, gettext_noop("Show horizontal overscan area."), NULL, MDFNST_BOOL, "1" },
2178 
2179  { "ss.h_blend", MDFNSF_NOFLAGS, gettext_noop("Enable horizontal blend(blur) filter."), gettext_noop("Intended for use in combination with the \"goat\" OpenGL shader, or with bilinear interpolation or linear interpolation on the X axis enabled.  Has a more noticeable effect with the Saturn's higher horizontal resolution modes(640/704)."), MDFNST_BOOL, "0" },
2180 
2181  { "ss.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" },
2182 
2183  { "ss.slstartp", MDFNSF_NOFLAGS, gettext_noop("First displayed scanline in PAL mode."), NULL, MDFNST_INT, "0", "-16", "271" },
2184  { "ss.slendp", MDFNSF_NOFLAGS, gettext_noop("Last displayed scanline in PAL mode."), NULL, MDFNST_INT, "255", "-16", "271" },
2185 
2186  { "ss.affinity.vdp2", MDFNSF_NOFLAGS, gettext_noop("VDP2 rendering thread CPU affinity mask."), gettext_noop("Set to 0 to disable changing affinity."), MDFNST_UINT, "0", "0x0000000000000000", "0xFFFFFFFFFFFFFFFF" },
2187 
2188 #ifdef MDFN_ENABLE_DEV_BUILD
2189  { "ss.dbg_mask", MDFNSF_SUPPRESS_DOC, gettext_noop("Debug printf mask."), NULL, MDFNST_MULTI_ENUM, "none", NULL, NULL, NULL, NULL, DBGMask_List },
2190 #endif
2191 
2192  { "ss.dbg_exe_cdpath", MDFNSF_SUPPRESS_DOC | MDFNSF_CAT_PATH, gettext_noop("CD image to use with bootable cart ROM image loading."), NULL, MDFNST_STRING, "" },
2193  { "ss.dbg_exe_cem", MDFNSF_SUPPRESS_DOC | MDFNSF_NONPERSISTENT, gettext_noop("Cache emulation mode to use with bootable cart ROM image loading."), NULL, MDFNST_ENUM, "data", NULL, NULL, NULL, NULL, CEM_List },
2194  { "ss.dbg_exe_hh", MDFNSF_SUPPRESS_DOC | MDFNSF_NONPERSISTENT, gettext_noop("Horrible hacks to use with bootable cart ROM image loading."), NULL, MDFNST_MULTI_ENUM, "none", NULL, NULL, NULL, NULL, HH_List },
2195 
2196  { NULL },
2197 };
2198 
2199 static const CheatInfoStruct CheatInfo =
2200 {
2201  NULL,
2202  NULL,
2203 
2204  CheatMemRead,
2205  CheatMemWrite,
2206 
2207  CheatFormatInfo_Empty,
2208 
2209  true
2210 };
2211 
2212 }
2213 
2214 using namespace MDFN_IEN_SS;
2215 
2216 MDFNGI EmulatedSS =
2217 {
2218  "ss",
2219  "Sega Saturn",
2220  KnownExtensions,
2221  MODPRIO_INTERNAL_HIGH,
2222  #ifdef WANT_DEBUGGER
2223  &DBGInfo,
2224  #else
2225  NULL,
2226  #endif
2227  SMPC_PortInfo,
2228  DB_GetInternalDB,
2229  Load,
2230  TestMagic,
2231  LoadCD,
2232  TestMagicCD,
2233  CloseGame,
2234 
2235  VDP2::SetLayerEnableMask,
2236  "NBG0\0NBG1\0NBG2\0NBG3\0RBG0\0RBG1\0Sprite\0",
2237 
2238  NULL,
2239  NULL,
2240 
2241  NULL,
2242  0,
2243 
2244  CheatInfo,
2245 
2246  false,
2247  StateAction,
2248  Emulate,
2249  SMPC_TransformInput,
2250  SMPC_SetInput,
2251  SetMedia,
2252  DoSimpleCommand,
2253  NULL,
2254  SSSettings,
2255  0,
2256  0,
2257 
2258  true, // Multires possible?
2259 
2260  //
2261  // Note: Following video settings will be overwritten during game load.
2262  //
2263  320,	// lcm_width
2264  240,	// lcm_height
2265  NULL,  // Dummy
2266 
2267  302,   // Nominal width
2268  240,   // Nominal height
2269 
2270  0,   // Framebuffer width
2271  0,   // Framebuffer height
2272  //
2273  //
2274  //
2275 
2276  2,     // Number of output sound channels
2277 };
2278 
2279