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