/***************************************************************************

								Air Buster
						    (C) 1990  Kaneko

				    driver by Luca Elia (eliavit@unina.it)

CPU   : Z-80 x 3
SOUND : YM2203C		M6295
OSC.  : 12.000MHz	16.000MHz

					Interesting routines (main cpu)
					-------------------------------

fd-fe	address of int: 0x38	(must alternate? see e600/1)
ff-100	address of int: 0x16
66		print "sub cpu caused nmi" and die!

7		after tests

1497	print string following call: (char)*,0. program continues after 0.
		base addr = c300 + HL & 08ff, DE=xy , BC=dxdy
		+0<-(e61b)	+100<-D	+200<-E	+300<-char	+400<-(e61c)

1642	A<- buttons status (bits 0&1)

					Interesting locations (main cpu)
					--------------------------------

2907	table of routines (f150 index = 1-3f, copied to e612)
		e60e<-address of routine, f150<-0. e60e is used until f150!=0

	1:	2bf4	service mode								next

	2:	2d33	3:	16bd		4:	2dcb		5:	2fcf
	6:	3262	7:	32b8

	8:	335d>	print gfx/color test page					next
	9:	33c0>	handle the above page

	a:	29c6		b:	2a24		c:	16ce

	d:	3e7e>	*
	e:	3ec5>	print "Sub Cpu / Ram Error"; **
	f:	3e17>	print "Coin error"; **
	10:	3528>	print (c) notice, not shown					next
	11:	3730>	show (c) notice, wait 0x100 calls			next

	12:		9658	13:	97c3		14:	a9fa		15:	aa6e
	16-19:	2985	1a:	9c2e		1b:	9ffa		1c:	29c6

	1d:	2988>	*

	1e:	a2c4		1f:	a31a		20:	a32f		21:	a344
	22:	a359		23:	a36e		24:	a383		25:	a398
	26:	a3ad		27:	a3c2		28:	a3d7		29:	a3f1
	2a:	a40e		2b:	a4e5		2c:	a69d		2d:	adb8
	2e:	ade9		2f:	2b8b		30:	a823

	31:	3d17>	print "warm up, wait few mins. secs left: 00"	next
	32:	3dc0>	pause (e624 counter).e626						next

	33:	96b4		34:	97ad

	35-3f:	3e7e>	*

*	Print "Command Error [$xx]" where xx is last routine index (e612)
**	ld (0000h),A (??) ; loop

3cd7	hiscores table (0x40 bytes, copied to e160)
		Try entering TERU as your name :)

7fff	country code: 0 <-> Japan; else World

e615	rank:	0-easy	1-normal	2-hard	3-hardest
e624	sound code during sound test

-- Shared RAM --

f148<-	sound code (copied from e624)
f14a->	read on nmi routine. main cpu writes the value and writes to port 02
f150<-	index of table of routines at 2907

----------------





					Interesting routines (sub cpu)
					-------------------------------

491		copy palette data	d000<-f200(a0)	d200<-f300(a0)	d400<-f400(200)

61c		f150<-A		f151<-A	(routine index of main cpu!)
		if dsw1-2 active, it does nothing (?!)

c8c		c000-c7ff<-0	c800-cfff<-0	f600<-f200(400)
1750	copies 10 lines of 20 bytes from 289e to f800

22cd	copy 0x100 bytes
22cf	copy 0x0FF bytes
238d	copy 0x0A0 bytes

					Interesting locations (sub cpu)
					--------------------------------

fd-fe	address of int: 0x36e	(same as 38)
ff-100	address of int: 0x4b0

-- Shared RAM --

f000	credits

f001/d<-IN 24 (Service)
f00e<-	bank
f002<-	dsw1 (cpl'd)
f003<-	dsw2 (cpl'd)
f004<-	IN 20 (cpl'd) (player 1)
f005<-	IN 22 (cpl'd) (player 2)
f006<-	start lives: dsw-2 & 0x30 index; values: 3,4,5,7		(5da table)
f007	current lives p1
f008	current lives p2

f009<-	coin/credit 1: dsw-1 & 0x30 index; values: 11,12,21,23	(5de table)
f00a<-	coin 1
f00b<-	coin/credit 2: dsw-1 & 0xc0 index; values: 11,12,21,23	(5e2 table)
f00c<-	coin 2

f00f	?? outa (28h)
f010	written by sub cpu, bit 4 read by main cpu.
		bit 0	p1 playing
		bit 1	p2 playing

f014	index (1-f) of routine called during int 36e (table at c3f)
	1:	62b			2:	66a			3:	6ad			4:	79f
	5:	7e0			6:	81b			7:	8a7			8:	8e9
	9:	b02			a:	0			b:	0			c:	bfc
	d:	c0d			e:	a6f			f:	ac3

f015	index of the routine to call, usually the one selected by f014
		but sometimes written directly.

Scroll registers: ports 04/06/08/0a/0c, written in sequence (101f)
port 06 <- f100 + f140	x		port 04 <- f104 + f142	y
port 0a <- f120 + f140	x		port 08 <- f124 + f142	y
port 0c <- f14c = bit 0/1/2/3 = port 6/4/a/8 val < FF

f148->	sound code (from main cpu)
f14c	scroll regs high bits

----------------








					Interesting routines (sound cpu)
					-------------------------------

50a		6295
521		6295
a96		2203 reg<-B		val<-A

					Interesting locations (sound cpu)
					---------------------------------

c715
c716	pending sound command
c760	rom bank


								To Do
								-----

- The bankswitching registers bits 4&5 are used (any Z80). What for ?
- Is the sub cpu / sound cpu communication status port (0e) correct ?
- Main cpu: port  01 ?
- Sub  cpu: ports 0x28, 0x38 ? Plus it can probably cause a nmi to main cpu
- incomplete DSW's
- Spriteram low 0x300 bytes (priority?)

***************************************************************************/

#include "driver.h"
#include "cpu/z80/z80.h"

unsigned char *devram, *sharedram;
int soundlatch_status, soundlatch2_status;

/* Variables that vidhrdw has access to */
extern unsigned char *spriteram;
int flipscreen;

/* Variables defined in vidhrdw */
extern unsigned char *airbustr_bgram, *airbustr_fgram;

/* Functions defined in vidhrdw */
extern void airbustr_bgram_w(int offset,int data);
extern void airbustr_fgram_w(int offset,int data);
extern void airbustr_scrollregs_w(int offset,int data);
extern int  airbustr_vh_start(void);
extern void airbustr_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh);

/* Debug stuff (bound to go away sometime) */
int u1, u2, u3, u4;


static void bankswitch_w(int offs,int data);
static void bankswitch2_w(int offs,int data);
static void sound_bankswitch_w(int offs,int data);

static void airbustr_init_machine (void)
{
	soundlatch_status = soundlatch2_status = 0;
	bankswitch_w(0,2);
	bankswitch2_w(0,2);
	sound_bankswitch_w(0,2);
}


/*
**
**				Main cpu data
**
*/

/*	Runs in IM 2	fd-fe	address of int: 0x38
					ff-100	address of int: 0x16	*/

int airbustr_interrupt(void)
{
static int addr = 0xff;

	addr ^= 0x02;

	return addr;
}


static int  sharedram_r(int offs)				{ return sharedram[offs]; }
static void sharedram_w(int offs, int data)	{ sharedram[offs] = data; }


/* There's an MCU here, possibly */
int devram_r(int offs)
{
	switch (offs)
	{
		/* Reading efe0 probably resets a watchdog mechanism
		   that would reset the main cpu. We avoid this and patch
		   the rom instead (main cpu has to be reset once at startup) */
		case 0xfe0:
			return 0/*watchdog_reset_r(0)*/;
			break;

		/* Reading a word at eff2 probably yelds the product
   		   of the words written to eff0 and eff2 */
		case 0xff2:
		case 0xff3:
		{
			int	x = (devram[0xff0] + devram[0xff1] * 256) *
					(devram[0xff2] + devram[0xff3] * 256);
			if (offs == 0xff2)	return (x & 0x00FF) >> 0;
			else				return (x & 0xFF00) >> 8;
		}	break;

		/* Reading eff4, F0 times must yield at most 80-1 consecutive
		   equal values */
		case 0xff4:
		{
			return rand();
		}	break;

		default:	{ return devram[offs]; break;}
	}

}
void devram_w(int offs, int data)	{	devram[offs] = data; }


static void bankswitch_w(int offs,int data)
{
unsigned char *RAM = memory_region(Machine->drv->cpu[0].memory_region);

	if ((data & 7) <  3)	RAM = &RAM[0x4000 * (data & 7)];
	else					RAM = &RAM[0x10000 + 0x4000 * ((data & 7)-3)];

	cpu_setbank(1,RAM);
//	if (errorlog && (data > 7))	fprintf(errorlog, "CPU #0 - suspicious bank: %d ! - PC = %04X\n", data, cpu_get_pc());

	u1 = data & 0xf8;
}

/* Memory */

static struct MemoryReadAddress readmem[] =
{
	{ 0x0000, 0x7fff, MRA_ROM },
	{ 0x8000, 0xbfff, MRA_BANK1 },
	{ 0xc000, 0xcfff, MRA_RAM },
	{ 0xd000, 0xdfff, MRA_RAM },
	{ 0xe000, 0xefff, devram_r },
	{ 0xf000, 0xffff, sharedram_r },
	{ -1 }
};
static struct MemoryWriteAddress writemem[] =
{
	{ 0x0000, 0xbfff, MWA_ROM },	// writing at 0 should cause a reset
	{ 0xc000, 0xcfff, MWA_RAM, &spriteram },			// RAM 0/1
	{ 0xd000, 0xdfff, MWA_RAM },						// RAM 2
	{ 0xe000, 0xefff, devram_w, &devram },				// RAM 3
	{ 0xf000, 0xffff, sharedram_w, &sharedram },
	{ -1 }
};

/* Ports */

void cause_nmi(int offset, int data)
{
	cpu_cause_interrupt(1,Z80_NMI_INT);	// cause a nmi to sub cpu
}

static struct IOReadPort readport[] =
{
	{ -1 }
};
static struct IOWritePort writeport[] =
{
	{ 0x00, 0x00, bankswitch_w },
//	{ 0x01, 0x01, IOWP_NOP },	// ?? only 2 (see 378b)
	{ 0x02, 0x02, cause_nmi },	// always 0. Cause a nmi to sub cpu
	{ -1 }
};










/*
**
**				Sub cpu data
**
**
*/

/*	Runs in IM 2	fd-fe	address of int: 0x36e	(same as 0x38)
					ff-100	address of int: 0x4b0	(only writes to port 38h)	*/

int airbustr_interrupt2(void)
{
static int addr = 0xfd;

	addr ^= 0x02;

	return addr;
}


static void bankswitch2_w(int offs,int data)
{
unsigned char *RAM = memory_region(Machine->drv->cpu[1].memory_region);

	if ((data & 7) <  3)	RAM = &RAM[0x4000 * (data & 7)];
	else					RAM = &RAM[0x10000 + 0x4000 * ((data & 7)-3)];

	cpu_setbank(2,RAM);
//	if (errorlog && (data > 7))	fprintf(errorlog, "CPU #1 - suspicious bank: %d ! - PC = %04X\n", data, cpu_get_pc());

	flipscreen = data & 0x10;	// probably..
	tilemap_set_flip(ALL_TILEMAPS,flipscreen ? (TILEMAP_FLIPY | TILEMAP_FLIPX) : 0);

	u2 = data & 0xf8;
}


void airbustr_paletteram_w(int offset,int data)
{
int r,g,b;

	/*	! byte 1 ! ! byte 0 !	*/
	/*	xGGG GGRR 	RRRB BBBB	*/
	/*	x432 1043 	2104 3210	*/

	paletteram[offset] = data;
	data = (paletteram[offset | 1] << 8) | paletteram[offset & ~1];

	g = (data >> 10) & 0x1f;
	r = (data >>  5) & 0x1f;
	b = (data >>  0) & 0x1f;

	palette_change_color(offset/2,	(r * 0xff) / 0x1f,
									(g * 0xff) / 0x1f,
									(b * 0xff) / 0x1f );
}


/* Memory */

static struct MemoryReadAddress readmem2[] =
{
	{ 0x0000, 0x7fff, MRA_ROM },
	{ 0x8000, 0xbfff, MRA_BANK2 },
	{ 0xc000, 0xcfff, MRA_RAM },
	{ 0xd000, 0xd5ff, paletteram_r },
	{ 0xd600, 0xdfff, MRA_RAM },
	{ 0xe000, 0xefff, MRA_RAM },
	{ 0xf000, 0xffff, sharedram_r },
	{ -1 }
};

static struct MemoryWriteAddress writemem2[] =
{
	{ 0x0000, 0xbfff, MWA_ROM },
	{ 0xc000, 0xc7ff, airbustr_fgram_w, &airbustr_fgram },
	{ 0xc800, 0xcfff, airbustr_bgram_w, &airbustr_bgram },
	{ 0xd000, 0xd5ff, airbustr_paletteram_w, &paletteram },
	{ 0xd600, 0xdfff, MWA_RAM },
	{ 0xe000, 0xefff, MWA_RAM },
	{ 0xf000, 0xffff, sharedram_w },
	{ -1 }
};


/* Ports */

/*
   Sub cpu and Sound cpu communicate bidirectionally:

	   Sub   cpu writes to soundlatch,  reads from soundlatch2
	   Sound cpu writes to soundlatch2, reads from soundlatch

   Each latch raises a flag when it's been written.
   The flag is cleared when the latch is read.

Code at 505: waits for bit 1 to go low, writes command, waits for bit
0 to go low, reads back value. Code at 3b2 waits bit 2 to go high
(called during int fd)

*/


static int soundcommand_status_r(int offset)
{
/* bits: 2 <-> ?	1 <-> soundlatch full	0 <-> soundlatch2 empty */
	return 4 + soundlatch_status * 2 + (1-soundlatch2_status);
}


static int soundcommand2_r(int offset)
{
	soundlatch2_status = 0;				// soundlatch2 has been read
	return soundlatch2_r(0);
}


static void soundcommand_w(int offset, int data)
{
	soundlatch_w(0,data);
	soundlatch_status = 1;				// soundlatch has been written
	cpu_cause_interrupt(2,Z80_NMI_INT);	// cause a nmi to sub cpu
}


void port_38_w(int offset, int data)	{	u4 = data; } // for debug


static struct IOReadPort readport2[] =
{
	{ 0x02, 0x02, soundcommand2_r },		// from sound cpu
	{ 0x0e, 0x0e, soundcommand_status_r },	// status of the latches ?
	{ 0x20, 0x20, input_port_0_r },			// player 1
	{ 0x22, 0x22, input_port_1_r },			// player 2
	{ 0x24, 0x24, input_port_2_r },			// service
	{ -1 }
};

static struct IOWritePort writeport2[] =
{
	{ 0x00, 0x00, bankswitch2_w },			// bits 2-0 bank; bit 4 (on if dsw1-1 active)? ; bit 5?
	{ 0x02, 0x02, soundcommand_w },			// to sound cpu
	{ 0x04, 0x0c, airbustr_scrollregs_w },	// Scroll values
//	{ 0x28, 0x28, port_38_w },				// ??
//	{ 0x38, 0x38, IOWP_NOP },				// ?? Followed by EI. Value isn't important
	{ -1 }
};










/*
**
** 				Sound cpu data
**
*/

static void sound_bankswitch_w(int offs,int data)
{
unsigned char *RAM = memory_region(Machine->drv->cpu[2].memory_region);

	if ((data & 7) <  3)	RAM = &RAM[0x4000 * (data & 7)];
	else					RAM = &RAM[0x10000 + 0x4000 * ((data & 7)-3)];

	cpu_setbank(3,RAM);
//	if (errorlog && (data > 7))	fprintf(errorlog, "CPU #2 - suspicious bank: %d ! - PC = %04X\n", data, cpu_get_pc());

	u3 = data & 0xf8;
}


/* Memory */

static struct MemoryReadAddress sound_readmem[] =
{
	{ 0x0000, 0x7fff, MRA_ROM },
	{ 0x8000, 0xbfff, MRA_BANK3 },
	{ 0xc000, 0xdfff, MRA_RAM },
	{ -1 }
};

static struct MemoryWriteAddress sound_writemem[] =
{
	{ 0x0000, 0xbfff, MWA_ROM },
	{ 0xc000, 0xdfff, MWA_RAM },
	{ -1 }
};


/* Ports */

int soundcommand_r(int offset)
{
	soundlatch_status = 0;		// soundlatch has been read
	return soundlatch_r(0);
}


void soundcommand2_w(int offset, int data)
{
	soundlatch2_status = 1;		// soundlatch2 has been written
	soundlatch2_w(0,data);
}


static struct IOReadPort sound_readport[] =
{
	{ 0x02, 0x02, YM2203_status_port_0_r },
	{ 0x03, 0x03, YM2203_read_port_0_r },
	{ 0x04, 0x04, OKIM6295_status_0_r },
	{ 0x06, 0x06, soundcommand_r },			// read command from sub cpu
	{ -1 }
};

static struct IOWritePort sound_writeport[] =
{
	{ 0x00, 0x00, sound_bankswitch_w },
	{ 0x02, 0x02, YM2203_control_port_0_w },
	{ 0x03, 0x03, YM2203_write_port_0_w },
	{ 0x04, 0x04, OKIM6295_data_0_w },
	{ 0x06, 0x06, soundcommand2_w },		// write command result to sub cpu
	{ -1 }
};




/*	Input Ports:
	[0] Player 1		[1] Player 2
	[2] Service
	[3] Dsw 1			[4] Dsw 2	*/

INPUT_PORTS_START( airbustr )

	PORT_START	// IN0 - Player 1
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP | IPF_8WAY )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN | IPF_8WAY )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT | IPF_8WAY )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT | IPF_8WAY )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START	// IN1 - Player 2
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_JOYSTICK_UP | IPF_8WAY | IPF_PLAYER2 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_JOYSTICK_DOWN | IPF_8WAY | IPF_PLAYER2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_JOYSTICK_LEFT | IPF_8WAY | IPF_PLAYER2 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_JOYSTICK_RIGHT | IPF_8WAY | IPF_PLAYER2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_BUTTON1 | IPF_PLAYER2 )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_BUTTON2 | IPF_PLAYER2 )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )

	PORT_START	// IN2 - Service
	PORT_BIT( 0x01, IP_ACTIVE_LOW, IPT_START1 )
	PORT_BIT( 0x02, IP_ACTIVE_LOW, IPT_START2 )
	PORT_BIT( 0x04, IP_ACTIVE_LOW, IPT_COIN1 )
	PORT_BIT( 0x08, IP_ACTIVE_LOW, IPT_COIN2 )
	PORT_BIT( 0x10, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x20, IP_ACTIVE_LOW, IPT_UNKNOWN )
	PORT_BIT( 0x40, IP_ACTIVE_LOW, IPT_COIN3 )
	PORT_BIT( 0x80, IP_ACTIVE_LOW, IPT_UNKNOWN )	// used

	PORT_START	// IN3 - DSW-1
	PORT_DIPNAME( 0x01, 0x01, "Unknown 1-0" )
	PORT_DIPSETTING(    0x01, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x02, 0x02, DEF_STR( Flip_Screen ) )	// if active, bit 4 of cpu2 bank is on ..
	PORT_DIPSETTING(    0x02, DEF_STR( Off ) )			// is this a flip screen?
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )			// it changes the scroll offsets
	PORT_SERVICE( 0x04, IP_ACTIVE_LOW )
	PORT_DIPNAME( 0x08, 0x08, "Unknown 1-3" )		//	routine 56d:	11 21 12 16 (bit 3 active)
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )		//					11 21 13 14 (bit 3 not active)
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x30, DEF_STR( Coin_A ) )	//	routine 546:	11 12 21 23
	PORT_DIPSETTING(    0x10, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0x30, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 2C_3C ) )
	PORT_DIPSETTING(    0x20, DEF_STR( 1C_2C ) )
	PORT_DIPNAME( 0xc0, 0xc0, DEF_STR( Coin_B ) )
	PORT_DIPSETTING(    0x40, DEF_STR( 2C_1C ) )
	PORT_DIPSETTING(    0xc0, DEF_STR( 1C_1C ) )
	PORT_DIPSETTING(    0x00, DEF_STR( 2C_3C ) )
	PORT_DIPSETTING(    0x80, DEF_STR( 1C_2C ) )

	PORT_START	// IN4 - DSW-2
	PORT_DIPNAME( 0x03, 0x03, DEF_STR( Difficulty ) )
	PORT_DIPSETTING(    0x02, "Easy" )
	PORT_DIPSETTING(    0x03, "Normal" )
	PORT_DIPSETTING(    0x01, "Hard" )
	PORT_DIPSETTING(    0x00, "Hardest" )
	PORT_DIPNAME( 0x04, 0x04, "Unknown 2-2" )
	PORT_DIPSETTING(    0x04, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x08, 0x08, "Freeze" )
	PORT_DIPSETTING(    0x08, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )
	PORT_DIPNAME( 0x30, 0x30, DEF_STR( Lives ) )
	PORT_DIPSETTING(    0x30, "3" )
	PORT_DIPSETTING(    0x20, "4" )
	PORT_DIPSETTING(    0x10, "5" )
	PORT_DIPSETTING(    0x00, "7" )
	PORT_DIPNAME( 0x40, 0x40, DEF_STR( Demo_Sounds ) )
	PORT_DIPSETTING(    0x00, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x40, DEF_STR( On ) )
	PORT_DIPNAME( 0x80, 0x80, "Unknown 2-7" )
	PORT_DIPSETTING(    0x80, DEF_STR( Off ) )
	PORT_DIPSETTING(    0x00, DEF_STR( On ) )

INPUT_PORTS_END



/*
**
** 				Gfx data
**
*/


/* displacement in bits to lower part of gfx */
#define lo (8*8*4*2)
#define layout16x16(_name_,_romsize_) \
static struct GfxLayout _name_ =\
{\
	16,16,\
	(_romsize_)*8/(16*16*4),\
	4,\
	{0, 1, 2, 3},\
	{0*4,1*4,2*4,3*4,4*4,5*4,6*4,7*4, \
	 0*4+32*8,1*4+32*8,2*4+32*8,3*4+32*8,4*4+32*8,5*4+32*8,6*4+32*8,7*4+32*8},\
	{0*32,1*32,2*32,3*32,4*32,5*32,6*32,7*32,\
	 0*32+lo,1*32+lo,2*32+lo,3*32+lo,4*32+lo,5*32+lo,6*32+lo,7*32+lo}, \
	16*16*4\
};

layout16x16(tilelayout,  0x080000)
layout16x16(spritelayout,0x100000)

static struct GfxDecodeInfo gfxdecodeinfo[] =
{
	{ 1, 0x000000, &tilelayout,   256*0, (256*2) / 16 }, // [0] Layers
	{ 1, 0x080000, &spritelayout, 256*2, (256*1) / 16 }, // [1] Sprites
	{ -1 }
};



static struct YM2203interface ym2203_interface =
{
	1,
	3000000,					/* ?? */
	{ YM2203_VOL(0xff,0xff) },	/* gain,volume */
	AY8910_DEFAULT_GAIN,
	{ input_port_3_r },			/* DSW-1 connected to port A */
	{ input_port_4_r },			/* DSW-2 connected to port B */
	{ 0 },
	{ 0 },
	{ 0 }
};

static struct OKIM6295interface okim6295_interface =
{
	1,
	{ 18000 },		/* ?? */
	{ 4  },
	{ 50 }
};


static struct MachineDriver airbustr_machine_driver =
{
	{
		{
			CPU_Z80,
			6000000,	/* ?? */
			0,
			readmem,writemem,readport,writeport,
			airbustr_interrupt, 2	/* nmi caused by sub cpu?, ? */
		},
		{
			CPU_Z80,
			6000000,	/* ?? */
			2,
			readmem2,writemem2,readport2,writeport2,
			airbustr_interrupt2, 2	/* nmi caused by main cpu, ? */
		},
		{
			CPU_Z80,	/* Sound CPU, reads DSWs. Hence it can't be disabled */
			6000000,	/* ?? */
			3,
			sound_readmem,sound_writemem,sound_readport,sound_writeport,
			interrupt,1	/* nmi are caused by sub cpu writing a sound command */
		},
	},
	60,DEFAULT_60HZ_VBLANK_DURATION,
	100,	/* Palette RAM is filled by sub cpu with data supplied by main cpu */
			/* Maybe an high value is safer in order to avoid glitches */
	airbustr_init_machine,

	/* video hardware */
	256, 256, { 0, 256-1, 0+16, 256-16-1 },
	gfxdecodeinfo,
	256 * 3, 256 * 3,
	0,
	VIDEO_TYPE_RASTER | VIDEO_MODIFIES_PALETTE,
	0,
	airbustr_vh_start,
	0,
	airbustr_vh_screenrefresh,

	/* sound hardware */
	0,0,0,0,
	{
		{
			SOUND_YM2203,
			&ym2203_interface
		},
		{
			SOUND_OKIM6295,
			&okim6295_interface
		}
	}
};



/***************************************************************************

  Game driver(s)

***************************************************************************/

ROM_START( airbustr )

	ROM_REGION(0x24000)				/* Region 0 - main cpu */
	ROM_LOAD( "pr-14j.bin", 0x00000, 0x0c000, 0x6b9805bd )
	ROM_CONTINUE(           0x10000, 0x14000      )

	ROM_REGION_DISPOSE(0x180000)	/* Region 1 - temporary for gfx roms */
	ROM_LOAD( "pr-000.bin", 0x000000, 0x80000, 0x8ca68f0d ) // scrolling layers
	ROM_LOAD( "pr-001.bin", 0x080000, 0x80000, 0x7e6cb377 ) // sprites
	ROM_LOAD( "pr-02.bin",  0x100000, 0x10000, 0x6bbd5e46 )

	ROM_REGION(0x24000)				/* Region 2 - sub cpu */
	ROM_LOAD( "pr-11j.bin", 0x00000, 0x0c000, 0x85464124 )
	ROM_CONTINUE(           0x10000, 0x14000      )

	ROM_REGION(0x24000)				/* Region 3 - sound cpu */
	ROM_LOAD( "pr-21.bin",  0x00000, 0x0c000, 0x6e0a5df0 )
	ROM_CONTINUE(           0x10000, 0x14000      )

	ROM_REGION(0x40000)				/* Region 4 - OKI-M6295 samples */
	ROM_LOAD( "pr-200.bin", 0x00000, 0x40000, 0xa4dd3390 )

ROM_END

void airbustr_rom_decode(void)
{
int i;
unsigned char *RAM;

	/* One gfx rom seems to have scrambled data (bad read?): */
	/* let's swap even and odd nibbles */
	RAM = Machine->memory_region[1] + 0x000000;
	for (i = 0; i < 0x80000; i ++)
	{
		RAM[i] = ((RAM[i] & 0xF0)>>4) + ((RAM[i] & 0x0F)<<4);
	}

	RAM = memory_region(Machine->drv->cpu[0].memory_region);
	RAM[0x37f4] = 0x00;		RAM[0x37f5] = 0x00;	// startup check. We need a reset
												// so I patch a busy loop with jp 0

	RAM = memory_region(Machine->drv->cpu[1].memory_region);
	RAM[0x0258] = 0x53; // include EI in the busy loop.
						// It's an hack to repair nested nmi troubles
}

/***********************************************************************

  Air Buster high score save routine - RJF (May 12, 1999)

***********************************************************************/

static int airbustr_hiload(void)
{
	unsigned char *RAM = memory_region(Machine->drv->cpu[0].memory_region);

	/* check if the hi score table has already been initialized */
        if (memcmp(&RAM[0xe160],"\x01\x68\x00",3) == 0)
	{
		void *f;

		if ((f = osd_fopen(Machine->gamedrv->name,0,OSD_FILETYPE_HIGHSCORE,0)) != 0)
		{
                        osd_fread(f,&RAM[0xe160], 8*8);        /* HS table */
			osd_fclose(f);
		}

		return 1;
	}
	else return 0;	/* we can't load the hi scores yet */
}

static void airbustr_hisave(void)
{
	void *f;
	unsigned char *RAM = memory_region(Machine->drv->cpu[0].memory_region);

	if ((f = osd_fopen(Machine->gamedrv->name,0,OSD_FILETYPE_HIGHSCORE,1)) != 0)
	{
                osd_fwrite(f,&RAM[0xe160], 8*8);       /* HS table */
		osd_fclose(f);
	}
}

struct GameDriver driver_airbustr =
{
	__FILE__,
	0,
	"airbustr",
	"Air Buster (Japan)",
	"1990",
	"Kaneko (Namco license)",
	"Luca Elia\n",
	0,
	&airbustr_machine_driver,
	0,

	rom_airbustr,
	airbustr_rom_decode, 0,
	0,
	0,

	input_ports_airbustr,

	0, 0, 0,
	ORIENTATION_DEFAULT,

	airbustr_hiload, airbustr_hisave
};
