1 /*
2     Copyright (C) 1999, 2000, 2001, 2002, 2003  Charles MacDonald
3 
4     This program is free software; you can redistribute it and/or modify
5     it under the terms of the GNU General Public License as published by
6     the Free Software Foundation; either version 2 of the License, or
7     (at your option) any later version.
8 
9     This program is distributed in the hope that it will be useful,
10     but WITHOUT ANY WARRANTY; without even the implied warranty of
11     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12     GNU General Public License for more details.
13 
14     You should have received a copy of the GNU General Public License
15     along with this program; if not, write to the Free Software
16     Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
17 */
18 
19 #include "shared.h"
20 #include "cart/cart.h"
21 #include "cd/cd.h"
22 #include <mednafen/hash/md5.h>
23 #include <mednafen/general.h>
24 #include <mednafen/mempatcher.h>
25 
26 #include <trio/trio.h>
27 
28 namespace MDFN_IEN_MD
29 {
30 
31 enum
32 {
33  REGION_SAME = 1,
34  REGION_GAME,
35  REGION_OVERSEAS_NTSC,
36  REGION_OVERSEAS_PAL,
37  REGION_DOMESTIC_NTSC,
38  REGION_DOMESTIC_PAL
39 };
40 
41 
42 static const MDFNSetting_EnumList MultiTap_List[] =
43 {
44  { "none", MTAP_NONE, gettext_noop("No multitap(s).") },
45 
46  { "tp1", MTAP_TP_PRT1, gettext_noop("Team Player/Sega Tap on MD/Genesis port 1.") },
47  { "tp2", MTAP_TP_PRT2, gettext_noop("Team Player/Sega Tap on MD/Genesis port 2.") },
48  { "tpd", MTAP_TP_DUAL, gettext_noop("Team Player/Sega Tap on both MD/Genesis ports.") },
49 
50  { "4way", MTAP_4WAY, gettext_noop("EA 4-Way Play") },
51 
52  { NULL, 0 },
53 };
54 
55 
56 int MD_HackyHackyMode = 0;
57 bool MD_IsCD;
58 static int32 z80_cycle_counter;
59 static int32 z80_last_ts;
60 int32 md_timestamp;
61 static bool suspend68k = false;
62 
63 static bool run_cpu;
64 
MD_Suspend68K(bool state)65 void MD_Suspend68K(bool state)
66 {
67  suspend68k = state;
68  Main68K.SetExtHalted(state);
69 }
70 
MD_Is68KSuspended(void)71 bool MD_Is68KSuspended(void)
72 {
73  return(suspend68k);
74 }
75 
system_init(bool overseas,bool PAL,bool overseas_reported,bool PAL_reported)76 static void system_init(bool overseas, bool PAL, bool overseas_reported, bool PAL_reported)
77 {
78     gen_running = 1;
79 
80     z80_init();
81     z80_readbyte = MD_Z80_ReadByte;
82     z80_writebyte = MD_Z80_WriteByte;
83     z80_readport = MD_Z80_ReadPort;
84     z80_writeport = MD_Z80_WritePort;
85 
86     gen_init();
87     MDIO_Init(overseas, PAL, overseas_reported, PAL_reported);
88     MainVDP.SetSettings(PAL, PAL_reported, MDFN_GetSettingB("md.correct_aspect"));
89 
90 #ifdef WANT_DEBUGGER
91     MDDBG_Init();
92 #endif
93 
94 }
95 
system_reset(bool poweron)96 static void system_reset(bool poweron)
97 {
98  obsim = 0;
99  z80_cycle_counter = 0;
100  z80_last_ts = 0;
101 
102  if(MD_IsCD)
103   MDCD_Reset(poweron);
104  else
105   MDCart_Reset();
106 
107  gen_reset(poweron);
108  if(poweron)
109   MainVDP.Reset();
110  MDSound_Power();
111 }
112 
MD_ExitCPULoop(void)113 void MD_ExitCPULoop(void)
114 {
115  run_cpu = false;
116 }
117 
MD_UpdateSubStuff(void)118 void MD_UpdateSubStuff(void)
119 {
120  int32 max_md_timestamp;
121 
122  max_md_timestamp = Main68K.timestamp * 7;
123 
124  if(zreset == 1 && zbusreq == 0)
125  {
126   z80_cycle_counter += max_md_timestamp - z80_last_ts;
127 
128   while(z80_cycle_counter > 0)
129   {
130    int32 z80_temp = z80_do_opcode() * 15;
131 
132    z80_cycle_counter -= z80_temp;
133    md_timestamp += z80_temp;
134 
135    if(md_timestamp > max_md_timestamp)
136    {
137     //printf("Meow: %d\n", md_timestamp - max_md_timestamp);
138     md_timestamp = max_md_timestamp;
139    }
140    MainVDP.Run();
141   }
142  }
143  z80_last_ts = max_md_timestamp;
144 
145  md_timestamp = max_md_timestamp;
146  MainVDP.Run();
147 
148  //if(MD_IsCD)
149  // MDCD_Run(master_cycles);
150 }
151 
MD_DBG(unsigned level,const char * format,...)152 void MD_DBG(unsigned level, const char *format, ...) throw()
153 {
154 #if 0
155  //if(md_dbg_level >= level)
156  {
157   va_list ap;
158 
159   va_start(ap, format);
160 
161   trio_vprintf(format, ap);
162 
163   va_end(ap);
164  }
165 #endif
166 }
167 
168 
system_frame(int do_skip)169 static int system_frame(int do_skip)
170 {
171  run_cpu = true;
172 
173  while(run_cpu > 0)
174  {
175   #ifdef WANT_DEBUGGER
176   if(MDFN_UNLIKELY(MD_DebugMode) && MDFN_UNLIKELY(!suspend68k))
177    MDDBG_CPUHook();
178   #endif
179 
180   Main68K.Step();
181   MD_UpdateSubStuff();
182  }
183  return gen_running;
184 }
185 
Emulate(EmulateSpecStruct * espec)186 static void Emulate(EmulateSpecStruct *espec)
187 {
188  //printf("%016llx %016llx %016llx\n", z80_tstates, last_z80_tstates, z80.interrupts_enabled_at);
189 
190  MDFNMP_ApplyPeriodicCheats();
191 
192  MDIO_BeginTimePeriod(md_timestamp);
193 
194  MDINPUT_Frame();
195 
196  if(espec->VideoFormatChanged)
197   MainVDP.SetPixelFormat(espec->surface->format); //.Rshift, espec->surface->format.Gshift, espec->surface->format.Bshift);
198 
199  if(espec->SoundFormatChanged)
200   MDSound_SetSoundRate(espec->SoundRate);
201 
202  MainVDP.SetSurface(espec);	//espec->surface, &espec->DisplayRect);
203 
204  system_frame(0);
205 
206  espec->MasterCycles = md_timestamp;
207 
208  espec->SoundBufSize = MDSound_Flush(espec->SoundBuf, espec->SoundBufMaxSize);
209 
210 #if 0
211  {
212   static double avg = 0;
213   static double s_avg = 0;
214 
215   avg += (espec->MasterCycles - avg) * 0.05;
216   s_avg += (espec->SoundBufSize - s_avg) * 0.05;
217   printf("%f, %f\n", avg / 262 / 10, 48000 / s_avg);
218  }
219 #endif
220 
221  MDIO_EndTimePeriod(md_timestamp);
222 
223  md_timestamp = 0;
224  z80_last_ts = 0;
225  Main68K.timestamp = 0;
226  MainVDP.ResetTS();
227 
228  //MainVDP.SetSurface(NULL);
229 }
230 
Cleanup(void)231 static void Cleanup(void)
232 {
233  MDCart_Kill();
234  MDIO_Kill();
235 
236  MDSound_Kill();
237 }
238 
CloseGame(void)239 static void CloseGame(void)
240 {
241  try
242  {
243   MDCart_SaveNV();
244  }
245  catch(std::exception &e)
246  {
247   MDFND_OutputNotice(MDFN_NOTICE_ERROR, e.what());
248  }
249 
250  Cleanup();
251 }
252 
253 static const struct
254 {
255  const char* prod_code;
256  uint32 crc32;
257  bool compat_6button;
258  unsigned tap;
259  unsigned max_players;
260 }
261 InputDB[] =
262 {
263  { NULL, 0x2c6cbd77, 		true, MTAP_TP_PRT1, 4 }, // Aq Renkan Awa (China) (Unl)
264  { "MK-1234 -00", 0x8c822884, 	true, MTAP_TP_PRT2, 4 }, // ATP Tour Championship Tennis (USA)
265  { "MK-1234 -50", 0x1a3da8c5, 	true, MTAP_TP_PRT2, 4 }, // ATP Tour (Europe)
266  { "T-172106-01", 0xac5bc26a,	true, MTAP_4WAY,    4 }, // Australian Rugby League (Europe)
267  { "T-119066-01", 0xde27357b, 	true, MTAP_TP_PRT1, 4 }, // Barkley Shut Up and Jam 2 (USA) (Beta)
268  { "T-119186-00", 0x321bb6bd, 	true, MTAP_TP_PRT1, 4 }, // Barkley Shut Up and Jam 2 (USA)
269  { "T-119066-00", 0x63fbf497, 	true, MTAP_TP_PRT1, 4 }, // Barkley Shut Up and Jam! (USA, Europe)
270  { "T-50826 -00", 0xa582f45a, 	true, MTAP_4WAY,    4 }, // Bill Walsh College Football 95 (USA)
271  { "T-50606 -00", 0x3ed83362, 	true, MTAP_4WAY,    4 }, // Bill Walsh College Football (USA, Europe)
272  { "T-172016-00", 0x67c309c6,	true, MTAP_4WAY,    4 }, // Coach K College Basketball (USA)
273  { "T-172046-00", 0xb9075385,	true, MTAP_4WAY,    4 }, // College Football USA 96 (USA)
274  { "T-172126-01", 0x2ebb90a3,	true, MTAP_4WAY,    4 }, // College Football USA 97 (USA)
275  { "MK-1241 -00", 0x65b64413, 	true, MTAP_TP_PRT1, 4 }, // College Football's National Championship II (USA)
276  { "MK-1227 -00", 0x172c5dbb, 	true, MTAP_TP_PRT1, 4 }, // College Football's National Championship (USA)
277  { "T-81576 -00", 0x96a42431, 	true, MTAP_TP_PRT1, 4 }, // College Slam (USA)
278  { "T-23056 -00", 0xdc678f6d, 	true, MTAP_TP_PRT2, 5 }, // Columns III - Revenge of Columns (USA)
279  { "00004801-00", 0xcd07462f, 	true, MTAP_TP_PRT2, 5 }, // Columns III - Taiketsu! Columns World (Japan, Korea)
280  { "T-70276-00", 0x4608f53a,	true, MTAP_TP_PRT2, 4 }, // Dino Dini's Soccer (Europe)
281  /* Can support dual multitap, *BUT* Some game modes are incompatible with multitap
282  { "T-95126-00", 0x8352b1d0, 	true, * }, // Double Dribble - The Playoff Edition (USA)
283  */
284  { "T-70286 -00", 0xfdeed51d, 	true, MTAP_TP_PRT1, 3 }, // Dragon - The Bruce Lee Story (Europe)
285  { "T-81496 -00", 0xefe850e5, 	true, MTAP_TP_PRT1, 3 }, // Dragon - The Bruce Lee Story (USA)
286  { "T-50976 -00", 0xe10a25c0,	true, MTAP_4WAY,    4 }, // Elitserien 95 (Sweden)
287  { "T-172096-00", 0x9821d0a3,	true, MTAP_4WAY,    4 }, // Elitserien 96 (Sweden)
288  { NULL, 0xa427814a, 		true, MTAP_TP_PRT1, 4 }, // ESPN National Hockey Night (USA) (Beta)
289  { "T-93176 -00", 0x1d08828c, 	true, MTAP_TP_PRT1, 4 }, // ESPN National Hockey Night (USA)
290  { "T-79196-50", 0xfac29677, 	true, MTAP_TP_DUAL, 8 }, // Fever Pitch Soccer (Europe) (En,Fr,De,Es,It)
291  { "T-172206-00", 0x96947f57,	true, MTAP_4WAY,    4 }, // FIFA 98 - Road to World Cup (Europe) (En,Fr,Es,It,Sv)
292  { "T-50706 -00", 0xbddbb763,	true, MTAP_4WAY,    4 }, // FIFA International Soccer (USA, Europe) (En,Fr,De,Es)
293  { "T-50916 -01", 0x012591f9,	true, MTAP_4WAY,    4 }, // FIFA Soccer 95 (Korea) (En,Fr,De,Es)
294  { "T-50916 -00", 0xb389d036,	true, MTAP_4WAY,    4 }, // FIFA Soccer 95 (USA, Europe) (En,Fr,De,Es)
295  { "T-172086-00", 0xbad30ffa,	true, MTAP_4WAY,    4 }, // FIFA Soccer 96 (USA, Europe) (En,Fr,De,Es,It,Sv)
296  { "T-172156-01", 0xa33d5803,	true, MTAP_4WAY,    4 }, // FIFA Soccer 97 (USA, Europe) (En,Fr,De,Es,It,Sv)
297  // Maybe port2? { "T-81476 -00", 0x863e0950, true, MTAP_TP_PRT1, 4 }, // Frank Thomas Big Hurt Baseball (USA, Europe)
298  // Maybe port2? { "133037   -00", 0xcdf5678f, true, MTAP_TP_PRT1, 4 }, // From TV Animation Slam Dunk - Kyougou Makkou Taiketsu! (Japan)
299  { "T-48216 -00", 0x3bf46dce, 	true, MTAP_TP_PRT1, 4 }, // Gauntlet IV (USA, Europe) (En,Ja) (August 1993)
300  { "T-48123 -00", 0xf9d60510, 	true, MTAP_TP_PRT1, 4 }, // Gauntlet IV (USA, Europe) (En,Ja) (September 1993)
301  { "T-48123 -00", 0xf9872055, 	true, MTAP_TP_PRT1, 4 }, // Gauntlet (Japan) (En,Ja)
302  { "T-106253-00", 0x05cc7369,	true, MTAP_4WAY,    4 }, // General Chaos (Japan)
303  { "T-50626 -00", 0xf1ecc4df,	true, MTAP_4WAY,    4 }, // General Chaos (USA, Europe)
304  { "T-79196", 0xdcffa327, 	true, MTAP_TP_DUAL, 8 }, // Head-On Soccer (USA)
305  /* Can support dual multitap, *BUT* Some game modes are incompatible with multitap
306  { "T-95126-00", 0xf27c576a, 	true, * }, // Hyper Dunk (Europe)
307  { NULL, 0xdb124bbb, 		true, * }, // Hyper Dunk - The Playoff Edition (Japan) (Beta)
308  { "T-95083-00", 0x5baf53d7, 	true, * }, // Hyper Dunk - The Playoff Edition (Japan)
309  */
310  { "T-50836 -00", 0xe04ffc2b,	true, MTAP_4WAY,    4 }, // IMG International Tour Tennis (USA, Europe)
311  { "T-95196-50", 0x9bb3b180, 	true, MTAP_TP_DUAL, 8 }, // International Superstar Soccer Deluxe (Europe)
312  { "G-5540  00", 0x9fe71002, 	true, MTAP_TP_PRT2, 4 }, // J. League Pro Striker 2 (Japan)
313  { "G-5547", 0xe35e25fb, 	true, MTAP_TP_PRT1, 4 }, // J. League Pro Striker Final Stage (Japan)
314  { "00005518-00", 0xec229156, 	true, MTAP_TP_PRT2, 4 }, // J. League Pro Striker (Japan) (v1.0)
315  { "00005518-03", 0x2d5b7a11, 	true, MTAP_TP_PRT2, 4 }, // J. League Pro Striker (Japan) (v1.3)
316  { "00005532-00", 0x0abed379, 	true, MTAP_TP_PRT2, 4 }, // J. League Pro Striker Perfect (Japan)
317  { NULL, 0x17bed25f, 		true, MTAP_TP_PRT1, 3 }, // Lost Vikings, The (Europe) (Beta)
318  { "T-70226-500", 0x1f14efc6, 	true, MTAP_TP_PRT1, 3 }, // Lost Vikings, The (Europe)
319  { "T-125016-00", 0x7ba49edb, 	true, MTAP_TP_PRT1, 3 }, // Lost Vikings, The (USA)
320  { "T-50676 -00", 0xd14b811b,	true, MTAP_4WAY,    4 }, // Madden NFL '94 (USA, Europe)
321  { "T-50926 -00", 0xdb0be0c2,	true, MTAP_4WAY,    4 }, // Madden NFL 95 (USA, Europe)
322  { "T-172076-00", 0xf126918b,	true, MTAP_4WAY,    4 }, // Madden NFL 96 (USA, Europe)
323  { "T-172136-00", 0xc4b4e112,	true, MTAP_4WAY,    4 }, // Madden NFL 97 (USA, Europe)
324  { "T-172196-00", 0xe051ea62,	true, MTAP_4WAY,    4 }, // Madden NFL 98 (USA)
325  { "MK-1573-00", 0x54ab3beb, 	true, MTAP_TP_PRT1, 4 }, // Mega Bomberman (Europe)
326  { "MK-1573-00", 0x4bd6667d, 	true, MTAP_TP_PRT1, 4 }, // Mega Bomberman (USA)
327  { NULL, 0xd41c0d81, 		true, MTAP_TP_DUAL, 8 }, // Mega Bomberman - 8 Player Demo (Unl)
328  { "T-120096-50", 0x01c22a5d, 	false, MTAP_TP_PRT1, 4 }, // Micro Machines 2 - Turbo Tournament (Europe) (J-Cart) (Alt 1)
329  { "T-120096-50", 0x42bfb7eb, 	false, MTAP_TP_PRT1, 4 }, // Micro Machines 2 - Turbo Tournament (Europe) (J-Cart)
330  { NULL, 0xb3abb15e, 		false, MTAP_TP_PRT1, 4 }, // Micro Machines Military (Europe) (J-Cart)
331  { NULL, 0x7492b1de, 		false, MTAP_TP_PRT1, 4 }, // Micro Machines Turbo Tournament 96 (Europe) (J-Cart)
332  { NULL, 0x23319d0d, 		false, MTAP_TP_PRT1, 4 }, // Micro Machines Turbo Tournament 96 (Europe) (v1.1) (J-Cart)
333  { "T-50816 -00", 0x14a8064d,	true, MTAP_4WAY,    4 }, // MLBPA Baseball (USA)
334  { "T-50766 -00", 0x3529180f,	true, MTAP_4WAY,    4 }, // Mutant League Hockey (USA, Europe)
335  { "MK-1221 -00", 0x99c348ba, 	true, MTAP_TP_PRT1, 5 }, // NBA Action '94 (USA)
336  { "MK-1236 -00", 0xaa7006d6, 	true, MTAP_TP_PRT1, 5 }, // NBA Action '95 Starring David Robinson (USA, Europe)
337  { "T-97136 -50", 0xedb4d4aa, 	true, MTAP_TP_PRT1, 4 }, // NBA Hang Time (Europe)
338  { "T-97136 -00", 0x176b0338, 	true, MTAP_TP_PRT1, 4 }, // NBA Hang Time (USA)
339  { "T-81033  00", 0xa6c6305a, 	true, MTAP_TP_PRT1, 4 }, // NBA Jam (Japan)
340  { "T-81406 -00", 0xe9ffcb37, 	true, MTAP_TP_PRT1, 4 }, // NBA Jam Tournament Edition (World)
341  { "T-081326 00", 0x10fa248f, 	true, MTAP_TP_PRT1, 4 }, // NBA Jam (USA, Europe)
342  { "T-081326 01", 0xeb8360e6, 	true, MTAP_TP_PRT1, 4 }, // NBA Jam (USA, Europe) (v1.1)
343  { "T-50936 -00", 0x779c1244,	true, MTAP_4WAY,    4 }, // NBA Live 95 (Korea)
344  { "T-50936 -00", 0x66018abc,	true, MTAP_4WAY,    4 }, // NBA Live 95 (USA, Europe)
345  { "T-172056-00", 0x49de0062,	true, MTAP_4WAY,    4 }, // NBA Live 96 (USA, Europe)
346  { "T-172166-00", 0x7024843a,	true, MTAP_4WAY,    4 }, // NBA Live 97 (USA, Europe)
347  { "T-172186-00", 0x23473a8a, 	true, MTAP_TP_PRT1, 4 }, // NBA Live 98 (USA)
348  { NULL, 0xeea19bce, 		true, MTAP_TP_PRT1, 4 }, // NBA Pro Basketball '94 (Japan)
349  { "T-50756 -00", 0x160b7090,	true, MTAP_4WAY,    4 }, // NBA Showdown '94 (USA, Europe)
350  { "T-158016-00", 0xed0c1303, 	true, MTAP_TP_PRT1, 5 }, // NCAA Final Four Basketball (USA)
351  { "T-87106 -00", 0x081012f0, 	true, MTAP_TP_PRT1, 4 }, // NCAA Football (USA)
352  { "MK-1237 -00", 0xb58e4a81, 	true, MTAP_TP_PRT1, 4 }, // NFL '95 (USA, Europe)
353  { "MK-1243 -00", 0xf73ec54c, 	true, MTAP_TP_PRT1, 4 }, // NFL 98 (USA)
354  { "T-081586-00", 0xd5a37cab, 	true, MTAP_TP_PRT1, 4 }, // NFL Quarterback Club 96 (USA, Europe)
355  { "T-081276 00", 0x94542eaf, 	true, MTAP_TP_PRT1, 4 }, // NFL Quarterback Club (World)
356  { "T-50656 -00", 0x9438f5dd,	true, MTAP_4WAY,    4 }, // NHL '94 (USA, Europe)
357  { "T-50856 -00", 0xe8ee917e,	true, MTAP_4WAY,    4 }, // NHL 95 (USA, Europe)
358  { "T-172036-00", 0x8135702c,	true, MTAP_4WAY,    4 }, // NHL 96 (USA, Europe)
359  { "T-172146-03", 0xf067c103,	true, MTAP_4WAY,    4 }, // NHL 97 (USA, Europe)
360  { "T-172176-00", 0x7b64cd98,	true, MTAP_4WAY,    4 }, // NHL 98 (USA)
361  { "00004107-00", 0x9d4b447a, 	true, MTAP_TP_PRT2, 5 }, // Party Quiz Mega Q (Japan)
362  { "T-119096-00", 0x05a486e9, 	true, MTAP_TP_PRT1, 4 }, // Pele II - World Tournament Soccer (USA, Europe)
363  { "G-4133-00", 0xd1e2324b, 	true, MTAP_TP_PRT2, 4 }, // Pepenga Pengo (Japan)
364  { "T-50796 -00", 0x8ca45acd,	true, MTAP_4WAY,    4 }, // PGA European Tour (USA, Europe)
365  { "T-50946 -00", 0xaeb3f65f,	true, MTAP_4WAY,    4 }, // PGA Tour Golf III (USA, Europe)
366  { "MK-1240 -00", 0x5aa53cbc, 	true, MTAP_TP_PRT1, 4 }, // Prime Time NFL Starring Deion Sanders (USA)
367  { "G-4128  -00", 0x7bdec762, 	true, MTAP_TP_PRT1, 4 }, // Puzzle & Action - Ichidanto-R (Japan)
368  { "G-4118  -00", 0xd2d2d437, 	true, MTAP_TP_PRT1, 4 }, // Puzzle & Action - Tanto-R (Japan)
369  { "T-50956 -00", 0x61f90a8a,	true, MTAP_4WAY,    4 }, // Rugby World Cup 95 (USA, Europe) (En,Fr,It)
370  { "MK-1183 -00", 0x07fedaf1, 	true, MTAP_TP_PRT2, 4 }, // Sega Sports 1 (Europe)
371  { NULL, 0x72dd884f, 		true, MTAP_TP_PRT1, 4 }, // Shi Jie Zhi Bang Zheng Ba Zhan - World Pro Baseball 94 (China) (Unl)
372  { "MK-1233 -00", 0x7e3ecabf, 	true, MTAP_TP_PRT1, 5 }, // Sport Games (Brazil) [b]
373  { "T-177016-00", 0x1a58d5fe, 	true, MTAP_TP_PRT1, 4 }, // Street Racer (Europe)
374  { "T-95146-00", 0x1227b2b2, 	true, MTAP_TP_PRT1, 4 }, // Tiny Toon Adventures - Acme All-Stars (Europe)
375  { "T-95146-00", 0x2f9faa1d, 	true, MTAP_TP_PRT1, 4 }, // Tiny Toon Adventures - Acme All-Stars (USA, Korea)
376  { "T-172026-04", 0xf1748e91,	true, MTAP_4WAY,    4 }, // Triple Play 96 (USA)
377  { "T-172116-00", 0xbbe69017,	true, MTAP_4WAY,    4 }, // Triple Play - Gold Edition (USA)
378  //maybe port2? { NULL, 0x9d451f72, 		true, MTAP_TP_PRT1, 4 }, // Ultimate Soccer (Europe) (En,Fr,De,Es,It) (Beta)
379  { "MK1219  -00", 0x83db6e58, 	true, MTAP_TP_DUAL, 8 }, // Ultimate Soccer (Europe) (En,Fr,De,Es,It)
380  { "T-119156-00", 0x9920e7b7, 	true, MTAP_TP_PRT1, 4 }, // Unnecessary Roughness '95 (USA)
381  { "T-048416-00", 0xc2c13b81, 	true, MTAP_TP_PRT1, 4 }, // Wayne Gretzky and the NHLPA All-Stars (USA, Europe)
382  { "MK-1224 -50", 0xb791a435, 	true, MTAP_TP_PRT2, 4 }, // Wimbledon Championship Tennis (Europe)
383  { "G-4110  -00", 0x3e0c9daf, 	true, MTAP_TP_PRT2, 4 }, // Wimbledon Championship Tennis (Japan)
384  { NULL, 0x9febc760, 		true, MTAP_TP_PRT2, 4 }, // Wimbledon Championship Tennis (USA) (Beta)
385  { "MK-1224 -00", 0xf9142aee, 	true, MTAP_TP_PRT2, 4 }, // Wimbledon Championship Tennis (USA)
386  { "MK-1233 -00", 0x6065774d, 	true, MTAP_TP_PRT1, 5 }, // World Championship Soccer II (Europe)
387  { "MK-1233 -00", 0xc1dd1c8e, 	true, MTAP_TP_PRT1, 5 }, // World Championship Soccer II (USA)
388  { "T-79116-00", 0x0171b47f, 	true, MTAP_TP_PRT1, 4 }, // World Cup USA 94 (USA, Europe)
389  { "T-081316-00", 0x4ef5d411, 	true, MTAP_TP_PRT1, 4 }, // WWF Raw (World)
390  { "G-004122-00", 0x71ceac6f, 	true, MTAP_TP_PRT1, 4 }, // Yuu Yuu Hakusho - Makyou Toitsusen (Japan)
391  { "G-004122-00", 0xfe3fb8ee, 	true, MTAP_TP_PRT1, 4 }, // Yuu Yuu Hakusho - Sunset Fighters (Brazil)
392 };
393 
decode_region_setting(const int setting,bool & overseas,bool & pal)394 static bool decode_region_setting(const int setting, bool &overseas, bool &pal)
395 {
396  switch(setting)
397  {
398   default: assert(0);
399 	   return(false);
400 
401   case REGION_OVERSEAS_NTSC:
402 	overseas = true;
403 	pal = false;
404 	return(true);
405 
406   case REGION_OVERSEAS_PAL:
407 	overseas = true;
408 	pal = true;
409 	return(true);
410 
411   case REGION_DOMESTIC_NTSC:
412 	overseas = false;
413 	pal = false;
414 	return(true);
415 
416   case REGION_DOMESTIC_PAL:
417 	overseas = false;
418 	pal = true;
419 	return(true);
420  }
421 }
422 
LoadCommonPost(const md_game_info & ginfo)423 static void LoadCommonPost(const md_game_info &ginfo)
424 {
425  MDFN_printf(_("ROM:       %dKiB\n"), (ginfo.rom_size + 1023) / 1024);
426  MDFN_printf(_("ROM CRC32: 0x%08x\n"), ginfo.crc32);
427  MDFN_printf(_("ROM MD5:   0x%s\n"), md5_context::asciistr(ginfo.md5, 0).c_str());
428  MDFN_printf(_("Header MD5: 0x%s\n"), md5_context::asciistr(ginfo.info_header_md5, 0).c_str());
429  MDFN_printf(_("Product Code: %s\n"), ginfo.product_code);
430  MDFN_printf(_("Domestic name: %s\n"), ginfo.domestic_name); // TODO: Character set conversion(shift_jis -> utf-8)
431  MDFN_printf(_("Overseas name: %s\n"), ginfo.overseas_name);
432  MDFN_printf(_("Copyright: %s\n"), ginfo.copyright);
433  if(ginfo.checksum == ginfo.checksum_real)
434   MDFN_printf(_("Checksum:  0x%04x\n"), ginfo.checksum);
435  else
436   MDFN_printf(_("Checksum:  0x%04x\n Warning: calculated checksum(0x%04x) does not match\n"), ginfo.checksum, ginfo.checksum_real);
437 
438  MDFN_printf(_("Supported I/O devices:\n"));
439  MDFN_indent(1);
440  for(unsigned int iot = 0; iot < sizeof(IO_types) / sizeof(IO_type_t); iot++)
441  {
442   if(ginfo.io_support & (1 << IO_types[iot].id))
443    MDFN_printf(_("%s\n"), _(IO_types[iot].name));
444  }
445  MDFN_indent(-1);
446 
447  MDFNMP_Init(8192, (1 << 24) / 8192);
448 
449  for(uint32 A = (0x7 << 21); A < (0x8 << 21); A += 65536)
450   MDFNMP_AddRAM(65536, A, work_ram, (A == 0xFF0000));
451 
452  MDSound_Init();
453 
454  MDFN_printf(_("Supported regions:\n"));
455  MDFN_indent(1);
456  if(ginfo.region_support & REGIONMASK_JAPAN_NTSC)
457   MDFN_printf(_("Japan/Domestic NTSC\n"));
458  if(ginfo.region_support & REGIONMASK_JAPAN_PAL)
459   MDFN_printf(_("Japan/Domestic PAL\n"));
460  if(ginfo.region_support & REGIONMASK_OVERSEAS_NTSC)
461   MDFN_printf(_("Overseas NTSC\n"));
462  if(ginfo.region_support & REGIONMASK_OVERSEAS_PAL)
463   MDFN_printf(_("Overseas PAL\n"));
464  MDFN_indent(-1);
465 
466  {
467   const int region_setting = MDFN_GetSettingI("md.region");
468   const int reported_region_setting = MDFN_GetSettingI("md.reported_region");
469 
470   // Default, in case the game doesn't support any regions!
471   bool game_overseas = true;
472   bool game_pal = false;
473   bool overseas;
474   bool pal;
475   bool overseas_reported;
476   bool pal_reported;
477 
478   // Preference order, TODO:  Make it configurable
479   if(ginfo.region_support & REGIONMASK_OVERSEAS_NTSC)
480   {
481    game_overseas = true;
482    game_pal = false;
483   }
484   else if(ginfo.region_support & REGIONMASK_JAPAN_NTSC)
485   {
486    game_overseas = false;
487    game_pal = false;
488   }
489   else if(ginfo.region_support & REGIONMASK_OVERSEAS_PAL)
490   {
491    game_overseas = true;
492    game_pal = true;
493   }
494   else if(ginfo.region_support & REGIONMASK_JAPAN_PAL) // WTF?
495   {
496    game_overseas = false;
497    game_pal = true;
498   }
499 
500   if(region_setting == REGION_GAME)
501   {
502    overseas = game_overseas;
503    pal = game_pal;
504   }
505   else
506   {
507    decode_region_setting(region_setting, overseas, pal);
508   }
509 
510   if(reported_region_setting == REGION_GAME)
511   {
512    overseas_reported = game_overseas;
513    pal_reported = game_pal;
514   }
515   else if(reported_region_setting == REGION_SAME)
516   {
517    overseas_reported = overseas;
518    pal_reported = pal;
519   }
520   else
521   {
522    decode_region_setting(reported_region_setting, overseas_reported, pal_reported);
523   }
524 
525   MDFN_printf("\n");
526   MDFN_printf(_("Active Region: %s %s\n"), overseas ? _("Overseas") : _("Domestic"), pal ? _("PAL") : _("NTSC"));
527   MDFN_printf(_("Active Region Reported: %s %s\n"), overseas_reported ? _("Overseas") : _("Domestic"), pal_reported ? _("PAL") : _("NTSC"));
528 
529   system_init(overseas, pal, overseas_reported, pal_reported);
530 
531   if(pal)
532    MDFNGameInfo->nominal_height = 240;
533   else
534    MDFNGameInfo->nominal_height = 224;
535 
536   MDFNGameInfo->MasterClock = MDFN_MASTERCLOCK_FIXED(pal ? CLOCK_PAL : CLOCK_NTSC);
537 
538   if(pal)
539    MDFNGameInfo->fps = (int64)CLOCK_PAL * 65536 * 256 / (313 * 3420);
540   else
541    MDFNGameInfo->fps = (int64)CLOCK_NTSC * 65536 * 256 / (262 * 3420);
542 
543   //printf("%f\n", (double)MDFNGameInfo->fps / 65536 / 256);
544  }
545 
546  if(MDFN_GetSettingB("md.correct_aspect"))
547  {
548   MDFNGameInfo->nominal_width = 292;
549   MDFNGameInfo->lcm_width = 1280;
550  }
551  else
552  {
553   MDFNGameInfo->nominal_width = 320;
554   MDFNGameInfo->lcm_width = 320;
555  }
556 
557  MDFNGameInfo->lcm_height = MDFNGameInfo->nominal_height * 2;
558 
559  MDFNGameInfo->LayerNames = "BG0\0BG1\0OBJ\0";
560 
561  //
562  //
563  {
564   unsigned mtt = MDFN_GetSettingUI("md.input.multitap");
565 
566   if(MDFN_GetSettingB("md.input.auto"))
567   {
568    for(auto const& e : InputDB)
569    {
570     if(e.crc32 == ginfo.crc32 && (!e.prod_code || !strcmp(e.prod_code, ginfo.product_code)))
571     {
572      MDFNGameInfo->DesiredInput.resize(8);
573 
574      for(unsigned n = e.max_players; n < 8; n++)	// Particularly for Gauntlet 4.
575       MDFNGameInfo->DesiredInput[n] = "none";
576 
577      mtt = e.tap;
578      break;
579     }
580    }
581   }
582 
583   for(const auto* mte = MultiTap_List; mte->string; mte++)
584   {
585    if((unsigned)mte->number == mtt)
586    {
587     MDFN_printf(_("Active Multitap(s): %s\n"), mte->description);
588     break;
589    }
590   }
591 
592   MDINPUT_SetMultitap(mtt);
593  }
594 
595  //
596  //
597 
598  system_reset(true);
599 }
600 
Load(GameFile * gf)601 static void Load(GameFile* gf)
602 {
603  try
604  {
605   md_game_info ginfo;
606 
607   memset(&ginfo, 0, sizeof(md_game_info));
608   MDCart_Load(&ginfo, gf);
609 
610   memcpy(MDFNGameInfo->MD5, ginfo.md5, 16);
611 
612   MD_IsCD = false;
613 
614   MD_ExtRead8 = MDCart_Read8;
615   MD_ExtRead16 = MDCart_Read16;
616   MD_ExtWrite8 = MDCart_Write8;
617   MD_ExtWrite16 = MDCart_Write16;
618 
619   MDCart_LoadNV();
620 
621   LoadCommonPost(ginfo);
622  }
623  catch(...)
624  {
625   Cleanup();
626   throw;
627  }
628 }
629 
LoadCD(std::vector<CDInterface * > * CDInterfaces)630 static void LoadCD(std::vector<CDInterface*> *CDInterfaces)
631 {
632  try
633  {
634   md_game_info ginfo;
635 
636   memset(&ginfo, 0, sizeof(md_game_info));
637 
638   MD_IsCD = true;
639 
640   MDCD_Load(CDInterfaces, &ginfo);
641 
642   LoadCommonPost(ginfo);
643  }
644  catch(...)
645  {
646   Cleanup();
647   throw;
648  }
649 }
650 
TestMagicCD(std::vector<CDInterface * > * CDInterfaces)651 static bool TestMagicCD(std::vector<CDInterface*> *CDInterfaces)
652 {
653  return(MDCD_TestMagic(CDInterfaces));
654 }
655 
DoSimpleCommand(int cmd)656 static void DoSimpleCommand(int cmd)
657 {
658  switch(cmd)
659  {
660   case MDFN_MSC_POWER: system_reset(true); break;
661   case MDFN_MSC_RESET: system_reset(false); break;
662  }
663 }
664 
StateAction(StateMem * sm,const unsigned load,const bool data_only)665 static void StateAction(StateMem *sm, const unsigned load, const bool data_only)
666 {
667  uint8 c68k_state[M68K::OldStateLen];
668 
669  SFORMAT StateRegs[] =
670  {
671   SFPTR8(work_ram, 65536),
672   SFPTR8(zram, 8192),
673   SFVAR(zbusreq),
674   SFVAR(zreset),
675   SFVAR(zbusack),
676   SFVAR(zirq),
677   SFVAR(zbank),
678 
679   SFVAR(suspend68k),
680   SFVAR(z80_cycle_counter),
681 
682   SFVAR(obsim),
683 
684   SFPTR8N((load && load < 0x939) ? c68k_state : NULL, sizeof(c68k_state), "c68k_state"),
685   SFEND
686  };
687 
688 
689  MDFNSS_StateAction(sm, load, data_only, StateRegs, "MAIN");
690  if(load)
691  {
692   zbusreq &= 1;
693   zreset &= 1;
694   zbusack &= 1;
695 
696   if(z80_cycle_counter > 0)
697    z80_cycle_counter = 0;
698  }
699 
700 
701  z80_state_action(sm, load, data_only, "Z80");
702  MDINPUT_StateAction(sm, load, data_only);
703  MainVDP.StateAction(sm, load, data_only);
704  MDSound_StateAction(sm, load, data_only);
705  MDCart_StateAction(sm, load, data_only);
706 
707  if(!load || load >= 0x939)
708   Main68K.StateAction(sm, load, data_only, "M68K");
709 
710  if(load)
711  {
712   z80_set_interrupt(zirq);
713   //
714   if(load < 0x939)
715   {
716    Main68K.LoadOldState(c68k_state);
717    Main68K.SetExtHalted(suspend68k);
718   }
719  }
720 }
721 
722 static const MDFNSetting_EnumList RegionList[] =
723 {
724  { "game", REGION_GAME, gettext_noop("Match game's header."), gettext_noop("Emulate the region that the game indicates it expects to run in via data in the header(or in an internal database for a few games that may have bad header data).") },
725 
726  { "overseas_ntsc", REGION_OVERSEAS_NTSC, gettext_noop("Overseas(non-Japan), NTSC"), gettext_noop("Region used in North America.") },
727  { "overseas_pal", REGION_OVERSEAS_PAL, gettext_noop("Overseas(non-Japan), PAL"), gettext_noop("Region used in Europe.") },
728 
729  { "domestic_ntsc", REGION_DOMESTIC_NTSC, gettext_noop("Domestic(Japan), NTSC"), gettext_noop("Region used in Japan.") },
730  { "domestic_pal", REGION_DOMESTIC_PAL, gettext_noop("Domestic(Japan), PAL"), gettext_noop("Probably an invalid region, but available for testing purposes anyway.") },
731 
732  { NULL, 0 }
733 };
734 
735 static const MDFNSetting_EnumList ReportedRegionList[] =
736 {
737  { "same", REGION_SAME, gettext_noop("Match the region emulated.") },
738 
739  { "game", REGION_GAME, gettext_noop("Match game's header."), gettext_noop("This option, in conjunction with the \"md.region\" setting, can be used to run all games at NTSC speeds, or all games at PAL speeds.")  },
740 
741  { "overseas_ntsc", REGION_OVERSEAS_NTSC, gettext_noop("Overseas(non-Japan), NTSC"), gettext_noop("Region used in North America.") },
742  { "overseas_pal", REGION_OVERSEAS_PAL, gettext_noop("Overseas(non-Japan), PAL"), gettext_noop("Region used in Europe.") },
743 
744  { "domestic_ntsc", REGION_DOMESTIC_NTSC, gettext_noop("Domestic(Japan), NTSC"), gettext_noop("Region used in Japan.") },
745  { "domestic_pal", REGION_DOMESTIC_PAL, gettext_noop("Domestic(Japan), PAL"), gettext_noop("Probably an invalid region, but available for testing purposes anyway.") },
746 
747  { NULL, 0 },
748 };
749 
750 static const MDFNSetting MDSettings[] =
751 {
752  { "md.region", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Emulate the specified region's Genesis/MegaDrive"), NULL, MDFNST_ENUM, "game", NULL, NULL, NULL, NULL, RegionList },
753  { "md.reported_region", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Region reported to the game."), NULL, MDFNST_ENUM, "same", NULL, NULL, NULL, NULL, ReportedRegionList },
754 
755  { "md.cdbios", MDFNSF_EMU_STATE | MDFNSF_CAT_PATH, gettext_noop("Path to the CD BIOS"), gettext_noop("SegaCD/MegaCD emulation is currently nonfunctional."), MDFNST_STRING, "us_scd1_9210.bin" },
756 
757  { "md.correct_aspect", MDFNSF_CAT_VIDEO, gettext_noop("Correct the aspect ratio."), NULL, MDFNST_BOOL, "1" },
758 
759  { "md.input.auto", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Automatically select appropriate input devices."),
760 	gettext_noop("Automatically select appropriate input devices, based on an internal database.  Currently, only multitap device usage data is contained in the database."),
761 	MDFNST_BOOL, "1" },
762  { "md.input.multitap", MDFNSF_EMU_STATE | MDFNSF_UNTRUSTED_SAFE, gettext_noop("Enable multitap(s)."), NULL, MDFNST_ENUM, "none", NULL, NULL, NULL, NULL, MultiTap_List },
763 
764  { "md.input.mouse_sensitivity", MDFNSF_NOFLAGS, gettext_noop("Emulated mouse sensitivity."), NULL, MDFNST_FLOAT, "1.00", NULL, NULL },
765 
766  { NULL }
767 };
768 
769 static const FileExtensionSpecStruct KnownExtensions[] =
770 {
771  { ".bin", -80, gettext_noop("Super Magic Drive binary ROM Image") },
772  { ".smd", -10, gettext_noop("Super Magic Drive interleaved format ROM Image") },
773  { ".md", 0, gettext_noop("Multi Game Doctor format ROM Image") },
774 
775  { NULL, 0, NULL }
776 };
777 
SetLayerEnableMask(uint64 mask)778 void SetLayerEnableMask(uint64 mask)
779 {
780  MainVDP.SetLayerEnableMask(mask);
781 }
782 
783 }
784 
785 
786 MDFNGI EmulatedMD =
787 {
788  "md",
789  "Sega Genesis/MegaDrive",
790  KnownExtensions,
791  MODPRIO_INTERNAL_HIGH,
792  #ifdef WANT_DEBUGGER
793  &DBGInfo,
794  #else
795  NULL,
796  #endif
797  MDPortInfo,
798  NULL,
799  Load,
800  MDCart_TestMagic,
801  LoadCD,
802  TestMagicCD,
803  CloseGame,
804 
805  SetLayerEnableMask,
806  NULL,
807 
808  NULL,
809  NULL,
810 
811  NULL,
812  0,
813 
814  CheatInfo_Empty,
815 
816  false,
817  StateAction,
818  Emulate,
819  NULL,
820  MDINPUT_SetInput,
821  NULL,
822  DoSimpleCommand,
823  NULL,
824  MDSettings,
825  0,	// MasterClock(set in game loading code)
826  0,
827  true, // Multires possible?
828 
829  0,   // lcm_width		// Calculated in game load
830  0,   // lcm_height         	// Calculated in game load
831  NULL,  // Dummy
832 
833 
834  // We want maximum values for nominal width and height here so the automatic fullscreen setting generation code will have
835  // selected a setting suitable if aspect ratio correction is turned off.
836  320,   // Nominal width(adjusted in game loading code, with aspect ratio correction enabled, it's 292, otherwise 320)
837  240,   // Nominal height(adjusted in game loading code to 224 for NTSC, and 240 for PAL)
838  1024,	// Framebuffer width
839  512,	// Framebuffer height
840 
841  2,     // Number of output sound channels
842 };
843 
844