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

  HD63484 ACRTC
  Advanced CRT Controller.

  This chip is used in:
  - shanghai.c
  - adp.c
  - sigmab52.c

  ACRTC memory map:

  00000-3ffff = RAM
  40000-7ffff = ROM     handled with a hack in the drivers
  80000-bffff = unused
  c0000-fffff = unused

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

#include "emu.h"
#include "video/hd63484.h"

#define LOG_COMMANDS 0

/* the on-chip FIFO is 16 bytes long, but we use a larger one to simplify */
/* decoding of long commands. Commands can be up to 64KB long... but Shanghai */
/* doesn't reach that length. */

#define FIFO_LENGTH 256

typedef struct _hd63484_state hd63484_state;
struct _hd63484_state
{
	UINT16 *   ram;
	UINT16 reg[256/2];

	int          fifo_counter;
	UINT16       fifo[FIFO_LENGTH];
	UINT16       readfifo;

	UINT16       pattern[16];
	int          org, org_dpd, rwp;
	UINT16       cl0, cl1, ccmp, edg, mask, ppy, pzcy, ppx, pzcx, psy, psx, pey, pzy, pex, pzx, xmin, ymin, xmax, ymax, rwp_dn;
	INT16        cpx, cpy;

	int          regno;

	int          skattva_hack;
};

/*****************************************************************************
    INLINE FUNCTIONS
*****************************************************************************/

INLINE hd63484_state *get_safe_token( running_device *device )
{
	assert(device != NULL);
	assert(device->type() == HD63484);

	return (hd63484_state *)downcast<legacy_device_base *>(device)->token();
}

INLINE const hd63484_interface *get_interface( running_device *device )
{
	assert(device != NULL);
	assert(device->type() == HD63484);
	return (const hd63484_interface *) device->baseconfig().static_config();
}

/*****************************************************************************
    IMPLEMENTATION
*****************************************************************************/

static int get_pixel(running_device *device, int x, int y);

static const int instruction_length[64] =
{
	 0, 3, 2, 1,	/* 0x */
	 0, 0,-1, 2,	/* 1x */
	 0, 3, 3, 3,	/* 2x */
	 0, 0, 0, 0,	/* 3x */
	 0, 1, 2, 2,	/* 4x */
	 0, 0, 4, 4,	/* 5x */
	 5, 5, 5, 5,	/* 6x */
	 5, 5, 5, 5,	/* 7x */
	 3, 3, 3, 3,	/* 8x */
	 3, 3,-2,-2,	/* 9x */
	-2,-2, 2, 4,	/* Ax */
	 5, 5, 7, 7,	/* Bx */
	 3, 3, 1, 1,	/* Cx */
	 2, 2, 2, 2,	/* Dx */
	 5, 5, 5, 5,	/* Ex */
	 5, 5, 5, 5 	/* Fx */
};

static const char *const instruction_name[64] =
{
	"undef","ORG  ","WPR  ","RPR  ",	/* 0x */
	"undef","undef","WPTN ","RPTN ",	/* 1x */
	"undef","DRD  ","DWT  ","DMOD ",	/* 2x */
	"undef","undef","undef","undef",	/* 3x */
	"undef","RD   ","WT   ","MOD  ",	/* 4x */
	"undef","undef","CLR  ","SCLR ",	/* 5x */
	"CPY  ","CPY  ","CPY  ","CPY  ",	/* 6x */
	"SCPY ","SCPY ","SCPY ","SCPY ",	/* 7x */
	"AMOVE","RMOVE","ALINE","RLINE",	/* 8x */
	"ARCT ","RRCT ","APLL ","RPLL ",	/* 9x */
	"APLG ","RPLG ","CRCL ","ELPS ",	/* Ax */
	"AARC ","RARC ","AEARC","REARC",	/* Bx */
	"AFRCT","RFRCT","PAINT","DOT  ",	/* Cx */
	"PTN  ","PTN  ","PTN  ","PTN  ",	/* Dx */
	"AGCPY","AGCPY","AGCPY","AGCPY",	/* Ex */
	"RGCPY","RGCPY","RGCPY","RGCPY" 	/* Fx */
};

static void doclr16( running_device *device, int opcode, UINT16 fill, int *dst, INT16 _ax, INT16 _ay )
{
	hd63484_state *hd63484 = get_safe_token(device);
	INT16 ax,ay;

	ax = _ax;
	ay = _ay;

	for (;;)
	{
		for (;;)
		{
			switch (opcode & 0x0003)
			{
				case 0:
					hd63484->ram[*dst]  = fill;
					break;
				case 1:
					hd63484->ram[*dst] |= fill;
					break;
				case 2:
					hd63484->ram[*dst] &= fill;
					break;
				case 3:
					hd63484->ram[*dst] ^= fill;
					break;
			}
			if (ax == 0)
				break;
			else if (ax > 0)
			{
				*dst = (*dst + 1) & (HD63484_RAM_SIZE - 1);
				ax--;
			}
			else
			{
				*dst = (*dst - 1) & (HD63484_RAM_SIZE - 1);
				ax++;
			}
		}

		ax = _ax;
		if (_ay < 0)
		{
			*dst = (*dst + (hd63484->reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
			if (ay == 0)
				break;
			ay++;
		}
		else
		{
			*dst = (*dst - (hd63484->reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
			if (ay == 0)
				break;
			ay--;
		}
	}
}

static void docpy16( running_device *device, int opcode, int src, int *dst, INT16 _ax, INT16 _ay )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int dstep1,dstep2;
	int ax = _ax;
	int ay = _ay;

	switch (opcode & 0x0700)
	{
		default:
		case 0x0000: dstep1 =  1; dstep2 = -1 * (hd63484->reg[0xca/2] & 0x0fff) - ax * dstep1; break;
		case 0x0100: dstep1 =  1; dstep2 =      (hd63484->reg[0xca/2] & 0x0fff) - ax * dstep1; break;
		case 0x0200: dstep1 = -1; dstep2 = -1 * (hd63484->reg[0xca/2] & 0x0fff) + ax * dstep1; break;
		case 0x0300: dstep1 = -1; dstep2 =      (hd63484->reg[0xca/2] & 0x0fff) + ax * dstep1; break;
		case 0x0400: dstep1 = -1 * (hd63484->reg[0xca/2] & 0x0fff); dstep2 =  1 - ay * dstep1; break;
		case 0x0500: dstep1 =      (hd63484->reg[0xca/2] & 0x0fff); dstep2 =  1 - ay * dstep1; break;
		case 0x0600: dstep1 = -1 * (hd63484->reg[0xca/2] & 0x0fff); dstep2 = -1 + ay * dstep1; break;
		case 0x0700: dstep1 =      (hd63484->reg[0xca/2] & 0x0fff); dstep2 = -1 + ay * dstep1; break;
	}

	for (;;)
	{
		for (;;)
		{
			switch (opcode & 0x0007)
			{
				case 0:
					hd63484->ram[*dst]  = hd63484->ram[src];
					break;
				case 1:
					hd63484->ram[*dst] |= hd63484->ram[src];
					break;
				case 2:
					hd63484->ram[*dst] &= hd63484->ram[src];
					break;
				case 3:
					hd63484->ram[*dst] ^= hd63484->ram[src];
					break;
				case 4:
					if (hd63484->ram[*dst] == (hd63484->ccmp & 0xff))
						hd63484->ram[*dst] = hd63484->ram[src];
					break;
				case 5:
					if (hd63484->ram[*dst] != (hd63484->ccmp & 0xff))
						hd63484->ram[*dst] = hd63484->ram[src];
					break;
				case 6:
					if (hd63484->ram[*dst] < hd63484->ram[src])
						hd63484->ram[*dst] = hd63484->ram[src];
					break;
				case 7:
					if (hd63484->ram[*dst] > hd63484->ram[src])
						hd63484->ram[*dst] = hd63484->ram[src];
					break;
			}

			if (opcode & 0x0800)
			{
				if (ay == 0) break;
				if (_ay > 0)
				{
					src = (src - (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
					*dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
					ay--;
				}
				else
				{
					src = (src + (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
					*dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
					ay++;
				}
			}
			else
			{
				if (ax == 0) break;
				else if (ax > 0)
				{
					src = (src + 1) & (HD63484_RAM_SIZE - 1);
					*dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
					ax--;
				}
				else
				{
					src = (src - 1) & (HD63484_RAM_SIZE - 1);
					*dst = (*dst + dstep1) & (HD63484_RAM_SIZE - 1);
					ax++;
				}
			}
		}

		if (opcode & 0x0800)
		{
			ay = _ay;
			if (_ax < 0)
			{
				src = (src - 1 + ay * (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
				*dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
				if (ax == 0) break;
				ax++;
			}
			else
			{
				src = (src + 1 - ay * (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);
				*dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
				if (ax == 0) break;
				ax--;
			}
		}
		else
		{
			ax = _ax;
			if (_ay < 0)
			{
				src = (src + (hd63484->reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
				*dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
				if (ay == 0) break;
				ay++;
			}
			else
			{
				src = (src - (hd63484->reg[0xca/2] & 0x0fff) - ax) & (HD63484_RAM_SIZE - 1);
				*dst = (*dst + dstep2) & (HD63484_RAM_SIZE - 1);
				if (ay == 0) break;
				ay--;
			}
		}
	}
}

static int org_first_pixel( running_device *device, int _org_dpd )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int gbm = (hd63484->reg[0x02/2] & 0x700) >> 8;

	switch (gbm)
	{
		case 0:
			return (_org_dpd & 0x0f);
		case 1:
			return (_org_dpd & 0x0e) >> 1;
		case 2:
			return (_org_dpd & 0x0c) >> 2;
		case 3:
			return (_org_dpd & 0x08) >> 3;
		case 4:
			return 0;

		default:
			logerror ("Graphic bit mode not supported\n");
			return 0;
	}
}

static void dot( running_device *device, int x, int y, int opm, UINT16 color )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int dst, x_int, x_mod, bpp;
	UINT16 color_shifted, bitmask, bitmask_shifted;

	x += org_first_pixel(device, hd63484->org_dpd);

	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			bpp = 1;
			bitmask = 0x0001;
			break;
		case 1:
			bpp = 2;
			bitmask = 0x0003;
			break;
		case 2:
			bpp = 4;
			bitmask = 0x000f;
			break;
		case 3:
			bpp = 8;
			bitmask = 0x00ff;
			break;
		case 4:
			bpp = 16;
			bitmask = 0xffff;
			break;

		default:
			bpp = 0;
			bitmask = 0x0000;
			logerror ("Graphic bit mode not supported\n");
	}

	// bpp = 4;          // for skattva
	// bitmask = 0x000f; // for skattva

	if (x >= 0)
	{
		x_int = x / (16 / bpp);
		x_mod = x % (16 / bpp);
	}
	else
	{
		x_int = x / (16 / bpp);
		x_mod = -1 * (x % (16 / bpp));
		if (x_mod) {
			x_int--;
			x_mod = (16 / bpp) - x_mod;
		}
	}

	color &= bitmask;

	bitmask_shifted = bitmask << (x_mod * bpp);
	color_shifted = color << (x_mod * bpp);

	dst = (hd63484->org + x_int - y * (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);

	switch (opm)
	{
		case 0:
			hd63484->ram[dst] = (hd63484->ram[dst] & ~bitmask_shifted) | color_shifted;
			break;
		case 1:
			hd63484->ram[dst] = hd63484->ram[dst] | color_shifted;
			break;
		case 2:
			hd63484->ram[dst] = hd63484->ram[dst] & ((hd63484->ram[dst] & ~bitmask_shifted) | color_shifted);
			break;
		case 3:
			hd63484->ram[dst] = hd63484->ram[dst] ^ color_shifted;
			break;
		case 4:
			if (get_pixel(device, x, y) == (hd63484->ccmp & bitmask))
				hd63484->ram[dst] = (hd63484->ram[dst] & ~bitmask_shifted) | color_shifted;
			break;
		case 5:
			if (get_pixel(device, x, y) != (hd63484->ccmp & bitmask))
				hd63484->ram[dst] = (hd63484->ram[dst] & ~bitmask_shifted) | color_shifted;
			break;
		case 6:
			if (get_pixel(device, x, y) < (hd63484->cl0 & bitmask))
				hd63484->ram[dst] = (hd63484->ram[dst] & ~bitmask_shifted) | color_shifted;
			break;
		case 7:
			if (get_pixel(device, x, y) > (hd63484->cl0 & bitmask))
				hd63484->ram[dst] = (hd63484->ram[dst] & ~bitmask_shifted) | color_shifted;
			break;
	}
}

static int get_pixel( running_device *device, int x, int y )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int dst, x_int, x_mod, bpp;
	UINT16 bitmask, bitmask_shifted;

	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			bpp = 1;
			bitmask = 0x0001;
			break;
		case 1:
			bpp = 2;
			bitmask = 0x0003;
			break;
		case 2:
			bpp = 4;
			bitmask = 0x000f;
			break;
		case 3:
			bpp = 8;
			bitmask = 0x00ff;
			break;
		case 4:
			bpp = 16;
			bitmask = 0xffff;
			break;

		default:
			bpp = 0;
			bitmask = 0x0000;
			logerror ("Graphic bit mode not supported\n");
	}
	if (x >= 0)
	{
		x_int = x / (16 / bpp);
		x_mod = x % (16 / bpp);
	}
	else
	{
		x_int = x / (16 / bpp);
		x_mod = -1 * (x % (16 / bpp));
		if (x_mod) {
			x_int--;
			x_mod = (16 / bpp) - x_mod;
		}
	}

	bitmask_shifted = bitmask << (x_mod * bpp);

	dst = (hd63484->org + x_int - y * (hd63484->reg[0xca/2] & 0x0fff)) & (HD63484_RAM_SIZE - 1);

	return ((hd63484->ram[dst] & bitmask_shifted) >> (x_mod * bpp));
}

static int get_pixel_ptn( running_device *device, int x, int y )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int dst, x_int, x_mod, bpp;
	UINT16 bitmask, bitmask_shifted;

	bpp = 1;
	bitmask = 0x0001;

	if (x >= 0)
	{
		x_int = x / (16 / bpp);
		x_mod = x % (16 / bpp);
	}
	else
	{
		x_int = x / (16 / bpp);
		x_mod = -1 * (x % (16 / bpp));
		if (x_mod) {
			x_int--;
			x_mod = (16 / bpp) - x_mod;
		}
	}

	bitmask_shifted = bitmask << (x_mod * bpp);

	dst = (x_int + y * 1);

	if ((hd63484->pattern[dst] & bitmask_shifted) >> (x_mod * bpp))
		return 1;
	else
		return 0;
}

static void agcpy( running_device *device, int opcode, int src_x, int src_y, int dst_x, int dst_y, INT16 _ax, INT16 _ay )
{
	int dst_step1_x,dst_step1_y,dst_step2_x,dst_step2_y;
	int src_step1_x,src_step1_y,src_step2_x,src_step2_y;
	int ax_neg,ay_neg;
	int ax = _ax;
	int ay = _ay;
	int xxs = src_x;
	int yys = src_y;
	int xxd = dst_x;
	int yyd = dst_y;

	if (ax < 0)
		ax_neg = -1;
	else
		ax_neg = 1;
	if (ay < 0)
		ay_neg = -1;
	else
		ay_neg = 1;

	if (opcode & 0x0800)
		switch (opcode & 0x0700)
		{
			default:
			case 0x0000: dst_step1_x =  1; dst_step1_y =  0; dst_step2_x = -ay_neg*ay; dst_step2_y =   1; break;
			case 0x0100: dst_step1_x =  1; dst_step1_y =  0; dst_step2_x = -ay_neg*ay; dst_step2_y =  -1; break;
			case 0x0200: dst_step1_x = -1; dst_step1_y =  0; dst_step2_x =  ay_neg*ay; dst_step2_y =   1; break;
			case 0x0300: dst_step1_x = -1; dst_step1_y =  0; dst_step2_x =  ay_neg*ay; dst_step2_y =  -1; break;
			case 0x0400: dst_step1_x =  0; dst_step1_y =  1; dst_step2_x =  1; dst_step2_y = -ay_neg*ay; break;
			case 0x0500: dst_step1_x =  0; dst_step1_y = -1; dst_step2_x =  1; dst_step2_y =  ay_neg*ay; break;
			case 0x0600: dst_step1_x =  0; dst_step1_y =  1; dst_step2_x = -1; dst_step2_y = -ay_neg*ay; break;
			case 0x0700: dst_step1_x =  0; dst_step1_y = -1; dst_step2_x = -1; dst_step2_y =  ay_neg*ay; break;
		}
	else
		switch (opcode & 0x0700)
		{
			default:
			case 0x0000: dst_step1_x =  1; dst_step1_y =  0; dst_step2_x = -ax_neg*ax; dst_step2_y =   1; break;
			case 0x0100: dst_step1_x =  1; dst_step1_y =  0; dst_step2_x = -ax_neg*ax; dst_step2_y =  -1; break;
			case 0x0200: dst_step1_x = -1; dst_step1_y =  0; dst_step2_x =  ax_neg*ax; dst_step2_y =   1; break;
			case 0x0300: dst_step1_x = -1; dst_step1_y =  0; dst_step2_x =  ax_neg*ax; dst_step2_y =  -1; break;
			case 0x0400: dst_step1_x =  0; dst_step1_y =  1; dst_step2_x =  1; dst_step2_y =  ax_neg*ax; break;
			case 0x0500: dst_step1_x =  0; dst_step1_y = -1; dst_step2_x =  1; dst_step2_y = -ax_neg*ax; break;
			case 0x0600: dst_step1_x =  0; dst_step1_y =  1; dst_step2_x = -1; dst_step2_y =  ax_neg*ax; break;
			case 0x0700: dst_step1_x =  0; dst_step1_y = -1; dst_step2_x = -1; dst_step2_y = -ax_neg*ax; break;
		}

	if ((_ax >= 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0000))
		{ src_step1_x =  1; src_step1_y =  0; src_step2_x = -ax; src_step2_y =   1; }
	else if ((_ax >= 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0000))
		{ src_step1_x =  1; src_step1_y =  0; src_step2_x = -ax; src_step2_y =  -1; }
	else if ((_ax < 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0000))
		{ src_step1_x = -1; src_step1_y =  0; src_step2_x = -ax; src_step2_y =   1; }
	else if ((_ax < 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0000))
		{ src_step1_x = -1; src_step1_y =  0; src_step2_x = -ax; src_step2_y =  -1; }
	else if ((_ax >= 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0800))
		{ src_step1_x =  0; src_step1_y =  1; src_step2_x =   1; src_step2_y = -ay; }
	else if ((_ax >= 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0800))
		{ src_step1_x =  0; src_step1_y = -1; src_step2_x =   1; src_step2_y = -ay; }
	else if ((_ax < 0) && (_ay >= 0) && ((opcode & 0x0800) == 0x0800))
		{ src_step1_x =  0; src_step1_y =  1; src_step2_x =  -1; src_step2_y = -ay; }
	else // ((_ax < 0) && (_ay < 0) && ((opcode & 0x0800) == 0x0800))
		{ src_step1_x =  0; src_step1_y = -1; src_step2_x =  -1; src_step2_y = -ay; }

	for (;;)
	{
		for (;;)
		{
			dot(device, xxd, yyd, opcode & 0x0007, get_pixel(device, xxs, yys));

			if (opcode & 0x0800)
			{
				if (ay == 0) break;
				if (_ay > 0)
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ay--;
				}
				else
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ay++;
				}
			}
			else
			{
				if (ax == 0) break;
				else if (ax > 0)
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ax--;
				}
				else
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ax++;
				}
			}
		}

		if (opcode & 0x0800)
		{
			ay = _ay;
			if (_ax < 0)
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ax == 0) break;
				ax++;
			}
			else
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ax == 0) break;
				ax--;
			}
		}
		else
		{
			ax = _ax;
			if (_ay < 0)
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ay == 0) break;
				ay++;
			}
			else
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ay == 0) break;
				ay--;
			}
		}
	}
}

static void ptn( running_device *device, int opcode, int src_x, int src_y, INT16 _ax, INT16 _ay )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int dst_step1_x = 0,dst_step1_y = 0,dst_step2_x = 0,dst_step2_y = 0;
	int src_step1_x,src_step1_y,src_step2_x,src_step2_y;
	int ax = _ax;
	int ay = _ay;
	int ax_neg,ay_neg;
	int xxs = src_x;
	int yys = src_y;
	int xxd = hd63484->cpx;
	int yyd = hd63484->cpy;
	int getpixel;

	if (ax < 0)
		ax_neg = -1;
	else
		ax_neg = 1;
	if (ay < 0)
		ay_neg = -1;
	else
		ay_neg = 1;

	if (opcode & 0x0800)
		switch (opcode & 0x0700)
		{
			default:
			case 0x0000: logerror("PTN: not supported"); break;
			case 0x0100: logerror("PTN: not supported"); break;
			case 0x0200: logerror("PTN: not supported"); break;
			case 0x0300: logerror("PTN: not supported"); break;
			case 0x0400: logerror("PTN: not supported"); break;
			case 0x0500: logerror("PTN: not supported"); break;
			case 0x0600: logerror("PTN: not supported"); break;
			case 0x0700: logerror("PTN: not supported"); break;
		}
	else
		switch (opcode & 0x0700)
		{
			default:
			case 0x0000: dst_step1_x =  1; dst_step1_y =  0; dst_step2_x = -ax_neg*ax; dst_step2_y =  1; break;
			case 0x0100: logerror("PTN: not supported"); break;
			case 0x0200: dst_step1_x =  0; dst_step1_y =  1; dst_step2_x = -1; dst_step2_y = -ax_neg*ax; break;
			case 0x0300: logerror("PTN: not supported"); break;
			case 0x0400: dst_step1_x = -1; dst_step1_y =  0; dst_step2_x =  ax_neg*ax; dst_step2_y = -1; break;
			case 0x0500: logerror("PTN: not supported"); break;
			case 0x0600: dst_step1_x =  0; dst_step1_y = -1; dst_step2_x =  1; dst_step2_y =  ax_neg*ax; break;
			case 0x0700: logerror("PTN: not supported"); break;
		}

	src_step1_x =  1; src_step1_y =  0; src_step2_x = -ax; src_step2_y =  1;

	for (;;)
	{
		for (;;)
		{
			getpixel = get_pixel_ptn(device, xxs, yys);
			switch ((opcode & 0x0018) >> 3)
			{
				case 0x0000:
					if (getpixel)
						dot(device, xxd, yyd, opcode & 0x0007, hd63484->cl1);
					else
						dot(device, xxd, yyd, opcode & 0x0007, hd63484->cl0);
					break;
				case 0x0001:
					if (getpixel)
						dot(device, xxd, yyd, opcode & 0x0007, hd63484->cl1);
					break;
				case 0x0002:
					if (getpixel == 0)
						dot(device, xxd, yyd, opcode & 0x0007, hd63484->cl0);
					break;
				case 0x0003:
					logerror("PTN: not supported"); break;
					break;
			}

			if (opcode & 0x0800)
			{
				if (ay == 0) break;
				if (_ay > 0)
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ay--;
				}
				else
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ay++;
				}
			}
			else
			{
				if (ax == 0) break;
				else if (ax > 0)
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ax--;
				}
				else
				{
					xxs += src_step1_x;
					yys += src_step1_y;
					xxd += dst_step1_x;
					yyd += dst_step1_y;
					ax++;
				}
			}
		}

		if (opcode & 0x0800)
		{
			ay = _ay;
			if (_ax < 0)
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ax == 0) break;
				ax++;
			}
			else
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ax == 0) break;
				ax--;
			}
		}
		else
		{
			ax = _ax;
			if (_ay < 0)
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ay == 0) break;
				ay++;
			}
			else
			{
				xxs += src_step2_x;
				yys += src_step2_y;
				xxd += dst_step2_x;
				yyd += dst_step2_y;
				if (ay == 0) break;
				ay--;
			}
		}
	}
}

static void line( running_device *device, INT16 sx, INT16 sy, INT16 ex, INT16 ey, INT16 col )
{
	hd63484_state *hd63484 = get_safe_token(device);
	INT16 ax,ay;

	int cpx_t = sx;
	int cpy_t = sy;

	ax = ex - sx;
	ay = ey - sy;

	if (abs(ax) >= abs(ay))
	{
		while (ax)
		{
			dot(device, cpx_t, cpy_t, col & 7, hd63484->cl0);

			if (ax > 0)
			{
				cpx_t++;
				ax--;
			}
			else
			{
				cpx_t--;
				ax++;
			}
			cpy_t = sy + ay * (cpx_t - sx) / (ex - sx);
		}
	}
	else
	{
		while (ay)
		{
			dot(device, cpx_t, cpy_t, col & 7, hd63484->cl0);

			if (ay > 0)
			{
				cpy_t++;
				ay--;
			}
			else
			{
				cpy_t--;
				ay++;
			}
			cpx_t = sx + ax * (cpy_t - sy) / (ey - sy);
		}
	}

}

static void circle( running_device *device, INT16 sx, INT16 sy, UINT16 r, INT16 col )
{
	const float DEG2RAD = 3.14159f/180;
	hd63484_state *hd63484 = get_safe_token(device);
	int i;

	for (i = 0; i < 360 * (r / 10); i++)
	{
		float degInRad = i * DEG2RAD / (r / 10);
		dot(device, sx + cos(degInRad) * r,sy + sin(degInRad) * r, col & 7, hd63484->cl0);
	}
}

static void paint( running_device *device, int sx, int sy, int col )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int getpixel;
	dot(device, sx, sy, 0, col);

	getpixel = get_pixel(device, sx+1,sy);
	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			break;
		case 1:
			break;
		case 2:
			getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
			break;
		case 3:
			getpixel = (getpixel << 8) | (getpixel << 0);
			break;
		case 4:
			break;

		default:
			logerror ("Graphic bit mode not supported\n");
	}
	if ((getpixel != col) && (getpixel != hd63484->edg))
		{
			sx++;
			paint(device, sx, sy, col);
			sx--;
		}

	getpixel = get_pixel(device, sx - 1, sy);
	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			break;
		case 1:
			break;
		case 2:
			getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
			break;
		case 3:
			getpixel = (getpixel << 8) | (getpixel << 0);
			break;
		case 4:
			break;

		default:
			logerror ("Graphic bit mode not supported\n");
	}
	if ((getpixel != col) && (getpixel != hd63484->edg))
		{
			sx--;
			paint(device, sx, sy, col);
			sx++;
		}

	getpixel = get_pixel(device, sx, sy + 1);
	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			break;
		case 1:
			break;
		case 2:
			getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
			break;
		case 3:
			getpixel = (getpixel << 8) | (getpixel << 0);
			break;
		case 4:
			break;

		default:
			logerror ("Graphic bit mode not supported\n");
	}
	if ((getpixel != col) && (getpixel != hd63484->edg))
		{
			sy++;
			paint(device, sx, sy, col);
			sy--;
		}

	getpixel = get_pixel(device, sx, sy - 1);
	switch ((hd63484->reg[0x02/2] & 0x700) >> 8)
	{
		case 0:
			break;
		case 1:
			break;
		case 2:
			getpixel = (getpixel << 12) | (getpixel << 8) | (getpixel << 4) | (getpixel << 0);
			break;
		case 3:
			getpixel = (getpixel << 8) | (getpixel << 0);
			break;
		case 4:
			break;

		default:
			logerror ("Graphic bit mode not supported\n");
	}
	if ((getpixel != col) && (getpixel != hd63484->edg))
		{
			sy--;
			paint(device, sx, sy, col);
			sy++;
		}
}

static void hd63484_command_w(running_device *device, UINT16 cmd)
{
	hd63484_state *hd63484 = get_safe_token(device);
	int len;

	hd63484->fifo[hd63484->fifo_counter++] = cmd;

	len = instruction_length[hd63484->fifo[0] >> 10];
	if (len == -1)
	{
		if (hd63484->fifo_counter < 2) return;
		else len = hd63484->fifo[1] + 2;
	}
	else if (len == -2)
	{
		if (hd63484->fifo_counter < 2) return;
		else len = 2 * hd63484->fifo[1] + 2;
	}

	if (hd63484->fifo_counter >= len)
	{
#if LOG_COMMANDS
		int i;

		logerror("%s: HD63484 command %s (%04x) ", cpuexec_describe_context(device->machine), instruction_name[hd63484->fifo[0] >> 10], hd63484->fifo[0]);
		for (i = 1; i < hd63484->fifo_counter; i++)
			logerror("%04x ", hd63484->fifo[i]);
		logerror("\n");
#endif

		if (hd63484->fifo[0] == 0x0400) { /* ORG */
			hd63484->org = ((hd63484->fifo[1] & 0x00ff) << 12) | ((hd63484->fifo[2] & 0xfff0) >> 4);
			hd63484->org_dpd = hd63484->fifo[2] & 0x000f;
		}
		else if ((hd63484->fifo[0] & 0xffe0) == 0x0800)	/* WPR */
		{
			if (hd63484->fifo[0] == 0x0800)
				hd63484->cl0 = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0801)
				hd63484->cl1 = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0802)
				hd63484->ccmp = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0803)
				hd63484->edg = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0804)
				hd63484->mask = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0805)
				{
					hd63484->ppy  = (hd63484->fifo[1] & 0xf000) >> 12;
					hd63484->pzcy = (hd63484->fifo[1] & 0x0f00) >> 8;
					hd63484->ppx  = (hd63484->fifo[1] & 0x00f0) >> 4;
					hd63484->pzcx = (hd63484->fifo[1] & 0x000f) >> 0;
				}
			else if (hd63484->fifo[0] == 0x0806)
				{
					hd63484->psy  = (hd63484->fifo[1] & 0xf000) >> 12;
					hd63484->psx  = (hd63484->fifo[1] & 0x00f0) >> 4;
				}
			else if (hd63484->fifo[0] == 0x0807)
				{
					hd63484->pey  = (hd63484->fifo[1] & 0xf000) >> 12;
					hd63484->pzy  = (hd63484->fifo[1] & 0x0f00) >> 8;
					hd63484->pex  = (hd63484->fifo[1] & 0x00f0) >> 4;
					hd63484->pzx  = (hd63484->fifo[1] & 0x000f) >> 0;
				}
			else if (hd63484->fifo[0] == 0x0808)
				hd63484->xmin = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x0809)
				hd63484->ymin = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x080a)
				hd63484->xmax = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x080b)
				hd63484->ymax = hd63484->fifo[1];
			else if (hd63484->fifo[0] == 0x080c)
				{
					hd63484->rwp = (hd63484->rwp & 0x00fff) | ((hd63484->fifo[1] & 0x00ff) << 12);
					hd63484->rwp_dn = (hd63484->fifo[1] & 0xc000) >> 14;
				}
			else if (hd63484->fifo[0] == 0x080d)
				{
					hd63484->rwp = (hd63484->rwp & 0xff000) | ((hd63484->fifo[1] & 0xfff0) >> 4);
				}
			else
				logerror("unsupported register\n");
		}
		else if ((hd63484->fifo[0] & 0xffe0) == 0x0c00)	/* RPR */
		{
			if (hd63484->fifo[0] == 0x0c00)
				hd63484->fifo[1] = hd63484->cl0;
			else if (hd63484->fifo[0] == 0x0c01)
				hd63484->fifo[1] = hd63484->cl1;
			else if (hd63484->fifo[0] == 0x0c02)
				hd63484->fifo[1] = hd63484->ccmp;
			else if (hd63484->fifo[0] == 0x0c03)
				hd63484->fifo[1] = hd63484->edg;
			else if (hd63484->fifo[0] == 0x0c04)
				hd63484->fifo[1] = hd63484->mask;
			else if (hd63484->fifo[0] == 0x0c05)
				{
					hd63484->fifo[1] = (hd63484->ppy << 12) | (hd63484->pzcy << 8) | (hd63484->ppx << 4) | hd63484->pzcx;
				}
			else if (hd63484->fifo[0] == 0x0c06)
				{
					hd63484->fifo[1] = (hd63484->psx << 12) | (hd63484->psx << 4);
				}
			else if (hd63484->fifo[0] == 0x0c07)
				{
					hd63484->fifo[1] = (hd63484->pey << 12) | (hd63484->pzy << 8) | (hd63484->pex << 4) | hd63484->pzx;
				}
			else if (hd63484->fifo[0] == 0x0c08)
				hd63484->fifo[1] = hd63484->xmin;
			else if (hd63484->fifo[0] == 0x0c09)
				hd63484->fifo[1] = hd63484->ymin;
			else if (hd63484->fifo[0] == 0x0c0a)
				hd63484->fifo[1] = hd63484->xmax;
			else if (hd63484->fifo[0] == 0x0c0b)
				hd63484->fifo[1] = hd63484->ymax;
			else if (hd63484->fifo[0] == 0x0c0c)
				{
					hd63484->fifo[1] = (hd63484->rwp_dn << 14) | ((hd63484->rwp >> 12) & 0xff);
				}
			else if (hd63484->fifo[0] == 0x0c0d)
				{
					hd63484->fifo[1] = (hd63484->rwp & 0x0fff) << 4;
				}
			else if (hd63484->fifo[0] == 0x0c10)
				{
					// TODO
				}
			else if (hd63484->fifo[0] == 0x0c11)
				{
					// TODO
				}
			else if (hd63484->fifo[0] == 0x0c12)
				{
					hd63484->fifo[1] = hd63484->cpx;
				}
			else if (hd63484->fifo[0] == 0x0c13)
				{
					hd63484->fifo[1] = hd63484->cpy;
				}
			else
				logerror("unsupported register\n");
		}
		else if ((hd63484->fifo[0] & 0xfff0) == 0x1800)	/* WPTN */
		{
			int i;
			int start = hd63484->fifo[0] & 0x000f;
			int n = hd63484->fifo[1];
			for (i = 0; i < n; i++)
				hd63484->pattern[start + i] = hd63484->fifo[2 + i];
		}
		else if (hd63484->fifo[0] == 0x4400)	/* RD */
		{
			hd63484->readfifo = hd63484->ram[hd63484->rwp];
			hd63484->rwp = (hd63484->rwp + 1) & (HD63484_RAM_SIZE - 1);
		}
		else if (hd63484->fifo[0] == 0x4800)	/* WT */
		{
			hd63484->ram[hd63484->rwp] = hd63484->fifo[1];
			hd63484->rwp = (hd63484->rwp + 1) & (HD63484_RAM_SIZE - 1);
		}
		else if (hd63484->fifo[0] == 0x5800)	/* CLR */
		{
			doclr16(device, hd63484->fifo[0], hd63484->fifo[1], &hd63484->rwp, hd63484->fifo[2], hd63484->fifo[3]);

            {
			int fifo2 = (int)hd63484->fifo[2], fifo3 = (int)hd63484->fifo[3];
			if (fifo2 < 0) fifo2 *= -1;
			if (fifo3 < 0) fifo3 *= -1;
			hd63484->rwp += ((fifo2 + 1) * (fifo3 + 1));
            }

		}
		else if ((hd63484->fifo[0] & 0xfffc) == 0x5c00)	/* SCLR */
		{
			doclr16(device, hd63484->fifo[0], hd63484->fifo[1], &hd63484->rwp, hd63484->fifo[2], hd63484->fifo[3]);

            {
                int fifo2 = (int)hd63484->fifo[2], fifo3 = (int)hd63484->fifo[3];
                if (fifo2 < 0) fifo2 *= -1;
                if (fifo3 < 0) fifo3 *= -1;
                hd63484->rwp += ((fifo2 + 1) * (fifo3 + 1));
            }

		}
		else if ((hd63484->fifo[0] & 0xf0ff) == 0x6000)	/* hd63484->cpy */
		{
			docpy16(device, hd63484->fifo[0], ((hd63484->fifo[1] & 0x00ff) << 12) | ((hd63484->fifo[2] & 0xfff0) >> 4), &hd63484->rwp, hd63484->fifo[3], hd63484->fifo[4]);

            {
                int fifo2 = (int)hd63484->fifo[2], fifo3 = (int)hd63484->fifo[3];
                if (fifo2 < 0) fifo2 *= -1;
                if (fifo3 < 0) fifo3 *= -1;
                hd63484->rwp += ((fifo2 + 1) * (fifo3 + 1));
            }

		}
		else if ((hd63484->fifo[0] & 0xf0fc) == 0x7000)	/* Shd63484->cpy */
		{
			docpy16(device, hd63484->fifo[0], ((hd63484->fifo[1] & 0x00ff) << 12) | ((hd63484->fifo[2] & 0xfff0) >> 4), &hd63484->rwp, hd63484->fifo[3], hd63484->fifo[4]);

            {
                int fifo2 = (int)hd63484->fifo[2], fifo3 = (int)hd63484->fifo[3];
                if (fifo2 < 0) fifo2 *= -1;
                if (fifo3 < 0) fifo3 *= -1;
                hd63484->rwp += ((fifo2 + 1) * (fifo3 + 1));
            }

		}
		else if (hd63484->fifo[0] == 0x8000)	/* AMOVE */
		{
			hd63484->cpx = hd63484->fifo[1];
			hd63484->cpy = hd63484->fifo[2];
		}
		else if (hd63484->fifo[0] == 0x8400)	/* RMOVE */
		{
			hd63484->cpx += (INT16)hd63484->fifo[1];
			hd63484->cpy += (INT16)hd63484->fifo[2];
		}
		else if ((hd63484->fifo[0] & 0xff00) == 0x8800)	/* ALINE */
		{
			line(device, hd63484->cpx, hd63484->cpy, hd63484->fifo[1], hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			hd63484->cpx = (INT16)hd63484->fifo[1];
			hd63484->cpy = (INT16)hd63484->fifo[2];
		}
		else if ((hd63484->fifo[0] & 0xff00) == 0x8c00)	/* RLINE */
		{
			line(device, hd63484->cpx, hd63484->cpy, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy + (INT16)hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			hd63484->cpx += (INT16)hd63484->fifo[1];
			hd63484->cpy += (INT16)hd63484->fifo[2];
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0x9000)	/* ARCT */
		{
			line(device, hd63484->cpx, hd63484->cpy, (INT16)hd63484->fifo[1], hd63484->cpy, hd63484->fifo[0] & 0xff);
			line(device, (INT16)hd63484->fifo[1], hd63484->cpy, (INT16)hd63484->fifo[1], (INT16)hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, (INT16)hd63484->fifo[1], (INT16)hd63484->fifo[2], hd63484->cpx, (INT16)hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx, (INT16)hd63484->fifo[2], hd63484->cpx, hd63484->cpy, hd63484->fifo[0] & 0xff);
			hd63484->cpx = (INT16)hd63484->fifo[1];
			hd63484->cpy = (INT16)hd63484->fifo[2];
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0x9400)	/* RRCT  added*/
		{
			line(device, hd63484->cpx, hd63484->cpy, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy, hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy + (INT16)hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy + (INT16)hd63484->fifo[2], hd63484->cpx, hd63484->cpy + (INT16)hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx, hd63484->cpy + (INT16)hd63484->fifo[2], hd63484->cpx, hd63484->cpy, hd63484->fifo[0] & 0xff);

			hd63484->cpx += (INT16)hd63484->fifo[1];
			hd63484->cpy += (INT16)hd63484->fifo[2];
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0xa400)	/* RPLG  added*/
		{
			int nseg, sx, sy, ex, ey;
			sx = hd63484->cpx;
			sy = hd63484->cpy;
			for (nseg = 0; nseg < hd63484->fifo[1]; nseg++)
			{
				ex = sx + (INT16)hd63484->fifo[2 + nseg * 2];
				ey = sy + (INT16)hd63484->fifo[2 + nseg * 2 + 1];
				line(device, sx, sy, ex, ey, hd63484->fifo[0] & 7);
				sx = ex;
				sy = ey;
			}
			line(device, sx, sy, hd63484->cpx, hd63484->cpy, hd63484->fifo[0] & 7);
		}
		else if ((hd63484->fifo[0] & 0xfe00) == 0xa800)	/* CRCL  added*/
		{
			circle(device, hd63484->cpx, hd63484->cpy, hd63484->fifo[1] & 0x1fff, hd63484->fifo[0] & 7); // only 13 bit are used for the radius
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0xc000)	/* AFRCT */
		{
			INT16 pcx, pcy;
			INT16 ax, ay, xx, yy;


			pcx = hd63484->fifo[1];
			pcy = hd63484->fifo[2];
			ax = pcx - hd63484->cpx;
			ay = pcy - hd63484->cpy;
			xx = hd63484->cpx;
			yy = hd63484->cpy;

			for (;;)
			{
				for (;;)
				{
					dot(device, xx, yy, hd63484->fifo[0] & 0x07, hd63484->cl0);

					if (ax == 0) break;
					else if (ax > 0)
					{
						xx++;
						ax--;
					}
					else
					{
						xx--;
						ax++;
					}
				}

				ax = pcx - hd63484->cpx;
				if (pcy < hd63484->cpy)
				{
					yy--;
					xx -= ax;
					if (ay == 0) break;
					ay++;
				}
				else
				{
					yy++;
					xx -= ax;
					if (ay == 0) break;
					ay--;
				}
			}
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0xc400)	/* RFRCT  added TODO*/
		{
			line(device, hd63484->cpx, hd63484->cpy, hd63484->cpx + (INT16)hd63484->fifo[1], hd63484->cpy, hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx + hd63484->fifo[1], hd63484->cpy, hd63484->cpx + hd63484->fifo[1], hd63484->cpy + hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx + hd63484->fifo[1], hd63484->cpy + hd63484->fifo[2], hd63484->cpx, hd63484->cpy + hd63484->fifo[2], hd63484->fifo[0] & 0xff);
			line(device, hd63484->cpx, hd63484->cpy + hd63484->fifo[2], hd63484->cpx, hd63484->cpy, hd63484->fifo[0] & 0xff);

			hd63484->cpx = hd63484->cpx + (INT16)hd63484->fifo[1];
			hd63484->cpy = hd63484->cpy + (INT16)hd63484->fifo[2];
		}
		else if (hd63484->fifo[0] == 0xc800)	/* PAINT */
		{
			paint(device, hd63484->cpx, hd63484->cpy, hd63484->cl0);
		}
		else if ((hd63484->fifo[0] & 0xfff8) == 0xcc00)	/* DOT */
		{
			dot(device, hd63484->cpx, hd63484->cpy, hd63484->fifo[0] & 0xff, hd63484->cl0);
		}
		else if ((hd63484->fifo[0] & 0xf000) == 0xd000)	/* PTN (to do) */
		{
			ptn(device, hd63484->fifo[0], hd63484->psx, hd63484->psy, hd63484->pex - hd63484->psx, hd63484->pey - hd63484->psy);

			if ((hd63484->fifo[0] & 0x0800) == 0x0000)
				switch (hd63484->fifo[0] & 0x0700)
				{
					case 0x0000:
						if ((hd63484->pey - hd63484->psy) > 0)
							hd63484->cpy += (hd63484->pey - hd63484->psy);
						else
							hd63484->cpy -= (hd63484->pey - hd63484->psy);
						break;
					case 0x0100:
						// missing
						break;
					case 0x0200:
						if ((hd63484->pey - hd63484->psy) > 0)
							hd63484->cpx += (hd63484->pey - hd63484->psy);
						else
							hd63484->cpx -= (hd63484->pey - hd63484->psy);
						break;
					case 0x0300:
						// missing
						break;
					case 0x0400:
						if ((hd63484->pey - hd63484->psy) > 0)
							hd63484->cpy -= (hd63484->pey - hd63484->psy);
						else
							hd63484->cpy += (hd63484->pey - hd63484->psy);
						break;
					case 0x0500:
						// missing
						break;
					case 0x0600:
						if ((hd63484->pey - hd63484->psy) > 0)
							hd63484->cpx -= (hd63484->pey - hd63484->psy);
						else
							hd63484->cpx += (hd63484->pey - hd63484->psy);
						break;
					case 0x0700:
						// missing
						break;
				}
			else
				{
					// missing
				}
		}
		else if ((hd63484->fifo[0] & 0xf018) == 0xe000)	/* agcpy */
		{
			agcpy(device, hd63484->fifo[0], (INT16)hd63484->fifo[1], (INT16)hd63484->fifo[2], hd63484->cpx, hd63484->cpy, hd63484->fifo[3], hd63484->fifo[4]);

			switch (hd63484->fifo[0] & 0x0700)
			{
				case 0x0000:
					if (hd63484->fifo[4] > 0)
						hd63484->cpy += hd63484->fifo[4];
					else
						hd63484->cpy -= hd63484->fifo[4];
					break;
				case 0x0100:
					if (hd63484->fifo[4] > 0)
						hd63484->cpy -= hd63484->fifo[4];
					else
						hd63484->cpy += hd63484->fifo[4];
					break;
				case 0x0200:
					if (hd63484->fifo[4] > 0)
						hd63484->cpy += hd63484->fifo[4];
					else
						hd63484->cpy -= hd63484->fifo[4];
					break;
				case 0x0300:
					if (hd63484->fifo[4] > 0)
						hd63484->cpy -= hd63484->fifo[4];
					else
						hd63484->cpy += hd63484->fifo[4];
					break;
				case 0x0400:
					if (hd63484->fifo[3] > 0)
						hd63484->cpx += hd63484->fifo[3];
					else
						hd63484->cpx -= hd63484->fifo[3];
					break;
				case 0x0500:
					if (hd63484->fifo[3] > 0)
						hd63484->cpx += hd63484->fifo[3];
					else
						hd63484->cpx -= hd63484->fifo[3];
					break;
				case 0x0600:
					if (hd63484->fifo[3] > 0)
						hd63484->cpx -= hd63484->fifo[3];
					else
						hd63484->cpx += hd63484->fifo[3];
					break;
				case 0x0700:
					if (hd63484->fifo[3] > 0)
						hd63484->cpx -= hd63484->fifo[3];
					else
						hd63484->cpx += hd63484->fifo[3];
					break;
			}
		}
		else
		{
			logerror("unsupported command\n");
			popmessage("unsupported command %s (%04x)", instruction_name[hd63484->fifo[0] >> 10], hd63484->fifo[0]);
		}

		hd63484->fifo_counter = 0;
	}
}

READ16_DEVICE_HANDLER( hd63484_status_r )
{
//  if (cpu_get_pc(space->cpu) != 0xfced6 && cpu_get_pc(space->cpu) != 0xfe1d6)
//      logerror("%05x: HD63484 status read\n",cpu_get_pc(space->cpu));

	return 0xff22 | (mame_rand(device->machine) & 0x0004);	/* write FIFO ready + command end    +  (read FIFO ready or read FIFO not ready) */
}

WRITE16_DEVICE_HANDLER( hd63484_address_w )
{
	hd63484_state *hd63484 = get_safe_token(device);

	/* only low 8 bits are used */
	if (ACCESSING_BITS_0_7)
		hd63484->regno = data;
}

WRITE16_DEVICE_HANDLER( hd63484_data_w )
{
	hd63484_state *hd63484 = get_safe_token(device);

	COMBINE_DATA(&hd63484->reg[hd63484->regno/2]);

	if (hd63484->skattva_hack)
		hd63484->reg[2/2] = (hd63484->reg[2/2] & 0xf8ff) | 0x0200; // hack to set proper color depth in skattva

	if (hd63484->regno & 0x80)
		hd63484->regno += 2;	/* autoincrement */

#if LOG_COMMANDS
//  logerror("PC %05x: HD63484 register %02x write %04x\n", cpu_get_pc(space->cpu), hd63484->regno, hd63484->reg[hd63484->regno/2]);
#endif

	if (hd63484->regno == 0)	/* FIFO */
		hd63484_command_w(device, hd63484->reg[0]);
}

READ16_DEVICE_HANDLER( hd63484_data_r )
{
	hd63484_state *hd63484 = get_safe_token(device);
	int res;

	if (hd63484->regno == 0x80)
		res = device->machine->primary_screen->vpos();
	else if (hd63484->regno == 0)
	{
#if LOG_COMMANDS
//      logerror("%05x: HD63484 read FIFO\n", cpu_get_pc(space->cpu));
#endif
		res = hd63484->readfifo;
	}
	else
	{
#if LOG_COMMANDS
//      logerror("%05x: HD63484 read register %02x\n", cpu_get_pc(space->cpu), hd63484->regno);
#endif
		res = 0;
	}

	return res;
}

READ16_DEVICE_HANDLER( hd63484_ram_r )
{
	hd63484_state *hd63484 = get_safe_token(device);

	return hd63484->ram[offset];
}

READ16_DEVICE_HANDLER( hd63484_regs_r )
{
	hd63484_state *hd63484 = get_safe_token(device);

	return hd63484->reg[offset];
}

WRITE16_DEVICE_HANDLER( hd63484_ram_w )
{
	hd63484_state *hd63484 = get_safe_token(device);

	COMBINE_DATA(&hd63484->ram[offset]);
}

WRITE16_DEVICE_HANDLER( hd63484_regs_w )
{
	hd63484_state *hd63484 = get_safe_token(device);

	COMBINE_DATA(&hd63484->reg[offset]);
}


static DEVICE_START( hd63484 )
{
	hd63484_state *hd63484 = get_safe_token(device);
	const hd63484_interface *intf = get_interface(device);

	hd63484->skattva_hack = intf->skattva_hack;
	hd63484->ram = auto_alloc_array_clear(device->machine, UINT16, HD63484_RAM_SIZE);

//  state_save_register_device_item(device, 0, hd63484->clear_bitmap);
//  state_save_register_device_item_pointer(device, 0, hd63484->spriteram, 0x1000);
//  state_save_register_device_item_bitmap(device, 0, hd63484->sprites_bitmap);
}

static DEVICE_RESET( hd63484 )
{
	hd63484_state *hd63484 = get_safe_token(device);

	hd63484->fifo_counter = 0;
}

static const char DEVTEMPLATE_SOURCE[] = __FILE__;

#define DEVTEMPLATE_ID( p, s )	p##hd63484##s
#define DEVTEMPLATE_FEATURES	DT_HAS_START | DT_HAS_RESET
#define DEVTEMPLATE_NAME		"HD63484"
#define DEVTEMPLATE_FAMILY		"HD63484 Video Controller"
#include "devtempl.h"


DEFINE_LEGACY_DEVICE(HD63484, hd63484);
