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

    Atari Jaguar hardware

****************************************************************************

    ------------------------------------------------------------
    TOM REGISTERS
    ------------------------------------------------------------
    F00000-F0FFFF   R/W   xxxxxxxx xxxxxxxx   Internal Registers
    F00000          R/W   -x-xx--- xxxxxxxx   MEMCON1 - memory config reg 1
                          -x------ --------      (CPU32 - is the CPU 32bits?)
                          ---xx--- --------      (IOSPEED - external I/O clock cycles)
                          -------- x-------      (FASTROM - reduces ROM clock cycles)
                          -------- -xx-----      (DRAMSPEED - sets RAM clock cycles)
                          -------- ---xx---      (ROMSPEED - sets ROM clock cycles)
                          -------- -----xx-      (ROMWIDTH - sets width of ROM: 8,16,32,64 bits)
                          -------- -------x      (ROMHI - controls ROM mapping)
    F00002          R/W   --xxxxxx xxxxxxxx   MEMCON2 - memory config reg 2
                          --x----- --------      (HILO - image display bit order)
                          ---x---- --------      (BIGEND - big endian addressing?)
                          ----xxxx --------      (REFRATE - DRAM refresh rate)
                          -------- xx------      (DWIDTH1 - DRAM1 width: 8,16,32,64 bits)
                          -------- --xx----      (COLS1 - DRAM1 columns: 256,512,1024,2048)
                          -------- ----xx--      (DWIDTH0 - DRAM0 width: 8,16,32,64 bits)
                          -------- ------xx      (COLS0 - DRAM0 columns: 256,512,1024,2048)
    F00004          R/W   -----xxx xxxxxxxx   HC - horizontal count
                          -----x-- --------      (which half of the display)
                          ------xx xxxxxxxx      (10-bit counter)
    F00006          R/W   ----xxxx xxxxxxxx   VC - vertical count
                          ----x--- --------      (which field is being generated)
                          -----xxx xxxxxxxx      (11-bit counter)
    F00008          R     -----xxx xxxxxxxx   LPH - light pen horizontal position
    F0000A          R     -----xxx xxxxxxxx   LPV - light pen vertical position
    F00010-F00017   R     xxxxxxxx xxxxxxxx   OB - current object code from the graphics processor
    F00020-F00023     W   xxxxxxxx xxxxxxxx   OLP - start of the object list
    F00026            W   -------- -------x   OBF - object processor flag
    F00028            W   ----xxxx xxxxxxxx   VMODE - video mode
                      W   ----xxx- --------      (PWIDTH1-8 - width of pixel in video clock cycles)
                      W   -------x --------      (VARMOD - enable variable color resolution)
                      W   -------- x-------      (BGEN - clear line buffere to BG color)
                      W   -------- -x------      (CSYNC - enable composite sync on VSYNC)
                      W   -------- --x-----      (BINC - local border color if INCEN)
                      W   -------- ---x----      (INCEN - encrustation enable)
                      W   -------- ----x---      (GENLOCK - enable genlock)
                      W   -------- -----xx-      (MODE - CRY16,RGB24,DIRECT16,RGB16)
                      W   -------- -------x      (VIDEN - enables video)
    F0002A            W   xxxxxxxx xxxxxxxx   BORD1 - border color (red/green)
    F0002C            W   -------- xxxxxxxx   BORD2 - border color (blue)
    F0002E            W   ------xx xxxxxxxx   HP - horizontal period
    F00030            W   -----xxx xxxxxxxx   HBB - horizontal blanking begin
    F00032            W   -----xxx xxxxxxxx   HBE - horizontal blanking end
    F00034            W   -----xxx xxxxxxxx   HSYNC - horizontal sync
    F00036            W   ------xx xxxxxxxx   HVS - horizontal vertical sync
    F00038            W   -----xxx xxxxxxxx   HDB1 - horizontal display begin 1
    F0003A            W   -----xxx xxxxxxxx   HDB2 - horizontal display begin 2
    F0003C            W   -----xxx xxxxxxxx   HDE - horizontal display end
    F0003E            W   -----xxx xxxxxxxx   VP - vertical period
    F00040            W   -----xxx xxxxxxxx   VBB - vertical blanking begin
    F00042            W   -----xxx xxxxxxxx   VBE - vertical blanking end
    F00044            W   -----xxx xxxxxxxx   VS - vertical sync
    F00046            W   -----xxx xxxxxxxx   VDB - vertical display begin
    F00048            W   -----xxx xxxxxxxx   VDE - vertical display end
    F0004A            W   -----xxx xxxxxxxx   VEB - vertical equalization begin
    F0004C            W   -----xxx xxxxxxxx   VEE - vertical equalization end
    F0004E            W   -----xxx xxxxxxxx   VI - vertical interrupt
    F00050            W   xxxxxxxx xxxxxxxx   PIT0 - programmable interrupt timer 0
    F00052            W   xxxxxxxx xxxxxxxx   PIT1 - programmable interrupt timer 1
    F00054            W   ------xx xxxxxxxx   HEQ - horizontal equalization end
    F00058            W   xxxxxxxx xxxxxxxx   BG - background color
    F000E0          R/W   ---xxxxx ---xxxxx   INT1 - CPU interrupt control register
                          ---x---- --------      (C_JERCLR - clear pending Jerry ints)
                          ----x--- --------      (C_PITCLR - clear pending PIT ints)
                          -----x-- --------      (C_OPCLR - clear pending object processor ints)
                          ------x- --------      (C_GPUCLR - clear pending graphics processor ints)
                          -------x --------      (C_VIDCLR - clear pending video timebase ints)
                          -------- ---x----      (C_JERENA - enable Jerry ints)
                          -------- ----x---      (C_PITENA - enable PIT ints)
                          -------- -----x--      (C_OPENA - enable object processor ints)
                          -------- ------x-      (C_GPUENA - enable graphics processor ints)
                          -------- -------x      (C_VIDENA - enable video timebase ints)
    F000E2            W   -------- --------   INT2 - CPU interrupt resume register
    F00400-F005FF   R/W   xxxxxxxx xxxxxxxx   CLUT - color lookup table A
    F00600-F007FF   R/W   xxxxxxxx xxxxxxxx   CLUT - color lookup table B
    F00800-F00D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer A
    F01000-F0159F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer B
    F01800-F01D9F   R/W   xxxxxxxx xxxxxxxx   LBUF - line buffer currently selected
    ------------------------------------------------------------
    F02000-F021FF   R/W   xxxxxxxx xxxxxxxx   GPU control registers
    F02100          R/W   xxxxxxxx xxxxxxxx   G_FLAGS - GPU flags register
                    R/W   x------- --------      (DMAEN - DMA enable)
                    R/W   -x------ --------      (REGPAGE - register page)
                      W   --x----- --------      (G_BLITCLR - clear blitter interrupt)
                      W   ---x---- --------      (G_OPCLR - clear object processor int)
                      W   ----x--- --------      (G_PITCLR - clear PIT interrupt)
                      W   -----x-- --------      (G_JERCLR - clear Jerry interrupt)
                      W   ------x- --------      (G_CPUCLR - clear CPU interrupt)
                    R/W   -------x --------      (G_BLITENA - enable blitter interrupt)
                    R/W   -------- x-------      (G_OPENA - enable object processor int)
                    R/W   -------- -x------      (G_PITENA - enable PIT interrupt)
                    R/W   -------- --x-----      (G_JERENA - enable Jerry interrupt)
                    R/W   -------- ---x----      (G_CPUENA - enable CPU interrupt)
                    R/W   -------- ----x---      (IMASK - interrupt mask)
                    R/W   -------- -----x--      (NEGA_FLAG - ALU negative)
                    R/W   -------- ------x-      (CARRY_FLAG - ALU carry)
                    R/W   -------- -------x      (ZERO_FLAG - ALU zero)
    F02104            W   -------- ----xxxx   G_MTXC - matrix control register
                      W   -------- ----x---      (MATCOL - column/row major)
                      W   -------- -----xxx      (MATRIX3-15 - matrix width)
    F02108            W   ----xxxx xxxxxx--   G_MTXA - matrix address register
    F0210C            W   -------- -----xxx   G_END - data organization register
                      W   -------- -----x--      (BIG_INST - big endian instruction fetch)
                      W   -------- ------x-      (BIG_PIX - big endian pixels)
                      W   -------- -------x      (BIG_IO - big endian I/O)
    F02110          R/W   xxxxxxxx xxxxxxxx   G_PC - GPU program counter
    F02114          R/W   xxxxxxxx xx-xxxxx   G_CTRL - GPU control/status register
                    R     xxxx---- --------      (VERSION - GPU version code)
                    R/W   ----x--- --------      (BUS_HOG - hog the bus!)
                    R/W   -----x-- --------      (G_BLITLAT - blitter interrupt latch)
                    R/W   ------x- --------      (G_OPLAT - object processor int latch)
                    R/W   -------x --------      (G_PITLAT - PIT interrupt latch)
                    R/W   -------- x-------      (G_JERLAT - Jerry interrupt latch)
                    R/W   -------- -x------      (G_CPULAT - CPU interrupt latch)
                    R/W   -------- ---x----      (SINGLE_GO - single step one instruction)
                    R/W   -------- ----x---      (SINGLE_STEP - single step mode)
                    R/W   -------- -----x--      (FORCEINT0 - cause interrupt 0 on GPU)
                    R/W   -------- ------x-      (CPUINT - send GPU interrupt to CPU)
                    R/W   -------- -------x      (GPUGO - enable GPU execution)
    F02118-F0211B   R/W   xxxxxxxx xxxxxxxx   G_HIDATA - high data register
    F0211C-F0211F   R     xxxxxxxx xxxxxxxx   G_REMAIN - divide unit remainder
    F0211C            W   -------- -------x   G_DIVCTRL - divide unit control
                      W   -------- -------x      (DIV_OFFSET - 1=16.16 divide, 0=32-bit divide)
    ------------------------------------------------------------

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

#include "emu.h"
#include "memconv.h"
#include "machine/atarigen.h"
#include "cpu/mips/r3000.h"
#include "cpu/m68000/m68000.h"
#include "includes/jaguar.h"
#include "jagblit.h"


#define ENABLE_BORDERS		0

#define LOG_BLITS			0
#define LOG_BAD_BLITS		0
#define LOG_BLITTER_STATS	0
#define LOG_BLITTER_WRITE	0
#define LOG_UNHANDLED_BLITS	0


// interrupts to main CPU:
//  0 = video (on the VI scanline)
//  1 = GPU (write from GPU coprocessor)
//  2 = object (stop object interrupt in display list)
//  3 = timer (from the PIT)
//  4 = jerry


/* GPU registers */
enum
{
	MEMCON1,	MEMCON2,	HC,			VC,
	LPH,		LPV,		GPU0,		GPU1,
	OB_HH,		OB_HL,		OB_LH,		OB_LL,
	GPU2,		GPU3,		GPU4,		GPU5,
	OLP_L,		OLP_H,		GPU6,		OBF,
	VMODE,		BORD1,		BORD2,		HP,
	HBB,		HBE,		HSYNC,		HVS,
	HDB1,		HDB2,		HDE,		VP,
	VBB,		VBE,		VS,			VDB,
	VDE,		VEB,		VEE,		VI,
	PIT0,		PIT1,		HEQ,		GPU7,
	BG,
	INT1 = 0xe0/2,
	INT2,
	GPU_REGS
};



/*************************************
 *
 *  Local variables
 *
 *************************************/

/* blitter variables */
static UINT32 blitter_regs[BLITTER_REGS];
static UINT16 gpu_regs[GPU_REGS];

static emu_timer *object_timer;
static UINT8 cpu_irq_state;

static bitmap_rgb32 *screen_bitmap;

static pen_t *pen_table;
static int pixel_clock;

UINT8 blitter_status;


/*************************************
 *
 *  Prototypes
 *
 *************************************/

/* from jagobj.c */
static void jagobj_init(running_machine &machine);
static void process_object_list(running_machine &machine, int vc, UINT16 *_scanline);

/* from jagblit.c */
static void generic_blitter(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_09800001_010020_010020(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_09800009_000020_000020(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_01800009_000028_000028(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_01800001_000018_000018(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_01c00001_000018_000018(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_00010000_xxxxxx_xxxxxx(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_01800001_xxxxxx_xxxxxx(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);
static void blitter_x1800x01_xxxxxx_xxxxxx(running_machine &machine, UINT32 command, UINT32 a1flags, UINT32 a2flags);



/*************************************
 *
 *  Compute X/Y coordinates
 *
 *************************************/

INLINE void get_crosshair_xy(running_machine &machine, int player, int *x, int *y)
{
	const rectangle &visarea = machine.primary_screen->visible_area();

	int width = visarea.width();
	int height = visarea.height();
	/* only 2 lightguns are connected */
	*x = visarea.min_x + (((machine.root_device().ioport(player ? "FAKE2_X" : "FAKE1_X")->read() & 0xff) * width) >> 8);
	*y = visarea.min_y + (((machine.root_device().ioport(player ? "FAKE2_Y" : "FAKE1_Y")->read() & 0xff) * height) >> 8);
}



/*************************************
 *
 *  Horizontal display values
 *
 *************************************/

INLINE int effective_hvalue(int value)
{
	if (!(value & 0x400))
		return value & 0x3ff;
	else
		return (value & 0x3ff) + (gpu_regs[HP] & 0x3ff) + 1;
}



/*************************************
 *
 *  Object processor timer
 *
 *************************************/

INLINE int adjust_object_timer(running_machine &machine, int vc)
{
	int hdbpix[2];
	int hdb = 0;

	/* extract the display begin registers */
	hdbpix[0] = (gpu_regs[HDB1] & 0x7ff) / 2;
	hdbpix[1] = (gpu_regs[HDB2] & 0x7ff) / 2;

	/* sort */
	if (hdbpix[0] > hdbpix[1])
	{
		int temp = hdbpix[0];
		hdbpix[0] = hdbpix[1];
		hdbpix[1] = temp;
	}

	/* select the target one */
	hdb = hdbpix[vc % 2];

	/* if setting the second one in a line, make sure we will ever actually hit it */
	if (vc % 2 == 1 && (hdbpix[1] == hdbpix[0] || hdbpix[1] >= machine.primary_screen->width()))
		return FALSE;

	/* adjust the timer */
	object_timer->adjust(machine.primary_screen->time_until_pos(vc / 2, hdb), vc | (hdb << 16));
	return TRUE;
}



/*************************************
 *
 *  GPU optimization control
 *
 *************************************/

void jaguar_gpu_suspend(running_machine &machine)
{
	machine.device<cpu_device>("gpu")->suspend(SUSPEND_REASON_SPIN, 1);
}


void jaguar_gpu_resume(running_machine &machine)
{
	machine.device<cpu_device>("gpu")->resume(SUSPEND_REASON_SPIN);
}



/*************************************
 *
 *  Main CPU interrupts
 *
 *************************************/

static void update_cpu_irq(running_machine &machine)
{
	if (cpu_irq_state & gpu_regs[INT1] & 0x1f)
		cputag_set_input_line(machine, "maincpu", cojag_is_r3000 ? R3000_IRQ4 : M68K_IRQ_6, ASSERT_LINE);
	else
		cputag_set_input_line(machine, "maincpu", cojag_is_r3000 ? R3000_IRQ4 : M68K_IRQ_6, CLEAR_LINE);
}


void jaguar_gpu_cpu_int(device_t *device)
{
	cpu_irq_state |= 2;
	update_cpu_irq(device->machine());
}


void jaguar_dsp_cpu_int(device_t *device)
{
	cpu_irq_state |= 16;
	update_cpu_irq(device->machine());
}



/*************************************
 *
 *  Palette init
 *
 *************************************/

static void jaguar_set_palette(UINT16 vmode)
{
	static const UINT8 red_lookup[256] =
	{
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,
		 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 19,  0,
		 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 43, 21,  0,
		102,102,102,102,102,102,102,102,102,102,102, 95, 71, 47, 23,  0,
		135,135,135,135,135,135,135,135,135,135,130,104, 78, 52, 26,  0,
		169,169,169,169,169,169,169,169,169,170,141,113, 85, 56, 28,  0,
		203,203,203,203,203,203,203,203,203,183,153,122, 91, 61, 30,  0,
		237,237,237,237,237,237,237,237,230,197,164,131, 98, 65, 32,  0,
		255,255,255,255,255,255,255,255,247,214,181,148,115, 82, 49, 17,
		255,255,255,255,255,255,255,255,255,235,204,173,143,112, 81, 51,
		255,255,255,255,255,255,255,255,255,255,227,198,170,141,113, 85,
		255,255,255,255,255,255,255,255,255,255,249,223,197,171,145,119,
		255,255,255,255,255,255,255,255,255,255,255,248,224,200,177,153,
		255,255,255,255,255,255,255,255,255,255,255,255,252,230,208,187,
		255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,221,
		255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255
	};

	static const UINT8 grn_lookup[256] =
	{
		  0, 17, 34, 51, 68, 85,102,119,136,153,170,187,204,221,238,255,
		  0, 19, 38, 57, 77, 96,115,134,154,173,182,211,231,250,255,255,
		  0, 21, 43, 64, 86,107,129,150,172,193,215,236,255,255,255,255,
		  0, 23, 47, 71, 96,119,142,166,190,214,238,255,255,255,255,255,
		  0, 26, 52, 78,104,130,156,182,208,234,255,255,255,255,255,255,
		  0, 28, 56, 85,113,141,170,198,226,255,255,255,255,255,255,255,
		  0, 30, 61, 91,122,153,183,214,244,255,255,255,255,255,255,255,
		  0, 32, 65, 98,131,164,197,230,255,255,255,255,255,255,255,255,
		  0, 32, 65, 98,131,164,197,230,255,255,255,255,255,255,255,255,
		  0, 30, 61, 91,122,153,183,214,244,255,255,255,255,255,255,255,
		  0, 28, 56, 85,113,141,170,198,226,255,255,255,255,255,255,255,
		  0, 26, 52, 78,104,130,156,182,208,234,255,255,255,255,255,255,
		  0, 23, 47, 71, 96,119,142,166,190,214,238,255,255,255,255,255,
		  0, 21, 43, 64, 86,107,129,150,172,193,215,236,255,255,255,255,
		  0, 19, 38, 57, 77, 96,115,134,154,173,182,211,231,250,255,255,
		  0, 17, 34, 51, 68, 85,102,119,136,153,170,187,204,221,238,255
	};

	static const UINT8 blu_lookup[256] =
	{
		255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,255,
		255,255,255,255,255,255,255,255,255,255,255,255,255,255,240,221,
		255,255,255,255,255,255,255,255,255,255,255,255,252,230,208,187,
		255,255,255,255,255,255,255,255,255,255,255,248,224,200,177,153,
		255,255,255,255,255,255,255,255,255,255,249,223,197,171,145,119,
		255,255,255,255,255,255,255,255,255,255,227,198,170,141,113, 85,
		255,255,255,255,255,255,255,255,255,235,204,173,143,112, 81, 51,
		255,255,255,255,255,255,255,255,247,214,181,148,115, 82, 49, 17,
		237,237,237,237,237,237,237,237,230,197,164,131, 98, 65, 32,  0,
		203,203,203,203,203,203,203,203,203,183,153,122, 91, 61, 30,  0,
		169,169,169,169,169,169,169,169,169,170,141,113, 85, 56, 28,  0,
		135,135,135,135,135,135,135,135,135,135,130,104, 78, 52, 26,  0,
		102,102,102,102,102,102,102,102,102,102,102, 95, 71, 47, 23,  0,
		 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 68, 64, 43, 21,  0,
		 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 34, 19,  0,
		  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0,  0
	};

	int i;

	/* switch off the mode */
	switch (vmode & 0x106)
	{
		/* YCC full */
		case 0x000:
		/* RGB24 */
		case 0x002:
			for (i = 0; i < 65536; i++)
			{
				UINT8 r = (red_lookup[i >> 8] * (i & 0xff)) >> 8;
				UINT8 g = (grn_lookup[i >> 8] * (i & 0xff)) >> 8;
				UINT8 b = (blu_lookup[i >> 8] * (i & 0xff)) >> 8;
				pen_table[i] = MAKE_RGB(r, g, b);
			}
			break;

		/* YCC VARMOD */
		case 0x100:
			for (i = 0; i < 65536; i++)
			{
				UINT8 r = (red_lookup[i >> 8] * (i & 0xff)) >> 8;
				UINT8 g = (grn_lookup[i >> 8] * (i & 0xff)) >> 8;
				UINT8 b = (blu_lookup[i >> 8] * (i & 0xff)) >> 8;

				/* if the low bit is set, treat it as 5-5-5 RGB instead */
				if (i & 1)
				{
					r = (i >> 11) & 31;
					g = (i >> 1) & 31;
					b = (i >> 6) & 31;
					r = (r << 3) | (r >> 2);
					g = (g << 3) | (g >> 2);
					b = (b << 3) | (b >> 2);
				}
				pen_table[i] = MAKE_RGB(r, g, b);
			}
			break;

		/* RGB VARMOD */
		case 0x106:
			for (i = 0; i < 65536; i++)
			{
				if (i & 1) // FIXME: controls RGB 5-5-5 / 5-6-5 format or it's just ignored? Used by UBI Soft logo in Rayman
					pen_table[i] = MAKE_RGB(pal5bit(i >> 11), pal5bit(i >> 1), pal5bit(i >> 6));
				else
					pen_table[i] = MAKE_RGB(pal5bit(i >> 11), pal6bit(i >> 0), pal5bit(i >> 6));
			}
			break;

		/* RGB full */
		case 0x006:
			for (i = 0; i < 65536; i++)
				pen_table[i] = MAKE_RGB(pal5bit(i >> 11), pal6bit(i >> 0), pal5bit(i >> 6));
			break;

		/* others */
		default:
			logerror("Can't handle mode %X\n", vmode);
			fprintf(stderr, "Can't handle mode %X\n", vmode);
			break;
	}
}



/*************************************
 *
 *  Fast memory accessors
 *
 *************************************/

static UINT8 *get_jaguar_memory(running_machine &machine, UINT32 offset)
{
	address_space *space = machine.device("gpu")->memory().space(AS_PROGRAM);
	return (UINT8 *)space->get_read_ptr(offset);
}



/*************************************
 *
 *  32-bit access to the blitter
 *
 *************************************/

static void blitter_run(running_machine &machine)
{
	UINT32 command = blitter_regs[B_CMD] & STATIC_COMMAND_MASK;
	UINT32 a1flags = blitter_regs[A1_FLAGS] & STATIC_FLAGS_MASK;
	UINT32 a2flags = blitter_regs[A2_FLAGS] & STATIC_FLAGS_MASK;

	g_profiler.start(PROFILER_USER1);

	if (a1flags == a2flags)
	{
		if (command == 0x09800001 && a1flags == 0x010020)
		{
			blitter_09800001_010020_010020(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
			return;
		}
		if (command == 0x09800009 && a1flags == 0x000020)
		{
			blitter_09800009_000020_000020(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
			return;
		}
		if (command == 0x01800009 && a1flags == 0x000028)
		{
			blitter_01800009_000028_000028(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
			return;
		}

		if (command == 0x01800001 && a1flags == 0x000018)
		{
			blitter_01800001_000018_000018(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
			return;
		}

		if (command == 0x01c00001 && a1flags == 0x000018)
		{
			blitter_01c00001_000018_000018(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
			return;
		}
	}

	if (command == 0x00010000)
	{
		blitter_00010000_xxxxxx_xxxxxx(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
		return;
	}

	if (command == 0x01800001)
	{
		blitter_01800001_xxxxxx_xxxxxx(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
		return;
	}

	if ((command & 0x0ffff0ff) == 0x01800001)
	{
		blitter_x1800x01_xxxxxx_xxxxxx(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
		return;
	}

if (LOG_BLITTER_STATS)
{
static UINT32 blitter_stats[1000][4];
static UINT64 blitter_pixels[1000];
static int blitter_count = 0;
static int reps = 0;
int i;
for (i = 0; i < blitter_count; i++)
	if (blitter_stats[i][0] == (blitter_regs[B_CMD] & STATIC_COMMAND_MASK) &&
		blitter_stats[i][1] == (blitter_regs[A1_FLAGS] & STATIC_FLAGS_MASK) &&
		blitter_stats[i][2] == (blitter_regs[A2_FLAGS] & STATIC_FLAGS_MASK))
		break;
if (i == blitter_count)
{
	blitter_stats[i][0] = blitter_regs[B_CMD] & STATIC_COMMAND_MASK;
	blitter_stats[i][1] = blitter_regs[A1_FLAGS] & STATIC_FLAGS_MASK;
	blitter_stats[i][2] = blitter_regs[A2_FLAGS] & STATIC_FLAGS_MASK;
	blitter_stats[i][3] = 0;
	blitter_pixels[i] = 0;
	blitter_count++;
}
blitter_stats[i][3]++;
blitter_pixels[i] += (blitter_regs[B_COUNT] & 0xffff) * (blitter_regs[B_COUNT] >> 16);
if (++reps % 100 == 99)
{
	mame_printf_debug("---\nBlitter stats:\n");
	for (i = 0; i < blitter_count; i++)
		mame_printf_debug("  CMD=%08X A1=%08X A2=%08X %6d times, %08X%08X pixels\n",
				blitter_stats[i][0], blitter_stats[i][1], blitter_stats[i][2],
				blitter_stats[i][3], (UINT32)(blitter_pixels[i] >> 32), (UINT32)(blitter_pixels[i]));
	mame_printf_debug("---\n");
}
}

	generic_blitter(machine, blitter_regs[B_CMD], blitter_regs[A1_FLAGS], blitter_regs[A2_FLAGS]);
	g_profiler.stop();
}

static TIMER_CALLBACK( blitter_done )
{
	blitter_status = 1;
}

READ32_HANDLER( jaguar_blitter_r )
{
	switch (offset)
	{
		case B_CMD:	/* B_CMD */
			return blitter_status & 3;

		default:
			logerror("%08X:Blitter read register @ F022%02X\n", cpu_get_previouspc(&space->device()), offset * 4);
			return 0;
	}
}


WRITE32_HANDLER( jaguar_blitter_w )
{
	COMBINE_DATA(&blitter_regs[offset]);
	if ((offset == B_CMD) && (mem_mask & 0x0000ffff))
	{
		blitter_status = 0;
		space->machine().scheduler().timer_set(attotime::from_usec(100), FUNC(blitter_done));
		blitter_run(space->machine());
	}

	if (LOG_BLITTER_WRITE)
	logerror("%08X:Blitter write register @ F022%02X = %08X\n", cpu_get_previouspc(&space->device()), offset * 4, data);
}



/*************************************
 *
 *  16-bit TOM register access
 *
 *************************************/

READ16_HANDLER( jaguar_tom_regs_r )
{
	if (offset != INT1 && offset != INT2 && offset != HC && offset != VC)
		logerror("%08X:TOM read register @ F00%03X\n", cpu_get_previouspc(&space->device()), offset * 2);

	switch (offset)
	{
		case INT1:
			return cpu_irq_state;

		case HC:
			return space->machine().primary_screen->hpos() % (space->machine().primary_screen->width() / 2);

		case VC:
		{
			UINT8 half_line;

			if(space->machine().primary_screen->hpos() >= (space->machine().primary_screen->width() / 2))
				half_line = 1;
			else
				half_line = 0;

			return space->machine().primary_screen->vpos() * 2 + half_line;
		}
	}

	return gpu_regs[offset];
}

/*
FIXME: this should be 1, but then MAME performance will be s*** with this (i.e. it drops the performance from 400% to 6% on an i5 machine).
But the PIT irq is definitely needed by some games (for example Pitfall refuses to enter into gameplay without this enabled).
*/
#define PIT_MULT_DBG_HACK 64

static TIMER_CALLBACK( jaguar_pit )
{
	attotime sample_period;
	cpu_irq_state |= 4;
	update_cpu_irq(machine);

	if (gpu_regs[PIT0])
	{
		sample_period = attotime::from_nsec(((machine.device("gpu")->unscaled_clock()*PIT_MULT_DBG_HACK) / (1+gpu_regs[PIT0])) / (1+gpu_regs[PIT1]));
		machine.scheduler().timer_set(sample_period, FUNC(jaguar_pit));
	}
}


WRITE16_HANDLER( jaguar_tom_regs_w )
{
	UINT32 reg_store = gpu_regs[offset];
	attotime sample_period;
	if (offset < GPU_REGS)
	{
		COMBINE_DATA(&gpu_regs[offset]);

		switch (offset)
		{
			case MEMCON1:
				if((gpu_regs[offset] & 1) == 0)
					printf("Warning: ROMHI = 0!\n");

				break;
			case PIT0:
			case PIT1:
				if (gpu_regs[PIT0] && gpu_regs[PIT0] != 0xffff) //FIXME: avoid too much small timers for now
				{
					sample_period = attotime::from_nsec(((space->machine().device("gpu")->unscaled_clock()*PIT_MULT_DBG_HACK) / (1+gpu_regs[PIT0])) / (1+gpu_regs[PIT1]));
					space->machine().scheduler().timer_set(sample_period, FUNC(jaguar_pit));
				}
				break;

			case INT1:
				cpu_irq_state &= ~(gpu_regs[INT1] >> 8);
				update_cpu_irq(space->machine());
				break;

			case VMODE:
				if (reg_store != gpu_regs[offset])
					jaguar_set_palette(gpu_regs[VMODE]);
				break;

			case OBF:	/* clear GPU interrupt */
				cpu_irq_state &= 0xfd;
				update_cpu_irq(space->machine());
				break;

			case HP:
			case HBB:
			case HBE:
			case HDB1:
			case HDB2:
			case HDE:
			case VP:
			case VBB:
			case VBE:
			case VDB:
			case VDE:
			{
				if (reg_store != gpu_regs[offset])
				{
					int hperiod = 2 * ((gpu_regs[HP] & 0x3ff) + 1);
					int hbend = effective_hvalue(ENABLE_BORDERS ? gpu_regs[HBE] : MIN(gpu_regs[HDB1], gpu_regs[HDB2]));
					int hbstart = effective_hvalue(gpu_regs[ENABLE_BORDERS ? HBB : HDE]);
					int vperiod = (gpu_regs[VP] & 0x7ff) + 1;
					int vbend = MAX(gpu_regs[VBE],gpu_regs[VDB]) & 0x7ff;
					int vbstart = gpu_regs[VBB] & 0x7ff;

					/* adjust for the half-lines */
					if (hperiod != 0 && vperiod != 0 && hbend < hbstart && vbend < vbstart && hbstart < hperiod)
					{
						rectangle visarea(hbend / 2, hbstart / 2 - 1, vbend / 2, vbstart / 2 - 1);
						space->machine().primary_screen->configure(hperiod / 2, vperiod / 2, visarea, HZ_TO_ATTOSECONDS((double)pixel_clock * 2 / hperiod / vperiod));
					}
				}
				break;
			}
		}
	}

	if (offset != INT2 && offset != VI && offset != INT1)
		logerror("%08X:TOM write register @ F00%03X = %04X\n", cpu_get_previouspc(&space->device()), offset * 2, data);
}



/*************************************
 *
 *  32-bit TOM register access
 *
 *************************************/

READ32_HANDLER( jaguar_tom_regs32_r )
{
	return read32be_with_16be_handler(jaguar_tom_regs_r, space, offset, mem_mask);
}


WRITE32_HANDLER( jaguar_tom_regs32_w )
{
	write32be_with_16be_handler(jaguar_tom_regs_w, space, offset, data, mem_mask);
}



/*************************************
 *
 *  Gun input
 *
 *************************************/

READ32_HANDLER( cojag_gun_input_r )
{
	int beamx, beamy;

	switch (offset)
	{
		case 0:
			get_crosshair_xy(space->machine(), 1, &beamx, &beamy);
			return (beamy << 16) | (beamx ^ 0x1ff);

		case 1:
			get_crosshair_xy(space->machine(), 0, &beamx, &beamy);
			return (beamy << 16) | (beamx ^ 0x1ff);

		case 2:
			return space->machine().root_device().ioport("IN3")->read();
	}
	return 0;
}



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

static TIMER_CALLBACK( cojag_scanline_update )
{
	int vc = param & 0xffff;
	int hdb = param >> 16;
	const rectangle &visarea = machine.primary_screen->visible_area();

	/* only run if video is enabled and we are past the "display begin" */
	if ((gpu_regs[VMODE] & 1) && vc >= (gpu_regs[VDB] & 0x7ff))
	{
		UINT32 *dest = &screen_bitmap->pix32(vc >> 1);
		int maxx = visarea.max_x;
		int hde = effective_hvalue(gpu_regs[HDE]) >> 1;
		UINT16 x,scanline[760];
		UINT8 y,pixel_width = ((gpu_regs[VMODE]>>10)&3)+1;

		/* if we are first on this scanline, clear to the border color */
		if (ENABLE_BORDERS && vc % 2 == 0)
		{
			rgb_t border = MAKE_RGB(gpu_regs[BORD1] & 0xff, gpu_regs[BORD1] >> 8, gpu_regs[BORD2] & 0xff);
			for (x = visarea.min_x; x <= visarea.max_x; x++)
				dest[x] = border;
		}

		/* process the object list for this counter value */
		process_object_list(machine, vc, scanline);

		/* copy the data to the target, clipping */
		if ((gpu_regs[VMODE] & 0x106) == 0x002)	/* RGB24 */
		{
			for (x = 0; x < 760 && hdb <= maxx && hdb < hde; x+=2)
				for (y = 0; y < pixel_width; y++)
				{
					UINT8 r = pen_table[(scanline[x]&0xff)|256];
					UINT8 g = pen_table[(scanline[x]>>8)|512];
					UINT8 b = pen_table[scanline[x+1]&0xff];
					dest[hdb++] = MAKE_RGB(r, g, b);
				}
		}
		else
		{
			for (x = 0; x < 760 && hdb <= maxx && hdb < hde; x++)
				for (y = 0; y < pixel_width; y++)
					dest[hdb++] = pen_table[scanline[x]];
		}
	}

	/* adjust the timer in a loop, to handle missed cases */
	do
	{
		/* handle vertical interrupts */
		if (vc == gpu_regs[VI])
		{
			cpu_irq_state |= 1;
			update_cpu_irq(machine);
		}

		/* point to the next counter value */
		if (++vc / 2 >= machine.primary_screen->height())
			vc = 0;

	} while (!adjust_object_timer(machine, vc));
}

VIDEO_START( cojag )
{
	memset(&blitter_regs, 0, sizeof(blitter_regs));
	memset(&gpu_regs, 0, sizeof(gpu_regs));
	cpu_irq_state = 0;

	object_timer = machine.scheduler().timer_alloc(FUNC(cojag_scanline_update));
	adjust_object_timer(machine, 0);

	screen_bitmap = auto_bitmap_rgb32_alloc(machine, 760, 512);

	jagobj_init(machine);

	pen_table = auto_alloc_array(machine, pen_t, 65536);

	state_save_register_global_pointer(machine, pen_table, 65536);
	state_save_register_global_array(machine, blitter_regs);
	state_save_register_global_array(machine, gpu_regs);
	state_save_register_global(machine, cpu_irq_state);
	machine.save().register_postload(save_prepost_delegate(FUNC(update_cpu_irq), &machine));
	pixel_clock = COJAG_PIXEL_CLOCK;
}

VIDEO_START( jaguar )
{
	VIDEO_START_CALL( cojag );
	pixel_clock = JAGUAR_CLOCK;
}


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

SCREEN_UPDATE_RGB32( cojag )
{
	/* if not enabled, just blank */
	if (!(gpu_regs[VMODE] & 1))
	{
		bitmap.fill(0, cliprect);
		return 0;
	}

	/* render the object list */
	copybitmap(bitmap, *screen_bitmap, 0, 0, 0, 0, cliprect);
	return 0;
}



/*************************************
 *
 *  Object processor
 *
 *************************************/

#define INCLUDE_OBJECT_PROCESSOR
#include "jagobj.c"



/*************************************
 *
 *  Blitter macros
 *
 *************************************/

/* generic blitters */
#define FUNCNAME	generic_blitter
#define COMMAND		command
#define A1FIXED		a1flags
#define A2FIXED		a2flags
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

/* optimized common blitters */
#define FUNCNAME	blitter_09800001_010020_010020
#define COMMAND		0x09800001
#define A1FIXED		0x010020
#define A2FIXED		0x010020
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_09800009_000020_000020
#define COMMAND		0x09800009
#define A1FIXED		0x000020
#define A2FIXED		0x000020
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_01800009_000028_000028
#define COMMAND		0x01800009
#define A1FIXED		0x000028
#define A2FIXED		0x000028
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_01800001_000018_000018
#define COMMAND		0x01800001
#define A1FIXED		0x000018
#define A2FIXED		0x000018
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_01c00001_000018_000018
#define COMMAND		0x01c00001
#define A1FIXED		0x000018
#define A2FIXED		0x000018
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME


#define FUNCNAME	blitter_00010000_xxxxxx_xxxxxx
#define COMMAND		0x00010000
#define A1FIXED		a1flags
#define A2FIXED		a2flags
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_01800001_xxxxxx_xxxxxx
#define COMMAND		0x01800001
#define A1FIXED		a1flags
#define A2FIXED		a2flags
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

#define FUNCNAME	blitter_x1800x01_xxxxxx_xxxxxx
#define COMMAND		((command & 0xf0000f00) | 0x01800001)
#define A1FIXED		a1flags
#define A2FIXED		a2flags
#include "jagblit.c"
#undef A2FIXED
#undef A1FIXED
#undef COMMAND
#undef FUNCNAME

