#include "driver.h"
#include "sndhrdw/m72.h"



unsigned char *m72_videoram1,*m72_videoram2,*majtitle_rowscrollram;
static unsigned char *m72_spriteram;
static int rastersplit;
static int splitline;
static struct tilemap *fg_tilemap,*bg_tilemap;
static int xadjust;
static int scrollx1[256],scrolly1[256],scrollx2[256],scrolly2[256];
extern unsigned char *spriteram,*spriteram_2;
extern int spriteram_size;

static int irq1,irq2;

void m72_init_machine(void)
{
	int i;

	irq1 = 0x20;
	irq2 = 0x22;

	/* R-Type II doesn't clear the scroll registers on reset, so we have to do it ourselves */
	for (i = 0;i < 256;i++)
		scrollx1[i] = scrolly1[i] = 0;

	m72_init_sound();
}

void xmultipl_init_machine(void)
{
	irq1 = 0x08;
	irq2 = 0x0a;
	m72_init_sound();
}

int m72_interrupt(void)
{
	int line = 255 - cpu_getiloops();

	if (line == 255)	/* vblank */
	{
		rastersplit = 0;
		interrupt_vector_w(0,irq1);
		return interrupt();
	}
	else
	{
		if (line != splitline - 128)
			return ignore_interrupt();

		rastersplit = line + 1;

		/* this is used to do a raster effect and show the score display at
		   the bottom of the screen or other things. The line where the
		   interrupt happens is programmable (and the interrupt can be triggered
		   multiple times, be changing the interrupt line register in the
		   interrupt handler).
		 */
		interrupt_vector_w(0,irq2);
		return interrupt();
	}
}



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

  Callbacks for the TileMap code

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

static void m72_get_bg_tile_info(int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = m72_videoram2[tile_index+1];
	SET_TILE_INFO(2,m72_videoram2[tile_index] + ((attr & 0x3f) << 8),m72_videoram2[tile_index+2] & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0xc0) >> 6);
}

static void m72_get_fg_tile_info(int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = m72_videoram1[tile_index+1];
	SET_TILE_INFO(1,m72_videoram1[tile_index] + ((attr & 0x3f) << 8),m72_videoram1[tile_index+2] & 0x0f)
/* bchopper: (videoram[tile_index+2] & 0x10) is used, priority? */
	tile_info.flags = TILE_FLIPYX((attr & 0xc0) >> 6);

	tile_info.priority = (m72_videoram1[tile_index+2] & 0x80) >> 7;
}

static void dbreed_get_bg_tile_info(int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = m72_videoram2[tile_index+1];
	SET_TILE_INFO(2,m72_videoram2[tile_index] + ((attr & 0x3f) << 8),m72_videoram2[tile_index+2] & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0xc0) >> 6);

	/* this seems to apply only to Dragon Breed, it breaks R-Type and Gallop */
	tile_info.priority = (m72_videoram2[tile_index+2] & 0x80) >> 7;
}

static void rtype2_get_bg_tile_info(int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = m72_videoram2[tile_index+2];
	SET_TILE_INFO(1,m72_videoram2[tile_index] + (m72_videoram2[tile_index+1] << 8),attr & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0x60) >> 5);
}

static void rtype2_get_fg_tile_info(int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = m72_videoram1[tile_index+2];
	SET_TILE_INFO(1,m72_videoram1[tile_index] + (m72_videoram1[tile_index+1] << 8),attr & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0x60) >> 5);

	tile_info.priority = m72_videoram1[tile_index+3] & 0x01;

/* TODO: this is used on the continue screen by rtype2. Maybe it selects split tilemap */
/* like in M92 (top 8 pens appear over sprites), however if it is only used in that */
/* place there's no need to support it, it's just a black screen... */
	tile_info.priority |= (m72_videoram1[tile_index+2] & 0x80) >> 7;

/* (videoram[tile_index+2] & 0x10) is used by majtitle on the green, but it's not clear for what */
/* (videoram[tile_index+3] & 0xfe) are used as well */
}

static void majtitle_get_bg_tile_info(int col,int row)
{
	int tile_index = 4*(256*row+col);
	unsigned char attr = m72_videoram2[tile_index+2];
	SET_TILE_INFO(1,m72_videoram2[tile_index] + (m72_videoram2[tile_index+1] << 8),attr & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0x60) >> 5);
/* (videoram[tile_index+2] & 0x10) is used, but it's not clear for what (priority?) */
}

INLINE void hharry_get_tile_info(int gfxnum,unsigned char *videoram,int col,int row)
{
	int tile_index = 4*(64*row+col);
	unsigned char attr = videoram[tile_index+1];
	SET_TILE_INFO(gfxnum,videoram[tile_index] + ((attr & 0x3f) << 8),videoram[tile_index+2] & 0x0f)
	tile_info.flags = TILE_FLIPYX((attr & 0xc0) >> 6);
/* (videoram[tile_index+2] & 0x10) is used, but it's not clear for what (priority?) */
}

static void hharry_get_bg_tile_info(int col,int row)
{
	hharry_get_tile_info(1,m72_videoram2,col,row);
}

static void hharry_get_fg_tile_info(int col,int row)
{
	hharry_get_tile_info(1,m72_videoram1,col,row);
}


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

  Start the video hardware emulation.

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

int m72_vh_start(void)
{
	int i;


	bg_tilemap = tilemap_create(m72_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,64,64);
	fg_tilemap = tilemap_create(m72_get_fg_tile_info,TILEMAP_TRANSPARENT,8,8,64,64);

	m72_spriteram = malloc(spriteram_size);

	if (!fg_tilemap || !bg_tilemap || !m72_spriteram)
		return 1;

	fg_tilemap->transparent_pen = 0;

	memset(m72_spriteram,0,spriteram_size);

	xadjust = 0;

	/* improves bad gfx in nspirit (but this is not a complete fix, maybe there's a */
	/* layer enalbe register */
	for (i = 0;i < Machine->drv->total_colors;i++)
		palette_change_color(i,0,0,0);

	return 0;
}

int dbreed_vh_start(void)
{
	bg_tilemap = tilemap_create(dbreed_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,64,64);
	fg_tilemap = tilemap_create(m72_get_fg_tile_info,TILEMAP_TRANSPARENT,8,8,64,64);

	m72_spriteram = malloc(spriteram_size);

	if (!fg_tilemap || !bg_tilemap || !m72_spriteram)
		return 1;

	fg_tilemap->transparent_pen = 0;

	memset(m72_spriteram,0,spriteram_size);

	xadjust = 0;

	return 0;
}

int rtype2_vh_start(void)
{
	bg_tilemap = tilemap_create(rtype2_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,64,64);
	fg_tilemap = tilemap_create(rtype2_get_fg_tile_info,TILEMAP_TRANSPARENT,8,8,64,64);

	m72_spriteram = malloc(spriteram_size);

	if (!fg_tilemap || !bg_tilemap || !m72_spriteram)
		return 1;

	fg_tilemap->transparent_pen = 0;

	memset(m72_spriteram,0,spriteram_size);

	xadjust = -4;

	return 0;
}

/* Major Title has a larger background RAM, and rowscroll */
int majtitle_vh_start(void)
{
// tilemap can be 256x64, but seems to be used at 128x64 (scroll wraparound) */
//	bg_tilemap = tilemap_create(majtitle_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,256,64);
	bg_tilemap = tilemap_create(majtitle_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,128,64);
	fg_tilemap = tilemap_create(rtype2_get_fg_tile_info  ,TILEMAP_TRANSPARENT,8,8,64,64);

	m72_spriteram = malloc(spriteram_size);

	if (!fg_tilemap || !bg_tilemap || !m72_spriteram)
		return 1;

	fg_tilemap->transparent_pen = 0;

	memset(m72_spriteram,0,spriteram_size);

	xadjust = -4;

	return 0;
}

int hharry_vh_start(void)
{
	bg_tilemap = tilemap_create(hharry_get_bg_tile_info,TILEMAP_OPAQUE,     8,8,64,64);
	fg_tilemap = tilemap_create(hharry_get_fg_tile_info,TILEMAP_TRANSPARENT,8,8,64,64);

	m72_spriteram = malloc(spriteram_size);

	if (!fg_tilemap || !bg_tilemap || !m72_spriteram)
		return 1;

	fg_tilemap->transparent_pen = 0;

	memset(m72_spriteram,0,spriteram_size);

	xadjust = -4;

	return 0;
}

void m72_vh_stop(void)
{
	free(m72_spriteram);
	m72_spriteram = 0;
}



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

  Memory handlers

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

int m72_palette1_r(int offset)
{
	return paletteram[offset];
}

int m72_palette2_r(int offset)
{
	return paletteram_2[offset];
}

INLINE void changecolor(int color,int r,int g,int b)
{
	r = (r << 3) | (r >> 2);
	g = (g << 3) | (g >> 2);
	b = (b << 3) | (b >> 2);

	palette_change_color(color,r,g,b);
}

void m72_palette1_w(int offset,int data)
{
	paletteram[offset] = data;
	if (offset & 1) return;
	offset &= 0x3ff;
	changecolor(offset / 2,
			paletteram[offset + 0x000],
			paletteram[offset + 0x400],
			paletteram[offset + 0x800]);
}

void m72_palette2_w(int offset,int data)
{
	paletteram_2[offset] = data;
	if (offset & 1) return;
	offset &= 0x3ff;
	changecolor(offset / 2 + 512,
			paletteram_2[offset + 0x000],
			paletteram_2[offset + 0x400],
			paletteram_2[offset + 0x800]);
}

int m72_videoram1_r(int offset)
{
	return m72_videoram1[offset];
}

int m72_videoram2_r(int offset)
{
	return m72_videoram2[offset];
}

void m72_videoram1_w(int offset,int data)
{
	if (m72_videoram1[offset] != data)
	{
		m72_videoram1[offset] = data;
		tilemap_mark_tile_dirty(fg_tilemap,(offset/4)%64,(offset/4)/64);
	}
}

void m72_videoram2_w(int offset,int data)
{
	if (m72_videoram2[offset] != data)
	{
		m72_videoram2[offset] = data;
		tilemap_mark_tile_dirty(bg_tilemap,(offset/4)%64,(offset/4)/64);
	}
}

void majtitle_videoram2_w(int offset,int data)
{
	if (m72_videoram2[offset] != data)
	{
		m72_videoram2[offset] = data;
//		tilemap_mark_tile_dirty(bg_tilemap,(offset/4)%256,(offset/4)/256);
// tilemap can be 256x64, but seems to be used at 128x64 (scroll wraparound) */
if ((offset/4)%256 < 128)
		tilemap_mark_tile_dirty(bg_tilemap,(offset/4)%256,(offset/4)/256);
	}
}

void m72_irq_line_w(int offset,int data)
{
	offset *= 8;
	splitline = (splitline & (0xff00 >> offset)) | (data << offset);
}

void m72_scrollx1_w(int offset,int data)
{
	int i;

	offset *= 8;
	scrollx1[rastersplit] = (scrollx1[rastersplit] & (0xff00 >> offset)) | (data << offset);

	for (i = rastersplit+1;i < 256;i++)
		scrollx1[i] = scrollx1[rastersplit];
}

void m72_scrollx2_w(int offset,int data)
{
	int i;

	offset *= 8;
	scrollx2[rastersplit] = (scrollx2[rastersplit] & (0xff00 >> offset)) | (data << offset);

	for (i = rastersplit+1;i < 256;i++)
		scrollx2[i] = scrollx2[rastersplit];
}

void m72_scrolly1_w(int offset,int data)
{
	int i;

	offset *= 8;
	scrolly1[rastersplit] = (scrolly1[rastersplit] & (0xff00 >> offset)) | (data << offset);

	for (i = rastersplit+1;i < 256;i++)
		scrolly1[i] = scrolly1[rastersplit];
}

void m72_scrolly2_w(int offset,int data)
{
	int i;

	offset *= 8;
	scrolly2[rastersplit] = (scrolly2[rastersplit] & (0xff00 >> offset)) | (data << offset);

	for (i = rastersplit+1;i < 256;i++)
		scrolly2[i] = scrolly2[rastersplit];
}

void m72_spritectrl_w(int offset,int data)
{
//if (errorlog) fprintf(errorlog,"%04x: write %02x to sprite ctrl+%d\n",cpu_get_pc(),data,offset);
	/* TODO: this is ok for R-Type, but might be wrong for others */
	if (offset == 1)
	{
		memcpy(m72_spriteram,spriteram,spriteram_size);
		if (data & 0x40) memset(spriteram,0,spriteram_size);
		/* bit 7 is used by bchopper, nspirit, imgfight, loht, gallop - meaning unknown */
		/* rtype2 uses bits 4,5,6 and 7 - of course it could be a different chip */
	}
}

void hharry_spritectrl_w(int offset,int data)
{
//if (errorlog) fprintf(errorlog,"%04x: write %02x to sprite ctrl+%d\n",cpu_get_pc(),data,offset);
	if (offset == 0)
	{
		memcpy(m72_spriteram,spriteram,spriteram_size);
		memset(spriteram,0,spriteram_size);
	}
}

void hharryu_spritectrl_w(int offset,int data)
{
//if (errorlog) fprintf(errorlog,"%04x: write %02x to sprite ctrl+%d\n",cpu_get_pc(),data,offset);
	if (offset == 1)
	{
		memcpy(m72_spriteram,spriteram,spriteram_size);
		if (data & 0x80) memset(spriteram,0,spriteram_size);
		/* hharryu uses bits 2,3,4,5,6 and 7 - of course it could be a different chip */
		/* majtitle uses bits 2,3,5,6 and 7 - of course it could be a different chip */
	}
}



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

  Display refresh

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

static void draw_sprites(struct osd_bitmap *bitmap)
{
	int offs;

	for (offs = 0;offs < spriteram_size;offs += 8)
	{
		int code,color,sx,sy,flipx,flipy,w,h,x,y;


		code = m72_spriteram[offs+2] | (m72_spriteram[offs+3] << 8);
		color = m72_spriteram[offs+4] & 0x0f;
		sx = -256+(m72_spriteram[offs+6] | ((m72_spriteram[offs+7] & 0x03) << 8));
		sy = 512-(m72_spriteram[offs+0] | ((m72_spriteram[offs+1] & 0x01) << 8));
		flipx = m72_spriteram[offs+5] & 0x08;
		flipy = m72_spriteram[offs+5] & 0x04;

		w = 1 << ((m72_spriteram[offs+5] & 0xc0) >> 6);
		h = 1 << ((m72_spriteram[offs+5] & 0x30) >> 4);
		sy -= 16 * h;

		for (x = 0;x < w;x++)
		{
			for (y = 0;y < h;y++)
			{
				int c = code;

				if (flipx) c += 8*(w-1-x);
				else c += 8*x;
				if (flipy) c += h-1-y;
				else c += y;

				drawgfx(bitmap,Machine->gfx[0],
						c,
						color,
						flipx,flipy,
						sx + 16*x,sy + 16*y,
						&Machine->drv->visible_area,TRANSPARENCY_PEN,0);
			}
		}
	}
}

static void majtitle_draw_sprites(struct osd_bitmap *bitmap)
{
	int offs;

	for (offs = 0;offs < spriteram_size;offs += 8)
	{
		int code,color,sx,sy,flipx,flipy,w,h,x,y;


		code = spriteram_2[offs+2] | (spriteram_2[offs+3] << 8);
		color = spriteram_2[offs+4] & 0x0f;
		sx = -256+(spriteram_2[offs+6] | ((spriteram_2[offs+7] & 0x03) << 8));
		sy = 512-(spriteram_2[offs+0] | ((spriteram_2[offs+1] & 0x01) << 8));
		flipx = spriteram_2[offs+5] & 0x08;
		flipy = spriteram_2[offs+5] & 0x04;

		w = 1;// << ((spriteram_2[offs+5] & 0xc0) >> 6);
		h = 1 << ((spriteram_2[offs+5] & 0x30) >> 4);
		sy -= 16 * h;

		for (x = 0;x < w;x++)
		{
			for (y = 0;y < h;y++)
			{
				int c = code;

				if (flipx) c += 8*(w-1-x);
				else c += 8*x;
				if (flipy) c += h-1-y;
				else c += y;

				drawgfx(bitmap,Machine->gfx[2],
						c,
						color,
						flipx,flipy,
						sx + 16*x,sy + 16*y,
						&Machine->drv->visible_area,TRANSPARENCY_PEN,0);
			}
		}
	}
}

static void mark_sprite_colors(unsigned char *ram)
{
	int offs,color,i;
	int colmask[32];
	int pal_base;


	pal_base = Machine->drv->gfxdecodeinfo[0].color_codes_start;

	for (color = 0;color < 32;color++) colmask[color] = 0;

	for (offs = 0;offs < spriteram_size;offs += 8)
	{
		color = ram[offs+4] & 0x0f;
		colmask[color] |= 0xffff;
	}

	for (color = 0;color < 32;color++)
	{
		for (i = 1;i < 16;i++)
		{
			if (colmask[color] & (1 << i))
				palette_used_colors[pal_base + 16 * color + i] |= PALETTE_COLOR_VISIBLE;
		}
	}
}

static void draw_layer(struct osd_bitmap *bitmap,
		struct tilemap *tilemap,int *scrollx,int *scrolly,int priority)
{
	int start,i;
	/* use clip regions to split the screen */
	struct rectangle clip;

	clip.min_x = Machine->drv->visible_area.min_x;
	clip.max_x = Machine->drv->visible_area.max_x;
	start = Machine->drv->visible_area.min_y - 128;
	do
	{
		i = start;
		while (scrollx[i+1] == scrollx[start] && scrolly[i+1] == scrolly[start]
				&& i < Machine->drv->visible_area.max_y - 128)
			i++;

		clip.min_y = start + 128;
		clip.max_y = i + 128;
		tilemap_set_clip(tilemap,&clip);
		tilemap_set_scrollx(tilemap,0,scrollx[start] + xadjust);
		tilemap_set_scrolly(tilemap,0,scrolly[start]);
		tilemap_draw(bitmap,tilemap,priority);

		start = i+1;
	} while (start < Machine->drv->visible_area.max_y - 128);
}

static void draw_bg(struct osd_bitmap *bitmap,int priority)
{
	draw_layer(bitmap,bg_tilemap,scrollx2,scrolly2,priority);
}

static void draw_fg(struct osd_bitmap *bitmap,int priority)
{
	draw_layer(bitmap,fg_tilemap,scrollx1,scrolly1,priority);
}


void m72_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
{
	tilemap_set_clip(fg_tilemap,0);
	tilemap_set_clip(bg_tilemap,0);

	tilemap_update(bg_tilemap);
	tilemap_update(fg_tilemap);

	palette_init_used_colors();
	mark_sprite_colors(m72_spriteram);
	if (palette_recalc())
		tilemap_mark_all_pixels_dirty(ALL_TILEMAPS);

	tilemap_render(ALL_TILEMAPS);

	draw_bg(bitmap,0);
	draw_fg(bitmap,0);
	draw_sprites(bitmap);
	draw_fg(bitmap,1);
}

void dbreed_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
{
	tilemap_set_clip(fg_tilemap,0);
	tilemap_set_clip(bg_tilemap,0);

	tilemap_update(bg_tilemap);
	tilemap_update(fg_tilemap);

	palette_init_used_colors();
	mark_sprite_colors(m72_spriteram);
	if (palette_recalc())
		tilemap_mark_all_pixels_dirty(ALL_TILEMAPS);

	tilemap_render(ALL_TILEMAPS);

	draw_bg(bitmap,0);
	draw_fg(bitmap,0);
	draw_sprites(bitmap);
	draw_bg(bitmap,1);
	draw_fg(bitmap,1);
}

void majtitle_vh_screenrefresh(struct osd_bitmap *bitmap,int full_refresh)
{
	int i;


	tilemap_set_clip(fg_tilemap,0);

	/* TODO: find how rowscroll is disabled */
	if (1)
	{
		tilemap_set_scroll_rows(bg_tilemap,512);
		for (i = 0;i < 512;i++)
			tilemap_set_scrollx(bg_tilemap,(i+scrolly2[0])&0x1ff,
					256 + majtitle_rowscrollram[2*i] + (majtitle_rowscrollram[2*i+1] << 8) + xadjust);
	}
	else
	{
		tilemap_set_scroll_rows(bg_tilemap,1);
		tilemap_set_scrollx(bg_tilemap,0,256 + scrollx2[0] + xadjust);
	}
	tilemap_set_scrolly(bg_tilemap,0,scrolly2[0]);
	tilemap_update(bg_tilemap);
	tilemap_update(fg_tilemap);

	palette_init_used_colors();
	mark_sprite_colors(m72_spriteram);
	mark_sprite_colors(spriteram_2);
	if (palette_recalc())
		tilemap_mark_all_pixels_dirty(ALL_TILEMAPS);

	tilemap_render(ALL_TILEMAPS);

	tilemap_draw(bitmap,bg_tilemap,0);

	draw_fg(bitmap,0);
	majtitle_draw_sprites(bitmap);
	draw_sprites(bitmap);
	draw_fg(bitmap,1);
}
