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

  vidhrdw.c

  Functions to emulate the video hardware of the machine.

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

#include "driver.h"
#include "vidhrdw/generic.h"
#include "vidhrdw/tms34061.h"
#include "cpu/m6809/m6809.h"

unsigned char *capbowl_rowaddress;

static unsigned char *raw_video_ram;
static unsigned int  color_count[4096];
static unsigned char dirty_row[256];

static int max_col, max_row, max_col_offset;

static int  capbowl_tms34061_getfunction(int offset);
static int  capbowl_tms34061_getrowaddress(int offset);
static int  capbowl_tms34061_getcoladdress(int offset);
static int  capbowl_tms34061_getpixel(int col, int row);
static void capbowl_tms34061_setpixel(int col, int row, int pixel);

#define PAL_SIZE  0x20

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

  Start the video hardware emulation.

***************************************************************************/
static int capbowl_vertical_interrupt(void)
{
	return M6809_INT_FIRQ;
}

static struct TMS34061interface tms34061_interface =
{
	capbowl_tms34061_getfunction,
	capbowl_tms34061_getrowaddress,
	capbowl_tms34061_getcoladdress,
	capbowl_tms34061_getpixel,
	capbowl_tms34061_setpixel,
	0,
    capbowl_vertical_interrupt,  /* Vertical interrupt causes a FIRQ */
};

int capbowl_vh_start(void)
{
	int i;

	if ((tmpbitmap = osd_new_bitmap(Machine->drv->screen_width,Machine->drv->screen_height,Machine->scrbitmap->depth)) == 0)
	{
		return 1;
	}

	if ((raw_video_ram = malloc(256 * 256)) == 0)
	{
		osd_free_bitmap(tmpbitmap);
		return 1;
	}

	// Initialize TMS34061 emulation
    if (TMS34061_start(&tms34061_interface))
	{
		free(raw_video_ram);
		osd_free_bitmap(tmpbitmap);
		return 1;
	}

	max_row = Machine->drv->visible_area.max_y;
	max_col = Machine->drv->visible_area.max_x;
	max_col_offset = (max_col + 1) / 2 + PAL_SIZE;

	// Initialize color areas. The screen is blank
	memset(raw_video_ram, 0, 256*256);
	palette_init_used_colors();
	memset(color_count, 0, sizeof(color_count));
	memset(dirty_row, 1, sizeof(dirty_row));

	for (i = 0; i < max_row * 16; i+=16)
	{
		palette_used_colors[i] = PALETTE_COLOR_USED;
		color_count[i] = max_col + 1;  // All the pixels are pen 0
	}

	return 0;
}



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

  Stop the video hardware emulation.

***************************************************************************/
void capbowl_vh_stop(void)
{
	free(raw_video_ram);
	osd_free_bitmap(tmpbitmap);

	TMS34061_stop();
}


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

  TMS34061 callbacks

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

static int capbowl_tms34061_getfunction(int offset)
{
	/* The function inputs (FS0-FS2) are hooked up the following way:

	   FS0 = A8
	   FS1 = A9
	   FS2 = grounded
	 */

	return (offset >> 8) & 0x03;
}


static int capbowl_tms34061_getrowaddress(int offset)
{
	/* Row address (RA0-RA8) is not dependent on the offset */
	return *capbowl_rowaddress;
}


static int capbowl_tms34061_getcoladdress(int offset)
{
	/* Column address (CA0-CA8) is hooked up the A0-A7, with A1 being inverted
	   during register access. CA8 is ignored */
	int col = (offset & 0xff);

	if (!(offset & 0x300))
	{
		col ^= 0x02;
	}

	return col;
}


static void capbowl_tms34061_setpixel(int col, int row, int pixel)
{
	int off = ((row << 8) | col);
	int penstart = row << 4;

	int oldpixel = raw_video_ram[off];

	raw_video_ram[off] = pixel;

	if (row > max_row || col >= max_col_offset) return;

	if (col >= PAL_SIZE)
	{
		int oldpen1 = penstart | (oldpixel >> 4);
		int oldpen2 = penstart | (oldpixel & 0x0f);
		int newpen1 = penstart | (pixel >> 4);
		int newpen2 = penstart | (pixel & 0x0f);

		if (oldpen1 != newpen1)
		{
			dirty_row[row] = 1;

			color_count[oldpen1]--;
			if (!color_count[oldpen1]) palette_used_colors[oldpen1] = PALETTE_COLOR_UNUSED;

			color_count[newpen1]++;
			palette_used_colors[newpen1] = PALETTE_COLOR_USED;
		}

		if (oldpen2 != newpen2)
		{
			dirty_row[row] = 1;

			color_count[oldpen2]--;
			if (!color_count[oldpen2]) palette_used_colors[oldpen2] = PALETTE_COLOR_UNUSED;

			color_count[newpen2]++;
			palette_used_colors[newpen2] = PALETTE_COLOR_USED;
		}
	}
	else
	{
		/* Offsets 0-1f are the palette */

		int r = (raw_video_ram[off & ~1] & 0x0f);
		int g = (raw_video_ram[off |  1] >> 4);
		int b = (raw_video_ram[off |  1] & 0x0f);
		r = (r << 4) + r;
		g = (g << 4) + g;
		b = (b << 4) + b;

		palette_change_color(penstart | (col >> 1),r,g,b);
	}
}


static int capbowl_tms34061_getpixel(int col, int row)
{
	return raw_video_ram[row << 8 | col];
}

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

  Draw the game screen in the given osd_bitmap.
  Do NOT call osd_update_display() from this function, it will be called by
  the main emulation engine.

***************************************************************************/
INLINE void plotpixel(int col,int row,int pen)
{
	if (Machine->orientation & ORIENTATION_SWAP_XY)
	{
		int temp;

		temp = col;
		col = row;
		row = temp;
	}
	if (Machine->orientation & ORIENTATION_FLIP_X)
		col = tmpbitmap->width - col - 1;
	if (Machine->orientation & ORIENTATION_FLIP_Y)
		row = tmpbitmap->height - row - 1;

	if (tmpbitmap->depth == 16)
		((unsigned short *)Machine->scrbitmap->line[row])[col] = ((unsigned short *)tmpbitmap->line[row])[col] = pen;
	else
		Machine->scrbitmap->line[row][col] = tmpbitmap->line[row][col] = pen;
}

void capbowl_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
{
	int col, row;
	const unsigned char *remapped;


	if (TMS34061_display_blanked())
	{
		fillbitmap(bitmap,palette_transparent_pen,&Machine->drv->visible_area);

		return;
	}

	if ((remapped = palette_recalc()) != 0)
	{
		for (row = 0; row <= max_row; row++)
		{
			if (dirty_row[row] == 0)
			{
				int i;

				for (i = 0;i < 16;i++)
				{
					if (remapped[16 * row + i] != 0)
					{
						dirty_row[row] = 1;
						break;
					}
				}
			}
		}
	}

	for (row = 0; row <= max_row; row++)
	{
		if (dirty_row[row])
		{
			int col1 = 0;
			int row1 = (row << 8 | PAL_SIZE);
			int row2 =  row << 4;

			dirty_row[row] = 0;

			for (col = PAL_SIZE; col < max_col_offset; col++)
			{
				int pixel = raw_video_ram[row1++];

				plotpixel(col1++,row,Machine->pens[row2 | (pixel >> 4)  ]);
				plotpixel(col1++,row,Machine->pens[row2 | (pixel & 0x0f)]);
			}
		}
	}

	if (full_refresh)
		copybitmap(bitmap,tmpbitmap,0,0,0,0,&Machine->drv->visible_area,TRANSPARENCY_NONE,0);
}
