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

    Irem M52 hardware

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

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

#define BGHEIGHT 64


/*************************************
 *
 *  Palette configuration
 *
 *************************************/

PALETTE_INIT( m52 )
{
	const UINT8 *char_pal = color_prom + 0x000;
	const UINT8 *back_pal = color_prom + 0x200;
	const UINT8 *sprite_pal = color_prom + 0x220;
	const UINT8 *sprite_table = color_prom + 0x240;
	static const int resistances_3[3] = { 1000, 470, 220 };
	static const int resistances_2[2]  = { 470, 220 };
	double weights_r[3], weights_g[3], weights_b[3], scale;
	int i;

	machine.colortable = colortable_alloc(machine, 512 + 32 + 32);

	/* compute palette information for characters/backgrounds */
	scale = compute_resistor_weights(0,	255, -1.0,
			3, resistances_3, weights_r, 0, 0,
			3, resistances_3, weights_g, 0, 0,
			2, resistances_2, weights_b, 0, 0);

	/* character palette */
	for (i = 0; i < 512; i++)
	{
		UINT8 promval = char_pal[i];
		int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2));
		int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5));
		int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7));

		colortable_palette_set_color(machine.colortable, i, MAKE_RGB(r,g,b));
	}

	/* background palette */
	for (i = 0; i < 32; i++)
	{
		UINT8 promval = back_pal[i];
		int r = combine_3_weights(weights_r, BIT(promval,0), BIT(promval,1), BIT(promval,2));
		int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5));
		int b = combine_2_weights(weights_b, BIT(promval,6), BIT(promval,7));

		colortable_palette_set_color(machine.colortable, 512+i, MAKE_RGB(r,g,b));
	}

	/* compute palette information for sprites */
	compute_resistor_weights(0,	255, scale,
			2, resistances_2, weights_r, 470, 0,
			3, resistances_3, weights_g, 470, 0,
			3, resistances_3, weights_b, 470, 0);

	/* sprite palette */
	for (i = 0; i < 32; i++)
	{
		UINT8 promval = sprite_pal[i];
		int r = combine_2_weights(weights_r, BIT(promval,6), BIT(promval,7));
		int g = combine_3_weights(weights_g, BIT(promval,3), BIT(promval,4), BIT(promval,5));
		int b = combine_3_weights(weights_b, BIT(promval,0), BIT(promval,1), BIT(promval,2));

		colortable_palette_set_color(machine.colortable, 512 + 32 + i, MAKE_RGB(r,g,b));
	}

	/* character lookup table */
	for (i = 0; i < 512; i++)
		colortable_entry_set_value(machine.colortable, i, i);

	/* sprite lookup table */
	for (i = 0; i < 16 * 4; i++)
	{
		UINT8 promval = sprite_table[(i & 3) | ((i & ~3) << 1)];
		colortable_entry_set_value(machine.colortable, 512 + i, 512 + 32 + promval);
	}

	/* background */
	/* the palette is a 32x8 PROM with many colors repeated. The address of */
	/* the colors to pick is as follows: */
	/* xbb00: mountains */
	/* 0xxbb: hills */
	/* 1xxbb: city */
	colortable_entry_set_value(machine.colortable, 512+16*4+0*4+0, 512);
	colortable_entry_set_value(machine.colortable, 512+16*4+0*4+1, 512+4);
	colortable_entry_set_value(machine.colortable, 512+16*4+0*4+2, 512+8);
	colortable_entry_set_value(machine.colortable, 512+16*4+0*4+3, 512+12);
	colortable_entry_set_value(machine.colortable, 512+16*4+1*4+0, 512);
	colortable_entry_set_value(machine.colortable, 512+16*4+1*4+1, 512+1);
	colortable_entry_set_value(machine.colortable, 512+16*4+1*4+2, 512+2);
	colortable_entry_set_value(machine.colortable, 512+16*4+1*4+3, 512+3);
	colortable_entry_set_value(machine.colortable, 512+16*4+2*4+0, 512);
	colortable_entry_set_value(machine.colortable, 512+16*4+2*4+1, 512+16+1);
	colortable_entry_set_value(machine.colortable, 512+16*4+2*4+2, 512+16+2);
	colortable_entry_set_value(machine.colortable, 512+16*4+2*4+3, 512+16+3);
}



/*************************************
 *
 *  Tilemap info callback
 *
 *************************************/

static TILE_GET_INFO( get_tile_info )
{
	m52_state *state = machine.driver_data<m52_state>();
	UINT8 video = state->m_videoram[tile_index];
	UINT8 color = state->m_colorram[tile_index];

	int flag = 0;
	int code = 0;

	code = video;

	if (color & 0x80)
	{
		code |= 0x100;
	}

	if (tile_index / 32 <= 6)
	{
		flag |= TILE_FORCE_LAYER0; /* lines 0 to 6 are opaqe? */
	}

	SET_TILE_INFO(0, code, color & 0x3f, flag);
}



/*************************************
 *
 *  Video startup
 *
 *************************************/

VIDEO_START( m52 )
{
	m52_state *state = machine.driver_data<m52_state>();

	state->m_bg_tilemap = tilemap_create(machine, get_tile_info, tilemap_scan_rows,  8, 8, 32, 32);

	tilemap_set_transparent_pen(state->m_bg_tilemap, 0);
	tilemap_set_scrolldx(state->m_bg_tilemap, 128 - 1, -1);
	tilemap_set_scrolldy(state->m_bg_tilemap, 16, 16);
	tilemap_set_scroll_rows(state->m_bg_tilemap, 4); /* only lines 192-256 scroll */

	state->save_item(NAME(state->m_bg1xpos));
	state->save_item(NAME(state->m_bg1ypos));
	state->save_item(NAME(state->m_bg2xpos));
	state->save_item(NAME(state->m_bg2ypos));
	state->save_item(NAME(state->m_bgcontrol));
}



/*************************************
 *
 *  Scroll registers
 *
 *************************************/

WRITE8_HANDLER( m52_scroll_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
/*
    According to the schematics there is only one video register that holds the X scroll value
    with a NAND gate on the V64 and V128 lines to control when it's read, and when
    255 (via 8 pull up resistors) is used.

    So we set the first 3 quarters to 255 and the last to the scroll value
*/
	tilemap_set_scrollx(state->m_bg_tilemap, 0, 255);
	tilemap_set_scrollx(state->m_bg_tilemap, 1, 255);
	tilemap_set_scrollx(state->m_bg_tilemap, 2, 255);
	tilemap_set_scrollx(state->m_bg_tilemap, 3, -data);
}



/*************************************
 *
 *  Video RAM write handlers
 *
 *************************************/

WRITE8_HANDLER( m52_videoram_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();

	state->m_videoram[offset] = data;
	tilemap_mark_tile_dirty(state->m_bg_tilemap, offset);
}


WRITE8_HANDLER( m52_colorram_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();

	state->m_colorram[offset] = data;
	tilemap_mark_tile_dirty(state->m_bg_tilemap, offset);
}



/*************************************
 *
 *  Custom protection
 *
 *************************************/

/* This looks like some kind of protection implemented by a custom chip on the
   scroll board. It mangles the value written to the port m52_bg1xpos_w, as
   follows: result = popcount(value & 0x7f) ^ (value >> 7) */
READ8_HANDLER( m52_protection_r )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	int popcount = 0;
	int temp;

	for (temp = state->m_bg1xpos & 0x7f; temp != 0; temp >>= 1)
		popcount += temp & 1;
	return popcount ^ (state->m_bg1xpos >> 7);
}



/*************************************
 *
 *  Background control write handlers
 *
 *************************************/

WRITE8_HANDLER( m52_bg1ypos_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	state->m_bg1ypos = data;
}

WRITE8_HANDLER( m52_bg1xpos_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	state->m_bg1xpos = data;
}

WRITE8_HANDLER( m52_bg2xpos_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	state->m_bg2xpos = data;
}

WRITE8_HANDLER( m52_bg2ypos_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	state->m_bg2ypos = data;
}

WRITE8_HANDLER( m52_bgcontrol_w )
{
	m52_state *state = space->machine().driver_data<m52_state>();
	state->m_bgcontrol = data;
}



/*************************************
 *
 *  Outputs
 *
 *************************************/

WRITE8_HANDLER( m52_flipscreen_w )
{
	/* screen flip is handled both by software and hardware */
	flip_screen_set(space->machine(), (data & 0x01) ^ (~input_port_read(space->machine(), "DSW2") & 0x01));

	coin_counter_w(space->machine(), 0, data & 0x02);
	coin_counter_w(space->machine(), 1, data & 0x20);
}

WRITE8_HANDLER( alpha1v_flipscreen_w )
{
	flip_screen_set(space->machine(), data & 0x01);
}



/*************************************
 *
 *  Background rendering
 *
 *************************************/

static void draw_background(running_machine &machine, bitmap_t *bitmap, const rectangle *cliprect, int xpos, int ypos, int image)
{
	rectangle rect;
	const rectangle &visarea = machine.primary_screen->visible_area();

	if (flip_screen_get(machine))
	{
		xpos = 255 - xpos;
		ypos = 255 - ypos - BGHEIGHT;
	}

	xpos += 128;

	/* this may not be correct */
	ypos = ypos + (22 - 8);

	drawgfx_transpen(bitmap, cliprect,
		machine.gfx[image],
		0, 0,
		flip_screen_get(machine),
		flip_screen_get(machine),
		xpos,
		ypos, 0);

	drawgfx_transpen(bitmap, cliprect,
		machine.gfx[image],
		0, 0,
		flip_screen_get(machine),
		flip_screen_get(machine),
		xpos - 256,
		ypos, 0);

	rect.min_x = visarea.min_x;
	rect.max_x = visarea.max_x;

	if (flip_screen_get(machine))
	{
		rect.min_y = ypos - BGHEIGHT;
		rect.max_y = ypos - 1;
	}
	else
	{
		rect.min_y = ypos + BGHEIGHT;
		rect.max_y = ypos + 2 * BGHEIGHT - 1;
	}

	bitmap_fill(bitmap, &rect, machine.gfx[image]->color_base + 3);
}



/*************************************
 *
 *  Video render
 *
 *************************************/

SCREEN_UPDATE( m52 )
{
	m52_state *state = screen->machine().driver_data<m52_state>();
	int offs;

	bitmap_fill(bitmap, cliprect, 0);

	if (!(state->m_bgcontrol & 0x20))
	{
		if (!(state->m_bgcontrol & 0x10))
			draw_background(screen->machine(), bitmap, cliprect, state->m_bg2xpos, state->m_bg2ypos, 2); /* distant mountains */

		if (!(state->m_bgcontrol & 0x02))
			draw_background(screen->machine(), bitmap, cliprect, state->m_bg1xpos, state->m_bg1ypos, 3); /* hills */

		if (!(state->m_bgcontrol & 0x04))
			draw_background(screen->machine(), bitmap, cliprect, state->m_bg1xpos, state->m_bg1ypos, 4); /* cityscape */
	}

	tilemap_set_flip(state->m_bg_tilemap, flip_screen_get(screen->machine()) ? TILEMAP_FLIPX | TILEMAP_FLIPY : 0);

	tilemap_draw(bitmap, cliprect, state->m_bg_tilemap, 0, 0);

	/* draw the sprites */
	for (offs = 0xfc; offs >= 0; offs -= 4)
	{
		int sy = 257 - state->m_spriteram[offs];
		int color = state->m_spriteram[offs + 1] & 0x3f;
		int flipx = state->m_spriteram[offs + 1] & 0x40;
		int flipy = state->m_spriteram[offs + 1] & 0x80;
		int code = state->m_spriteram[offs + 2];
		int sx = state->m_spriteram[offs + 3];
		rectangle clip;

		/* sprites from offsets $00-$7F are processed in the upper half of the frame */
		/* sprites from offsets $80-$FF are processed in the lower half of the frame */
		clip = *cliprect;
		if (!(offs & 0x80))
			clip.min_y = 0, clip.max_y = 127;
		else
			clip.min_y = 128, clip.max_y = 255;

		/* adjust for flipping */
		if (flip_screen_get(screen->machine()))
		{
			int temp = clip.min_y;
			clip.min_y = 255 - clip.max_y;
			clip.max_y = 255 - temp;
			flipx = !flipx;
			flipy = !flipy;
			sx = 240 - sx;
			sy = 257 + 11 - sy;
		}

		sx += 128;

		/* in theory anyways; in practice, some of the molecule-looking guys get clipped */
#ifdef SPLIT_SPRITES
		sect_rect(&clip, cliprect);
#else
		clip = *cliprect;
#endif

		drawgfx_transmask(bitmap, &clip, screen->machine().gfx[1],
			code, color, flipx, flipy, sx, sy,
			colortable_get_transpen_mask(screen->machine().colortable, screen->machine().gfx[1], color, 512 + 32));
	}
	return 0;
}
