#include "ncdz.h"


#define SX	80	// = (SCR_WIDTH - 320) / 2
#define SY	8	// = (SCR_HEIGHT - 256) / 2


typedef struct tile_cache_s
{
	u32 key;
	int used;
	u16 tex_idx;
	struct tile_cache_s *next;
} tile_cache_t __attribute__((aligned(16)));


/*--------------------------------------------------------------
   VRAM mapping

              0                          479     512
  FRAMESIZE*0 +---------------------------+------+
              | GU  Display / Draw buffer | Not  |
              |                           | used |
              | draw_frame = 0              |      |
  FRAMESIZE*1 +---------------------------+------+
              | GU  Display / Draw buffer | Not  |
              |                           | used |
              | draw_frame = 1              |      |
  FRAMESIZE*2 +---------------------------+------+
              | Draw frame                       |
              |                                  |
              | neogeo_blitter > 0               |
  FRAMESIZE*3 +----------------------------------+
              | FIX cache (tex_fix)              |
          +127+----------------------------------+
              | SPR cache 1 (tex_spr1)           |
   +(127+512) +----------------------------------+
              | SPR cache 2 (tex_spr2)           |
 +(127+512*2) +----------------------------------+

---------------------------------------------------------------*/

int neogeo_cacheclear;
int neogeo_paletterefresh;
int screen_is_dirty;

/*--------------------------------------------------------------
	FIX key: xxxx xxxx xxxx AAAA xxxx BBBB BBBB BBBB
                                           ^^^^ ^^^^
	A =  4 bit fix palette number
	B = 12 bit fix code
	x = not used
	^ = used as hash (8 bit)
---------------------------------------------------------------*/

#define FIX_CACHE_SIZE		1024
#define FIX_HASH_MASK		0xff
#define FIX_HASH_SIZE		0x100
#define FIX_MAX_SPRITES		(40 * 28)
#define FIX_CACHE_MAX_DELAY 4

static tile_cache_t *tile_fix_cache_ptr[FIX_HASH_SIZE];
static tile_cache_t tile_fix_cache_data[FIX_CACHE_SIZE];
static tile_cache_t *tile_fix_cache_first_free;

static u16 *tex_fix;
static u32 tile_fix_nb;
static u16 tile_fix_x[FIX_MAX_SPRITES];
static u16 tile_fix_y[FIX_MAX_SPRITES];
static u16 tile_fix_idx[FIX_MAX_SPRITES];
static int tile_fix_cached;
static int tile_fix_cache_reset;


/*--------------------------------------------------------------
	SPR key: xxxx xxxx xxxx AAAA BBBB BBBB BBBB BBBB
                                         ^ ^^^^ ^^^^
	A =  8 bit spr palette number
	B = 16 bit spr code
	x = not used
	^ = used as hash (9 bit)
---------------------------------------------------------------*/

#define SPR_CACHE_SIZE		2048
#define SPR_HASH_MASK		0x1ff
#define SPR_HASH_SIZE		0x200
#define SPR_MAX_SPRITES		0x2000
#define SPR_CACHE_MAX_DELAY	1

static tile_cache_t *tile_spr_cache_ptr[SPR_HASH_SIZE];
static tile_cache_t tile_spr_cache_data[SPR_CACHE_SIZE];
static tile_cache_t *tile_spr_cache_first_free;

static u16 *tex_spr1;
static u16 *tex_spr2;
static u32 tile_spr_nb;
static u16 tile_spr_x[SPR_MAX_SPRITES];
static u16 tile_spr_y[SPR_MAX_SPRITES];
static u8  tile_spr_w[SPR_MAX_SPRITES];
static u8  tile_spr_h[SPR_MAX_SPRITES];
static u16 tile_spr_idx[SPR_MAX_SPRITES];
static u8  tile_spr_att[SPR_MAX_SPRITES];
static int tile_spr_cached;
static int tile_spr_cache_reset;


#define SHIFTB(c)		(((c) << 9) & 0xf80000)
#define SHIFTG(c)		(((c) << 6) & 0x00f800)
#define SHIFTR(c)		(((c) << 3) & 0x0000f8)
//#define CNVCOL15TO32(c)	(0xff000000 | SHIFTB(c) | SHIFTG(c) | SHIFTR(c))
#define CNVCOL15TO32(c)	(SHIFTB(c) | SHIFTG(c) | SHIFTR(c))

static u32 clear_color;
static u8 palette_mark_dirty[0x100];
static int fix_palette_is_dirty;
static int spr_palette_is_dirty;



static int blit_fix_get_cache(int key);
static int blit_spr_get_cache(int key);
static int blit_fix_insert_cache(u32 key);
static int blit_spr_insert_cache(u32 key);
static void blit_reset_fix_cache(void);
static void blit_reset_spr_cache(void);
static void blit_fix_clean_cache(int delay);
static void blit_spr_clean_cache(int delay);
static void blit_fix_clear_dirty_palette(void);
static void blit_spr_clear_dirty_palette(void);



void neogeo_sprite_reset(void)
{
	int i;

	screen_is_dirty = 0;

	//init cache texure map location
	tex_fix  = video_frame_addr(tex_frame, 0, 0);
	tex_spr2 = tex_fix + (FIX_CACHE_SIZE * 8 * 8);
	tex_spr1 = tex_spr2 + 512 * 512;

	for (i = 0; i < FIX_CACHE_SIZE - 1; i++)
		tile_fix_cache_data[i].tex_idx = i;

	for (i = 0; i < SPR_CACHE_SIZE - 1; i++)
		tile_spr_cache_data[i].tex_idx = i;

	blit_reset_fix_cache();
	blit_reset_spr_cache();
}



/*--------------------------------------------------------
	Ask for a reset at next rendering pass
 -------------------------------------------------------*/

void blit_fix_resetcache(void)
{
	tile_fix_cache_reset = 1;
	fix_palette_is_dirty = 0;
}


/*--------------------------------------------------------
	Ask for a reset at next rendering pass
 -------------------------------------------------------*/

void blit_spr_resetcache(void)
{
	tile_spr_cache_reset = 1;
	spr_palette_is_dirty = 0;
}


/*--------------------------------------------------------
	Mark palette is dirty
 -------------------------------------------------------*/

void blit_palette_mark_dirty(int palno)
{
	if (neogeo_paletterefresh)
	{
		palette_mark_dirty[palno] = 1;

		if (palno < 0x10) fix_palette_is_dirty = 1;
		spr_palette_is_dirty = 1;
	}
	else
	{
		tile_fix_cache_reset = 1;
		tile_spr_cache_reset = 1;
	}
}


/*--------------------------------------------------------
	Reset dirty mark
 -------------------------------------------------------*/

void blit_palette_reset_mark(void)
{
	memset(palette_mark_dirty, 0, 0x100);
	fix_palette_is_dirty = 0;
	spr_palette_is_dirty = 0;
}


// get tile with hashkey=key
// return texure index if found, 0 if not
static int blit_fix_get_cache(int key)
{
	tile_cache_t *p = tile_fix_cache_ptr[key & FIX_HASH_MASK];

	while (p)
	{
		if (p->key == key)
		{
			p->used = frames_displayed;
			return p->tex_idx;
	 	}
		p = p->next;
	}
	return -1;
}


static int blit_spr_get_cache(int key)
{
	tile_cache_t *p = tile_spr_cache_ptr[key & SPR_HASH_MASK];

	while (p)
	{
		if (p->key == key)
		{
			p->used = frames_displayed;
			return p->tex_idx;
		}
		p = p->next;
	}
	return -1;
}

// insert a new tile in cache
static int blit_fix_insert_cache(u32 key)
{
	tile_cache_t *p = tile_fix_cache_ptr[key & FIX_HASH_MASK];
	tile_cache_t *q;

	//get first free cache entry
	q = tile_fix_cache_first_free;
	if (!q) return -1;

	//update first free
	tile_fix_cache_first_free = tile_fix_cache_first_free->next;

	//update new cached tile data
	q->next = NULL;
	q->key  = key;
	q->used = frames_displayed;

	if (!p)
	{
		//insert first
		tile_fix_cache_ptr[key & FIX_HASH_MASK] = q;
	}
	else
	{
		//insert at end
		while (p->next) p = p->next;
		p->next = q;
	}
	return q->tex_idx;
}


static int blit_spr_insert_cache(u32 key)
{
	tile_cache_t *p = tile_spr_cache_ptr[key & SPR_HASH_MASK];
	tile_cache_t *q;

	//get first free cache entry
	q = tile_spr_cache_first_free;
	if (!q) return -1;

	//update first free
	tile_spr_cache_first_free = tile_spr_cache_first_free->next;

	//update new cached tile data
	q->next = NULL;
	q->key  = key;
	q->used = frames_displayed;

	if (!p)
	{
		//insert first
		tile_spr_cache_ptr[key & SPR_HASH_MASK] = q;
	}
	else
	{
		//insert at end
		while (p->next) p = p->next;
		p->next = q;
	}
	return q->tex_idx;
}


// reset tile cache
static void blit_reset_fix_cache(void)
{
	int i;

	for (i = 0; i < FIX_CACHE_SIZE - 1; i++)
		tile_fix_cache_data[i].next = &tile_fix_cache_data[i + 1];

	tile_fix_cache_data[i].next = NULL;
	tile_fix_cache_first_free = &tile_fix_cache_data[0];
	memset(tile_fix_cache_ptr, 0, sizeof(tile_cache_t *) * FIX_HASH_SIZE);
	tile_fix_cached = 0;
	tile_fix_cache_reset = 0;
	fix_palette_is_dirty = 0;
}


static void blit_reset_spr_cache(void)
{
	int i;

	for (i = 0; i < SPR_CACHE_SIZE - 1; i++)
		tile_spr_cache_data[i].next = &tile_spr_cache_data[i + 1];

	tile_spr_cache_data[i].next = NULL;
	tile_spr_cache_first_free = &tile_spr_cache_data[0];
	memset(tile_spr_cache_ptr, 0, sizeof(tile_cache_t *) * SPR_HASH_SIZE);
	tile_spr_cached = 0;
	tile_spr_cache_reset = 0;
	spr_palette_is_dirty = 0;
}


// clean used cached tile & recount total
static void blit_fix_clean_cache(int delay)
{
	int i;
	tile_cache_t *p, *prev_p;

	for (i = 0; i < FIX_HASH_SIZE; i++)
	{
		prev_p = NULL;
		p = tile_fix_cache_ptr[i];

		while (p)
		{
			if (frames_displayed - p->used > delay)
			{
				//delete entry
				tile_fix_cached--;

				if (!prev_p)
				{
					//free tile in first pos
					tile_fix_cache_ptr[i] = p->next;
					p->next = tile_fix_cache_first_free;
					tile_fix_cache_first_free = p;
					p = tile_fix_cache_ptr[i];
				}
				else
				{
					//free tile at n-th pos
					prev_p->next = p->next;
					p->next = tile_fix_cache_first_free;
					tile_fix_cache_first_free = p;
					p = prev_p->next;
				}
			}
			else
			{
				prev_p = p;
				p = p->next;
			}
		}
	}
}


static void blit_spr_clean_cache(int delay)
{
	int i;
	tile_cache_t *p, *prev_p;

	for (i = 0; i < SPR_HASH_SIZE; i++)
	{
		prev_p = NULL;
		p = tile_spr_cache_ptr[i];

		while (p)
		{
			if (frames_displayed - p->used > delay)
			{
				//delete entry
				tile_spr_cached--;

				if (!prev_p)
				{
					//free tile in first pos
					tile_spr_cache_ptr[i] = p->next;
					p->next = tile_spr_cache_first_free;
					tile_spr_cache_first_free = p;
					p = tile_spr_cache_ptr[i];
				}
				else
				{
					//free tile at n-th pos
					prev_p->next = p->next;
					p->next = tile_spr_cache_first_free;
					tile_spr_cache_first_free = p;
					p = prev_p->next;
				}
			}
			else
			{
				prev_p = p;
				p = p->next;
			}
		}
	}
}


/*--------------------------------------------------------
	Clear dirty FIX entry
 -------------------------------------------------------*/

static void blit_fix_clear_dirty_palette(void)
{
	tile_cache_t *p, *prev_p;
	int i, palno;

	for (palno = 0; palno < 0x10; palno++)
	{
		if (!palette_mark_dirty[palno]) continue;

		for (i = 0; i < FIX_HASH_SIZE; i++)
		{
			prev_p = NULL;
			p = tile_fix_cache_ptr[i];

			while (p)
			{
				if ((p->key >> 16) == palno)
				{
					// palette is dirty, delete entry
					tile_fix_cached--;

					if (!prev_p)
					{
						//free tile in first pos
						tile_fix_cache_ptr[i] = p->next;
						p->next = tile_fix_cache_first_free;
						tile_fix_cache_first_free = p;
						p = tile_fix_cache_ptr[i];
					}
					else
					{
						//free tile at n-th pos
						prev_p->next = p->next;
						p->next = tile_fix_cache_first_free;
						tile_fix_cache_first_free = p;
						p = prev_p->next;
					}
				}
				else
				{
					prev_p = p;
					p = p->next;
				}
			}
		}
	}

	fix_palette_is_dirty = 0;
}


/*--------------------------------------------------------
	Clear dirty SPR entry
 -------------------------------------------------------*/

static void blit_spr_clear_dirty_palette(void)
{
	tile_cache_t *p, *prev_p;
	int i, palno;

	for (palno = 0; palno < 0x100; palno++)
	{
		if (!palette_mark_dirty[palno]) continue;

		for (i = 0; i < SPR_HASH_SIZE; i++)
		{
			prev_p = NULL;
			p = tile_spr_cache_ptr[i];

			while (p)
			{
				if ((p->key >> 16) == palno)
				{
					// palette is dirty, delete entry
					tile_spr_cached--;

					if (!prev_p)
					{
						//free tile in first pos
						tile_spr_cache_ptr[i] = p->next;
						p->next = tile_spr_cache_first_free;
						tile_spr_cache_first_free = p;
						p = tile_spr_cache_ptr[i];
					}
					else
					{
						//free tile at n-th pos
						prev_p->next = p->next;
						p->next = tile_spr_cache_first_free;
						tile_spr_cache_first_free = p;
						p = prev_p->next;
					}
				}
				else
				{
					prev_p = p;
					p = p->next;
				}
			}
		}
	}

	spr_palette_is_dirty = 0;
}


void blit_clear_cache(void)
{
	if (tile_fix_cache_reset) blit_reset_fix_cache();
	if (tile_spr_cache_reset) blit_reset_spr_cache();

	if (fix_palette_is_dirty) blit_fix_clear_dirty_palette();
	if (spr_palette_is_dirty) blit_spr_clear_dirty_palette();
	memset(palette_mark_dirty, 0, 0x100);

	if (neogeo_cacheclear)
	{
		blit_fix_clean_cache(neogeo_cacheclear * 10);
		blit_spr_clean_cache(neogeo_cacheclear * 10);
	}
}


void blit_start(u32 fill_color)
{
	blit_clear_cache();

	tile_fix_nb = 0;
	tile_spr_nb = 0;

	clear_color = CNVCOL15TO32(fill_color);
}


void blit_finish(void)
{
	int i, frame, u, v;
	RECT clip1 = { 8 + SX, 16 + SY, 312 + SX, 240 + SY };

	frame = neogeo_blitter ? work_frame : draw_frame;

	if (screen_is_dirty > 0)
	{
		video_clear_frame(draw_frame);
		screen_is_dirty--;
	}

	if (neogeo_blitter) video_clear_frame(work_frame);

	sceGuStart(GU_DIRECT, gulist);

	sceGuDrawBufferList(GU_PSM_5551, (void *)(FRAMESIZE * frame), BUF_WIDTH);
	sceGuScissor(clip1.left, clip1.top, clip1.right, clip1.bottom);
	sceGuClearColor(clear_color);
	sceGuClear(GU_COLOR_BUFFER_BIT);
	sceGuEnable(GU_ALPHA_TEST);

	sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
	sceGuTexFilter(GU_NEAREST, GU_NEAREST);

	struct Vertex *vertices_spr = (struct Vertex *)sceGuGetMemory(tile_spr_nb * 2 * sizeof(struct Vertex));

	if (vertices_spr)
	{
		struct Vertex *vertices_spr_tmp = vertices_spr;
		int total_spr, cur_tex;

		total_spr = 0;
		cur_tex = 0;

		sceGuTexImage(0, 512, 512, 512, tex_spr1);

		for (i = 0; i < tile_spr_nb; i++)
		{
			v = (tile_spr_idx[i] >> 5) << 4;

			if ((v & 512) != cur_tex)
			{
				cur_tex ^= 512;
				sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);

				total_spr = 0;
				vertices_spr = vertices_spr_tmp;
				sceGuTexImage(0, 512, 512, 512, (cur_tex ? tex_spr2 : tex_spr1));
			}

			v &= 511;
			u = (tile_spr_idx[i] & 31) << 4;

			if (tile_spr_att[i] & 0x1)
			{
				//flipx
				vertices_spr_tmp[0].u = u + 16;
				vertices_spr_tmp[1].u = u;
			}
			else
			{
				vertices_spr_tmp[0].u = u;
				vertices_spr_tmp[1].u = u + 16;
			}

			if (tile_spr_att[i] & 0x2)
			{
				//flipy
				vertices_spr_tmp[0].v = v + 16;
				vertices_spr_tmp[1].v = v;
			}
			else
			{
				vertices_spr_tmp[0].v = v;
				vertices_spr_tmp[1].v = v + 16;
			}

			vertices_spr_tmp[0].x = SX + tile_spr_x[i];
			vertices_spr_tmp[0].y = SY + tile_spr_y[i];
			vertices_spr_tmp[0].z = i + 1;
			vertices_spr_tmp[0].color = 0xffff;

			vertices_spr_tmp[1].x = SX + (tile_spr_x[i] + tile_spr_w[i]);
			vertices_spr_tmp[1].y = SY + (tile_spr_y[i] + tile_spr_h[i]);
			vertices_spr_tmp[1].z = i + 1;
			vertices_spr_tmp[1].color = 0xffff;

			vertices_spr_tmp += 2;
			total_spr++;
		}

		if (total_spr)
			sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);
	}

	sceGuTexImage(0, 512, 512, 512, tex_fix);

	struct Vertex *vertices_fix = (struct Vertex *)sceGuGetMemory(tile_fix_nb * 2 * sizeof(struct Vertex));

	if (vertices_fix)
	{
		struct Vertex *vertices_fix_tmp = vertices_fix;

		for (i = 0; i < tile_fix_nb; i++)
		{
			u = (tile_fix_idx[i] & 63) << 3;
			v = (tile_fix_idx[i] >> 6) << 3;

			vertices_fix_tmp[0].u = u;
			vertices_fix_tmp[0].v = v;
			vertices_fix_tmp[0].x = SX + tile_fix_x[i];
			vertices_fix_tmp[0].y = SY + tile_fix_y[i];
			vertices_fix_tmp[0].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[0].color = 0xffff;

			vertices_fix_tmp[1].u = u + 8;
			vertices_fix_tmp[1].v = v + 8;
			vertices_fix_tmp[1].x = SX + tile_fix_x[i] + 8;
			vertices_fix_tmp[1].y = SY + tile_fix_y[i] + 8;
			vertices_fix_tmp[1].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[1].color = 0xffff;

			vertices_fix_tmp += 2;
		}

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * tile_fix_nb, 0, vertices_fix);
	}

	sceGuFinish();
	sceGuSync(0, 0);

	if (neogeo_blitter)
	{
		RECT clip2 = { 0, 0, SCR_WIDTH, SCR_HEIGHT };

		switch (neogeo_blitter)
		{
		case 1:
			clip2.left  = (SCR_WIDTH - 388) / 2;
			clip2.right = (SCR_WIDTH - 388) / 2 + 388;
			break;

		case 2:
			clip2.left  = (SCR_WIDTH - 420) / 2;
			clip2.right = (SCR_WIDTH - 420) / 2 + 420;
			break;
		}

		video_copy_rect(work_frame, draw_frame, &clip1, &clip2);
	}
}


#ifdef EMULATE_RASTER_EFFECT

struct raster_t
{
	RECT clip;
	int last_line;
	int spr_start;
	int spr_end;
} raster[224];

static int raster_count;

void blit_partial_start(u32 fill_color)
{
	blit_clear_cache();

	tile_fix_nb = 0;
	tile_spr_nb = 0;

	raster_count = 0;
	raster[0].spr_start = 0;

	clear_color = CNVCOL15TO32(fill_color);
}


void blit_partial_update(struct rectangle *clip)
{
	RECT clip1 = { 8 + SX, 16 + SY, 312 + SX, 240 + SY };

	clip1.top = SY + clip->min_y;
	clip1.bottom = SY + clip->max_y + 1;

	raster[raster_count].clip.left   = clip1.left;
	raster[raster_count].clip.top    = clip1.top;
	raster[raster_count].clip.right  = clip1.right;
	raster[raster_count].clip.bottom = clip1.bottom;
	raster[raster_count].spr_end     = tile_spr_nb;
	raster_count++;

	raster[raster_count].spr_start = tile_spr_nb;
}


void blit_partial_finish(void)
{
	int i, frame, count, u, v;
	RECT clip1 = { 8 + SX, 16 + SY, 312 + SX, 240 + SY };

	frame = neogeo_blitter ? work_frame : draw_frame;

	if (screen_is_dirty > 0)
	{
		video_clear_frame(draw_frame);
		screen_is_dirty--;
	}

	if (neogeo_blitter) video_clear_frame(work_frame);

	sceGuStart(GU_DIRECT, gulist);

	sceGuDrawBufferList(GU_PSM_5551, (void *)(FRAMESIZE * frame), BUF_WIDTH);
	sceGuScissor(clip1.left, clip1.top, clip1.right, clip1.bottom);
	sceGuClearColor(clear_color);
	sceGuClear(GU_COLOR_BUFFER_BIT);
	sceGuEnable(GU_ALPHA_TEST);

	for (count = 0; count < raster_count; count++)
	{
		sceGuScissor(raster[count].clip.left, raster[count].clip.top, raster[count].clip.right, raster[count].clip.bottom);
		sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
		sceGuTexFilter(GU_NEAREST, GU_NEAREST);

		struct Vertex *vertices_spr = (struct Vertex *)sceGuGetMemory((raster[count].spr_end - raster[count].spr_start) * 2 * sizeof(struct Vertex));

		if (vertices_spr)
		{
			struct Vertex *vertices_spr_tmp = vertices_spr;
			int total_spr, cur_tex;

			total_spr = 0;
			cur_tex = 0;

			sceGuTexImage(0, 512, 512, 512, tex_spr1);

			for (i = raster[count].spr_start; i < raster[count].spr_end; i++)
			{
				v = (tile_spr_idx[i] >> 5) << 4;

				if ((v & 512) != cur_tex)
				{
					cur_tex ^= 512;
					sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);

					total_spr = 0;
					vertices_spr = vertices_spr_tmp;
					sceGuTexImage(0, 512, 512, 512, (cur_tex ? tex_spr2 : tex_spr1));
				}

				v &= 511;
				u = (tile_spr_idx[i] & 31) << 4;

				if (tile_spr_att[i] & 0x1)
				{
					//flipx
					vertices_spr_tmp[0].u = u + 16;
					vertices_spr_tmp[1].u = u;
				}
				else
				{
					vertices_spr_tmp[0].u = u;
					vertices_spr_tmp[1].u = u + 16;
				}

				if (tile_spr_att[i] & 0x2)
				{
					//flipy
					vertices_spr_tmp[0].v = v + 16;
					vertices_spr_tmp[1].v = v;
				}
				else
				{
					vertices_spr_tmp[0].v = v;
					vertices_spr_tmp[1].v = v + 16;
				}

				vertices_spr_tmp[0].x = SX + tile_spr_x[i];
				vertices_spr_tmp[0].y = SY + tile_spr_y[i];
				vertices_spr_tmp[0].z = i + 1;
				vertices_spr_tmp[0].color = 0xffff;

				vertices_spr_tmp[1].x = SX + (tile_spr_x[i] + tile_spr_w[i]);
				vertices_spr_tmp[1].y = SY + (tile_spr_y[i] + tile_spr_h[i]);
				vertices_spr_tmp[1].z = i + 1;
				vertices_spr_tmp[1].color = 0xffff;

				vertices_spr_tmp += 2;
				total_spr++;
			}

			if (total_spr)
				sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);
		}
	}

	sceGuScissor(clip1.left, clip1.top, clip1.right, clip1.bottom);
	sceGuTexImage(0, 512, 512, 512, tex_fix);

	struct Vertex *vertices_fix = (struct Vertex *)sceGuGetMemory(tile_fix_nb * 2 * sizeof(struct Vertex));

	if (vertices_fix)
	{
		struct Vertex *vertices_fix_tmp = vertices_fix;

		for (i = 0; i < tile_fix_nb; i++)
		{
			u = (tile_fix_idx[i] & 63) << 3;
			v = (tile_fix_idx[i] >> 6) << 3;

			vertices_fix_tmp[0].u = u;
			vertices_fix_tmp[0].v = v;
			vertices_fix_tmp[0].x = SX + tile_fix_x[i];
			vertices_fix_tmp[0].y = SY + tile_fix_y[i];
			vertices_fix_tmp[0].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[0].color = 0xffff;

			vertices_fix_tmp[1].u = u + 8;
			vertices_fix_tmp[1].v = v + 8;
			vertices_fix_tmp[1].x = SX + tile_fix_x[i] + 8;
			vertices_fix_tmp[1].y = SY + tile_fix_y[i] + 8;
			vertices_fix_tmp[1].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[1].color = 0xffff;

			vertices_fix_tmp += 2;
		}

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * tile_fix_nb, 0, vertices_fix);
	}

	sceGuFinish();
	sceGuSync(0, 0);

	if (neogeo_blitter)
	{
		RECT clip2 = { 0, 0, SCR_WIDTH, SCR_HEIGHT };

		switch (neogeo_blitter)
		{
		case 1:
			clip2.left  = (SCR_WIDTH - 388) / 2;
			clip2.right = (SCR_WIDTH - 388) / 2 + 388;
			break;

		case 2:
			clip2.left  = (SCR_WIDTH - 420) / 2;
			clip2.right = (SCR_WIDTH - 420) / 2 + 420;
			break;
		}

		video_copy_rect(work_frame, draw_frame, &clip1, &clip2);
	}
}
#endif


void blit_drawfix(int x, int y, u16 *pal, u32 *tex, u32 tile_key)
{
	int i, u, v, idx;

	if (tile_fix_nb == FIX_MAX_SPRITES) return;

	idx = blit_fix_get_cache(tile_key);

	if (idx < 0)
	{
		if (tile_fix_cached == FIX_CACHE_SIZE - 1)
		{
			//to much tile cached, clean
			blit_fix_clean_cache(0);

			//still to much, exit
			if (tile_fix_cached == FIX_CACHE_SIZE - 1)
				return;
		}

		idx = blit_fix_insert_cache(tile_key);
		u = (idx & 63) << 3;
		v = (idx >> 6) << 3;
		tile_fix_cached++;

		u32 mydword;
		u16 *dest = &(tex_fix[u + (v << 9)]);

		i = 8;
		while (i--)
		{
			mydword  = *tex++;
			dest[0] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[1] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[2] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[3] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[4] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[5] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[6] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[7] = pal[mydword & 0x0f];
			dest += BUF_WIDTH;
		}
	}

	tile_fix_idx[tile_fix_nb] = idx;
	tile_fix_x[tile_fix_nb] = x;
	tile_fix_y[tile_fix_nb] = y;
	tile_fix_nb++;
}


void blit_drawspr(int x, int y, int w, int h, int att, u16 *pal, u32 *tex, u32 tile_key)
{
	int i, u, v, idx;

	if (tile_spr_nb == SPR_MAX_SPRITES) return;

	idx = blit_spr_get_cache(tile_key);

	if (idx < 0)
	{
		if (tile_spr_cached == SPR_CACHE_SIZE - 1)
		{
			//to much tile cached, clean
			blit_spr_clean_cache(0);

			//tile_spr_cache_reset=1;
			//still to much, exit
			if (tile_spr_cached == SPR_CACHE_SIZE - 1)
				return;
		}

		idx = blit_spr_insert_cache(tile_key);
		tile_spr_cached++;

		u = (idx & 31) << 4;
		v = (idx >> 5) << 4;

		u32 mydword;
		u16 *dest;

		if (v < 512)
			dest = &(tex_spr1[u | (v << 9)]);
		else
			dest = &(tex_spr2[u | ((v & 511) << 9)]);

		i = 16;
		while (i--)
		{
			mydword  = *tex++;
			dest[ 0] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 1] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 2] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 3] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 4] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 5] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 6] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 7] = pal[mydword & 0x0f];
			mydword  = *tex++;
			dest[ 8] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[ 9] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[10] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[11] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[12] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[13] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[14] = pal[mydword & 0x0f]; mydword >>= 4;
			dest[15] = pal[mydword & 0x0f];
			dest += BUF_WIDTH;
		}
	}

	tile_spr_idx[tile_spr_nb] = idx;
	tile_spr_x[tile_spr_nb] = x;
	tile_spr_y[tile_spr_nb] = y;
	tile_spr_w[tile_spr_nb] = w;
	tile_spr_h[tile_spr_nb] = h;
	tile_spr_att[tile_spr_nb] = att;
	tile_spr_nb++;
}


#ifdef SAVE_STATE

void state_save_screen(u8 *state_buf)
{
	int i, u, v;
	RECT clip1 = { 8 + SX, 16 + SY, 312 + SX, 240 + SY };
	RECT clip2;

	blit_fix_resetcache();
	blit_spr_resetcache();
	blit_palette_reset_mark();

	video_clear_frame(work_frame);

	state_screenrefresh();

	sceGuStart(GU_DIRECT, gulist);
	sceGuDrawBufferList(GU_PSM_5551, (void *)(FRAMESIZE * work_frame), BUF_WIDTH);
	sceGuScissor(clip1.left, clip1.top, clip1.right, clip1.bottom);
	sceGuClearColor(clear_color);
	sceGuClear(GU_COLOR_BUFFER_BIT);
	sceGuEnable(GU_ALPHA_TEST);
	sceGuTexFunc(GU_TFX_MODULATE, GU_TCC_RGBA);
	sceGuTexFilter(GU_NEAREST, GU_NEAREST);

	struct Vertex *vertices_spr = (struct Vertex *)sceGuGetMemory(tile_spr_nb * 2 * sizeof(struct Vertex));

	if (vertices_spr)
	{
		struct Vertex *vertices_spr_tmp = vertices_spr;
		int total_spr, cur_tex;

		total_spr = 0;
		cur_tex = 0;

		sceGuTexImage(0, 512, 512, 512, tex_spr1);

		for (i = 0; i < tile_spr_nb; i++)
		{
			v = (tile_spr_idx[i] >> 5) << 4;

			if ((v & 512) != cur_tex)
			{
				cur_tex ^= 512;
				sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);

				total_spr = 0;
				vertices_spr = vertices_spr_tmp;
				sceGuTexImage(0, 512, 512, 512, (cur_tex ? tex_spr2 : tex_spr1));
			}

			v &= 511;
			u = (tile_spr_idx[i] & 31) << 4;

			if (tile_spr_att[i] & 0x1)
			{
				//flipx
				vertices_spr_tmp[0].u = u + 16;
				vertices_spr_tmp[1].u = u;
			}
			else
			{
				vertices_spr_tmp[0].u = u;
				vertices_spr_tmp[1].u = u + 16;
			}

			if (tile_spr_att[i] & 0x2)
			{
				//flipy
				vertices_spr_tmp[0].v = v + 16;
				vertices_spr_tmp[1].v = v;
			}
			else
			{
				vertices_spr_tmp[0].v = v;
				vertices_spr_tmp[1].v = v + 16;
			}

			vertices_spr_tmp[0].x = SX + tile_spr_x[i];
			vertices_spr_tmp[0].y = SY + tile_spr_y[i];
			vertices_spr_tmp[0].z = i + 1;
			vertices_spr_tmp[0].color = 0xffff;

			vertices_spr_tmp[1].x = SX + (tile_spr_x[i] + tile_spr_w[i]);
			vertices_spr_tmp[1].y = SY + (tile_spr_y[i] + tile_spr_h[i]);
			vertices_spr_tmp[1].z = i + 1;
			vertices_spr_tmp[1].color = 0xffff;

			vertices_spr_tmp += 2;
			total_spr++;
		}

		if (total_spr)
			sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * total_spr, 0, vertices_spr);
	}

	sceGuTexImage(0, 512, 512, 512, tex_fix);

	struct Vertex *vertices_fix = (struct Vertex *)sceGuGetMemory(tile_fix_nb * 2 * sizeof(struct Vertex));

	if (vertices_fix)
	{
		struct Vertex *vertices_fix_tmp = vertices_fix;

		for (i = 0; i < tile_fix_nb; i++)
		{
			u = (tile_fix_idx[i] & 63) << 3;
			v = (tile_fix_idx[i] >> 6) << 3;

			vertices_fix_tmp[0].u = u;
			vertices_fix_tmp[0].v = v;
			vertices_fix_tmp[0].x = SX + tile_fix_x[i];
			vertices_fix_tmp[0].y = SY + tile_fix_y[i];
			vertices_fix_tmp[0].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[0].color = 0xffff;

			vertices_fix_tmp[1].u = u + 8;
			vertices_fix_tmp[1].v = v + 8;
			vertices_fix_tmp[1].x = SX + tile_fix_x[i] + 8;
			vertices_fix_tmp[1].y = SY + tile_fix_y[i] + 8;
			vertices_fix_tmp[1].z = FIX_CACHE_SIZE + SPR_CACHE_SIZE;
			vertices_fix_tmp[1].color = 0xffff;

			vertices_fix_tmp += 2;
		}

		sceGuDrawArray(GU_SPRITES, TEXTURE_FLAGS, 2 * tile_fix_nb, 0, vertices_fix);
	}

	sceGuFinish();
	sceGuSync(0, 0);

	clip2.left   = clip1.left;
	clip2.top    = clip1.top;
	clip2.right  = clip1.left + 152;
	clip2.bottom = clip1.top + 112;

	video_clear_frame(draw_frame);
	video_copy_rect(work_frame, draw_frame, &clip1, &clip2);

	int x, y;
	u16 *src = video_frame_addr(draw_frame, 0, 0);
	u16 *dst = (u16 *)state_buf;

	for (y = 0; y < 112; y++)
	{
		for (x = 0; x < 152; x++)
		{
			dst[y * 152 + x] = src[(clip2.top + y) * BUF_WIDTH + (clip2.left + x)];
		}
	}

	load_background();
}

#endif
