/* vidhrdw/namconb1.c */

#include "driver.h"
#include "vidhrdw/generic.h"
#include "namconb1.h"
#include "namcoic.h"
#include "namcos2.h"

static data32_t tilemap_tile_bank[4];

/* nth_word32 is a general-purpose utility function, which allows us to
 * read from 32-bit aligned memory as if it were an array of 16 bit words.
 */
INLINE data16_t
nth_word32( const data32_t *source, int which )
{
	source += which/2;
	if( which&1 )
	{
		return (*source)&0xffff;
	}
	else
	{
		return (*source)>>16;
	}
}

/* nth_byte32 is a general-purpose utility function, which allows us to
 * read from 32-bit aligned memory as if it were an array of bytes.
 */
INLINE data8_t
nth_byte32( const data32_t *pSource, int which )
{
	data32_t data = pSource[which/4];
	switch( which&3 )
	{
	case 0: return data>>24;
	case 1: return (data>>16)&0xff;
	case 2: return (data>>8)&0xff;
	default: return data&0xff;
	}
} /* nth_byte32 */

INLINE void
NB1TilemapCB(data16_t code, int *tile, int *mask )
{
	*tile = code;
	*mask = code;
} /* NB1TilemapCB */

INLINE void
NB2TilemapCB(data16_t code, int *tile, int *mask )
{
	int mangle;

	if( namcos2_gametype == NAMCONB2_MACH_BREAKERS )
	{
		/*  00010203 04050607 00010203 04050607 (normal) */
		/*  00010718 191a1b07 00010708 090a0b07 (alt bank) */
		int bank = nth_byte32( namconb1_tilebank32, (code>>13)+8 );
		mangle = (code&0x1fff) + bank*0x2000;
		*tile = mangle;
		*mask = mangle;
	}
	else
	{
		/* the pixmap index is mangled, the transparency bitmask index is not */
		mangle = code&~(0x140);
		if( code&0x100 ) mangle |= 0x040;
		if( code&0x040 ) mangle |= 0x100;
		*tile = mangle;
		*mask = code;
	}
} /* NB2TilemapCB */

static void
namconb1_install_palette( void )
{
	int pen, page, dword_offset, byte_offset;
	data32_t r,g,b;
	data32_t *pSource;

	/**
     * This is unnecessarily expensive.  Better would be to mark palette entries dirty as
     * they are modified, and only process those that have changed.
     */
	pen = 0;
	for( page=0; page<4; page++ )
	{
		pSource = &paletteram32[page*0x2000/4];
		for( dword_offset=0; dword_offset<0x800/4; dword_offset++ )
		{
			r = pSource[dword_offset+0x0000/4];
			g = pSource[dword_offset+0x0800/4];
			b = pSource[dword_offset+0x1000/4];

			for( byte_offset=0; byte_offset<4; byte_offset++ )
			{
				palette_set_color( pen++, r>>24, g>>24, b>>24 );
				r<<=8; g<<=8; b<<=8;
			}
		}
	}
} /* namconb1_install_palette */

/**
 * MCU simulation.  It manages coinage, input ports, and presumably
 * communication with the sound CPU.
 */
static void
handle_mcu( void )
{
	static int toggle;
	static data16_t credits;
	static int old_coin_state;
	static int old_p1;
	static int old_p2;
	static int old_p3;
	static int old_p4;
	int new_coin_state = readinputport(0)&0x3; /* coin1,2 */
	unsigned dsw = readinputport(1)<<16;
	unsigned p1 = readinputport(2);
	unsigned p2 = readinputport(3);
	unsigned p3;
	unsigned p4;
	toggle = !toggle;
	if( toggle ) dsw &= ~(0x80<<16);
	if( namcos2_gametype == NAMCONB2_MACH_BREAKERS )
	{
		p3 = readinputport(4);
		p4 = readinputport(5);
	}
	else
	{
		p3 = 0;
		p4 = 0;
	}

	p1 = (p1&(~old_p1))|(p1<<8);
	p2 = (p2&(~old_p2))|(p2<<8);
	p3 = (p3&(~old_p3))|(p3<<8);
	p4 = (p4&(~old_p4))|(p4<<8);

	old_p1 = p1;
	old_p2 = p2;
	old_p3 = p3;
	old_p4 = p4;

	namconb1_workram32[0x6000/4] = dsw|p1;
	namconb1_workram32[0x6004/4] = (p2<<16)|p3;
	namconb1_workram32[0x6008/4] = p4<<16;

	if( new_coin_state && !old_coin_state )
	{
		credits++;
	}
	old_coin_state = new_coin_state;
	namconb1_workram32[0x601e/4] &= 0xffff0000;
	namconb1_workram32[0x601e/4] |= credits;
} /* handle_mcu */

static void
video_update_common( struct mame_bitmap *bitmap, const struct rectangle *cliprect, int bROZ )
{
	int pri;
	handle_mcu();
	namconb1_install_palette();

	if( bROZ )
	{
		for( pri=0; pri<16; pri++ )
		{
			namco_roz_draw( bitmap,cliprect,pri );
			if( (pri&1)==0 )
			{
				namco_tilemap_draw( bitmap, cliprect, pri/2 );
			}
			namco_obj_draw( bitmap, cliprect, pri );
		}
	}
	else
	{
		for( pri=0; pri<8; pri++ )
		{
			namco_tilemap_draw( bitmap, cliprect, pri );
			namco_obj_draw( bitmap, cliprect, pri );
		}
	}
} /* video_update_common */

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

VIDEO_UPDATE( namconb1 )
{
	int beamx,beamy;
	/* compute window for custom screen blanking */
	struct rectangle clip;
	/*004a 016a 0021 0101 0144 0020 (nebulas ray) */
	data32_t xclip = paletteram32[0x1800/4];
	data32_t yclip = paletteram32[0x1804/4];
	clip.min_x = (xclip>>16)    - 0x4a;
	clip.max_x = (xclip&0xffff) - 0x4a - 1;
	clip.min_y = (yclip>>16)    - 0x21;
	clip.max_y = (yclip&0xffff) - 0x21 - 1;
	/* intersect with master clip rectangle */
	if( clip.min_x < cliprect->min_x ){ clip.min_x = cliprect->min_x; }
	if( clip.min_y < cliprect->min_y ){ clip.min_y = cliprect->min_y; }
	if( clip.max_x > cliprect->max_x ){ clip.max_x = cliprect->max_x; }
	if( clip.max_y > cliprect->max_y ){ clip.max_y = cliprect->max_y; }

	fillbitmap( bitmap, get_black_pen(), cliprect );

	video_update_common( bitmap, &clip, 0 );

	if( namcos2_gametype == NAMCONB1_GUNBULET )
	{
		beamx = ((readinputport(4))*288)/256;
		beamy = ((readinputport(5))*224)/256;
		draw_crosshair( bitmap, beamx, beamy, cliprect );

		beamx = ((readinputport(6))*288)/256;
		beamy = ((readinputport(7))*224)/256;
		draw_crosshair( bitmap, beamx, beamy, cliprect );
	}
}

static int
NB1objcode2tile( int code )
{
	int bank = nth_word32( namconb1_spritebank32, code>>11 );
	return (code&0x7ff) + bank*0x800;
}

VIDEO_START( namconb1 )
{
	if( namco_tilemap_init( NAMCONB1_TILEGFX, memory_region(NAMCONB1_TILEMASKREGION), NB1TilemapCB )==0 )
	{
		namco_obj_init(NAMCONB1_SPRITEGFX,0x0,NB1objcode2tile);
		return 0; /* no error */
	}
	return -1;
} /* namconb1 */

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

VIDEO_UPDATE( namconb2 )
{
	/* compute window for custom screen blanking */
	struct rectangle clip;
	/*004a016a 00210101 01440020 */
	data32_t xclip = paletteram32[0x1800/4];
	data32_t yclip = paletteram32[0x1804/4];
	clip.min_x = (xclip>>16)    - 0x4b;
	clip.max_x = (xclip&0xffff) - 0x4b - 1;
	clip.min_y = (yclip>>16)    - 0x21;
	clip.max_y = (yclip&0xffff) - 0x21 - 1;
	/* intersect with master clip rectangle */
	if( clip.min_x < cliprect->min_x ){ clip.min_x = cliprect->min_x; }
	if( clip.min_y < cliprect->min_y ){ clip.min_y = cliprect->min_y; }
	if( clip.max_x > cliprect->max_x ){ clip.max_x = cliprect->max_x; }
	if( clip.max_y > cliprect->max_y ){ clip.max_y = cliprect->max_y; }

	fillbitmap( bitmap, get_black_pen(), cliprect );

	if( memcmp(tilemap_tile_bank,namconb1_tilebank32,sizeof(tilemap_tile_bank))!=0 )
	{
		namco_tilemap_invalidate();
		memcpy(tilemap_tile_bank,namconb1_tilebank32,sizeof(tilemap_tile_bank));
	}
	video_update_common( bitmap, &clip, 1 );
} /* namconb2_vh_screenrefresh */

static int
NB2objcode2tile( int code )
{
	int bank = nth_byte32( namconb1_spritebank32, (code>>11)&0xf );
	code &= 0x7ff;
	if( namcos2_gametype == NAMCONB2_MACH_BREAKERS )
	{
		if( bank&0x01 ) code |= 0x01*0x800;
		if( bank&0x02 ) code |= 0x02*0x800;
		if( bank&0x04 ) code |= 0x04*0x800;
		if( bank&0x08 ) code |= 0x08*0x800;
		if( bank&0x10 ) code |= 0x10*0x800;
		if( bank&0x40 ) code |= 0x20*0x800;
	}
	else
	{
		if( bank&0x01 ) code |= 0x01*0x800;
		if( bank&0x02 ) code |= 0x04*0x800;
		if( bank&0x04 ) code |= 0x02*0x800;
		if( bank&0x08 ) code |= 0x08*0x800;
		if( bank&0x10 ) code |= 0x10*0x800;
		if( bank&0x40 ) code |= 0x20*0x800;
	}
	return code;
} /* NB2objcode2tile */

VIDEO_START( namconb2 )
{
	if( namco_tilemap_init(
		NAMCONB1_TILEGFX, memory_region(NAMCONB1_TILEMASKREGION), NB2TilemapCB ) == 0 )
	{
		namco_obj_init(NAMCONB1_SPRITEGFX,0x0,NB2objcode2tile);
		if( namco_roz_init(NAMCONB1_ROTGFX,NAMCONB1_ROTMASKREGION)==0 )
		{
			return 0;
		}
	}
	return -1;
} /* namconb2_vh_start */
