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

    Incredible Technologies/Strata system
    (32-bit blitter variant)

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

#include "emu.h"
#include "profiler.h"
#include "cpu/m68000/m68000.h"
#include "includes/itech32.h"


/*************************************
 *
 *  Debugging
 *
 *************************************/

#define BLIT_LOGGING			0



/*************************************
 *
 *  Blitter constants
 *
 *************************************/

#define VIDEO_UNKNOWN00			itech32_video[0x00/2]	/* $0087 at startup */
#define VIDEO_STATUS			itech32_video[0x00/2]
#define VIDEO_INTSTATE			itech32_video[0x02/2]
#define VIDEO_INTACK			itech32_video[0x02/2]
#define VIDEO_TRANSFER			itech32_video[0x04/2]
#define VIDEO_TRANSFER_FLAGS	itech32_video[0x06/2]	/* $5080 at startup (kept at $1512) */
#define VIDEO_COMMAND			itech32_video[0x08/2]	/* $0005 at startup */
#define VIDEO_INTENABLE			itech32_video[0x0a/2]	/* $0144 at startup (kept at $1514) */
#define VIDEO_TRANSFER_HEIGHT	itech32_video[0x0c/2]
#define VIDEO_TRANSFER_WIDTH	itech32_video[0x0e/2]
#define VIDEO_TRANSFER_ADDRLO	itech32_video[0x10/2]
#define VIDEO_TRANSFER_X		itech32_video[0x12/2]
#define VIDEO_TRANSFER_Y		itech32_video[0x14/2]
#define VIDEO_SRC_YSTEP			itech32_video[0x16/2]	/* $0011 at startup */
#define VIDEO_SRC_XSTEP			itech32_video[0x18/2]
#define VIDEO_DST_XSTEP			itech32_video[0x1a/2]
#define VIDEO_DST_YSTEP			itech32_video[0x1c/2]
#define VIDEO_YSTEP_PER_X		itech32_video[0x1e/2]
#define VIDEO_XSTEP_PER_Y		itech32_video[0x20/2]
#define VIDEO_UNKNOWN22			itech32_video[0x22/2]	/* $0033 at startup */
#define VIDEO_LEFTCLIP			itech32_video[0x24/2]
#define VIDEO_RIGHTCLIP			itech32_video[0x26/2]
#define VIDEO_TOPCLIP			itech32_video[0x28/2]
#define VIDEO_BOTTOMCLIP		itech32_video[0x2a/2]
#define VIDEO_INTSCANLINE		itech32_video[0x2c/2]	/* $00ef at startup */
#define VIDEO_TRANSFER_ADDRHI	itech32_video[0x2e/2]	/* $0000 at startup */

#define VIDEO_UNKNOWN30			itech32_video[0x30/2]	/* $0040 at startup */
#define VIDEO_VTOTAL			itech32_video[0x32/2]	/* $0106 at startup */
#define VIDEO_VSYNC				itech32_video[0x34/2]	/* $0101 at startup */
#define VIDEO_VBLANK_START		itech32_video[0x36/2]	/* $00f3 at startup */
#define VIDEO_VBLANK_END		itech32_video[0x38/2]	/* $0003 at startup */
#define VIDEO_HTOTAL			itech32_video[0x3a/2]	/* $01fc at startup */
#define VIDEO_HSYNC				itech32_video[0x3c/2]	/* $01e4 at startup */
#define VIDEO_HBLANK_START		itech32_video[0x3e/2]	/* $01b2 at startup */
#define VIDEO_HBLANK_END		itech32_video[0x40/2]	/* $0032 at startup */
#define VIDEO_UNKNOWN42			itech32_video[0x42/2]	/* $0015 at startup */
#define VIDEO_DISPLAY_YORIGIN1	itech32_video[0x44/2]	/* $0000 at startup */
#define VIDEO_DISPLAY_YORIGIN2	itech32_video[0x46/2]	/* $0000 at startup */
#define VIDEO_DISPLAY_YSCROLL2	itech32_video[0x48/2]	/* $0000 at startup */
#define VIDEO_UNKNOWN4a			itech32_video[0x4a/2]	/* $0000 at startup */
#define VIDEO_DISPLAY_XORIGIN1	itech32_video[0x4c/2]	/* $0000 at startup */
#define VIDEO_DISPLAY_XORIGIN2	itech32_video[0x4e/2]	/* $0000 at startup */
#define VIDEO_DISPLAY_XSCROLL2	itech32_video[0x50/2]	/* $0000 at startup */
#define VIDEO_UNKNOWN52			itech32_video[0x52/2]	/* $0000 at startup */
#define VIDEO_UNKNOWN54			itech32_video[0x54/2]	/* $0080 at startup */
#define VIDEO_UNKNOWN56			itech32_video[0x56/2]	/* $00c0 at startup */
#define VIDEO_UNKNOWN58			itech32_video[0x58/2]	/* $01c0 at startup */
#define VIDEO_UNKNOWN5a			itech32_video[0x5a/2]	/* $01c0 at startup */
#define VIDEO_UNKNOWN5c			itech32_video[0x5c/2]	/* $01cf at startup */
#define VIDEO_UNKNOWN5e			itech32_video[0x5e/2]	/* $01cf at startup */
#define VIDEO_UNKNOWN60			itech32_video[0x60/2]	/* $01e3 at startup */
#define VIDEO_UNKNOWN62			itech32_video[0x62/2]	/* $01cf at startup */
#define VIDEO_UNKNOWN64			itech32_video[0x64/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN66			itech32_video[0x66/2]	/* $0183 at startup */
#define VIDEO_UNKNOWN68			itech32_video[0x68/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN6a			itech32_video[0x6a/2]	/* $000f at startup */
#define VIDEO_UNKNOWN6c			itech32_video[0x6c/2]	/* $018f at startup */
#define VIDEO_UNKNOWN6e			itech32_video[0x6e/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN70			itech32_video[0x70/2]	/* $000f at startup */
#define VIDEO_UNKNOWN72			itech32_video[0x72/2]	/* $000f at startup */
#define VIDEO_UNKNOWN74			itech32_video[0x74/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN76			itech32_video[0x76/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN78			itech32_video[0x78/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN7a			itech32_video[0x7a/2]	/* $01ff at startup */
#define VIDEO_UNKNOWN7c			itech32_video[0x7c/2]	/* $0820 at startup */
#define VIDEO_UNKNOWN7e			itech32_video[0x7e/2]	/* $0100 at startup */

#define VIDEO_STARTSTEP			itech32_video[0x80/2]	/* drivedge only? */
#define VIDEO_LEFTSTEPLO		itech32_video[0x82/2]	/* drivedge only? */
#define VIDEO_LEFTSTEPHI		itech32_video[0x84/2]	/* drivedge only? */
#define VIDEO_RIGHTSTEPLO		itech32_video[0x86/2]	/* drivedge only? */
#define VIDEO_RIGHTSTEPHI		itech32_video[0x88/2]	/* drivedge only? */

#define VIDEOINT_SCANLINE		0x0004
#define VIDEOINT_BLITTER		0x0040

#define XFERFLAG_TRANSPARENT	0x0001
#define XFERFLAG_XFLIP			0x0002
#define XFERFLAG_YFLIP			0x0004
#define XFERFLAG_DSTXSCALE		0x0008
#define XFERFLAG_DYDXSIGN		0x0010
#define XFERFLAG_DXDYSIGN		0x0020
#define XFERFLAG_UNKNOWN8		0x0100
#define XFERFLAG_CLIP			0x0400
#define XFERFLAG_UNKNOWN15		0x8000

#define XFERFLAG_KNOWNFLAGS		(XFERFLAG_TRANSPARENT | XFERFLAG_XFLIP | XFERFLAG_YFLIP | XFERFLAG_DSTXSCALE | XFERFLAG_DYDXSIGN | XFERFLAG_DXDYSIGN | XFERFLAG_CLIP)

#define VRAM_WIDTH				512



/*************************************
 *
 *  Global variables
 *
 *************************************/

UINT16 *itech32_video;
UINT32 *drivedge_zbuf_control;
UINT8 itech32_planes;
UINT16 itech32_vram_height;

static UINT16 xfer_xcount, xfer_ycount;
static UINT16 xfer_xcur, xfer_ycur;

static rectangle clip_rect, scaled_clip_rect;
static rectangle clip_save;

static emu_timer *scanline_timer;

static UINT8 *grom_base;
static UINT32 grom_size;
static UINT32 grom_bank;
static UINT32 grom_bank_mask;

static UINT16 color_latch[2];
static UINT8 enable_latch[2];

static UINT16 *videoplane[2];
static UINT32 vram_mask;
static UINT32 vram_xmask, vram_ymask;

static int is_drivedge;

static TIMER_CALLBACK( scanline_interrupt );



/*************************************
 *
 *  Macros and inlines
 *
 *************************************/

#define ADJUSTED_HEIGHT(x) ((((x) >> 1) & 0x100) | ((x) & 0xff))

INLINE offs_t compute_safe_address(int x, int y)
{
	return ((y & vram_ymask) * 512) + (x & vram_xmask);
}

INLINE void disable_clipping(void)
{
	clip_save = clip_rect;

	clip_rect.min_x = clip_rect.min_y = 0;
	clip_rect.max_x = clip_rect.max_y = 0xfff;

	scaled_clip_rect.min_x = scaled_clip_rect.min_y = 0;
	scaled_clip_rect.max_x = scaled_clip_rect.max_y = 0xfff << 8;
}

INLINE void enable_clipping(void)
{
	clip_rect = clip_save;

	scaled_clip_rect.min_x = clip_rect.min_x << 8;
	scaled_clip_rect.max_x = clip_rect.max_x << 8;
	scaled_clip_rect.min_y = clip_rect.min_y << 8;
	scaled_clip_rect.max_y = clip_rect.max_y << 8;
}



/*************************************
 *
 *  Video start
 *
 *************************************/

VIDEO_START( itech32 )
{
	int i;

	/* allocate memory */
	machine->generic.videoram.u16 = auto_alloc_array(machine, UINT16, VRAM_WIDTH * (itech32_vram_height + 16) * 2);
	memset(machine->generic.videoram.u16, 0xff, VRAM_WIDTH * (itech32_vram_height + 16) * 2 * 2);

	/* videoplane[0] is the foreground; videoplane[1] is the background */
	videoplane[0] = &machine->generic.videoram.u16[0 * VRAM_WIDTH * (itech32_vram_height + 16) + 8 * VRAM_WIDTH];
	videoplane[1] = &machine->generic.videoram.u16[1 * VRAM_WIDTH * (itech32_vram_height + 16) + 8 * VRAM_WIDTH];

	/* set the masks */
	vram_mask = VRAM_WIDTH * itech32_vram_height - 1;
	vram_xmask = VRAM_WIDTH - 1;
	vram_ymask = itech32_vram_height - 1;

	/* clear the planes initially */
	for (i = 0; i < VRAM_WIDTH * itech32_vram_height; i++)
		videoplane[0][i] = videoplane[1][i] = 0xff;

	/* fetch the GROM base */
	grom_base = memory_region(machine, "gfx1");
	grom_size = memory_region_length(machine, "gfx1");
	grom_bank = 0;
	grom_bank_mask = grom_size >> 24;
	if (grom_bank_mask == 2)
		grom_bank_mask = 3;

	/* reset statics */
	memset(itech32_video, 0, 0x80);

	scanline_timer = timer_alloc(machine, scanline_interrupt, NULL);
	enable_latch[0] = 1;
	enable_latch[1] = (itech32_planes > 1) ? 1 : 0;

	is_drivedge = 0;
}



/*************************************
 *
 *  Latches
 *
 *************************************/

WRITE16_HANDLER( timekill_colora_w )
{
	if (ACCESSING_BITS_0_7)
	{
		enable_latch[0] = (~data >> 5) & 1;
		enable_latch[1] = (~data >> 7) & 1;
		color_latch[0] = (data & 0x0f) << 8;
	}
}


WRITE16_HANDLER( timekill_colorbc_w )
{
	if (ACCESSING_BITS_0_7)
		color_latch[1] = ((data & 0xf0) << 4) | 0x1000;
}


WRITE16_HANDLER( timekill_intensity_w )
{
	if (ACCESSING_BITS_0_7)
	{
		double intensity = (double)(data & 0xff) / (double)0x60;
		int i;
		for (i = 0; i < 8192; i++)
			palette_set_pen_contrast(space->machine, i, intensity);
	}
}


WRITE16_HANDLER( bloodstm_color1_w )
{
	if (ACCESSING_BITS_0_7)
		color_latch[0] = (data & 0x7f) << 8;
}


WRITE16_HANDLER( bloodstm_color2_w )
{
	if (ACCESSING_BITS_0_7)
		color_latch[1] = (data & 0x7f) << 8;
}


WRITE16_HANDLER( bloodstm_plane_w )
{
	if (ACCESSING_BITS_0_7)
	{
		enable_latch[0] = (~data >> 1) & 1;
		enable_latch[1] = (~data >> 2) & 1;
	}
}


WRITE32_HANDLER( drivedge_color0_w )
{
	if (ACCESSING_BITS_16_23)
		color_latch[0] = ((data >> 16) & 0x7f) << 8;
}


WRITE32_HANDLER( itech020_color1_w )
{
	if (ACCESSING_BITS_0_7)
		color_latch[1] = (data & 0x7f) << 8;
}


WRITE32_HANDLER( itech020_color2_w )
{
	if (ACCESSING_BITS_0_7)
		color_latch[0] = (data & 0x7f) << 8;
}


WRITE32_HANDLER( itech020_plane_w )
{
	if (ACCESSING_BITS_8_15)
	{
		enable_latch[0] = (~data >> 9) & 1;
		enable_latch[1] = (~data >> 10) & 1;
		grom_bank = ((data >> 14) & grom_bank_mask) << 24;
	}
}



/*************************************
 *
 *  Palette I/O
 *
 *************************************/

WRITE16_HANDLER( timekill_paletteram_w )
{
	int r, g, b;

	COMBINE_DATA(&space->machine->generic.paletteram.u16[offset]);

	r = space->machine->generic.paletteram.u16[offset & ~1] & 0xff;
	g = space->machine->generic.paletteram.u16[offset & ~1] >> 8;
	b = space->machine->generic.paletteram.u16[offset |  1] >> 8;

	palette_set_color(space->machine, offset / 2, MAKE_RGB(r, g, b));
}


WRITE16_HANDLER( bloodstm_paletteram_w )
{
	int r, g, b;

	/* in test mode, the LSB is used; in game mode, the MSB is used */
	if (!ACCESSING_BITS_0_7 && (offset & 1))
		data >>= 8, mem_mask >>= 8;
	COMBINE_DATA(&space->machine->generic.paletteram.u16[offset]);

	r = space->machine->generic.paletteram.u16[offset & ~1] & 0xff;
	g = space->machine->generic.paletteram.u16[offset & ~1] >> 8;
	b = space->machine->generic.paletteram.u16[offset |  1] & 0xff;

	palette_set_color(space->machine, offset / 2, MAKE_RGB(r, g, b));
}


WRITE32_HANDLER( drivedge_paletteram_w )
{
	int r, g, b;

	COMBINE_DATA(&space->machine->generic.paletteram.u32[offset]);

	r = space->machine->generic.paletteram.u32[offset] & 0xff;
	g = (space->machine->generic.paletteram.u32[offset] >> 8) & 0xff;
	b = (space->machine->generic.paletteram.u32[offset] >> 16) & 0xff;

	palette_set_color(space->machine, offset, MAKE_RGB(r, g, b));
}


WRITE32_HANDLER( itech020_paletteram_w )
{
	int r, g, b;

	COMBINE_DATA(&space->machine->generic.paletteram.u32[offset]);

	r = (space->machine->generic.paletteram.u32[offset] >> 16) & 0xff;
	g = (space->machine->generic.paletteram.u32[offset] >> 8) & 0xff;
	b = space->machine->generic.paletteram.u32[offset] & 0xff;

	palette_set_color(space->machine, offset, MAKE_RGB(r, g, b));
}



/*************************************
 *
 *  Debugging
 *
 *************************************/

static void logblit(running_machine *machine, const char *tag)
{
	if (!input_code_pressed(machine, KEYCODE_L))
		return;
	if (is_drivedge && VIDEO_TRANSFER_FLAGS == 0x5490)
	{
		/* polygon drawing */
		logerror("%s: e=%d%d f=%04x s=(%03x-%03x,%03x) w=%03x h=%03x b=%02x%04x c=%02x%02x ss=%04x,%04x ds=%04x,%04x ls=%04x%04x rs=%04x%04x u80=%04x", tag,
			enable_latch[0], enable_latch[1],
			VIDEO_TRANSFER_FLAGS,
			VIDEO_TRANSFER_X, VIDEO_RIGHTCLIP, VIDEO_TRANSFER_Y, VIDEO_TRANSFER_WIDTH, VIDEO_TRANSFER_HEIGHT,
			VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO,
			color_latch[0] >> 8, color_latch[1] >> 8,
			VIDEO_SRC_XSTEP, VIDEO_SRC_YSTEP,
			VIDEO_DST_XSTEP, VIDEO_DST_YSTEP,
			VIDEO_LEFTSTEPHI, VIDEO_LEFTSTEPLO, VIDEO_RIGHTSTEPHI, VIDEO_RIGHTSTEPLO,
			VIDEO_STARTSTEP);
	}

	else if (itech32_video[0x16/2] == 0x100 && itech32_video[0x18/2] == 0x100 &&
		itech32_video[0x1a/2] == 0x000 && itech32_video[0x1c/2] == 0x100 &&
		itech32_video[0x1e/2] == 0x000 && itech32_video[0x20/2] == 0x000)
	{
		logerror("%s: e=%d%d f=%04x c=%02x%02x %02x%04x -> (%03x,%03x) %3dx%3dc=(%03x,%03x)-(%03x,%03x)", tag,
				enable_latch[0], enable_latch[1],
				VIDEO_TRANSFER_FLAGS,
				color_latch[0] >> 8, color_latch[1] >> 8,
				VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO,
				VIDEO_TRANSFER_X, VIDEO_TRANSFER_Y,
				VIDEO_TRANSFER_WIDTH, ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT),
				VIDEO_LEFTCLIP, VIDEO_TOPCLIP, VIDEO_RIGHTCLIP, VIDEO_BOTTOMCLIP);
	}
	else
	{
		logerror("%s: e=%d%d f=%04x c=%02x%02x %02x%04x -> (%03x,%03x) %3dx%3d c=(%03x,%03x)-(%03x,%03x) s=%04x %04x %04x %04x %04x %04x", tag,
				enable_latch[0], enable_latch[1],
				VIDEO_TRANSFER_FLAGS,
				color_latch[0] >> 8, color_latch[1] >> 8,
				VIDEO_TRANSFER_ADDRHI, VIDEO_TRANSFER_ADDRLO,
				VIDEO_TRANSFER_X, VIDEO_TRANSFER_Y,
				VIDEO_TRANSFER_WIDTH, ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT),
				VIDEO_LEFTCLIP, VIDEO_TOPCLIP, VIDEO_RIGHTCLIP, VIDEO_BOTTOMCLIP,
				itech32_video[0x16/2], itech32_video[0x18/2], itech32_video[0x1a/2],
				itech32_video[0x1c/2], itech32_video[0x1e/2], itech32_video[0x20/2]);
	}
	if (is_drivedge) logerror(" v0=%08x v1=%08x v2=%08x v3=%08x", drivedge_zbuf_control[0], drivedge_zbuf_control[1], drivedge_zbuf_control[2], drivedge_zbuf_control[3]);
	logerror("\n");
}



/*************************************
 *
 *  Video interrupts
 *
 *************************************/

static void update_interrupts(running_machine *machine, int fast)
{
	int scanline_state = 0, blitter_state = 0;

	if (VIDEO_INTSTATE & VIDEO_INTENABLE & VIDEOINT_SCANLINE)
		scanline_state = 1;
	if (VIDEO_INTSTATE & VIDEO_INTENABLE & VIDEOINT_BLITTER)
		blitter_state = 1;

	itech32_update_interrupts(machine, -1, blitter_state, scanline_state);
}


static TIMER_CALLBACK( scanline_interrupt )
{
	/* set timer for next frame */
	timer_adjust_oneshot(scanline_timer, machine->primary_screen->time_until_pos(VIDEO_INTSCANLINE), 0);

	/* set the interrupt bit in the status reg */
	logerror("-------------- (DISPLAY INT @ %d) ----------------\n", machine->primary_screen->vpos());
	VIDEO_INTSTATE |= VIDEOINT_SCANLINE;

	/* update the interrupt state */
	update_interrupts(machine, 0);
}



/*************************************
 *
 *  Uncompressed blitter functions
 *
 *************************************/

static void draw_raw(UINT16 *base, UINT16 color)
{
	UINT8 *src = &grom_base[(grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % grom_size];
	int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1;
	int width = VIDEO_TRANSFER_WIDTH << 8;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT) << 8;
	int xsrcstep = VIDEO_SRC_XSTEP;
	int ysrcstep = VIDEO_SRC_YSTEP;
	int sx, sy = (VIDEO_TRANSFER_Y & 0xfff) << 8;
	int startx = (VIDEO_TRANSFER_X & 0xfff) << 8;
	int xdststep = 0x100;
	int ydststep = VIDEO_DST_YSTEP;
	int x, y;

	/* adjust for (lack of) clipping */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		disable_clipping();

	/* adjust for scaling */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE)
		xdststep = VIDEO_DST_XSTEP;

	/* adjust for flipping */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP)
		xdststep = -xdststep;
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP)
		ydststep = -ydststep;

	/* loop over Y in src pixels */
	for (y = 0; y < height; y += ysrcstep, sy += ydststep)
	{
		UINT8 *rowsrc = &src[(y >> 8) * (width >> 8)];

		/* simpler case: VIDEO_YSTEP_PER_X is zero */
		if (VIDEO_YSTEP_PER_X == 0)
		{
			/* clip in the Y direction */
			if (sy >= scaled_clip_rect.min_y && sy < scaled_clip_rect.max_y)
			{
				UINT32 dstoffs;

				/* direction matters here */
				sx = startx;
				if (xdststep > 0)
				{
					/* skip left pixels */
					for (x = 0; x < width && sx < scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep) ;

					/* compute the address */
					dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);

					/* render middle pixels */
					for ( ; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep)
					{
						int pixel = rowsrc[x >> 8];
						if (pixel != transparent_pen)
							base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
					}
				}
				else
				{
					/* skip right pixels */
					for (x = 0; x < width && sx >= scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep) ;

					/* compute the address */
					dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);

					/* render middle pixels */
					for ( ; x < width && sx >= scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep)
					{
						int pixel = rowsrc[x >> 8];
						if (pixel != transparent_pen)
							base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
					}
				}
			}
		}

		/* slow case: VIDEO_YSTEP_PER_X is non-zero */
		else
		{
			int ystep = (VIDEO_TRANSFER_FLAGS & XFERFLAG_DYDXSIGN) ? -VIDEO_YSTEP_PER_X : VIDEO_YSTEP_PER_X;
			int ty = sy;

			/* render all pixels */
			sx = startx;
			for (x = 0; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep)
				if (ty >= scaled_clip_rect.min_y && ty < scaled_clip_rect.max_y &&
					sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
				{
					int pixel = rowsrc[x >> 8];
					if (pixel != transparent_pen)
						base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color;
				}
		}

		/* apply skew */
		if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN)
			startx += VIDEO_XSTEP_PER_Y;
		else
			startx -= VIDEO_XSTEP_PER_Y;
	}

	/* restore cliprects */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		enable_clipping();
}


static void draw_raw_drivedge(UINT16 *base, UINT16 *zbase, UINT16 color)
{
	UINT8 *src = &grom_base[(grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % grom_size];
	int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1;
	int width = VIDEO_TRANSFER_WIDTH << 8;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT) << 8;
	int xsrcstep = VIDEO_SRC_XSTEP;
	int ysrcstep = VIDEO_SRC_YSTEP;
	int sx, sy = ((VIDEO_TRANSFER_Y & 0xfff) << 8) + 0x80;
	int startx = ((VIDEO_TRANSFER_X & 0xfff) << 8) + 0x80;
	int xdststep = 0x100;
	int ydststep = VIDEO_DST_YSTEP;
	INT32 z0 = drivedge_zbuf_control[2] & 0x7ff00;
	INT32 zmatch = (drivedge_zbuf_control[2] & 0x1f) << 11;
	INT32 srcdelta = 0;
	int x, y;

	/* adjust for (lack of) clipping */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		disable_clipping();

	/* adjust for scaling */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE)
		xdststep = VIDEO_DST_XSTEP;

	/* adjust for flipping */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP)
		xdststep = -xdststep;
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP)
		ydststep = -ydststep;

	/* loop over Y in src pixels */
	for (y = 0; y < height; y += ysrcstep, sy += ydststep)
	{
		UINT8 *rowsrc = src + (srcdelta >> 8);

		/* in the polygon case, we don't factor in the Y */
		if (VIDEO_TRANSFER_FLAGS != 0x5490)
			rowsrc += (y >> 8) * (width >> 8);
		else
			width = 1000 << 8;

		/* simpler case: VIDEO_YSTEP_PER_X is zero */
		if (VIDEO_YSTEP_PER_X == 0)
		{
			/* clip in the Y direction */
			if (sy >= scaled_clip_rect.min_y && sy < scaled_clip_rect.max_y)
			{
				UINT32 dstoffs, zbufoffs;
				INT32 z = z0;

				/* direction matters here */
				sx = startx;
				if (xdststep > 0)
				{
					/* skip left pixels */
					for (x = 0; x < width && sx < scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep)
						z += (INT32)drivedge_zbuf_control[0];

					/* compute the address */
					dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);
					zbufoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);

					/* render middle pixels */
					if (drivedge_zbuf_control[3] & 0x8000)
					{
						for ( ; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen)
							{
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
								zbase[(zbufoffs + (sx >> 8)) & vram_mask] = (z >> 8) | zmatch;
							}
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
					else if (drivedge_zbuf_control[3] & 0x4000)
					{
						for ( ; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen && zmatch == (zbase[(zbufoffs + (sx >> 8)) & vram_mask] & (0x1f << 11)))
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
					else
					{
						for ( ; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen && ((z >> 8) <= (zbase[(zbufoffs + (sx >> 8)) & vram_mask] & 0x7ff)))
							{
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
								zbase[(zbufoffs + (sx >> 8)) & vram_mask] = (z >> 8) | zmatch;
							}
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
				}
				else
				{
					/* skip right pixels */
					for (x = 0; x < width && sx >= scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep)
						z += (INT32)drivedge_zbuf_control[0];

					/* compute the address */
					dstoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);
					zbufoffs = compute_safe_address(sx >> 8, sy >> 8) - (sx >> 8);

					/* render middle pixels */
					if (drivedge_zbuf_control[3] & 0x8000)
					{
						for ( ; x < width && sx >= scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen)
							{
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
								zbase[(zbufoffs + (sx >> 8)) & vram_mask] = (z >> 8) | zmatch;
							}
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
					else if (drivedge_zbuf_control[3] & 0x4000)
					{
						for ( ; x < width && sx >= scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen && zmatch == (zbase[(zbufoffs + (sx >> 8)) & vram_mask] & (0x1f << 11)))
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
					else
					{
						for ( ; x < width && sx >= scaled_clip_rect.min_x; x += xsrcstep, sx += xdststep)
						{
							int pixel = rowsrc[x >> 8];
							if (pixel != transparent_pen && ((z >> 8) <= (zbase[(zbufoffs + (sx >> 8)) & vram_mask] & 0x7ff)))
							{
								base[(dstoffs + (sx >> 8)) & vram_mask] = pixel | color;
								zbase[(zbufoffs + (sx >> 8)) & vram_mask] = (z >> 8) | zmatch;
							}
							z += (INT32)drivedge_zbuf_control[0];
						}
					}
				}
			}
		}

		/* slow case: VIDEO_YSTEP_PER_X is non-zero */
		else
		{
			int ystep = (VIDEO_TRANSFER_FLAGS & XFERFLAG_DYDXSIGN) ? -VIDEO_YSTEP_PER_X : VIDEO_YSTEP_PER_X;
			int ty = sy;
			INT32 z = z0;

			/* render all pixels */
			sx = startx;
			if (drivedge_zbuf_control[3] & 0x8000)
			{
				for (x = 0; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep)
					if (ty >= scaled_clip_rect.min_y && ty < scaled_clip_rect.max_y &&
						sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
					{
						int pixel = rowsrc[x >> 8];
						if (pixel != transparent_pen)
						{
							base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color;
							zbase[compute_safe_address(sx >> 8, ty >> 8)] = (z >> 8) | zmatch;
						}
						z += (INT32)drivedge_zbuf_control[0];
					}
			}
			else if (drivedge_zbuf_control[3] & 0x4000)
			{
				for (x = 0; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep)
					if (ty >= scaled_clip_rect.min_y && ty < scaled_clip_rect.max_y &&
						sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
					{
						int pixel = rowsrc[x >> 8];
						UINT16 *zbuf = &zbase[compute_safe_address(sx >> 8, ty >> 8)];
						if (pixel != transparent_pen && zmatch == (*zbuf & (0x1f << 11)))
						{
							base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color;
							*zbuf = (z >> 8) | zmatch;
						}
						z += (INT32)drivedge_zbuf_control[0];
					}
			}
			else
			{
				for (x = 0; x < width && sx < scaled_clip_rect.max_x; x += xsrcstep, sx += xdststep, ty += ystep)
					if (ty >= scaled_clip_rect.min_y && ty < scaled_clip_rect.max_y &&
						sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
					{
						int pixel = rowsrc[x >> 8];
						UINT16 *zbuf = &zbase[compute_safe_address(sx >> 8, ty >> 8)];
						if (pixel != transparent_pen && ((z >> 8) <= (*zbuf & 0x7ff)))
						{
							base[compute_safe_address(sx >> 8, ty >> 8)] = pixel | color;
							*zbuf = (z >> 8) | zmatch;
						}
						z += (INT32)drivedge_zbuf_control[0];
					}
			}
		}

		/* apply skew */
		if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN)
			startx += VIDEO_XSTEP_PER_Y;
		else
			startx -= VIDEO_XSTEP_PER_Y;

		/* update the per-scanline parameters */
		if (VIDEO_TRANSFER_FLAGS == 0x5490)
		{
			startx += (INT32)((VIDEO_LEFTSTEPHI << 16) | VIDEO_LEFTSTEPLO);
			scaled_clip_rect.max_x += (INT32)((VIDEO_RIGHTSTEPHI << 16) | VIDEO_RIGHTSTEPLO);
			srcdelta += (INT16)VIDEO_STARTSTEP;
		}
		z0 += (INT32)drivedge_zbuf_control[1];
	}

	/* restore cliprects */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		enable_clipping();

	/* reflect the final values into registers */
	VIDEO_TRANSFER_X = (VIDEO_TRANSFER_X & ~0xfff) | (startx >> 8);
	VIDEO_RIGHTCLIP = (VIDEO_RIGHTCLIP & ~0xfff) | (scaled_clip_rect.max_x >> 8);
	VIDEO_TRANSFER_Y = (VIDEO_TRANSFER_Y & ~0xfff) | ((VIDEO_TRANSFER_Y + (y >> 8)) & 0xfff);
	VIDEO_TRANSFER_ADDRLO += srcdelta >> 8;

	drivedge_zbuf_control[2] = (drivedge_zbuf_control[2] & ~0x7ff00) | (z0 & 0x7ff00);
}



/*************************************
 *
 *  Compressed blitter macros
 *
 *************************************/

#define GET_NEXT_RUN(xleft, count, innercount, src)	\
do {												\
	/* load next RLE chunk if needed */				\
	if (!count)										\
	{												\
		count = *src++;								\
		val = (count & 0x80) ? -1 : *src++;			\
		count &= 0x7f;								\
	}												\
													\
	/* determine how much to bite off */			\
	innercount = (xleft > count) ? count : xleft;	\
	count -= innercount;							\
	xleft -= innercount;							\
} while (0)


#define SKIP_RLE(skip, xleft, count, innercount, src)\
do {												\
	/* scan RLE until done */						\
	for (xleft = skip; xleft > 0; )					\
	{												\
		/* load next RLE chunk if needed */			\
		GET_NEXT_RUN(xleft, count, innercount, src);\
													\
		/* skip past the data */					\
		if (val == -1) src += innercount;			\
	}												\
} while (0)



/*************************************
 *
 *  Fast compressed blitter functions
 *
 *************************************/

INLINE void draw_rle_fast(UINT16 *base, UINT16 color)
{
	UINT8 *src = &grom_base[(grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % grom_size];
	int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1;
	int width = VIDEO_TRANSFER_WIDTH;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT);
	int sx = VIDEO_TRANSFER_X & 0xfff;
	int sy = (VIDEO_TRANSFER_Y & 0xfff) << 8;
	int xleft, y, count = 0, val = 0, innercount;
	int ydststep = VIDEO_DST_YSTEP;
	int lclip, rclip;

	/* determine clipping */
	lclip = clip_rect.min_x - sx;
	if (lclip < 0) lclip = 0;
	rclip = sx + width - clip_rect.max_x;
	if (rclip < 0) rclip = 0;
	width -= lclip + rclip;
	sx += lclip;

	/* adjust for flipping */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP)
		ydststep = -ydststep;

	/* loop over Y in src pixels */
	for (y = 0; y < height; y++, sy += ydststep)
	{
		UINT32 dstoffs;

		/* clip in the Y direction */
		if (sy < scaled_clip_rect.min_y || sy >= scaled_clip_rect.max_y)
		{
			SKIP_RLE(width + lclip + rclip, xleft, count, innercount, src);
			continue;
		}

		/* compute the address */
		dstoffs = compute_safe_address(sx, sy >> 8);

		/* left clip */
		SKIP_RLE(lclip, xleft, count, innercount, src);

		/* loop until gone */
		for (xleft = width; xleft > 0; )
		{
			/* load next RLE chunk if needed */
			GET_NEXT_RUN(xleft, count, innercount, src);

			/* run of literals */
			if (val == -1)
				while (innercount--)
				{
					int pixel = *src++;
					if (pixel != transparent_pen)
						base[dstoffs & vram_mask] = color | pixel;
					dstoffs++;
				}

			/* run of non-transparent repeats */
			else if (val != transparent_pen)
			{
				val |= color;
				while (innercount--)
					base[dstoffs++ & vram_mask] = val;
			}

			/* run of transparent repeats */
			else
				dstoffs += innercount;
		}

		/* right clip */
		SKIP_RLE(rclip, xleft, count, innercount, src);
	}
}


INLINE void draw_rle_fast_xflip(UINT16 *base, UINT16 color)
{
	UINT8 *src = &grom_base[(grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % grom_size];
	int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1;
	int width = VIDEO_TRANSFER_WIDTH;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT);
	int sx = VIDEO_TRANSFER_X & 0xfff;
	int sy = (VIDEO_TRANSFER_Y & 0xfff) << 8;
	int xleft, y, count = 0, val = 0, innercount;
	int ydststep = VIDEO_DST_YSTEP;
	int lclip, rclip;

	/* determine clipping */
	lclip = sx - clip_rect.max_x;
	if (lclip < 0) lclip = 0;
	rclip = clip_rect.min_x - (sx - width);
	if (rclip < 0) rclip = 0;
	width -= lclip + rclip;
	sx -= lclip;

	/* adjust for flipping */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP)
		ydststep = -ydststep;

	/* loop over Y in src pixels */
	for (y = 0; y < height; y++, sy += ydststep)
	{
		UINT32 dstoffs;

		/* clip in the Y direction */
		if (sy < scaled_clip_rect.min_y || sy >= scaled_clip_rect.max_y)
		{
			SKIP_RLE(width + lclip + rclip, xleft, count, innercount, src);
			continue;
		}

		/* compute the address */
		dstoffs = compute_safe_address(sx, sy >> 8);

		/* left clip */
		SKIP_RLE(lclip, xleft, count, innercount, src);

		/* loop until gone */
		for (xleft = width; xleft > 0; )
		{
			/* load next RLE chunk if needed */
			GET_NEXT_RUN(xleft, count, innercount, src);

			/* run of literals */
			if (val == -1)
				while (innercount--)
				{
					int pixel = *src++;
					if (pixel != transparent_pen)
						base[dstoffs & vram_mask] = color | pixel;
					dstoffs--;
				}

			/* run of non-transparent repeats */
			else if (val != transparent_pen)
			{
				val |= color;
				while (innercount--)
					base[dstoffs-- & vram_mask] = val;
			}

			/* run of transparent repeats */
			else
				dstoffs -= innercount;
		}

		/* right clip */
		SKIP_RLE(rclip, xleft, count, innercount, src);
	}
}



/*************************************
 *
 *  Slow compressed blitter functions
 *
 *************************************/

INLINE void draw_rle_slow(UINT16 *base, UINT16 color)
{
	UINT8 *src = &grom_base[(grom_bank | ((VIDEO_TRANSFER_ADDRHI & 0xff) << 16) | VIDEO_TRANSFER_ADDRLO) % grom_size];
	int transparent_pen = (VIDEO_TRANSFER_FLAGS & XFERFLAG_TRANSPARENT) ? 0xff : -1;
	int width = VIDEO_TRANSFER_WIDTH;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT);
	int sx, sy = (VIDEO_TRANSFER_Y & 0xfff) << 8;
	int xleft, y, count = 0, val = 0, innercount;
	int xdststep = 0x100;
	int ydststep = VIDEO_DST_YSTEP;
	int startx = (VIDEO_TRANSFER_X & 0xfff) << 8;

	/* adjust for scaling */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE)
		xdststep = VIDEO_DST_XSTEP;

	/* adjust for flipping */
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP)
		xdststep = -xdststep;
	if (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP)
		ydststep = -ydststep;

	/* loop over Y in src pixels */
	for (y = 0; y < height; y++, sy += ydststep)
	{
		UINT32 dstoffs;

		/* clip in the Y direction */
		if (sy < scaled_clip_rect.min_y || sy >= scaled_clip_rect.max_y)
		{
			SKIP_RLE(width, xleft, count, innercount, src);
			continue;
		}

		/* compute the address */
		sx = startx;
		dstoffs = compute_safe_address(clip_rect.min_x, sy >> 8) - clip_rect.min_x;

		/* loop until gone */
		for (xleft = width; xleft > 0; )
		{
			/* load next RLE chunk if needed */
			GET_NEXT_RUN(xleft, count, innercount, src);

			/* run of literals */
			if (val == -1)
				for ( ; innercount--; sx += xdststep)
				{
					int pixel = *src++;
					if (pixel != transparent_pen)
						if (sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
							base[(dstoffs + (sx >> 8)) & vram_mask] = color | pixel;
				}

			/* run of non-transparent repeats */
			else if (val != transparent_pen)
			{
				val |= color;
				for ( ; innercount--; sx += xdststep)
					if (sx >= scaled_clip_rect.min_x && sx < scaled_clip_rect.max_x)
						base[(dstoffs + (sx >> 8)) & vram_mask] = val;
			}

			/* run of transparent repeats */
			else
				sx += xdststep * innercount;
		}

		/* apply skew */
		if (VIDEO_TRANSFER_FLAGS & XFERFLAG_DXDYSIGN)
			startx += VIDEO_XSTEP_PER_Y;
		else
			startx -= VIDEO_XSTEP_PER_Y;
	}
}



static void draw_rle(UINT16 *base, UINT16 color)
{
	/* adjust for (lack of) clipping */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		disable_clipping();

	/* if we have an X scale, draw it slow */
	if (((VIDEO_TRANSFER_FLAGS & XFERFLAG_DSTXSCALE) && VIDEO_DST_XSTEP != 0x100) || VIDEO_XSTEP_PER_Y)
		draw_rle_slow(base, color);

	/* else draw it fast */
	else if (VIDEO_TRANSFER_FLAGS & XFERFLAG_XFLIP)
		draw_rle_fast_xflip(base, color);
	else
		draw_rle_fast(base, color);

	/* restore cliprects */
	if (!(VIDEO_TRANSFER_FLAGS & XFERFLAG_CLIP))
		enable_clipping();
}



/*************************************
 *
 *  Shift register manipulation
 *
 *************************************/

static void shiftreg_clear(UINT16 *base, UINT16 *zbase)
{
	int ydir = (VIDEO_TRANSFER_FLAGS & XFERFLAG_YFLIP) ? -1 : 1;
	int height = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT);
	int sx = VIDEO_TRANSFER_X & 0xfff;
	int sy = VIDEO_TRANSFER_Y & 0xfff;
	UINT16 *src;
	int y;

	/* first line is the source */
	src = &base[compute_safe_address(sx, sy)];
	sy += ydir;

	/* loop over height */
	for (y = 1; y < height; y++)
	{
		memcpy(&base[compute_safe_address(sx, sy)], src, 512*2);
		if (zbase)
		{
			UINT16 zval = ((drivedge_zbuf_control[2] >> 8) & 0x7ff) | ((drivedge_zbuf_control[2] & 0x1f) << 11);
			UINT16 *dst = &zbase[compute_safe_address(sx, sy)];
			int x;
			for (x = 0; x < 512; x++)
				*dst++ = zval;
		}
		sy += ydir;
	}
}



/*************************************
 *
 *  Video commands
 *
 *************************************/

static void handle_video_command(running_machine *machine)
{
	/* only 6 known commands */
	switch (VIDEO_COMMAND)
	{
		/* command 1: blit raw data */
		case 1:
			profiler_mark_start(PROFILER_USER1);
			if (BLIT_LOGGING) logblit(machine, "Blit Raw");

			if (is_drivedge)
			{
				if (enable_latch[0]) draw_raw_drivedge(videoplane[0], videoplane[1], color_latch[0]);
			}
			else
			{
				if (enable_latch[0]) draw_raw(videoplane[0], color_latch[0]);
				if (enable_latch[1]) draw_raw(videoplane[1], color_latch[1]);
			}

			profiler_mark_end();
			break;

		/* command 2: blit RLE-compressed data */
		case 2:
			profiler_mark_start(PROFILER_USER2);
			if (BLIT_LOGGING) logblit(machine, "Blit RLE");

			if (enable_latch[0]) draw_rle(videoplane[0], color_latch[0]);
			if (enable_latch[1]) draw_rle(videoplane[1], color_latch[1]);

			profiler_mark_end();
			break;

		/* command 3: set up raw data transfer */
		case 3:
			if (BLIT_LOGGING) logblit(machine, "Raw Xfer");
			xfer_xcount = VIDEO_TRANSFER_WIDTH;
			xfer_ycount = ADJUSTED_HEIGHT(VIDEO_TRANSFER_HEIGHT);
			xfer_xcur = VIDEO_TRANSFER_X & 0xfff;
			xfer_ycur = VIDEO_TRANSFER_Y & 0xfff;
			break;

		/* command 4: flush? */
		case 4:
			break;

		/* command 5: reset? */
		case 5:
			break;

		/* command 6: perform shift register copy */
		case 6:
			profiler_mark_start(PROFILER_USER3);
			if (BLIT_LOGGING) logblit(machine, "ShiftReg");

			if (is_drivedge)
			{
				if (enable_latch[0]) shiftreg_clear(videoplane[0], videoplane[1]);
			}
			else
			{
				if (enable_latch[0]) shiftreg_clear(videoplane[0], NULL);
				if (enable_latch[1]) shiftreg_clear(videoplane[1], NULL);
			}

			profiler_mark_end();
			break;

		default:
			if (BLIT_LOGGING) logerror("Unknown blit command %d\n", VIDEO_COMMAND);
			break;
	}

	/* tell the processor we're done */
	VIDEO_INTSTATE |= VIDEOINT_BLITTER;
	update_interrupts(machine, 1);
}



/*************************************
 *
 *  Video I/O
 *
 *************************************/

WRITE16_HANDLER( itech32_video_w )
{
	rectangle visarea;

	int old = itech32_video[offset];
	COMBINE_DATA(&itech32_video[offset]);

	switch (offset)
	{
		case 0x02/2:	/* VIDEO_INTACK */
			VIDEO_INTSTATE = old & ~data;
			update_interrupts(space->machine, 1);
			break;

		case 0x04/2:	/* VIDEO_TRANSFER */
			if (VIDEO_COMMAND == 3 && xfer_ycount)
			{
				offs_t addr = compute_safe_address(xfer_xcur, xfer_ycur);
				if (enable_latch[0])
				{
					VIDEO_TRANSFER = videoplane[0][addr];
					videoplane[0][addr] = (data & 0xff) | color_latch[0];
				}
				if (enable_latch[1])
				{
					VIDEO_TRANSFER = videoplane[1][addr];
					videoplane[1][addr] = (data & 0xff) | color_latch[1];
				}
				if (--xfer_xcount)
					xfer_xcur++;
				else if (--xfer_ycount)
					xfer_xcur = VIDEO_TRANSFER_X, xfer_xcount = VIDEO_TRANSFER_WIDTH, xfer_ycur++;
			}
			break;

		case 0x08/2:	/* VIDEO_COMMAND */
			handle_video_command(space->machine);
			break;

		case 0x0a/2:	/* VIDEO_INTENABLE */
			update_interrupts(space->machine, 1);
			break;

		case 0x24/2:	/* VIDEO_LEFTCLIP */
			clip_rect.min_x = VIDEO_LEFTCLIP;
			scaled_clip_rect.min_x = VIDEO_LEFTCLIP << 8;
			break;

		case 0x26/2:	/* VIDEO_RIGHTCLIP */
			clip_rect.max_x = VIDEO_RIGHTCLIP;
			scaled_clip_rect.max_x = VIDEO_RIGHTCLIP << 8;
			break;

		case 0x28/2:	/* VIDEO_TOPCLIP */
			clip_rect.min_y = VIDEO_TOPCLIP;
			scaled_clip_rect.min_y = VIDEO_TOPCLIP << 8;
			break;

		case 0x2a/2:	/* VIDEO_BOTTOMCLIP */
			clip_rect.max_y = VIDEO_BOTTOMCLIP;
			scaled_clip_rect.max_y = VIDEO_BOTTOMCLIP << 8;
			break;

		case 0x2c/2:	/* VIDEO_INTSCANLINE */
			timer_adjust_oneshot(scanline_timer, space->machine->primary_screen->time_until_pos(VIDEO_INTSCANLINE), 0);
			break;

		case 0x32/2:	/* VIDEO_VTOTAL */
		case 0x36/2:	/* VIDEO_VBLANK_START */
		case 0x38/2:	/* VIDEO_VBLANK_END */
		case 0x3a/2:	/* VIDEO_HTOTAL */
		case 0x3e/2:	/* VIDEO_HBLANK_START */
		case 0x40/2:	/* VIDEO_HBLANK_END */
			/* do some sanity checks first */
			if ((VIDEO_HTOTAL > 0) && (VIDEO_VTOTAL > 0) &&
			    (VIDEO_VBLANK_START != VIDEO_VBLANK_END) &&
			    (VIDEO_HBLANK_START != VIDEO_HBLANK_END) &&
			    (VIDEO_HBLANK_START < VIDEO_HTOTAL) &&
			    (VIDEO_HBLANK_END < VIDEO_HTOTAL) &&
			    (VIDEO_VBLANK_START < VIDEO_VTOTAL) &&
			    (VIDEO_VBLANK_END < VIDEO_VTOTAL))
			{
				visarea.min_x = visarea.min_y = 0;

				if (VIDEO_HBLANK_START > VIDEO_HBLANK_END)
					visarea.max_x = VIDEO_HBLANK_START - VIDEO_HBLANK_END - 1;
				else
					visarea.max_x = VIDEO_HTOTAL - VIDEO_HBLANK_END + VIDEO_HBLANK_START - 1;

				if (VIDEO_VBLANK_START > VIDEO_VBLANK_END)
					visarea.max_y = VIDEO_VBLANK_START - VIDEO_VBLANK_END - 1;
				else
					visarea.max_y = VIDEO_VTOTAL - VIDEO_VBLANK_END + VIDEO_VBLANK_START - 1;

				logerror("Configure Screen: HTOTAL: %x  HBSTART: %x  HBEND: %x  VTOTAL: %x  VBSTART: %x  VBEND: %x\n",
					VIDEO_HTOTAL, VIDEO_HBLANK_START, VIDEO_HBLANK_END, VIDEO_VTOTAL, VIDEO_VBLANK_START, VIDEO_VBLANK_END);
				space->machine->primary_screen->configure(VIDEO_HTOTAL, VIDEO_VTOTAL, visarea, HZ_TO_ATTOSECONDS(VIDEO_CLOCK) * VIDEO_HTOTAL * VIDEO_VTOTAL);
			}
			break;
	}
}


READ16_HANDLER( itech32_video_r )
{
	if (offset == 0)
	{
		return (itech32_video[offset] & ~0x08) | 0x04 | 0x01;
	}
	else if (offset == 3)
	{
		return 0xef;/*space->machine->primary_screen->vpos() - 1;*/
	}

	return itech32_video[offset];
}



/*************************************
 *
 *  Alternate video I/O
 *
 *************************************/

WRITE16_HANDLER( bloodstm_video_w )
{
	itech32_video_w(space, offset / 2, data, mem_mask);
}


READ16_HANDLER( bloodstm_video_r )
{
	return itech32_video_r(space, offset / 2, mem_mask);
}


WRITE32_HANDLER( itech020_video_w )
{
	if (ACCESSING_BITS_16_31)
		itech32_video_w(space, offset, data >> 16, mem_mask >> 16);
	else
		itech32_video_w(space, offset, data, mem_mask);
}


WRITE32_HANDLER( drivedge_zbuf_control_w )
{
	COMBINE_DATA(&drivedge_zbuf_control[offset]);
	is_drivedge = 1;
}


READ32_HANDLER( itech020_video_r )
{
	int result = itech32_video_r(space, offset, mem_mask);
	return (result << 16) | result;
}



/*************************************
 *
 *  Main refresh
 *
 *************************************/

VIDEO_UPDATE( itech32 )
{
	int y;

	/* loop over height */
	for (y = cliprect->min_y; y <= cliprect->max_y; y++)
	{
		UINT16 *src1 = &videoplane[0][compute_safe_address(VIDEO_DISPLAY_XORIGIN1, VIDEO_DISPLAY_YORIGIN1 + y)];

		/* handle multi-plane case */
		if (itech32_planes > 1)
		{
			UINT16 *src2 = &videoplane[1][compute_safe_address(VIDEO_DISPLAY_XORIGIN2 + VIDEO_DISPLAY_XSCROLL2, VIDEO_DISPLAY_YORIGIN2 + VIDEO_DISPLAY_YSCROLL2 + y)];
			UINT16 scanline[384];
			int x;

			/* blend the pixels in the scanline; color xxFF is transparent */
			for (x = cliprect->min_x; x <= cliprect->max_x; x++)
			{
				UINT16 pixel = src1[x];
				if ((pixel & 0xff) == 0xff)
					pixel = src2[x];
				scanline[x] = pixel;
			}

			/* draw from the buffer */
			draw_scanline16(bitmap, cliprect->min_x, y, cliprect->max_x - cliprect->min_x + 1, &scanline[cliprect->min_x], NULL);
		}

		/* otherwise, draw directly from VRAM */
		else
			draw_scanline16(bitmap, cliprect->min_x, y, cliprect->max_x - cliprect->min_x + 1, &src1[cliprect->min_x], NULL);
	}
	return 0;
}
