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

  video.c

  Functions to emulate the video hardware of the machine.

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

#include "emu.h"
#include "video/resnet.h"
#include "includes/taitosj.h"


#define GLOBAL_FLIP_X			(*state->m_video_mode & 0x01)
#define GLOBAL_FLIP_Y			(*state->m_video_mode & 0x02)
#define SPRITE_RAM_PAGE_OFFSET	((*state->m_video_mode & 0x04) ? 0x80 : 0x00)
#define SPRITES_ON				(*state->m_video_mode & 0x80)
#define TRANSPARENT_PEN			(0x40)



static const int layer_enable_mask[3] = { 0x10, 0x20, 0x40 };

typedef void (*copy_layer_func_t)(running_machine &, bitmap_t *,
								  const rectangle *, int, int *, rectangle *);


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

  I call the three layers with the conventional names "front", "middle" and
  "back", because that's their default order, but they can be arranged,
  together with the sprites, in any order. The priority is selected by
  register 0xd300, which works as follow:

  bits 0-3 go to A4-A7 of a 256x4 PROM
  bit 4 selects D0/D1 or D2/D3 of the PROM
  bit 5-7 n.c.
  A0-A3 of the PROM is fed with a mask of the inactive layers
  (i.e. all-zero) in the order sprites-front-middle-back
  the 2-bit code which comes out from the PROM selects the layer
  to display.

  Here is a dump of one of these PROMs; on the right is the resulting order
  (s = sprites f = front m = middle b = back). Note that, in theory, the
  PROM could encode some really funky priority schemes which couldn't be
  reconducted to the simple layer order given here. Luckily, none of the
  games seem to do that. Actually, all of them seem to use the same PROM,
  with the exception of Wild Western.

                                                        d300 pri    d300 pri
  00: 08 09 08 0A 00 05 00 0F 08 09 08 0A 00 05 00 0F |  00  sfmb    10  msfb
  10: 08 09 08 0B 00 0D 00 0F 08 09 08 0A 00 05 00 0F |  01  sfbm    11  msbf
  20: 08 0A 08 0A 04 05 00 0F 08 0A 08 0A 04 05 00 0F |  02  smfb    12  mfsb
  30: 08 0A 08 0A 04 07 0C 0F 08 0A 08 0A 04 05 00 0F |  03  smbf    13  mfbs
  40: 08 0B 08 0B 0C 0F 0C 0F 08 09 08 0A 00 05 00 0F |  04  sbfm    14  mbsf
  50: 08 0B 08 0B 0C 0F 0C 0F 08 0A 08 0A 04 05 00 0F |  05  sbmf    15  mbfs
  60: 0D 0D 0C 0E 0D 0D 0C 0F 01 05 00 0A 01 05 00 0F |  06  fsmb    16  bsfm
  70: 0D 0D 0C 0F 0D 0D 0C 0F 01 09 00 0A 01 05 00 0F |  07  fsbm    17  bsmf
  80: 0D 0D 0E 0E 0D 0D 0C 0F 05 05 02 0A 05 05 00 0F |  08  fmsb    18  bfsm
  90: 0D 0D 0E 0E 0D 0D 0F 0F 05 05 0A 0A 05 05 00 0F |  09  fmbs    19  bfms
  A0: 0D 0D 0F 0F 0D 0D 0F 0F 09 09 08 0A 01 05 00 0F |  0A  fbsm    1A  bmsf
  B0: 0D 0D 0F 0F 0D 0D 0F 0F 09 09 0A 0A 05 05 00 0F |  0B  fbms    1B  bmfs
  C0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F |  0C   -      1C   -
  D0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F |  0D   -      1D   -
  E0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F |  0E   -      1E   -
  F0: 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F 0F |  0F   -      1F   -

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



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

  Convert the color PROMs into a more useable format.

  The Taito games don't have a color PROM. They use RAM to dynamically
  create the palette. The resolution is 9 bit (3 bits per gun).

  The RAM is connected to the RGB output this way:

  bit 0 -- inverter -- 270 ohm resistor  -- RED
  bit 7 -- inverter -- 470 ohm resistor  -- RED
        -- inverter -- 1  kohm resistor  -- RED
        -- inverter -- 270 ohm resistor  -- GREEN
        -- inverter -- 470 ohm resistor  -- GREEN
        -- inverter -- 1  kohm resistor  -- GREEN
        -- inverter -- 270 ohm resistor  -- BLUE
        -- inverter -- 470 ohm resistor  -- BLUE
  bit 0 -- inverter -- 1  kohm resistor  -- BLUE

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

static void set_pens(running_machine &machine)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	static const int resistances[3] = { 1000, 470, 270 };
	double rweights[3], gweights[3], bweights[3];
	int i;

	/* compute the color output resistor weights */
	compute_resistor_weights(0,	255, -1.0,
			3, resistances, rweights, 0, 0,
			3, resistances, gweights, 0, 0,
			3, resistances, bweights, 0, 0);

	for (i = 0; i < 0x40; i++)
	{
		int bit0, bit1, bit2;
		int r, g, b, val;

		/* red component */
		val = state->m_paletteram[(i << 1) | 0x01];
		bit0 = (~val >> 6) & 0x01;
		bit1 = (~val >> 7) & 0x01;
		val = state->m_paletteram[(i << 1) | 0x00];
		bit2 = (~val >> 0) & 0x01;
		r = combine_3_weights(rweights, bit0, bit1, bit2);

		/* green component */
		val = state->m_paletteram[(i << 1) | 0x01];
		bit0 = (~val >> 3) & 0x01;
		bit1 = (~val >> 4) & 0x01;
		bit2 = (~val >> 5) & 0x01;
		g = combine_3_weights(gweights, bit0, bit1, bit2);

		/* blue component */
		val = state->m_paletteram[(i << 1) | 0x01];
		bit0 = (~val >> 0) & 0x01;
		bit1 = (~val >> 1) & 0x01;
		bit2 = (~val >> 2) & 0x01;
		b = combine_3_weights(bweights, bit0, bit1, bit2);

		palette_set_color(machine, i, MAKE_RGB(r, g, b));
	}
}

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

  Start the video hardware emulation.

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

static void compute_draw_order(running_machine &machine)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int i;
	UINT8 *color_prom = machine.region("proms")->base();

	/* do a simple conversion of the PROM into layer priority order. Note that */
	/* this is a simplification, which assumes the PROM encodes a sensible priority */
	/* scheme. */
	for (i = 0; i < 32; i++)
	{
		int j;
		int mask = 0;	/* start with all four layers active, so we'll get the highest */
						/* priority one in the first loop */
		for (j = 3; j >= 0; j--)
		{
			int data = color_prom[0x10 * (i & 0x0f) + mask] & 0x0f;

			if (i & 0x10)
				data = data >> 2;
			else
				data = data & 0x03;

			mask |= (1 << data);	/* in next loop, we'll see which of the remaining */
									/* layers has top priority when this one is transparent */
			state->m_draw_order[i][j] = data;
		}
	}
}


VIDEO_START( taitosj )
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int i;

	state->m_sprite_layer_collbitmap1 = auto_bitmap_alloc(machine,16,16,machine.primary_screen->format());

	for (i = 0; i < 3; i++)
	{
		state->m_layer_bitmap[i] = machine.primary_screen->alloc_compatible_bitmap();
		state->m_sprite_layer_collbitmap2[i] = machine.primary_screen->alloc_compatible_bitmap();
	}

	state->m_sprite_sprite_collbitmap1 = auto_bitmap_alloc(machine,32,32,machine.primary_screen->format());
	state->m_sprite_sprite_collbitmap2 = auto_bitmap_alloc(machine,32,32,machine.primary_screen->format());

	gfx_element_set_source(machine.gfx[0], state->m_characterram);
	gfx_element_set_source(machine.gfx[1], state->m_characterram);
	gfx_element_set_source(machine.gfx[2], state->m_characterram + 0x1800);
	gfx_element_set_source(machine.gfx[3], state->m_characterram + 0x1800);

	compute_draw_order(machine);
}



READ8_HANDLER( taitosj_gfxrom_r )
{
	taitosj_state *state = space->machine().driver_data<taitosj_state>();
	UINT8 ret;

	offs_t offs = state->m_gfxpointer[0] | (state->m_gfxpointer[1] << 8);

	if (offs < 0x8000)
		ret = space->machine().region("gfx1")->base()[offs];
	else
		ret = 0;

	offs = offs + 1;

	state->m_gfxpointer[0] = offs & 0xff;
	state->m_gfxpointer[1] = offs >> 8;

	return ret;
}



WRITE8_HANDLER( taitosj_characterram_w )
{
	taitosj_state *state = space->machine().driver_data<taitosj_state>();
	if (state->m_characterram[offset] != data)
	{
		if (offset < 0x1800)
		{
			gfx_element_mark_dirty(space->machine().gfx[0], (offset / 8) & 0xff);
			gfx_element_mark_dirty(space->machine().gfx[1], (offset / 32) & 0x3f);
		}
		else
		{
			gfx_element_mark_dirty(space->machine().gfx[2], (offset / 8) & 0xff);
			gfx_element_mark_dirty(space->machine().gfx[3], (offset / 32) & 0x3f);
		}

		state->m_characterram[offset] = data;
	}
}

WRITE8_HANDLER( junglhbr_characterram_w )
{
	taitosj_characterram_w(space, offset, data ^ 0xfc);
}


WRITE8_HANDLER( taitosj_collision_reg_clear_w )
{
	taitosj_state *state = space->machine().driver_data<taitosj_state>();
	state->m_collision_reg[0] = 0;
	state->m_collision_reg[1] = 0;
	state->m_collision_reg[2] = 0;
	state->m_collision_reg[3] = 0;
}


INLINE int get_sprite_xy(taitosj_state *state, UINT8 which, UINT8* sx, UINT8* sy)
{
	offs_t offs = which * 4;

	*sx =       state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 0] - 1;
	*sy = 240 - state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 1];

	return (*sy < 240);
}


INLINE const gfx_element *get_sprite_gfx_element(running_machine &machine, UINT8 which)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	offs_t offs = which * 4;

	return machine.gfx[(state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x40) ? 3 : 1];
}


static int check_sprite_sprite_bitpattern(running_machine &machine,
										  int sx1, int sy1, int which1,
										  int sx2, int sy2, int which2)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int x, y, minx, miny, maxx = 16, maxy = 16;

	offs_t offs1 = which1 * 4;
	offs_t offs2 = which2 * 4;

	/* normalize coordinates to (0,0) and compute overlap */
	if (sx1 < sx2)
	{
		sx2 -= sx1;
		sx1 = 0;
		minx = sx2;
	}
	else
	{
		sx1 -= sx2;
		sx2 = 0;
		minx = sx1;
	}

	if (sy1 < sy2)
	{
		sy2 -= sy1;
		sy1 = 0;
		miny = sy2;
	}
	else
	{
		sy1 -= sy2;
		sy2 = 0;
		miny = sy1;
	}

	/* draw the sprites into separate bitmaps and check overlapping region */
	bitmap_fill(state->m_sprite_layer_collbitmap1, NULL, TRANSPARENT_PEN);
	drawgfx_transpen(state->m_sprite_sprite_collbitmap1, 0, get_sprite_gfx_element(machine, which1),
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 3] & 0x3f,
			0,
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 2] & 0x01,
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs1 + 2] & 0x02,
			sx1, sy1, 0);

	bitmap_fill(state->m_sprite_sprite_collbitmap2, NULL, TRANSPARENT_PEN);
	drawgfx_transpen(state->m_sprite_sprite_collbitmap2, 0, get_sprite_gfx_element(machine, which2),
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 3] & 0x3f,
			0,
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 2] & 0x01,
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs2 + 2] & 0x02,
			sx2, sy2, 0);

	for (y = miny; y < maxy; y++)
		for (x = minx; x < maxx; x++)
			if ((*BITMAP_ADDR16(state->m_sprite_sprite_collbitmap1, y, x) != TRANSPARENT_PEN) &&
			    (*BITMAP_ADDR16(state->m_sprite_sprite_collbitmap2, y, x) != TRANSPARENT_PEN))
				return 1;  /* collided */

	return 0;
}


static void check_sprite_sprite_collision(running_machine &machine)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	if (SPRITES_ON)
	{
		int which1;

		/* chech each pair of sprites */
		for (which1 = 0; which1 < 0x20; which1++)
		{
			int which2;
			UINT8 sx1, sy1;

			if ((which1 >= 0x10) && (which1 <= 0x17)) continue;	/* no sprites here */

			if (!get_sprite_xy(state, which1, &sx1, &sy1)) continue;

			for (which2 = which1 + 1; which2 < 0x20; which2++)
			{
				UINT8 sx2, sy2;

				if ((which2 >= 0x10) && (which2 <= 0x17)) continue;	  /* no sprites here */

				if (!get_sprite_xy(state, which2, &sx2, &sy2)) continue;

				/* quickly rule out any pairs that cannot be touching */
				if ((abs((INT8)sx1 - (INT8)sx2) < 16) &&
					(abs((INT8)sy1 - (INT8)sy2) < 16))
				{
					int reg;

					if (!check_sprite_sprite_bitpattern(machine, sx1, sy1, which1, sx2, sy2, which2))  continue;

					/* mark sprite as collided */
					/* note that only the sprite with the higher number is marked */
					/* as collided. This is how the hardware works and required */
					/* by Pirate Pete to be able to finish the last round. */

					/* the last sprite has to be moved at the start of the list */
					if (which2 == 0x1f)
					{
						reg = which1 >> 3;
						if (reg == 3)  reg = 2;

						state->m_collision_reg[reg] |= (1 << (which1 & 0x07));
					}
					else
					{
						reg = which2 >> 3;
						if (reg == 3)  reg = 2;

						state->m_collision_reg[reg] |= (1 << (which2 & 0x07));
					}
				}
			}
		}
	}
}


static void calculate_sprite_areas(running_machine &machine, int *sprites_on, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int which;
	int width = machine.primary_screen->width();
	int height = machine.primary_screen->height();

	for (which = 0; which < 0x20; which++)
	{
		UINT8 sx, sy;

		if ((which >= 0x10) && (which <= 0x17)) continue;	/* no sprites here */

		if (get_sprite_xy(state, which, &sx, &sy))
		{
			int minx, miny, maxx, maxy;

			if (GLOBAL_FLIP_X)
				sx = 238 - sx;

			if (GLOBAL_FLIP_Y)
				sy = 242 - sy;

			minx = sx;
			miny = sy;

			maxx = minx + 15;
			maxy = miny + 15;

			/* check for bitmap bounds to avoid illegal memory access */
			if (minx < 0) minx = 0;
			if (miny < 0) miny = 0;
			if (maxx >= width - 1)
				maxx = width - 1;
			if (maxy >= height - 1)
				maxy = height - 1;

			sprite_areas[which].min_x = minx;
			sprite_areas[which].max_x = maxx;
			sprite_areas[which].min_y = miny;
			sprite_areas[which].max_y = maxy;

			sprites_on[which] = 1;
		}
		/* sprite is off */
		else
			sprites_on[which] = 0;
	}
}


static int check_sprite_layer_bitpattern(running_machine &machine, int which, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int y, x;
	offs_t offs = which * 4;
	int result = 0;  /* no collisions */

	int	check_layer_1 = *state->m_video_mode & layer_enable_mask[0];
	int	check_layer_2 = *state->m_video_mode & layer_enable_mask[1];
	int	check_layer_3 = *state->m_video_mode & layer_enable_mask[2];

	int minx = sprite_areas[which].min_x;
	int miny = sprite_areas[which].min_y;
	int maxx = sprite_areas[which].max_x + 1;
	int maxy = sprite_areas[which].max_y + 1;

	int flip_x = (state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x01) ^ GLOBAL_FLIP_X;
	int flip_y = (state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x02) ^ GLOBAL_FLIP_Y;

	/* draw sprite into a bitmap and check if layers collide */
	bitmap_fill(state->m_sprite_layer_collbitmap1, NULL, TRANSPARENT_PEN);
	drawgfx_transpen(state->m_sprite_layer_collbitmap1, 0,get_sprite_gfx_element(machine, which),
			state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x3f,
			0,
			flip_x, flip_y,
			0,0,0);

	for (y = miny; y < maxy; y++)
		for (x = minx; x < maxx; x++)
			if (*BITMAP_ADDR16(state->m_sprite_layer_collbitmap1, y - miny, x - minx) != TRANSPARENT_PEN) /* is there anything to check for ? */
			{
				if (check_layer_1 && (*BITMAP_ADDR16(state->m_sprite_layer_collbitmap2[0], y, x) != TRANSPARENT_PEN))
					result |= 0x01;  /* collided with layer 1 */

				if (check_layer_2 && (*BITMAP_ADDR16(state->m_sprite_layer_collbitmap2[1], y, x) != TRANSPARENT_PEN))
					result |= 0x02;  /* collided with layer 2 */

				if (check_layer_3 && (*BITMAP_ADDR16(state->m_sprite_layer_collbitmap2[2], y, x) != TRANSPARENT_PEN))
					result |= 0x04;  /* collided with layer 3 */
			}

	return result;
}


static void check_sprite_layer_collision(running_machine &machine, int *sprites_on, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	if (SPRITES_ON)
	{
		int which;

		/* check each sprite */
		for (which = 0; which < 0x20; which++)
		{
			if ((which >= 0x10) && (which <= 0x17)) continue;	/* no sprites here */

			if (sprites_on[which])
				state->m_collision_reg[3] |= check_sprite_layer_bitpattern(machine, which, sprite_areas);
		}
	}
}


static void draw_layers(running_machine &machine)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	offs_t offs;

	bitmap_fill(state->m_layer_bitmap[0], NULL, TRANSPARENT_PEN);
	bitmap_fill(state->m_layer_bitmap[1], NULL, TRANSPARENT_PEN);
	bitmap_fill(state->m_layer_bitmap[2], NULL, TRANSPARENT_PEN);

	for (offs = 0; offs < 0x0400; offs++)
	{
		int sx = offs % 32;
		int sy = offs / 32;

		if (GLOBAL_FLIP_X) sx = 31 - sx;
		if (GLOBAL_FLIP_Y) sy = 31 - sy;

		drawgfx_transpen(state->m_layer_bitmap[0],0,machine.gfx[state->m_colorbank[0] & 0x08 ? 2 : 0],
				state->m_videoram_1[offs],
				state->m_colorbank[0] & 0x07,
				GLOBAL_FLIP_X,GLOBAL_FLIP_Y,
				8*sx,8*sy,0);

		drawgfx_transpen(state->m_layer_bitmap[1],0,machine.gfx[state->m_colorbank[0] & 0x80 ? 2 : 0],
				state->m_videoram_2[offs],
				(state->m_colorbank[0] >> 4) & 0x07,
				GLOBAL_FLIP_X,GLOBAL_FLIP_Y,
				8*sx,8*sy,0);

		drawgfx_transpen(state->m_layer_bitmap[2],0,machine.gfx[state->m_colorbank[1] & 0x08 ? 2 : 0],
				state->m_videoram_3[offs],
				state->m_colorbank[1] & 0x07,
				GLOBAL_FLIP_X,GLOBAL_FLIP_Y,
				8*sx,8*sy,0);
	}
}


static void draw_sprites(running_machine &machine, bitmap_t *bitmap)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	/*
       sprite visibility area is missing 4 pixels from the sides, surely to reduce
       wraparound side effects. This was verified on a real Elevator Action.
       Note that the clipping is asymmetrical. This matches the real thing.
       I'm not sure of what should happen when the screen is flipped, though.
     */
	static const rectangle spritevisiblearea =
	{
		0*8+3, 32*8-1-1,
		2*8, 30*8-1
	};
	static const rectangle spritevisibleareaflip =
	{
		0*8+1, 32*8-3-1,
		2*8, 30*8-1
	};

	if (SPRITES_ON)
	{
		int sprite;

		/* drawing order is a bit strange. The last sprite has to be moved at the start of the list. */
		for (sprite = 0x1f; sprite >= 0; sprite--)
		{
			UINT8 sx, sy;

			int which = (sprite - 1) & 0x1f;	/* move last sprite at the head of the list */
			offs_t offs = which * 4;

			if ((which >= 0x10) && (which <= 0x17)) continue;	/* no sprites here */

			if (get_sprite_xy(state, which, &sx, &sy))
			{
				int code = state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 3] & 0x3f;
				int color = 2 * ((state->m_colorbank[1] >> 4) & 0x03) + ((state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] >> 2) & 0x01);
				int flip_x = state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x01;
				int flip_y = state->m_spriteram[SPRITE_RAM_PAGE_OFFSET + offs + 2] & 0x02;

				if (GLOBAL_FLIP_X)
				{
					sx = 238 - sx;
					flip_x = !flip_x;
				}

				if (GLOBAL_FLIP_Y)
				{
					sy = 242 - sy;
					flip_y = !flip_y;
				}

				drawgfx_transpen(bitmap, GLOBAL_FLIP_X ? &spritevisibleareaflip : &spritevisiblearea,get_sprite_gfx_element(machine, which), code, color,
						flip_x, flip_y, sx, sy,0);

				/* draw with wrap around. The horizontal games (eg. sfposeid) need this */
				drawgfx_transpen(bitmap, GLOBAL_FLIP_X ? &spritevisibleareaflip : &spritevisiblearea,get_sprite_gfx_element(machine, which), code, color,
						flip_x, flip_y, sx - 0x100, sy,0);
			}
		}
	}
}


static void taitosj_copy_layer(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect,
							   int which, int *sprites_on, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	static const int fudge1[3] = { 3,  1, -1 };
	static const int fudge2[3] = { 8, 10, 12 };

	if (*state->m_video_mode & layer_enable_mask[which])
	{
		int i, scrollx, scrolly[32];

		scrollx = state->m_scroll[2 * which];

		if (GLOBAL_FLIP_X)
			scrollx =  (scrollx & 0xf8) + ((scrollx + fudge1[which]) & 7) + fudge2[which];
		else
			scrollx = -(scrollx & 0xf8) + ((scrollx + fudge1[which]) & 7) + fudge2[which];

		if (GLOBAL_FLIP_Y)
			for (i = 0;i < 32;i++)
				scrolly[31 - i] =  state->m_colscrolly[32 * which + i] + state->m_scroll[2 * which + 1];
		else
			for (i = 0;i < 32;i++)
				scrolly[i]      = -state->m_colscrolly[32 * which + i] - state->m_scroll[2 * which + 1];

		copyscrollbitmap_trans(bitmap, state->m_layer_bitmap[which], 1, &scrollx, 32, scrolly, cliprect, TRANSPARENT_PEN);

		/* store parts covered with sprites for sprites/layers collision detection */
		for (i = 0; i < 0x20; i++)
		{
			if ((i >= 0x10) && (i <= 0x17)) continue; /* no sprites here */

			if (sprites_on[i])
				copyscrollbitmap(state->m_sprite_layer_collbitmap2[which], state->m_layer_bitmap[which], 1, &scrollx, 32, scrolly, &sprite_areas[i]);
		}
	}
}


static void kikstart_copy_layer(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect,
								int which, int *sprites_on, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	if (*state->m_video_mode & layer_enable_mask[which])
	{
		int i, scrolly, scrollx[32 * 8];

		for (i = 1; i < 32*8; i++)	/* 1-255 ! */
			if (GLOBAL_FLIP_Y)
				switch (which)
				{
				case 0:	scrollx[32 * 8 - i] = 0 ;break;
				case 1:	scrollx[32 * 8 - i] = state->m_kikstart_scrollram[i] + ((state->m_scroll[2 * which] + 0x0a) & 0xff);break;
				case 2:	scrollx[32 * 8 - i] = state->m_kikstart_scrollram[0x100 + i] + ((state->m_scroll[2 * which] + 0xc) & 0xff);break;
				}
			else
				switch (which)
				{
				case 0:	scrollx[i] = 0 ;break;
				case 1:	scrollx[i] = 0xff - state->m_kikstart_scrollram[i - 1] - ((state->m_scroll[2 * which] - 0x10) & 0xff);break;
				case 2:	scrollx[i] = 0xff - state->m_kikstart_scrollram[0x100 + i - 1] - ((state->m_scroll[2 * which] - 0x12) & 0xff);break;
				}

		scrolly = state->m_scroll[2 * which + 1];	/* always 0 */
		copyscrollbitmap_trans(bitmap, state->m_layer_bitmap[which], 32 * 8, scrollx, 1, &scrolly, cliprect, TRANSPARENT_PEN);

		/* store parts covered with sprites for sprites/layers collision detection */
		for (i = 0; i < 0x20; i++)
		{
			if ((i >= 0x10) && (i <= 0x17)) continue; /* no sprites here */

			if (sprites_on[i])
				copyscrollbitmap(state->m_sprite_layer_collbitmap2[which], state->m_layer_bitmap[which], 32 * 8, scrollx, 1, &scrolly, &sprite_areas[i]);
		}
	}
}


static void copy_layer(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect,
					   copy_layer_func_t copy_layer_func, int which, int *sprites_on, rectangle *sprite_areas)
{
	if (which == 0)
		draw_sprites(machine, bitmap);
	else
		copy_layer_func(machine, bitmap, cliprect, which - 1, sprites_on, sprite_areas);
}


static void copy_layers(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect,
						copy_layer_func_t copy_layer_func, int *sprites_on, rectangle *sprite_areas)
{
	taitosj_state *state = machine.driver_data<taitosj_state>();
	int i = 0;

	/* fill the screen with the background color */
	bitmap_fill(bitmap, cliprect, 8 * (state->m_colorbank[1] & 0x07));

	for (i = 0; i < 4; i++)
	{
		int which = state->m_draw_order[*state->m_video_priority & 0x1f][i];

		copy_layer(machine, bitmap, cliprect, copy_layer_func, which, sprites_on, sprite_areas);
	}
}


static void check_collision(running_machine &machine, int *sprites_on, rectangle *sprite_areas)
{
	check_sprite_sprite_collision(machine);

	check_sprite_layer_collision(machine, sprites_on, sprite_areas);

	/*check_layer_layer_collision();*/	/* not implemented !!! */
}


static int video_update_common(running_machine &machine, bitmap_t *bitmap,
							   const rectangle *cliprect, copy_layer_func_t copy_layer_func)
{
	int sprites_on[0x20];			/* 1 if sprite is active */
	rectangle sprite_areas[0x20];	/* areas on bitmap (sprite locations) */

	set_pens(machine);

	draw_layers(machine);

	calculate_sprite_areas(machine, sprites_on, sprite_areas);

	copy_layers(machine, bitmap, cliprect, copy_layer_func, sprites_on, sprite_areas);

	/*check_sprite_layer_collision() uses drawn bitmaps, so it must me called _AFTER_ draw_layers() */
	check_collision(machine, sprites_on, sprite_areas);

	return 0;
}


SCREEN_UPDATE( taitosj )
{
	return video_update_common(screen->machine(), bitmap, cliprect, taitosj_copy_layer);
}


SCREEN_UPDATE( kikstart )
{
	return video_update_common(screen->machine(), bitmap, cliprect, kikstart_copy_layer);
}
