1 // FinalBurn Neo Stunt Air driver module
2 // Based on MAME driver by David Haywood
3 
4 #include "tiles_generic.h"
5 #include "z80_intf.h"
6 #include "ay8910.h"
7 #include "watchdog.h"
8 
9 static UINT8 *AllMem;
10 static UINT8 *AllRam;
11 static UINT8 *RamEnd;
12 static UINT8 *MemEnd;
13 static UINT8 *DrvZ80ROM[2];
14 static UINT8 *DrvGfxROM[3];
15 static UINT8 *DrvColPROM;
16 static UINT8 *DrvNVRAM;
17 static UINT8 *DrvBgARAM;
18 static UINT8 *DrvBgVRAM;
19 static UINT8 *DrvSprRAM;
20 static UINT8 *DrvFgRAM;
21 static UINT8 *DrvZ80RAM;
22 
23 static UINT32 *DrvPalette;
24 static UINT8 DrvRecalc;
25 
26 static INT32 soundlatch;
27 static INT32 bg_scrollx;
28 static INT32 spritebank;
29 static INT32 nmi_enable;
30 
31 static UINT8 DrvJoy1[8];
32 static UINT8 DrvJoy2[8];
33 static UINT8 DrvJoy3[8];
34 static UINT8 DrvDips[3];
35 static UINT8 DrvInputs[3];
36 static UINT8 DrvReset;
37 
38 static struct BurnInputInfo StuntairInputList[] = {
39 	{"P1 Coin",			BIT_DIGITAL,	DrvJoy2 + 3,	"p1 coin"	},
40 	{"P1 Start",		BIT_DIGITAL,	DrvJoy2 + 0,	"p1 start"	},
41 	{"P1 Up",			BIT_DIGITAL,	DrvJoy1 + 5,	"p1 up"		},
42 	{"P1 Down",			BIT_DIGITAL,	DrvJoy1 + 6,	"p1 down"	},
43 	{"P1 Left",			BIT_DIGITAL,	DrvJoy1 + 4,	"p1 left"	},
44 	{"P1 Right",		BIT_DIGITAL,	DrvJoy1 + 7,	"p1 right"	},
45 	{"P1 Button 1",		BIT_DIGITAL,	DrvJoy1 + 0,	"p1 fire 1"	},
46 	{"P1 Button 2",		BIT_DIGITAL,	DrvJoy1 + 1,	"p1 fire 2"	},
47 
48 	{"P2 Coin",			BIT_DIGITAL,	DrvJoy2 + 4,	"p2 coin"	},
49 	{"P2 Start",		BIT_DIGITAL,	DrvJoy2 + 1,	"p2 start"	},
50 
51 	{"Reset",			BIT_DIGITAL,	&DrvReset,		"reset"		},
52 	{"Dip A",			BIT_DIPSWITCH,	DrvDips + 0,	"dip"		},
53 	{"Dip B",			BIT_DIPSWITCH,	DrvDips + 1,	"dip"		},
54 	{"Dip C",			BIT_DIPSWITCH,	DrvDips + 2,	"dip"		},
55 };
56 
57 STDINPUTINFO(Stuntair)
58 
59 static struct BurnDIPInfo StuntairDIPList[]=
60 {
61 	{0x0b, 0xf0, 0xff, 0xff, "dip offset"                           },
62 
63 	{0x00, 0xff, 0xff, 0x08, NULL									},
64 	{0x01, 0xff, 0xff, 0xc0, NULL									},
65 	{0x02, 0xff, 0xff, 0x04, NULL									},
66 
67 	{0   , 0xfe, 0   ,    2, "Infinite Lives (Cheat)"				},
68 	{0x00, 0x01, 0x04, 0x00, "Off"									},
69 	{0x00, 0x01, 0x04, 0x04, "On"									},
70 
71 	{0   , 0xfe, 0   ,    4, "Difficulty"							},
72 	{0x00, 0x01, 0x28, 0x00, "Easy"									},
73 	{0x00, 0x01, 0x28, 0x08, "Normal"								},
74 	{0x00, 0x01, 0x28, 0x20, "Hard"									},
75 	{0x00, 0x01, 0x28, 0x28, "Hardest"								},
76 
77 	{0   , 0xfe, 0   ,    4, "Coin A"								},
78 	{0x01, 0x01, 0x18, 0x08, "2 Coins 1 Credits"					},
79 	{0x01, 0x01, 0x18, 0x00, "1 Coin  1 Credits"					},
80 	{0x01, 0x01, 0x18, 0x18, "1 Coin/1 Credit - 2 Coins/3 Credits"	},
81 	{0x01, 0x01, 0x18, 0x10, "1 Coin  2 Credits"					},
82 
83 	{0   , 0xfe, 0   ,    4, "Coin B"								},
84 	{0x01, 0x01, 0x24, 0x04, "2 Coins 1 Credits"					},
85 	{0x01, 0x01, 0x24, 0x00, "1 Coin  1 Credits"					},
86 	{0x01, 0x01, 0x24, 0x24, "1 Coin/1 Credit - 2 Coins/3 Credits"	},
87 	{0x01, 0x01, 0x24, 0x20, "1 Coin  2 Credits"					},
88 
89 	{0   , 0xfe, 0   ,    4, "Bonus Life"							},
90 	{0x01, 0x01, 0x42, 0x00, "10000 20000"							},
91 	{0x01, 0x01, 0x42, 0x40, "20000 30000"							},
92 	{0x01, 0x01, 0x42, 0x02, "30000 50000"							},
93 	{0x01, 0x01, 0x42, 0x42, "50000 100000"							},
94 
95 	{0   , 0xfe, 0   ,    4, "Lives"								},
96 	{0x01, 0x01, 0x81, 0x00, "2"									},
97 	{0x01, 0x01, 0x81, 0x80, "3"									},
98 	{0x01, 0x01, 0x81, 0x01, "4"									},
99 	{0x01, 0x01, 0x81, 0x81, "5"									},
100 
101 	{0   , 0xfe, 0   ,    2, "Clear Credits on Reset"				},
102 	{0x02, 0x01, 0x04, 0x04, "Off"									},
103 	{0x02, 0x01, 0x04, 0x00, "On"									},
104 };
105 
STDDIPINFO(Stuntair)106 STDDIPINFO(Stuntair)
107 
108 static void __fastcall stuntair_main_write(UINT16 address, UINT8 data)
109 {
110 	switch (address)
111 	{
112 		case 0xe000:
113 			// coin counter = data & 3
114 		return;
115 
116 		case 0xe800:
117 			bg_scrollx = data;
118 		return;
119 
120 		case 0xf000: return; // nop
121 		case 0xf001: { nmi_enable = data & 1; if (!nmi_enable) ZetSetIRQLine(0x20, CPU_IRQSTATUS_NONE); }return;
122 		case 0xf002: return;
123 		case 0xf003: spritebank = (spritebank & 2) | (data & 1); return;
124 		case 0xf004: return;
125 		case 0xf005: spritebank = (spritebank & 1) | ((data & 1) << 1); return;
126 		case 0xf006: return;
127 		case 0xf007: return;
128 
129 		case 0xfc03:
130 			soundlatch = data;
131 			ZetSetIRQLine(1, 0x20, (data & 0x80) ? CPU_IRQSTATUS_NONE : CPU_IRQSTATUS_ACK);
132 		return;
133 	}
134 }
135 
stuntair_main_read(UINT16 address)136 static UINT8 __fastcall stuntair_main_read(UINT16 address)
137 {
138 	switch (address)
139 	{
140 		case 0xe000:
141 			return DrvDips[1];
142 
143 		case 0xe800:
144 			return DrvDips[0];
145 
146 		case 0xf000:
147 			return DrvInputs[0];
148 
149 		case 0xf002:
150 			return (DrvInputs[1] & 0xfb) | (DrvDips[2] & 4);
151 
152 		case 0xf003:
153 			BurnWatchdogRead();
154 			return 0;
155 	}
156 
157 	return 0;
158 }
159 
stuntair_sound_write_port(UINT16 port,UINT8 data)160 static void __fastcall stuntair_sound_write_port(UINT16 port, UINT8 data)
161 {
162 	switch (port & 0xff)
163 	{
164 		case 0x03:
165 			AY8910Write(1, 0, data);
166 		return;
167 
168 		case 0x07:
169 			AY8910Write(1, 1, data);
170 		return;
171 
172 		case 0x0c:
173 		case 0x0d:
174 			AY8910Write(0, port & 1, data);
175 		return;
176 	}
177 }
178 
stuntair_sound_read_port(UINT16 port)179 static UINT8 __fastcall stuntair_sound_read_port(UINT16 port)
180 {
181 	switch (port & 0xff)
182 	{
183 		case 0x0c:
184 		case 0x0d:
185 			return AY8910Read(0);
186 	}
187 
188 	return 0;
189 }
190 
tilemap_callback(bg)191 static tilemap_callback( bg )
192 {
193 	INT32 attr = DrvBgARAM[offs];
194 	INT32 code = DrvBgVRAM[offs] + ((attr & 0x08) << 5);
195 
196 	TILE_SET_INFO(1, code, attr, 0);
197 }
198 
tilemap_callback(fg0)199 static tilemap_callback( fg0 )
200 {
201 	INT32 attr = DrvFgRAM[offs];
202 
203 	TILE_SET_INFO(0, attr & 0x7f, 0, TILE_GROUP((~attr & 0x80) >> 7));
204 }
205 
AY8910_0_portA(UINT32)206 static UINT8 AY8910_0_portA(UINT32)
207 {
208 	return soundlatch;
209 }
210 
DrvDoReset(INT32 clear_mem)211 static INT32 DrvDoReset(INT32 clear_mem)
212 {
213 	if (clear_mem) {
214 		memset (AllRam, 0, RamEnd - AllRam);
215 	}
216 
217 	ZetOpen(0);
218 	ZetReset();
219 	ZetClose();
220 
221 	ZetOpen(1);
222 	ZetReset();
223 	ZetClose();
224 
225 	AY8910Reset(0);
226 	AY8910Reset(1);
227 
228 	bg_scrollx = 0;
229 	soundlatch = 0;
230 	spritebank = 0;
231 	nmi_enable = 0;
232 
233 	return 0;
234 }
235 
MemIndex()236 static INT32 MemIndex()
237 {
238 	UINT8 *Next; Next = AllMem;
239 
240 	DrvZ80ROM[0]	= Next; Next += 0x00a000;
241 	DrvZ80ROM[1]	= Next; Next += 0x00a000;
242 
243 	DrvGfxROM[0]	= Next; Next += 0x010000;
244 	DrvGfxROM[1]	= Next; Next += 0x010000;
245 	DrvGfxROM[2]	= Next; Next += 0x010000;
246 
247 	DrvColPROM		= Next; Next += 0x000200;
248 
249 	DrvPalette		= (UINT32*)Next; Next += 0x0030 * sizeof(UINT32);
250 
251 	DrvNVRAM		= Next; Next += 0x000800;
252 
253 	AllRam			= Next;
254 
255 	DrvBgARAM		= Next; Next += 0x000400;
256 	DrvBgVRAM		= Next; Next += 0x000400;
257 	DrvSprRAM		= Next; Next += 0x000800;
258 	DrvFgRAM		= Next; Next += 0x000400;
259 	DrvZ80RAM		= Next; Next += 0x000400;
260 
261 	RamEnd			= Next;
262 
263 	MemEnd			= Next;
264 
265 	return 0;
266 }
267 
DrvGfxDecode()268 static INT32 DrvGfxDecode()
269 {
270 	INT32 Plane[2] = { 0, 0x2000*8 };
271 	INT32 XOffs[16] = { STEP8(0,1), STEP8(64,1) };
272 	INT32 YOffs[16] = { STEP8(0,8), STEP8(128,8) };
273 
274 	UINT8 *tmp = (UINT8*)BurnMalloc(0x4000);
275 	if (tmp == NULL) {
276 		return 1;
277 	}
278 
279 	memcpy (tmp, DrvGfxROM[0], 0x2000);
280 
281 	GfxDecode(0x0400, 1,  8,  8, Plane, XOffs, YOffs, 0x040, tmp, DrvGfxROM[0]);
282 
283 	memcpy (tmp, DrvGfxROM[1], 0x4000);
284 
285 	GfxDecode(0x0400, 2,  8,  8, Plane, XOffs, YOffs, 0x040, tmp, DrvGfxROM[1]);
286 
287 	memcpy (tmp, DrvGfxROM[2], 0x4000);
288 
289 	GfxDecode(0x0100, 2, 16, 16, Plane, XOffs, YOffs, 0x100, tmp, DrvGfxROM[2]);
290 
291 	BurnFree(tmp);
292 
293 	return 0;
294 }
295 
DrvInit()296 static INT32 DrvInit()
297 {
298 	AllMem = NULL;
299 	MemIndex();
300 	INT32 nLen = MemEnd - (UINT8 *)0;
301 	if ((AllMem = (UINT8 *)BurnMalloc(nLen)) == NULL) return 1;
302 	memset(AllMem, 0, nLen);
303 	MemIndex();
304 
305 	{
306 		INT32 k = 0;
307 		if (BurnLoadRom(DrvZ80ROM[0] + 0x0000, k++, 1)) return 1;
308 		if (BurnLoadRom(DrvZ80ROM[0] + 0x2000, k++, 1)) return 1;
309 		if (BurnLoadRom(DrvZ80ROM[0] + 0x4000, k++, 1)) return 1;
310 		if (BurnLoadRom(DrvZ80ROM[0] + 0x6000, k++, 1)) return 1;
311 		if (BurnLoadRom(DrvZ80ROM[0] + 0x8000, k++, 1)) return 1;
312 
313 		if (BurnLoadRom(DrvZ80ROM[1] + 0x0000, k++, 1)) return 1;
314 
315 		if (BurnLoadRom(DrvGfxROM[0] + 0x0000, k++, 1)) return 1;
316 
317 		if (BurnLoadRom(DrvGfxROM[1] + 0x0000, k++, 1)) return 1;
318 		if (BurnLoadRom(DrvGfxROM[1] + 0x2000, k++, 1)) return 1;
319 
320 		if (BurnLoadRom(DrvGfxROM[2] + 0x0000, k++, 1)) return 1;
321 		if (BurnLoadRom(DrvGfxROM[2] + 0x2000, k++, 1)) return 1;
322 
323 		if (BurnLoadRom(DrvColPROM   + 0x0000, k++, 1)) return 1;
324 		if (BurnLoadRom(DrvColPROM   + 0x0100, k++, 1)) return 1;
325 
326 		DrvGfxDecode();
327 	}
328 
329 	ZetInit(0);
330 	ZetOpen(0);
331 	ZetMapMemory(DrvZ80ROM[0],		0x0000, 0x9fff, MAP_ROM);
332 	ZetMapMemory(DrvNVRAM,			0xc000, 0xc7ff, MAP_RAM);
333 	ZetMapMemory(DrvBgARAM,			0xc800, 0xcbff, MAP_RAM);
334 	ZetMapMemory(DrvBgVRAM,			0xd000, 0xd3ff, MAP_RAM);
335 	ZetMapMemory(DrvSprRAM,			0xd800, 0xdfff, MAP_RAM);
336 	ZetMapMemory(DrvFgRAM,			0xf800, 0xfbff, MAP_RAM);
337 	ZetSetWriteHandler(stuntair_main_write);
338 	ZetSetReadHandler(stuntair_main_read);
339 	ZetClose();
340 
341 	ZetInit(1);
342 	ZetOpen(1);
343 	ZetMapMemory(DrvZ80ROM[1],		0x0000, 0x9fff, MAP_ROM);
344 	ZetMapMemory(DrvZ80RAM,			0x4000, 0x43ff, MAP_RAM);
345 	ZetSetOutHandler(stuntair_sound_write_port);
346 	ZetSetInHandler(stuntair_sound_read_port);
347 	ZetClose();
348 
349 	BurnWatchdogInit(DrvDoReset, -1/*180*/);
350 
351 	AY8910Init(0, 1536000, 0);
352 	AY8910Init(1, 1536000, 0);
353 	AY8910SetPorts(0, AY8910_0_portA, NULL, NULL, NULL);
354 	AY8910SetAllRoutes(0, 0.20, BURN_SND_ROUTE_BOTH);
355 	AY8910SetAllRoutes(1, 0.20, BURN_SND_ROUTE_BOTH);
356 
357 	GenericTilesInit();
358 	GenericTilemapInit(0, TILEMAP_SCAN_ROWS, bg_map_callback, 8, 8, 32, 32);
359 	GenericTilemapInit(1, TILEMAP_SCAN_ROWS, fg0_map_callback, 8, 8, 32, 32);
360 	GenericTilemapSetGfx(0, DrvGfxROM[0], 1, 8, 8, 0x10000, 0x20, 1);
361 	GenericTilemapSetGfx(1, DrvGfxROM[1], 2, 8, 8, 0x10000, 0x00, 7);
362 	GenericTilemapSetOffsets(TMAP_GLOBAL, 0, -16);
363 	GenericTilemapSetTransparent(1, 0);
364 
365 	DrvDoReset(1);
366 
367 	return 0;
368 }
369 
DrvExit()370 static INT32 DrvExit()
371 {
372 	GenericTilesExit();
373 	ZetExit();
374 	AY8910Exit(0);
375 	AY8910Exit(1);
376 	BurnWatchdogExit();
377 	BurnFree(AllMem);
378 
379 	return 0;
380 }
381 
DrvPaletteInit()382 static void DrvPaletteInit()
383 {
384 	for (INT32 i = 0xe0; i < 0x100; i++) {
385 		INT32 data = (DrvColPROM[i] & 0xf) | (DrvColPROM[i + 0x100] << 4);
386 
387 		INT32 r = (data >> 0) & 7;
388 		INT32 g = (data >> 3) & 7;
389 		INT32 b = (data >> 6) & 3;
390 
391 		r = (r << 5) | (r << 2) | (r >> 1);
392 		g = (g << 5) | (g << 2) | (g >> 1);
393 		b = (b << 6) | (b << 4) | (b << 2) | b;
394 
395 		DrvPalette[i - 0xe0] = BurnHighCol(r, g, b, 0);
396 	}
397 
398 	DrvPalette[0x20] = 0;
399 	DrvPalette[0x21] = BurnHighCol(0xff, 0xff, 0xff, 0);
400 }
401 
draw_sprites()402 static void draw_sprites()
403 {
404 	for (INT32 i = 0; i < 0x400; i+=0x10)
405 	{
406 		INT32 sy     = DrvSprRAM[i + 0];
407 		INT32 attr   = DrvSprRAM[i + 1];
408 		INT32 color  = DrvSprRAM[i + 4];
409 		INT32 sx     = DrvSprRAM[i + 5];
410 		INT32 code   =(attr & 0x3f) | (spritebank << 6);
411 		INT32 flipy  = attr & 0x80;
412 
413 		Draw16x16MaskTile(pTransDraw, code, sx, (240 - sy) - 16, 0, flipy, color & 7, 2, 0, 0, DrvGfxROM[2]);
414 	}
415 }
416 
DrvDraw()417 static INT32 DrvDraw()
418 {
419 	if (DrvRecalc) {
420 		DrvPaletteInit();
421 		DrvRecalc = 0;
422 	}
423 
424 	GenericTilemapSetScrollX(0, bg_scrollx);
425 
426 	if (~nBurnLayer & 1) BurnTransferClear();
427 
428 	if ( nBurnLayer & 1) GenericTilemapDraw(0, pTransDraw, 0);
429 	if ( nBurnLayer & 2) GenericTilemapDraw(1, pTransDraw, TMAP_SET_GROUP(1));
430 
431 	if ( nSpriteEnable & 1) draw_sprites();
432 
433 	if ( nBurnLayer & 4) GenericTilemapDraw(1, pTransDraw, TMAP_SET_GROUP(0) | TMAP_DRAWOPAQUE);
434 
435 	BurnTransferCopy(DrvPalette);
436 
437 	return 0;
438 }
439 
DrvFrame()440 static INT32 DrvFrame()
441 {
442 	BurnWatchdogUpdate();
443 
444 	if (DrvReset) {
445 		DrvDoReset(1);
446 	}
447 
448 	{
449 		memset (DrvInputs, 0xff, sizeof(DrvInputs));
450 
451 		for (INT32 i = 0; i < 8; i++) {
452 			DrvInputs[0] ^= (DrvJoy1[i] & 1) << i;
453 			DrvInputs[1] ^= (DrvJoy2[i] & 1) << i;
454 			DrvInputs[2] ^= (DrvJoy3[i] & 1) << i;
455 		}
456 	}
457 
458 	INT32 nInterleave = 70;
459 	INT32 nCyclesTotal[2] = { 3072000 / 60, 3072000 / 60 };
460 	INT32 nCyclesDone[2] = { 0, 0 };
461 
462 	for (INT32 i = 0; i < nInterleave; i++)
463 	{
464 		ZetOpen(0);
465 		CPU_RUN(0, Zet);
466 		if (i == (nInterleave - 1)) ZetNmi();
467 		ZetClose();
468 
469 		ZetOpen(1);
470 		CPU_RUN(1, Zet);
471 		if ((i % 10) == 9) ZetSetIRQLine(0, CPU_IRQSTATUS_HOLD); // 7x / frame
472 		ZetClose();
473 	}
474 
475 	if (pBurnSoundOut) {
476 		AY8910Render(pBurnSoundOut, nBurnSoundLen);
477 	}
478 
479 	if (pBurnDraw) {
480 		DrvDraw();
481 	}
482 
483 	return 0;
484 }
485 
DrvScan(INT32 nAction,INT32 * pnMin)486 static INT32 DrvScan(INT32 nAction, INT32 *pnMin)
487 {
488 	struct BurnArea ba;
489 
490 	if (pnMin) {
491 		*pnMin = 0x029702;
492 	}
493 
494 	if (nAction & ACB_VOLATILE) {
495 		memset(&ba, 0, sizeof(ba));
496 
497 		ba.Data	  = AllRam;
498 		ba.nLen	  = RamEnd - AllRam;
499 		ba.szName = "All Ram";
500 		BurnAcb(&ba);
501 
502 		ZetScan(nAction);
503 		AY8910Scan(nAction, pnMin);
504 		BurnWatchdogScan(nAction);
505 
506 		SCAN_VAR(soundlatch);
507 		SCAN_VAR(nmi_enable);
508 		SCAN_VAR(spritebank);
509 		SCAN_VAR(bg_scrollx);
510 	}
511 
512 	if (nAction & ACB_NVRAM) {
513 		ba.Data		= DrvNVRAM;
514 		ba.nLen		= 0x00800;
515 		ba.nAddress	= 0;
516 		ba.szName	= "NV RAM";
517 		BurnAcb(&ba);
518 	}
519 
520 	return 0;
521 }
522 
523 
524 // Stunt Air
525 
526 static struct BurnRomInfo stuntairRomDesc[] = {
527 	{ "stuntair.a0",	0x2000, 0xf61c4a1d, 1 | BRF_PRG | BRF_ESS }, //  0 Z80 #0 Code
528 	{ "stuntair.a1",	0x2000, 0x1546f041, 1 | BRF_PRG | BRF_ESS }, //  1
529 	{ "stuntair.a3",	0x2000, 0x63d00b97, 1 | BRF_PRG | BRF_ESS }, //  2
530 	{ "stuntair.a4",	0x2000, 0x01fe2697, 1 | BRF_PRG | BRF_ESS }, //  3
531 	{ "stuntair.a6",	0x2000, 0x6704d05c, 1 | BRF_PRG | BRF_ESS }, //  4
532 
533 	{ "stuntair.e14",	0x2000, 0x641fc9db, 2 | BRF_PRG | BRF_ESS }, //  5 Z80 #1 Code
534 
535 	{ "stuntair.a9",	0x2000, 0xbfd861f5, 3 | BRF_GRA },           //  6 Foreground Tiles
536 
537 	{ "stuntair.a11",	0x2000, 0x421fef4c, 4 | BRF_GRA },           //  7 Background Tiles
538 	{ "stuntair.a12",	0x2000, 0xe6ee7489, 4 | BRF_GRA },           //  8
539 
540 	{ "stuntair.a13",	0x2000, 0xbfdc0d38, 5 | BRF_GRA },           //  9 Sprites
541 	{ "stuntair.a15",	0x2000, 0x4531cab5, 5 | BRF_GRA },           // 10
542 
543 	{ "dm74s287n.11m",	0x0100, 0xd330ff90, 6 | BRF_GRA },           // 11 Color Data
544 	{ "dm74s287n.11l",	0x0100, 0x6c98f964, 6 | BRF_GRA },           // 12
545 
546 	{ "dm74s288n.7a",	0x0020, 0x5779e751, 0 | BRF_OPT },           // 13 Unknown
547 };
548 
549 STD_ROM_PICK(stuntair)
550 STD_ROM_FN(stuntair)
551 
552 struct BurnDriver BurnDrvStuntair = {
553 	"stuntair", NULL, NULL, NULL, "1983",
554 	"Stunt Air\0", NULL, "Nuova Videotron", "Miscellaneous",
555 	NULL, NULL, NULL, NULL,
556 	BDF_GAME_WORKING | BDF_ORIENTATION_VERTICAL | BDF_ORIENTATION_FLIPPED, 2, HARDWARE_MISC_PRE90S, GBF_VERSHOOT, 0,
557 	NULL, stuntairRomInfo, stuntairRomName, NULL, NULL, NULL, NULL, StuntairInputInfo, StuntairDIPInfo,
558 	DrvInit, DrvExit, DrvFrame, DrvDraw, DrvScan, &DrvRecalc, 0x22,
559 	224, 256, 3, 4
560 };
561