1 // Based on MAME driver by David Graves
2 
3 #include "tiles_generic.h"
4 #include "m68000_intf.h"
5 #include "z80_intf.h"
6 #include "taito.h"
7 #include "taito_ic.h"
8 #include "burn_ym2610.h"
9 #include "eeprom.h"
10 #include "burn_gun.h"
11 
12 static double OthunderYM2610AY8910RouteMasterVol;
13 static double OthunderYM2610Route1MasterVol;
14 static double OthunderYM2610Route2MasterVol;
15 static UINT8 *OthunderPan;
16 
17 #ifdef BUILD_A68K
18 static bool bUseAsm68KCoreOldValue = false;
19 #endif
20 
21 #define A(a, b, c, d) {a, b, (UINT8*)(c), d}
22 
23 static struct BurnInputInfo OthunderInputList[] =
24 {
25 	{"Coin 1"            , BIT_DIGITAL   , TC0220IOCInputPort0 + 3, "p1 coin"        },
26 	{"Start 1"           , BIT_DIGITAL   , TC0220IOCInputPort0 + 6, "p1 start"       },
27 	{"Coin 2"            , BIT_DIGITAL   , TC0220IOCInputPort0 + 2, "p2 coin"        },
28 	{"Start 2"           , BIT_DIGITAL   , TC0220IOCInputPort0 + 7, "p2 start"       },
29 
30 	A("P1 Gun X"         , BIT_ANALOG_REL, &TaitoAnalogPort0      , "mouse x-axis"   ),
31 	A("P1 Gun Y"         , BIT_ANALOG_REL, &TaitoAnalogPort1      , "mouse y-axis"   ),
32 	{"P1 Fire 1"         , BIT_DIGITAL   , TC0220IOCInputPort2 + 0, "mouse button 1" },
33 	{"P1 Fire 2"         , BIT_DIGITAL   , TC0220IOCInputPort2 + 2, "mouse button 2" },
34 
35 	A("P2 Gun X"         , BIT_ANALOG_REL, &TaitoAnalogPort2      , "p2 x-axis"      ),
36 	A("P2 Gun Y"         , BIT_ANALOG_REL, &TaitoAnalogPort3      , "p2 y-axis"      ),
37 	{"P2 Fire 1"         , BIT_DIGITAL   , TC0220IOCInputPort2 + 1, "p2 fire 1"      },
38 	{"P2 Fire 2"         , BIT_DIGITAL   , TC0220IOCInputPort2 + 3, "p2 fire 2"      },
39 
40 	{"Reset"             , BIT_DIGITAL   , &TaitoReset            , "reset"          },
41 	{"Service"           , BIT_DIGITAL   , TC0220IOCInputPort0 + 4, "service"        },
42 	{"Dip 1"             , BIT_DIPSWITCH , TC0220IOCDip + 0       , "dip"            },
43 	{"Dip 2"             , BIT_DIPSWITCH , TC0220IOCDip + 1       , "dip"            },
44 	{"Dip 3"             , BIT_DIPSWITCH , TaitoDip + 0           , "dip"            },
45 };
46 
STDINPUTINFO(Othunder)47 STDINPUTINFO(Othunder)
48 
49 #undef A
50 
51 static void TC0220IOCMakeInputs()
52 {
53 	// Reset Inputs
54 	TC0220IOCInput[0] = 0xff;
55 	TC0220IOCInput[1] = 0xff;
56 	TC0220IOCInput[2] = 0xff;
57 
58 	for (INT32 i = 0; i < 8; i++) {
59 		TC0220IOCInput[0] -= (TC0220IOCInputPort0[i] & 1) << i;
60 		TC0220IOCInput[1] -= (TC0220IOCInputPort1[i] & 1) << i;
61 		TC0220IOCInput[2] -= (TC0220IOCInputPort2[i] & 1) << i;
62 	}
63 }
64 
OthunderMakeInputs()65 static void OthunderMakeInputs()
66 {
67 	TC0220IOCMakeInputs();
68 
69 	BurnGunMakeInputs(0, (INT16)TaitoAnalogPort0, (INT16)TaitoAnalogPort1);
70 	BurnGunMakeInputs(1, (INT16)TaitoAnalogPort2, (INT16)TaitoAnalogPort3);
71 }
72 
73 static struct BurnDIPInfo OthunderDIPList[]=
74 {
75 	// Default Values
76 	{0x0e, 0xff, 0xff, 0xff, NULL                             },
77 	{0x0f, 0xff, 0xff, 0x7f, NULL                             },
78 	{0x10, 0xff, 0xff, 0x07, NULL                             },
79 
80 	// Dip 1
81 	{0   , 0xfe, 0   , 2   , "Allow Continue"                 },
82 	{0x0e, 0x01, 0x02, 0x00, "Off"                            },
83 	{0x0e, 0x01, 0x02, 0x02, "On"                             },
84 
85 	{0   , 0xfe, 0   , 2   , "Service Mode"                   },
86 	{0x0e, 0x01, 0x04, 0x04, "Off"                            },
87 	{0x0e, 0x01, 0x04, 0x00, "On"                             },
88 
89 	{0   , 0xfe, 0   , 2   , "Demo Sounds"                    },
90 	{0x0e, 0x01, 0x08, 0x00, "Off"                            },
91 	{0x0e, 0x01, 0x08, 0x08, "On"                             },
92 
93 	{0   , 0xfe, 0   , 4   , "Coin A"                         },
94 	{0x0e, 0x01, 0x30, 0x00, "4 Coins 1 Credit"               },
95 	{0x0e, 0x01, 0x30, 0x10, "3 Coins 1 Credit"               },
96 	{0x0e, 0x01, 0x30, 0x20, "2 Coins 1 Credit"               },
97 	{0x0e, 0x01, 0x30, 0x30, "1 Coin  1 Credit"               },
98 
99 	{0   , 0xfe, 0   , 4   , "Coin B"                         },
100 	{0x0e, 0x01, 0xc0, 0xc0, "1 Coin 2 Credits"               },
101 	{0x0e, 0x01, 0xc0, 0x80, "1 Coin 3 Credits"               },
102 	{0x0e, 0x01, 0xc0, 0x40, "1 Coin 4 Credits"               },
103 	{0x0e, 0x01, 0xc0, 0x00, "1 Coin 6 Credits"               },
104 
105 	// Dip 2
106 	{0   , 0xfe, 0   , 4   , "Difficulty"                     },
107 	{0x0f, 0x01, 0x03, 0x02, "Easy"                           },
108 	{0x0f, 0x01, 0x03, 0x03, "Medium"                         },
109 	{0x0f, 0x01, 0x03, 0x01, "Hard"                           },
110 	{0x0f, 0x01, 0x03, 0x00, "Hardest"                        },
111 
112 	{0   , 0xfe, 0   , 4   , "Magazines/Rockets"              },
113 	{0x0f, 0x01, 0x0c, 0x0c, "5/3"                            },
114 	{0x0f, 0x01, 0x0c, 0x08, "6/4"                            },
115 	{0x0f, 0x01, 0x0c, 0x04, "7/5"                            },
116 	{0x0f, 0x01, 0x0c, 0x00, "8/6"                            },
117 
118 	{0   , 0xfe, 0   , 4   , "Bullets per Magazine"           },
119 	{0x0f, 0x01, 0x30, 0x00, "30"                             },
120 	{0x0f, 0x01, 0x30, 0x10, "35"                             },
121 	{0x0f, 0x01, 0x30, 0x30, "40"                             },
122 	{0x0f, 0x01, 0x30, 0x20, "50"                             },
123 
124 	{0   , 0xfe, 0   , 2   , "Language"                    },
125 	{0x0f, 0x01, 0x80, 0x00, "English"                        },
126 	{0x0f, 0x01, 0x80, 0x80, "Japanese"                       },
127 
128 	// Dip 3
129 	{0   , 0xfe, 0   , 4   , "Stereo Seperation"              },
130 	{0x10, 0x01, 0x07, 0x07, "Maximum"                        },
131 	{0x10, 0x01, 0x07, 0x03, "High"                           },
132 	{0x10, 0x01, 0x07, 0x01, "Medium"                         },
133 	{0x10, 0x01, 0x07, 0x00, "Low"                            },
134 };
135 
136 STDDIPINFO(Othunder)
137 
138 static struct BurnDIPInfo OthunderjDIPList[]=
139 {
140 	// Default Values
141 	{0x0e, 0xff, 0xff, 0xff, NULL                             },
142 	{0x0f, 0xff, 0xff, 0x7f, NULL                             },
143 	{0x10, 0xff, 0xff, 0x07, NULL                             },
144 
145 	// Dip 1
146 	{0   , 0xfe, 0   , 2   , "Allow Continue"                 },
147 	{0x0e, 0x01, 0x02, 0x00, "Off"                            },
148 	{0x0e, 0x01, 0x02, 0x02, "On"                             },
149 
150 	{0   , 0xfe, 0   , 2   , "Service Mode"                   },
151 	{0x0e, 0x01, 0x04, 0x04, "Off"                            },
152 	{0x0e, 0x01, 0x04, 0x00, "On"                             },
153 
154 	{0   , 0xfe, 0   , 2   , "Demo Sounds"                    },
155 	{0x0e, 0x01, 0x08, 0x00, "Off"                            },
156 	{0x0e, 0x01, 0x08, 0x08, "On"                             },
157 
158 	{0   , 0xfe, 0   , 4   , "Coin A"                         },
159 	{0x0e, 0x01, 0x30, 0x10, "2 Coins 1 Credit"               },
160 	{0x0e, 0x01, 0x30, 0x30, "1 Coin  1 Credit"               },
161 	{0x0e, 0x01, 0x30, 0x00, "2 Coins 3 Credits"              },
162 	{0x0e, 0x01, 0x30, 0x20, "1 Coin  2 Credits"              },
163 
164 	{0   , 0xfe, 0   , 4   , "Coin B"                         },
165 	{0x0e, 0x01, 0xc0, 0x40, "2 Coins 1 Credit"               },
166 	{0x0e, 0x01, 0xc0, 0xc0, "1 Coin  1 Credit"               },
167 	{0x0e, 0x01, 0xc0, 0x00, "2 Coins 3 Credits"              },
168 	{0x0e, 0x01, 0xc0, 0x80, "1 Coin  2 Credits"              },
169 
170 	// Dip 2
171 	{0   , 0xfe, 0   , 4   , "Difficulty"                     },
172 	{0x0f, 0x01, 0x03, 0x02, "Easy"                           },
173 	{0x0f, 0x01, 0x03, 0x03, "Medium"                         },
174 	{0x0f, 0x01, 0x03, 0x01, "Hard"                           },
175 	{0x0f, 0x01, 0x03, 0x00, "Hardest"                        },
176 
177 	{0   , 0xfe, 0   , 4   , "Magazines/Rockets"              },
178 	{0x0f, 0x01, 0x0c, 0x0c, "5/3"                            },
179 	{0x0f, 0x01, 0x0c, 0x08, "6/4"                            },
180 	{0x0f, 0x01, 0x0c, 0x04, "7/5"                            },
181 	{0x0f, 0x01, 0x0c, 0x00, "8/6"                            },
182 
183 	{0   , 0xfe, 0   , 4   , "Bullets per Magazine"           },
184 	{0x0f, 0x01, 0x30, 0x00, "30"                             },
185 	{0x0f, 0x01, 0x30, 0x10, "35"                             },
186 	{0x0f, 0x01, 0x30, 0x30, "40"                             },
187 	{0x0f, 0x01, 0x30, 0x20, "50"                             },
188 
189 	{0   , 0xfe, 0   , 2   , "language"                    },
190 	{0x0f, 0x01, 0x80, 0x00, "English"                        },
191 	{0x0f, 0x01, 0x80, 0x80, "Japanese"                       },
192 
193 	// Dip 3
194 	{0   , 0xfe, 0   , 4   , "Stereo Seperation"              },
195 	{0x10, 0x01, 0x07, 0x07, "Maximum"                        },
196 	{0x10, 0x01, 0x07, 0x03, "High"                           },
197 	{0x10, 0x01, 0x07, 0x01, "Medium"                         },
198 	{0x10, 0x01, 0x07, 0x00, "Low"                            },
199 };
200 
201 STDDIPINFO(Othunderj)
202 
203 static struct BurnDIPInfo OthunderuDIPList[]=
204 {
205 	// Default Values
206 	{0x0e, 0xff, 0xff, 0xff, NULL                             },
207 	{0x0f, 0xff, 0xff, 0x7f, NULL                             },
208 	{0x10, 0xff, 0xff, 0x07, NULL                             },
209 
210 	// Dip 1
211 	{0   , 0xfe, 0   , 2   , "Allow Continue"                 },
212 	{0x0e, 0x01, 0x02, 0x00, "Off"                            },
213 	{0x0e, 0x01, 0x02, 0x02, "On"                             },
214 
215 	{0   , 0xfe, 0   , 2   , "Service Mode"                   },
216 	{0x0e, 0x01, 0x04, 0x04, "Off"                            },
217 	{0x0e, 0x01, 0x04, 0x00, "On"                             },
218 
219 	{0   , 0xfe, 0   , 2   , "Demo Sounds"                    },
220 	{0x0e, 0x01, 0x08, 0x00, "Off"                            },
221 	{0x0e, 0x01, 0x08, 0x08, "On"                             },
222 
223 	{0   , 0xfe, 0   , 4   , "Coin A"                         },
224 	{0x0e, 0x01, 0x30, 0x10, "2 Coins 1 Credit"               },
225 	{0x0e, 0x01, 0x30, 0x30, "1 Coin  1 Credit"               },
226 	{0x0e, 0x01, 0x30, 0x00, "2 Coins 3 Credits"              },
227 	{0x0e, 0x01, 0x30, 0x20, "1 Coin  2 Credits"              },
228 
229 	{0   , 0xfe, 0   , 4   , "Coin B"                         },
230 	{0x0e, 0x01, 0xc0, 0x40, "2 Coins 1 Credit"               },
231 	{0x0e, 0x01, 0xc0, 0xc0, "1 Coin  1 Credit"               },
232 	{0x0e, 0x01, 0xc0, 0x00, "2 Coins 3 Credits"              },
233 	{0x0e, 0x01, 0xc0, 0x80, "1 Coin  2 Credits"              },
234 
235 	// Dip 2
236 	{0   , 0xfe, 0   , 4   , "Difficulty"                     },
237 	{0x0f, 0x01, 0x03, 0x02, "Easy"                           },
238 	{0x0f, 0x01, 0x03, 0x03, "Medium"                         },
239 	{0x0f, 0x01, 0x03, 0x01, "Hard"                           },
240 	{0x0f, 0x01, 0x03, 0x00, "Hardest"                        },
241 
242 	{0   , 0xfe, 0   , 4   , "Magazines/Rockets"              },
243 	{0x0f, 0x01, 0x0c, 0x0c, "5/3"                            },
244 	{0x0f, 0x01, 0x0c, 0x08, "6/4"                            },
245 	{0x0f, 0x01, 0x0c, 0x04, "7/5"                            },
246 	{0x0f, 0x01, 0x0c, 0x00, "8/6"                            },
247 
248 	{0   , 0xfe, 0   , 4   , "Bullets per Magazine"           },
249 	{0x0f, 0x01, 0x30, 0x00, "30"                             },
250 	{0x0f, 0x01, 0x30, 0x10, "35"                             },
251 	{0x0f, 0x01, 0x30, 0x30, "40"                             },
252 	{0x0f, 0x01, 0x30, 0x20, "50"                             },
253 
254 	{0   , 0xfe, 0   , 2   , "Continue Price"                 },
255 	{0x0f, 0x01, 0x40, 0x00, "1 Coin 1 Credit"                },
256 	{0x0f, 0x01, 0x40, 0x40, "Same as Start"                  },
257 
258 	{0   , 0xfe, 0   , 2   , "language"                    },
259 	{0x0f, 0x01, 0x80, 0x00, "English"                        },
260 	{0x0f, 0x01, 0x80, 0x80, "Japanese"                       },
261 
262 	// Dip 3
263 	{0   , 0xfe, 0   , 4   , "Stereo Seperation"              },
264 	{0x10, 0x01, 0x07, 0x07, "Maximum"                        },
265 	{0x10, 0x01, 0x07, 0x03, "High"                           },
266 	{0x10, 0x01, 0x07, 0x01, "Medium"                         },
267 	{0x10, 0x01, 0x07, 0x00, "Low"                            },
268 };
269 
270 STDDIPINFO(Othunderu)
271 
272 static struct BurnRomInfo OthunderRomDesc[] = {
273 	{ "b67-20-1.ic63",         0x20000, 0x851a453b, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
274 	{ "b67-23-1.ic64",         0x20000, 0x6e4f3d56, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
275 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
276 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
277 
278 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
279 
280 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
281 
282 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
283 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
284 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
285 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
286 
287 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
288 
289 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
290 
291 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
292 
293 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
294 
295 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
296 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
297 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
298 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
299 };
300 
301 STD_ROM_PICK(Othunder)
302 STD_ROM_FN(Othunder)
303 
304 static struct BurnRomInfo OthunderoRomDesc[] = {
305 	{ "b67-20.ic63",           0x20000, 0x21439ea2, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
306 	{ "b67-23.ic64",           0x20000, 0x789e9daa, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
307 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
308 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
309 
310 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
311 
312 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
313 
314 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
315 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
316 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
317 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
318 
319 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
320 
321 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
322 
323 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
324 
325 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
326 
327 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
328 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
329 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
330 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
331 };
332 
333 STD_ROM_PICK(Othundero)
334 STD_ROM_FN(Othundero)
335 
336 static struct BurnRomInfo OthunderuRomDesc[] = {
337 	{ "b67-20-1.ic63",         0x20000, 0x851a453b, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
338 	{ "b67-22-1.ic64",         0x20000, 0x19480dc0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
339 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
340 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
341 
342 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
343 
344 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
345 
346 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
347 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
348 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
349 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
350 
351 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
352 
353 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
354 
355 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
356 
357 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
358 
359 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
360 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
361 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
362 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
363 };
364 
365 STD_ROM_PICK(Othunderu)
366 STD_ROM_FN(Othunderu)
367 
368 static struct BurnRomInfo OthunderuoRomDesc[] = {
369 	{ "b67-20.ic63",           0x20000, 0x21439ea2, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
370 	{ "b67-22.ic64",           0x20000, 0x0f99ad3c, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
371 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
372 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
373 
374 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
375 
376 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
377 
378 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
379 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
380 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
381 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
382 
383 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
384 
385 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
386 
387 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
388 
389 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
390 
391 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
392 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
393 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
394 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
395 };
396 
397 STD_ROM_PICK(Othunderuo)
398 STD_ROM_FN(Othunderuo)
399 
400 static struct BurnRomInfo OthunderjRomDesc[] = {
401 	{ "b67-20.ic63",           0x20000, 0x21439ea2, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
402 	{ "b67-21.ic64",           0x20000, 0x9690fc86, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
403 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
404 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
405 
406 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
407 
408 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
409 
410 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
411 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
412 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
413 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
414 
415 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
416 
417 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
418 
419 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
420 
421 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
422 
423 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
424 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
425 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
426 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
427 };
428 
429 STD_ROM_PICK(Othunderj)
430 STD_ROM_FN(Othunderj)
431 
432 static struct BurnRomInfo OthunderjscRomDesc[] = {
433 	// SC stands for Shopping Center. It was put in a smaller, single player cabinet aimed at children
434 	{ "b67-24.ic63",           0x20000, 0x18670e0b, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
435 	{ "b67-25.ic64",           0x20000, 0x3d422991, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
436 	{ "b67-14.ic61",           0x20000, 0x7f3dd724, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
437 	{ "b67-15.ic62",           0x20000, 0xe84f62d0, BRF_ESS | BRF_PRG | TAITO_68KROM1_BYTESWAP },
438 
439 	{ "b67-13.ic40",           0x10000, 0x2936b4b1, BRF_ESS | BRF_PRG | TAITO_Z80ROM1 },
440 
441 	{ "b67-06.ic66",           0x80000, 0xb9a38d64, BRF_GRA | TAITO_CHARS},
442 
443 	{ "b67-01.ic1",            0x80000, 0x81ad9acb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
444 	{ "b67-02.ic2",            0x80000, 0xc20cd2fb, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
445 	{ "b67-03.ic3",            0x80000, 0xbc9019ed, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
446 	{ "b67-04.ic4",            0x80000, 0x2af4c8af, BRF_GRA | TAITO_SPRITESA_BYTESWAP32 },
447 
448 	{ "b67-05.ic43",           0x80000, 0x9593e42b, BRF_GRA | TAITO_SPRITEMAP },
449 
450 	{ "b67-08.ic67",           0x80000, 0x458f41fb, BRF_SND | TAITO_YM2610A },
451 
452 	{ "b67-07.ic44",           0x80000, 0x4f834357, BRF_SND | TAITO_YM2610B },
453 
454 	{ "93c46_eeprom-othunder.ic86",   0x00080, 0x3729b844, BRF_PRG | TAITO_DEFAULT_EEPROM },
455 
456 	{ "plhs18p8b-b67-09.ic15", 0x00149, 0x62035487, BRF_OPT },
457 	{ "pal16l8a-b67-11.ic36",  0x00104, 0x3177fb06, BRF_OPT },
458 	{ "pal20l8b-b67-12.ic37",  0x00144, 0xa47c2798, BRF_OPT },
459 	{ "pal20l8b-b67-10.ic33",  0x00144, 0x4ced09c7, BRF_OPT },
460 };
461 
462 STD_ROM_PICK(Othunderjsc)
STD_ROM_FN(Othunderjsc)463 STD_ROM_FN(Othunderjsc)
464 
465 static INT32 MemIndex()
466 {
467 	UINT8 *Next; Next = TaitoMem;
468 
469 	Taito68KRom1                   = Next; Next += Taito68KRom1Size;
470 	TaitoZ80Rom1                   = Next; Next += TaitoZ80Rom1Size;
471 	TaitoSpriteMapRom              = Next; Next += TaitoSpriteMapRomSize;
472 	TaitoYM2610ARom                = Next; Next += TaitoYM2610ARomSize;
473 	TaitoYM2610BRom                = Next; Next += TaitoYM2610BRomSize;
474 	TaitoDefaultEEProm             = Next; Next += TaitoDefaultEEPromSize;
475 
476 	TaitoRamStart                  = Next;
477 
478 	Taito68KRam1                   = Next; Next += 0x10000;
479 	TaitoZ80Ram1                   = Next; Next += 0x02000;
480 	TaitoSpriteRam                 = Next; Next += 0x00600;
481 	OthunderPan                    = Next; Next += 0x00004;
482 
483 	TaitoRamEnd                    = Next;
484 
485 	TaitoChars                     = Next; Next += TaitoNumChar * TaitoCharWidth * TaitoCharHeight;
486 	TaitoSpritesA                  = Next; Next += TaitoNumSpriteA * TaitoSpriteAWidth * TaitoSpriteAHeight;
487 	TaitoPalette                   = (UINT32*)Next; Next += 0x01000 * sizeof(UINT32);
488 
489 	TaitoMemEnd                    = Next;
490 
491 	return 0;
492 }
493 
OthunderDoReset()494 static INT32 OthunderDoReset()
495 {
496 	TaitoDoReset();
497 
498 	return 0;
499 }
500 
OthunderInputBypassRead(INT32 Offset)501 static UINT8 OthunderInputBypassRead(INT32 Offset)
502 {
503 	switch (Offset) {
504 		case 0x03: {
505 			return (EEPROMRead() & 1)  << 7;
506 		}
507 
508 		default: {
509 			return TC0220IOCRead(Offset);
510 		}
511 	}
512 
513 	return 0;
514 }
515 
OthunderInputBypassWrite(INT32 Offset,UINT16 Data)516 static void OthunderInputBypassWrite(INT32 Offset, UINT16 Data)
517 {
518 	switch (Offset) {
519 		case 0x03: {
520 			EEPROMWrite(Data & 0x20, Data & 0x10, Data & 0x40);
521 			return;
522 		}
523 
524 		default: {
525 			TC0220IOCWrite(Offset, Data & 0xff);
526 		}
527 	}
528 }
529 
Othunder68KReadByte(UINT32 a)530 UINT8 __fastcall Othunder68KReadByte(UINT32 a)
531 {
532 	switch (a) {
533 		case 0x500001: {
534 			return ~BurnGunReturnX(0);
535 		}
536 
537 		case 0x500003: {
538 			return BurnGunReturnY(0);
539 		}
540 
541 		case 0x500005: {
542 			return ~BurnGunReturnX(1);
543 		}
544 
545 		case 0x500007: {
546 			return BurnGunReturnY(1);
547 		}
548 
549 		default: {
550 			bprintf(PRINT_NORMAL, _T("68K #1 Read byte => %06X\n"), a);
551 		}
552 	}
553 
554 	return 0;
555 }
556 
Othunder68KWriteByte(UINT32 a,UINT8 d)557 void __fastcall Othunder68KWriteByte(UINT32 a, UINT8 d)
558 {
559 	TC0100SCN0ByteWrite_Map(0x200000, 0x20ffff)
560 
561 	switch (a) {
562 		case 0x500001:
563 		case 0x500003:
564 		case 0x500005:
565 		case 0x500007: {
566 			nTaitoCyclesDone[0] += SekRun(10);
567 			SekSetIRQLine(6, CPU_IRQSTATUS_AUTO);
568 			return;
569 		}
570 
571 		default: {
572 			bprintf(PRINT_NORMAL, _T("68K #1 Write byte => %06X, %02X\n"), a, d);
573 		}
574 	}
575 }
576 
Othunder68KReadWord(UINT32 a)577 UINT16 __fastcall Othunder68KReadWord(UINT32 a)
578 {
579 	switch (a) {
580 		case 0x090000:
581 		case 0x090002:
582 		case 0x090004:
583 		case 0x090006:
584 		case 0x090008:
585 		case 0x09000a:
586 		case 0x09000c:
587 		case 0x09000e: {
588 			return OthunderInputBypassRead((a - 0x090000) >> 1);
589 		}
590 
591 		case 0x100002: {
592 			return TC0110PCRWordRead(0);
593 		}
594 
595 		default: {
596 			bprintf(PRINT_NORMAL, _T("68K #1 Read word => %06X\n"), a);
597 		}
598 	}
599 
600 	return 0;
601 }
602 
Othunder68KWriteWord(UINT32 a,UINT16 d)603 void __fastcall Othunder68KWriteWord(UINT32 a, UINT16 d)
604 {
605 	TC0100SCN0WordWrite_Map(0x200000, 0x20ffff)
606 	TC0100SCN0CtrlWordWrite_Map(0x220000)
607 
608 	switch (a) {
609 		case 0x090000:
610 		case 0x090002:
611 		case 0x090004:
612 		case 0x090006:
613 		case 0x090008:
614 		case 0x09000a:
615 		case 0x09000c:
616 		case 0x09000e: {
617 			OthunderInputBypassWrite((a - 0x090000) >> 1, d);
618 			return;
619 		}
620 
621 		case 0x100000:
622 		case 0x100002:
623 		case 0x100004: {
624 			TC0110PCRStep1RBSwapWordWrite(0, (a - 0x100000) >> 1, d);
625 			return;
626 		}
627 
628 		case 0x300000: {
629 			TC0140SYTPortWrite(d & 0xff);
630 			return;
631 		}
632 
633 		case 0x300002: {
634 			TC0140SYTCommWrite(d & 0xff);
635 			return;
636 		}
637 
638 		case 0x500000:
639 		case 0x500002:
640 		case 0x500004:
641 		case 0x500006: {
642 			nTaitoCyclesDone[0] += SekRun(10);
643 			SekSetIRQLine(6, CPU_IRQSTATUS_AUTO);
644 			return;
645 		}
646 
647 		case 0x600000:
648 		case 0x600002: {
649 			// irq ack
650 			return;
651 		}
652 
653 		default: {
654 			bprintf(PRINT_NORMAL, _T("68K #1 Write word => %06X, %04X\n"), a, d);
655 		}
656 	}
657 }
658 
OthunderZ80Read(UINT16 a)659 UINT8 __fastcall OthunderZ80Read(UINT16 a)
660 {
661 	switch (a) {
662 		case 0xe000: {
663 			return BurnYM2610Read(0);
664 		}
665 
666 		case 0xe002: {
667 			return BurnYM2610Read(2);
668 		}
669 
670 		case 0xe201: {
671 			return TC0140SYTSlaveCommRead();
672 		}
673 
674 		case 0xea00: {
675 			return TaitoDip[0];
676 		}
677 
678 		default: {
679 			bprintf(PRINT_NORMAL, _T("Z80 Read => %04X\n"), a);
680 		}
681 	}
682 
683 	return 0;
684 }
685 
OthunderZ80Write(UINT16 a,UINT8 d)686 void __fastcall OthunderZ80Write(UINT16 a, UINT8 d)
687 {
688 	switch (a) {
689 		case 0xe000: {
690 			BurnYM2610Write(0, d);
691 			return;
692 		}
693 
694 		case 0xe001: {
695 			BurnYM2610Write(1, d);
696 			return;
697 		}
698 
699 		case 0xe002: {
700 			BurnYM2610Write(2, d);
701 			return;
702 		}
703 
704 		case 0xe003: {
705 			BurnYM2610Write(3, d);
706 			return;
707 		}
708 
709 		case 0xe200: {
710 			TC0140SYTSlavePortWrite(d);
711 			return;
712 		}
713 
714 		case 0xe201: {
715 			TC0140SYTSlaveCommWrite(d);
716 			return;
717 		}
718 
719 		case 0xe400:
720 		case 0xe401:
721 		case 0xe402:
722 		case 0xe403: {
723 			INT32 lVol, rVol;
724 
725 			OthunderPan[a & 0x0003] = d & 0x1f;
726 
727 			rVol = (OthunderPan[0] + OthunderPan[2]) * 100 / (2 * 0x1f);
728 			lVol = (OthunderPan[1] + OthunderPan[3]) * 100 / (2 * 0x1f);
729 			BurnYM2610SetLeftVolume(BURN_SND_YM2610_AY8910_ROUTE, OthunderYM2610AY8910RouteMasterVol * lVol / 100.0);
730 			BurnYM2610SetRightVolume(BURN_SND_YM2610_AY8910_ROUTE, OthunderYM2610AY8910RouteMasterVol * rVol / 100.0);
731 
732 			rVol = OthunderPan[0] * 100 / 0x1f;
733 			lVol = OthunderPan[1] * 100 / 0x1f;
734 			if (rVol == 0) rVol = 100; // Fixes player gunshot can only be heard through the left speaker.
735 			BurnYM2610SetLeftVolume(BURN_SND_YM2610_YM2610_ROUTE_1, OthunderYM2610Route1MasterVol * lVol / 100.0);
736 			BurnYM2610SetRightVolume(BURN_SND_YM2610_YM2610_ROUTE_1, OthunderYM2610Route1MasterVol * rVol / 100.0);
737 
738 			/* CH2 */
739 			rVol = OthunderPan[2] * 100 / 0x1f;
740 			lVol = OthunderPan[3] * 100 / 0x1f;
741 			BurnYM2610SetLeftVolume(BURN_SND_YM2610_YM2610_ROUTE_2, OthunderYM2610Route2MasterVol * lVol / 100.0);
742 			BurnYM2610SetRightVolume(BURN_SND_YM2610_YM2610_ROUTE_2, OthunderYM2610Route2MasterVol * rVol / 100.0);
743 			return;
744 		}
745 
746 		case 0xe600: {
747 			// nop
748 			return;
749 		}
750 
751 		case 0xee00: {
752 			// nop
753 			return;
754 		}
755 
756 		case 0xf000: {
757 			// nop
758 			return;
759 		}
760 
761 		case 0xf200: {
762 			TaitoZ80Bank = (d - 1) & 7;
763 			ZetMapArea(0x4000, 0x7fff, 0, TaitoZ80Rom1 + 0x4000 + (TaitoZ80Bank * 0x4000));
764 			ZetMapArea(0x4000, 0x7fff, 2, TaitoZ80Rom1 + 0x4000 + (TaitoZ80Bank * 0x4000));
765 			return;
766 		}
767 
768 		default: {
769 			bprintf(PRINT_NORMAL, _T("Z80 Write => %04X, %02X\n"), a, d);
770 		}
771 	}
772 }
773 
774 static const eeprom_interface othunder_eeprom_interface = {
775 	6,				/* address bits */
776 	16,				/* data bits */
777 	"0110",			/* read command */
778 	"0101",			/* write command */
779 	"0111",			/* erase command */
780 	"0100000000",	/* lock command */
781 	"0100111111",	/* unlock command */
782 	0,
783 	0
784 };
785 
786 static const UINT8 othunder_default_eeprom[128] = {
787 	0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x41, 0x41, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xf0, 0xf0,
788 	0x00, 0x00, 0x00, 0xff, 0x00, 0x01, 0x41, 0x41, 0x00, 0x00, 0x00, 0xff, 0x00, 0x00, 0xf0, 0xf0,
789 	0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x80, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0xf0, 0x00,
790 	0x00, 0x01, 0x42, 0x85, 0x00, 0x00, 0xf1, 0xe3, 0x00, 0x01, 0x40, 0x00, 0x00, 0x00, 0xf0, 0x00,
791 	0x00, 0x01, 0x42, 0x85, 0x00, 0x00, 0xf1, 0xe3, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
792 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
793 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff,
794 	0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff, 0xff
795 };
796 
OthunderFMIRQHandler(INT32,INT32 nStatus)797 static void OthunderFMIRQHandler(INT32, INT32 nStatus)
798 {
799 	ZetSetIRQLine(0, (nStatus) ? CPU_IRQSTATUS_ACK : CPU_IRQSTATUS_NONE);
800 }
801 
802 static INT32 CharPlaneOffsets[4]   = { 0, 1, 2, 3 };
803 static INT32 CharXOffsets[8]       = { 8, 12, 0, 4, 24, 28, 16, 20 };
804 static INT32 CharYOffsets[8]       = { 0, 32, 64, 96, 128, 160, 192, 224 };
805 static INT32 SpritePlaneOffsets[4] = { 0, 8, 16, 24 };
806 static INT32 SpriteXOffsets[16]    = { 32, 33, 34, 35, 36, 37, 38, 39, 0, 1, 2, 3, 4, 5, 6, 7 };
807 static INT32 SpriteYOffsets[8]     = { 0, 64, 128, 192, 256, 320, 384, 448 };
808 
809 #ifdef BUILD_A68K
SwitchToMusashi()810 static void SwitchToMusashi()
811 {
812 	if (bBurnUseASMCPUEmulation) {
813 #if 1 && defined FBA_DEBUG
814 		bprintf(PRINT_NORMAL, _T("Switching to Musashi 68000 core\n"));
815 #endif
816 		bUseAsm68KCoreOldValue = bBurnUseASMCPUEmulation;
817 		bBurnUseASMCPUEmulation = false;
818 	}
819 }
820 #endif
821 
OthunderInit()822 static INT32 OthunderInit()
823 {
824 	INT32 nLen;
825 
826 	TaitoCharModulo = 0x100;
827 	TaitoCharNumPlanes = 4;
828 	TaitoCharWidth = 8;
829 	TaitoCharHeight = 8;
830 	TaitoCharPlaneOffsets = CharPlaneOffsets;
831 	TaitoCharXOffsets = CharXOffsets;
832 	TaitoCharYOffsets = CharYOffsets;
833 	TaitoNumChar = 0x4000;
834 
835 	TaitoSpriteAModulo = 0x200;
836 	TaitoSpriteANumPlanes = 4;
837 	TaitoSpriteAWidth = 16;
838 	TaitoSpriteAHeight = 8;
839 	TaitoSpriteAPlaneOffsets = SpritePlaneOffsets;
840 	TaitoSpriteAXOffsets = SpriteXOffsets;
841 	TaitoSpriteAYOffsets = SpriteYOffsets;
842 	TaitoNumSpriteA = 0x8000;
843 
844 	TaitoNum68Ks = 1;
845 	TaitoNumZ80s = 1;
846 	TaitoNumYM2610 = 1;
847 	TaitoNumEEPROM = 1;
848 
849 	TaitoLoadRoms(0);
850 
851 	// Allocate and Blank all required memory
852 	TaitoMem = NULL;
853 	MemIndex();
854 	nLen = TaitoMemEnd - (UINT8 *)0;
855 	if ((TaitoMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
856 	memset(TaitoMem, 0, nLen);
857 	MemIndex();
858 
859 	GenericTilesInit();
860 
861 	TC0100SCNInit(0, TaitoNumChar, 4, 8, 1, NULL);
862 	TC0110PCRInit(1, 0x1000);
863 	TC0140SYTInit(0);
864 	TC0220IOCInit();
865 
866 	if (TaitoLoadRoms(1)) return 1;
867 
868 #ifdef BUILD_A68K
869 	SwitchToMusashi();
870 #endif
871 
872 	// Setup the 68000 emulation
873 	SekInit(0, 0x68000);
874 	SekOpen(0);
875 	SekMapMemory(Taito68KRom1            , 0x000000, 0x07ffff, MAP_ROM);
876 	SekMapMemory(Taito68KRam1            , 0x080000, 0x08ffff, MAP_RAM);
877 	SekMapMemory(TC0100SCNRam[0]         , 0x200000, 0x20ffff, MAP_READ);
878 	SekMapMemory(TaitoSpriteRam          , 0x400000, 0x4005ff, MAP_RAM);
879 	SekSetReadWordHandler(0, Othunder68KReadWord);
880 	SekSetWriteWordHandler(0, Othunder68KWriteWord);
881 	SekSetReadByteHandler(0, Othunder68KReadByte);
882 	SekSetWriteByteHandler(0, Othunder68KWriteByte);
883 	SekClose();
884 
885 	ZetInit(0);
886 	ZetOpen(0);
887 	ZetSetReadHandler(OthunderZ80Read);
888 	ZetSetWriteHandler(OthunderZ80Write);
889 	ZetMapArea(0x0000, 0x3fff, 0, TaitoZ80Rom1               );
890 	ZetMapArea(0x0000, 0x3fff, 2, TaitoZ80Rom1               );
891 	ZetMapArea(0x4000, 0x7fff, 0, TaitoZ80Rom1 + 0x4000      );
892 	ZetMapArea(0x4000, 0x7fff, 2, TaitoZ80Rom1 + 0x4000      );
893 	ZetMapArea(0xc000, 0xdfff, 0, TaitoZ80Ram1               );
894 	ZetMapArea(0xc000, 0xdfff, 1, TaitoZ80Ram1               );
895 	ZetMapArea(0xc000, 0xdfff, 2, TaitoZ80Ram1               );
896 	ZetClose();
897 
898 	BurnYM2610Init(16000000 / 2, TaitoYM2610ARom, (INT32*)&TaitoYM2610ARomSize, TaitoYM2610BRom, (INT32*)&TaitoYM2610BRomSize, &OthunderFMIRQHandler, 0);
899 	BurnTimerAttachZet(16000000 / 4);
900 	OthunderYM2610AY8910RouteMasterVol = 0.25;
901 	OthunderYM2610Route1MasterVol = 1.00;
902 	OthunderYM2610Route2MasterVol = 1.00;
903 	bYM2610UseSeperateVolumes = 1;
904 
905 	EEPROMInit(&othunder_eeprom_interface);
906 	if (!EEPROMAvailable()) EEPROMFill(TaitoDefaultEEProm, 0, 128);
907 
908 	TaitoMakeInputsFunction = OthunderMakeInputs;
909 	TaitoIrqLine = 5;
910 	TaitoFrameInterleave = 100;
911 	TaitoFlipScreenX = 1;
912 
913 	nTaitoCyclesTotal[0] = 13000000 / 60;
914 	nTaitoCyclesTotal[1] = (16000000 / 4) / 60;
915 
916 	BurnGunInit(2, true);
917 
918 	// Reset the driver
919 	OthunderDoReset();
920 
921 	return 0;
922 }
923 
OthunderExit()924 static INT32 OthunderExit()
925 {
926 	TaitoExit();
927 
928 #ifdef BUILD_A68K
929 	// Switch back CPU core if needed
930 	if (bUseAsm68KCoreOldValue) {
931 #if 1 && defined FBA_DEBUG
932 		bprintf(PRINT_NORMAL, _T("Switching back to A68K core\n"));
933 #endif
934 		bUseAsm68KCoreOldValue = false;
935 		bBurnUseASMCPUEmulation = true;
936 	}
937 #endif
938 
939 	return 0;
940 }
941 
RenderSpriteZoom(INT32 Code,INT32 sx,INT32 sy,INT32 Colour,INT32 xFlip,INT32 yFlip,INT32 xScale,INT32 yScale,UINT8 * pSource)942 static void RenderSpriteZoom(INT32 Code, INT32 sx, INT32 sy, INT32 Colour, INT32 xFlip, INT32 yFlip, INT32 xScale, INT32 yScale, UINT8* pSource)
943 {
944 	// We can use sprite A for sizes, etc. as only Chase HQ uses sprite B and it has the same sizes and count
945 
946 	UINT8 *SourceBase = pSource + ((Code % TaitoNumSpriteA) * TaitoSpriteAWidth * TaitoSpriteAHeight);
947 
948 	INT32 SpriteScreenHeight = (yScale * TaitoSpriteAHeight + 0x8000) >> 16;
949 	INT32 SpriteScreenWidth = (xScale * TaitoSpriteAWidth + 0x8000) >> 16;
950 
951 	Colour = 0x10 * (Colour % 0x100);
952 
953 	if (TaitoFlipScreenX) {
954 		xFlip = !xFlip;
955 		sx = 320 - sx - (xScale >> 12);
956 	}
957 
958 	if (SpriteScreenWidth && SpriteScreenHeight) {
959 		INT32 dx = (TaitoSpriteAWidth << 16) / SpriteScreenWidth;
960 		INT32 dy = (TaitoSpriteAHeight << 16) / SpriteScreenHeight;
961 
962 		INT32 ex = sx + SpriteScreenWidth;
963 		INT32 ey = sy + SpriteScreenHeight;
964 
965 		INT32 xIndexBase;
966 		INT32 yIndex;
967 
968 		if (xFlip) {
969 			xIndexBase = (SpriteScreenWidth - 1) * dx;
970 			dx = -dx;
971 		} else {
972 			xIndexBase = 0;
973 		}
974 
975 		if (yFlip) {
976 			yIndex = (SpriteScreenHeight - 1) * dy;
977 			dy = -dy;
978 		} else {
979 			yIndex = 0;
980 		}
981 
982 		if (sx < 0) {
983 			INT32 Pixels = 0 - sx;
984 			sx += Pixels;
985 			xIndexBase += Pixels * dx;
986 		}
987 
988 		if (sy < 0) {
989 			INT32 Pixels = 0 - sy;
990 			sy += Pixels;
991 			yIndex += Pixels * dy;
992 		}
993 
994 		if (ex > nScreenWidth) {
995 			INT32 Pixels = ex - nScreenWidth;
996 			ex -= Pixels;
997 		}
998 
999 		if (ey > nScreenHeight) {
1000 			INT32 Pixels = ey - nScreenHeight;
1001 			ey -= Pixels;
1002 		}
1003 
1004 		if (ex > sx) {
1005 			INT32 y;
1006 
1007 			for (y = sy; y < ey; y++) {
1008 				UINT8 *Source = SourceBase + ((yIndex >> 16) * TaitoSpriteAWidth);
1009 				UINT16* pPixel = pTransDraw + (y * nScreenWidth);
1010 
1011 				INT32 x, xIndex = xIndexBase;
1012 				for (x = sx; x < ex; x++) {
1013 					INT32 c = Source[xIndex >> 16];
1014 					if (c != 0) {
1015 						pPixel[x] = c | Colour;
1016 					}
1017 					xIndex += dx;
1018 				}
1019 
1020 				yIndex += dy;
1021 			}
1022 		}
1023 	}
1024 }
1025 
OthunderRenderSprites(INT32 PriorityDraw)1026 static void OthunderRenderSprites(INT32 PriorityDraw)
1027 {
1028 	UINT16 *SpriteMap = (UINT16*)TaitoSpriteMapRom;
1029 	UINT16 *SpriteRam = (UINT16*)TaitoSpriteRam;
1030 	INT32 Offset, Data, Tile, Colour, xFlip, yFlip;
1031 	INT32 x, y, Priority, xCur, yCur;
1032 	INT32 xZoom, yZoom, zx, zy;
1033 	INT32 SpriteChunk, MapOffset, Code, j, k, px, py;
1034 	INT32 BadChunks;
1035 
1036 	for (Offset = 0x300 - 4; Offset >= 0; Offset -= 4) {
1037 		Data = BURN_ENDIAN_SWAP_INT16(SpriteRam[Offset + 1]);
1038 		Priority = (Data & 0x8000) >> 15;
1039 
1040 		if (Priority != 0 && Priority != 1) bprintf(PRINT_NORMAL, _T("Unused Priority %x\n"), Priority);
1041 		if (Priority != PriorityDraw) continue;
1042 
1043 		xFlip = (Data & 0x4000) >> 14;
1044 		x = Data & 0x1ff;
1045 
1046 		Data = BURN_ENDIAN_SWAP_INT16(SpriteRam[Offset + 3]);
1047 		yFlip = (Data & 0x8000) >> 15;
1048 		Tile = Data & 0x1fff;
1049 		if (!Tile) continue;
1050 
1051 		Data = BURN_ENDIAN_SWAP_INT16(SpriteRam[Offset + 0]);
1052 		yZoom = (Data & 0xfe00) >> 9;
1053 		y = Data & 0x1ff;
1054 
1055 		Data = BURN_ENDIAN_SWAP_INT16(SpriteRam[Offset + 2]);
1056 		Colour = (Data & 0xff00) >> 8;
1057 		xZoom = (Data & 0x7f);
1058 
1059 		MapOffset = Tile << 5;
1060 
1061 		xZoom += 1;
1062 		yZoom += 1;
1063 
1064 		y += 3;
1065 
1066 		if (x > 0x140) x -= 0x200;
1067 		if (y > 0x140) y -= 0x200;
1068 
1069 		BadChunks = 0;
1070 
1071 		for (SpriteChunk = 0; SpriteChunk < 32; SpriteChunk++) {
1072 			k = SpriteChunk % 4;
1073 			j = SpriteChunk / 4;
1074 
1075 			px = xFlip ? (3 - k) : k;
1076 			py = yFlip ? (7 - j) : j;
1077 
1078 			Code = BURN_ENDIAN_SWAP_INT16(SpriteMap[MapOffset + px + (py << 2)]);
1079 			Code &= (TaitoNumSpriteA - 1);
1080 
1081 			if (Code == 0xffff) {
1082 				BadChunks += 1;
1083 				continue;
1084 			}
1085 
1086 			xCur = x + ((k * xZoom) / 4);
1087 			yCur = y + ((j * yZoom) / 8);
1088 
1089 			zx = x + (((k + 1) * xZoom) / 4) - xCur;
1090 			zy = y + (((j + 1) * yZoom) / 8) - yCur;
1091 
1092 			yCur -= 16;
1093 
1094 			RenderSpriteZoom(Code, xCur, yCur, Colour, xFlip, yFlip, zx << 12, zy << 13, TaitoSpritesA);
1095 		}
1096 
1097 	}
1098 }
1099 
OthunderDraw()1100 static INT32 OthunderDraw()
1101 {
1102 	INT32 Disable = TC0100SCNCtrl[0][6] & 0xf7;
1103 
1104 	BurnTransferClear();
1105 
1106 	if (TC0100SCNBottomLayer(0)) {
1107 		if (!(Disable & 0x02)) TC0100SCNRenderFgLayer(0, 1, TaitoChars);
1108 		OthunderRenderSprites(1);
1109 		if (!(Disable & 0x01)) TC0100SCNRenderBgLayer(0, 0, TaitoChars);
1110 	} else {
1111 		if (!(Disable & 0x01)) TC0100SCNRenderBgLayer(0, 1, TaitoChars);
1112 		OthunderRenderSprites(1);
1113 		if (!(Disable & 0x02)) TC0100SCNRenderFgLayer(0, 0, TaitoChars);
1114 	}
1115 
1116 	OthunderRenderSprites(0);
1117 
1118 	if (!(Disable & 0x04)) TC0100SCNRenderCharLayer(0);
1119 	BurnTransferCopy(TC0110PCRPalette);
1120 
1121 	for (INT32 i = 0; i < nBurnGunNumPlayers; i++) {
1122 		BurnGunDrawTarget(i, BurnGunX[i] >> 8, BurnGunY[i] >> 8);
1123 	}
1124 
1125 	return 0;
1126 }
1127 
OthunderFrame()1128 static INT32 OthunderFrame()
1129 {
1130 	INT32 nInterleave = TaitoFrameInterleave;
1131 
1132 	if (TaitoReset) OthunderDoReset();
1133 
1134 	TaitoMakeInputsFunction();
1135 
1136 	nTaitoCyclesDone[0] = nTaitoCyclesDone[1] = 0;
1137 
1138 	SekNewFrame();
1139 	ZetNewFrame();
1140 
1141 	for (INT32 i = 0; i < nInterleave; i++) {
1142 		INT32 nCurrentCPU, nNext;
1143 
1144 		// Run 68000 #1
1145 		nCurrentCPU = 0;
1146 		SekOpen(0);
1147 		nNext = (i + 1) * nTaitoCyclesTotal[nCurrentCPU] / nInterleave;
1148 		nTaitoCyclesSegment = nNext - nTaitoCyclesDone[nCurrentCPU];
1149 		nTaitoCyclesDone[nCurrentCPU] += SekRun(nTaitoCyclesSegment);
1150 		if (i == (TaitoFrameInterleave - 1)) SekSetIRQLine(TaitoIrqLine, CPU_IRQSTATUS_AUTO);
1151 		SekClose();
1152 
1153 		ZetOpen(0);
1154 		BurnTimerUpdate(i * (nTaitoCyclesTotal[1] / nInterleave));
1155 		ZetClose();
1156 	}
1157 
1158 	ZetOpen(0);
1159 	BurnTimerEndFrame(nTaitoCyclesTotal[1]);
1160 	if (pBurnSoundOut) {
1161 		BurnYM2610Update(pBurnSoundOut, nBurnSoundLen);
1162 	}
1163 	ZetClose();
1164 
1165 	if (pBurnDraw) BurnDrvRedraw();
1166 
1167 	return 0;
1168 }
1169 
OthunderScan(INT32 nAction,INT32 * pnMin)1170 static INT32 OthunderScan(INT32 nAction, INT32 *pnMin)
1171 {
1172 	struct BurnArea ba;
1173 
1174 	if (pnMin != NULL) {			// Return minimum compatible version
1175 		*pnMin = 0x029709;
1176 	}
1177 
1178 	if (nAction & ACB_MEMORY_RAM) {
1179 		memset(&ba, 0, sizeof(ba));
1180 		ba.Data	  = TaitoRamStart;
1181 		ba.nLen	  = TaitoRamEnd-TaitoRamStart;
1182 		ba.szName = "All Ram";
1183 		BurnAcb(&ba);
1184 	}
1185 
1186 	TaitoICScan(nAction);
1187 
1188 	if (nAction & ACB_DRIVER_DATA) {
1189 		SekScan(nAction);
1190 		ZetScan(nAction);
1191 
1192 		BurnYM2610Scan(nAction, pnMin);
1193 
1194 		BurnGunScan();
1195 
1196 		SCAN_VAR(TaitoInput);
1197 		SCAN_VAR(TaitoZ80Bank);
1198 		SCAN_VAR(nTaitoCyclesDone);
1199 		SCAN_VAR(nTaitoCyclesSegment);
1200 	}
1201 
1202 	if (nAction & ACB_WRITE) {
1203 		if (TaitoZ80Bank) {
1204 			ZetOpen(0);
1205 			ZetMapArea(0x4000, 0x7fff, 0, TaitoZ80Rom1 + 0x4000 + (TaitoZ80Bank * 0x4000));
1206 			ZetMapArea(0x4000, 0x7fff, 2, TaitoZ80Rom1 + 0x4000 + (TaitoZ80Bank * 0x4000));
1207 			ZetClose();
1208 		}
1209 	}
1210 
1211 	return 0;
1212 }
1213 
1214 struct BurnDriver BurnDrvOthunder = {
1215 	"othunder", NULL, NULL, NULL, "1988",
1216 	"Operation Thunderbolt (World, rev 1)\0", NULL, "Taito Corporation Japan", "Taito Misc",
1217 	NULL, NULL, NULL, NULL,
1218 	BDF_GAME_WORKING, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1219 	NULL, OthunderRomInfo, OthunderRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderDIPInfo,
1220 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1221 	NULL, 0x1000, 320, 240, 4, 3
1222 };
1223 
1224 struct BurnDriver BurnDrvOthundero = {
1225 	"othundero", "othunder", NULL, NULL, "1988",
1226 	"Operation Thunderbolt (World)\0", NULL, "Taito Corporation Japan", "Taito Misc",
1227 	NULL, NULL, NULL, NULL,
1228 	BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1229 	NULL, OthunderoRomInfo, OthunderoRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderuDIPInfo,
1230 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1231 	NULL, 0x1000, 320, 240, 4, 3
1232 };
1233 
1234 struct BurnDriver BurnDrvOthunderu = {
1235 	"othunderu", "othunder", NULL, NULL, "1988",
1236 	"Operation Thunderbolt (US, rev 1)\0", NULL, "Taito America Corporation", "Taito Misc",
1237 	NULL, NULL, NULL, NULL,
1238 	BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1239 	NULL, OthunderuRomInfo, OthunderuRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderuDIPInfo,
1240 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1241 	NULL, 0x1000, 320, 240, 4, 3
1242 };
1243 
1244 struct BurnDriver BurnDrvOthunderuo = {
1245 	"othunderuo", "othunder", NULL, NULL, "1988",
1246 	"Operation Thunderbolt (US)\0", NULL, "Taito America Corporation", "Taito Misc",
1247 	NULL, NULL, NULL, NULL,
1248 	BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1249 	NULL, OthunderuoRomInfo, OthunderuoRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderuDIPInfo,
1250 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1251 	NULL, 0x1000, 320, 240, 4, 3
1252 };
1253 
1254 struct BurnDriver BurnDrvOthunderj = {
1255 	"othunderj", "othunder", NULL, NULL, "1988",
1256 	"Operation Thunderbolt (Japan)\0", NULL, "Taito Corporation", "Taito Misc",
1257 	NULL, NULL, NULL, NULL,
1258 	BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1259 	NULL, OthunderjRomInfo, OthunderjRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderjDIPInfo,
1260 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1261 	NULL, 0x1000, 320, 240, 4, 3
1262 };
1263 
1264 struct BurnDriver BurnDrvOthunderjsc = {
1265 	"othunderjsc", "othunder", NULL, NULL, "1988",
1266 	"Operation Thunderbolt (Japan, SC)\0", NULL, "Taito Corporation", "Taito Misc",
1267 	NULL, NULL, NULL, NULL,
1268 	BDF_GAME_WORKING | BDF_CLONE, 2, HARDWARE_TAITO_MISC, GBF_SHOOT, 0,
1269 	NULL, OthunderjscRomInfo, OthunderjscRomName, NULL, NULL, NULL, NULL, OthunderInputInfo, OthunderjDIPInfo,
1270 	OthunderInit, OthunderExit, OthunderFrame, OthunderDraw, OthunderScan,
1271 	NULL, 0x1000, 320, 240, 4, 3
1272 };