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