/***************************************************************************
 prim.c  -  description
 -------------------
 begin                : Sun Oct 28 2001
 copyright            : (C) 2001 by Pete Bernert
 email                : BlackDove@addcom.de
 ***************************************************************************/
/***************************************************************************
 *                                                                         *
 *   This program is free software; you can redistribute it and/or modify  *
 *   it under the terms of the GNU General Public License as published by  *
 *   the Free Software Foundation; either version 2 of the License, or     *
 *   (at your option) any later version. See also the license.txt file for *
 *   additional informations.                                              *
 *                                                                         *
 ***************************************************************************/
#include <stdio.h>

#include "globals.h"
#include "swap.h"

#define INFO_TW        0
#define INFO_DRAWSTART 1
#define INFO_DRAWEND   2
#define INFO_DRAWOFF   3

////////////////////////////////////////////////////////////////////////
// globals
////////////////////////////////////////////////////////////////////////

////////////////////////////////////////////////////////////////////////
// Some ASM color convertion by LEWPY
////////////////////////////////////////////////////////////////////////

#ifdef USE_NASM

#define BGR24to16 i386_BGR24to16
__inline unsigned short BGR24to16 (uint32_t BGR);

#else

inline unsigned short BGR24to16(uint32_t BGR) {
	return (unsigned short) (((BGR >> 3) & 0x1f) | ((BGR & 0xf80000) >> 9)
			| ((BGR & 0xf800) >> 6));
}

#endif

////////////////////////////////////////////////////////////////////////
// Update global TP infos
////////////////////////////////////////////////////////////////////////

inline void UpdateGlobalTP(unsigned short gdata) {
	g_soft.GlobalTextAddrX = (gdata << 6) & 0x3c0; // texture addr

	if (512 == 1024) {
		if (1 == 2) {
			g_soft.GlobalTextAddrY = ((gdata & 0x60) << 3);
			//g_zn.GlobalTextIL = (gdata & 0x2000) >> 13;
			g_soft.GlobalTextABR = (unsigned short) ((gdata >> 7) & 0x3);
			g_soft.GlobalTextTP = (gdata >> 9) & 0x3;
			if (g_soft.GlobalTextTP == 3)
				g_soft.GlobalTextTP = 2;
			g_prim.usMirror = 0;
			g_gpu.status_reg = (g_gpu.status_reg & 0xffffe000) | (gdata
					& 0x1fff);

			// tekken dithering? right now only if dithering is forced by user
			if (g_cfg.Dithering == 2)
				g_prim.iDither = 2;
			else
				g_prim.iDither = 0;

			return;
		} else {
			g_soft.GlobalTextAddrY = (unsigned short) (((gdata << 4) & 0x100)
					| ((gdata >> 2) & 0x200));
		}
	} else
		g_soft.GlobalTextAddrY = (gdata << 4) & 0x100;

	g_soft.GlobalTextTP = (gdata >> 7) & 0x3; // tex mode (4,8,15)

	if (g_soft.GlobalTextTP == 3)
		g_soft.GlobalTextTP = 2; // seen in Wild9 :(

	g_soft.GlobalTextABR = (gdata >> 5) & 0x3; // blend mode

	g_gpu.status_reg &= ~0x000001ff; // Clear the necessary bits
	g_gpu.status_reg |= (gdata & 0x01ff); // set the necessary bits

	switch (g_cfg.Dithering) {
	case 0:
		g_prim.iDither = 0;
		break;
	case 1:
		if (g_gpu.status_reg & 0x0200)
			g_prim.iDither = 2;
		else
			g_prim.iDither = 0;
		break;
	case 2:
		g_prim.iDither = 2;
		break;
	}
}

////////////////////////////////////////////////////////////////////////

inline void SetRenderMode(uint32_t DrawAttributes) {
	g_soft.DrawSemiTrans = (SEMITRANSBIT(DrawAttributes)) ? 1 : 0;

	if (SHADETEXBIT(DrawAttributes)) {
		g_soft.g_m1 = g_soft.g_m2 = g_soft.g_m3 = 128;
	} else {
		if ((g_prim.dwActFixes & 4) && ((DrawAttributes & 0x00ffffff) == 0))
			DrawAttributes |= 0x007f7f7f;

		g_soft.g_m1 = (short) (DrawAttributes & 0xff);
		g_soft.g_m2 = (short) ((DrawAttributes >> 8) & 0xff);
		g_soft.g_m3 = (short) ((DrawAttributes >> 16) & 0xff);
	}
}

////////////////////////////////////////////////////////////////////////

// oki, here are the psx gpu coord rules: poly coords are
// 11 bit signed values (-1024...1023). If the x or y distance 
// exceeds 1024, the polygon will not be drawn. 
// Since quads are treated as two triangles by the real gpu,
// this 'discard rule' applies to each of the quad's triangle 
// (so one triangle can be drawn, the other one discarded). 
// Also, y drawing is wrapped at 512 one time,
// then it will get negative (and therefore not drawn). The
// 'CheckCoord' funcs are a simple (not comlete!) approach to
// do things right, I will add a better detection soon... the 
// current approach will be easier to do in hw/accel plugins, imho

// 11 bit signed
#define SIGNSHIFT 21
#define CHKMAX_X 1024
#define CHKMAX_Y 512

void AdjustCoord4() {
	g_soft.lx0 = (short) (((int) g_soft.lx0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx1 = (short) (((int) g_soft.lx1 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx2 = (short) (((int) g_soft.lx2 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx3 = (short) (((int) g_soft.lx3 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly0 = (short) (((int) g_soft.ly0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly1 = (short) (((int) g_soft.ly1 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly2 = (short) (((int) g_soft.ly2 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly3 = (short) (((int) g_soft.ly3 << SIGNSHIFT) >> SIGNSHIFT);
}

void AdjustCoord3() {
	g_soft.lx0 = (short) (((int) g_soft.lx0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx1 = (short) (((int) g_soft.lx1 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx2 = (short) (((int) g_soft.lx2 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly0 = (short) (((int) g_soft.ly0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly1 = (short) (((int) g_soft.ly1 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly2 = (short) (((int) g_soft.ly2 << SIGNSHIFT) >> SIGNSHIFT);
}

void AdjustCoord2() {
	g_soft.lx0 = (short) (((int) g_soft.lx0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.lx1 = (short) (((int) g_soft.lx1 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly0 = (short) (((int) g_soft.ly0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly1 = (short) (((int) g_soft.ly1 << SIGNSHIFT) >> SIGNSHIFT);
}

void AdjustCoord1() {
	g_soft.lx0 = (short) (((int) g_soft.lx0 << SIGNSHIFT) >> SIGNSHIFT);
	g_soft.ly0 = (short) (((int) g_soft.ly0 << SIGNSHIFT) >> SIGNSHIFT);

	if (g_soft.lx0 < -512 && g_gpu.dsp.DrawOffset.x <= -512)
		g_soft.lx0 += 2048;

	if (g_soft.ly0 < -512 && g_gpu.dsp.DrawOffset.y <= -512)
		g_soft.ly0 += 2048;
}

/*
 ////////////////////////////////////////////////////////////////////////
 // special checks... nascar, syphon filter 2, mgs
 ////////////////////////////////////////////////////////////////////////

 // xenogears FT4: not removed correctly right now... the tri 0,1,2
 // should get removed, the tri 1,2,3 should stay... pfff

 // x -466 1023 180 1023
 // y   20 -228 222 -100

 // 0 __1
 //  \ / \
 //   2___3
 */
inline char CheckCoord4() {
	if (g_soft.lx0 < 0) {
		if (((g_soft.lx1 - g_soft.lx0) > CHKMAX_X)
				|| ((g_soft.lx2 - g_soft.lx0) > CHKMAX_X)) {
			if (g_soft.lx3 < 0) {
				if ((g_soft.lx1 - g_soft.lx3) > CHKMAX_X)
					return 1;
				if ((g_soft.lx2 - g_soft.lx3) > CHKMAX_X)
					return 1;
			}
		}
	}
	if (g_soft.lx1 < 0) {
		if ((g_soft.lx0 - g_soft.lx1) > CHKMAX_X)
			return 1;
		if ((g_soft.lx2 - g_soft.lx1) > CHKMAX_X)
			return 1;
		if ((g_soft.lx3 - g_soft.lx1) > CHKMAX_X)
			return 1;
	}
	if (g_soft.lx2 < 0) {
		if ((g_soft.lx0 - g_soft.lx2) > CHKMAX_X)
			return 1;
		if ((g_soft.lx1 - g_soft.lx2) > CHKMAX_X)
			return 1;
		if ((g_soft.lx3 - g_soft.lx2) > CHKMAX_X)
			return 1;
	}
	if (g_soft.lx3 < 0) {
		if (((g_soft.lx1 - g_soft.lx3) > CHKMAX_X)
				|| ((g_soft.lx2 - g_soft.lx3) > CHKMAX_X)) {
			if (g_soft.lx0 < 0) {
				if ((g_soft.lx1 - g_soft.lx0) > CHKMAX_X)
					return 1;
				if ((g_soft.lx2 - g_soft.lx0) > CHKMAX_X)
					return 1;
			}
		}
	}

	if (g_soft.ly0 < 0) {
		if ((g_soft.ly1 - g_soft.ly0) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly2 - g_soft.ly0) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly1 < 0) {
		if ((g_soft.ly0 - g_soft.ly1) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly2 - g_soft.ly1) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly3 - g_soft.ly1) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly2 < 0) {
		if ((g_soft.ly0 - g_soft.ly2) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly1 - g_soft.ly2) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly3 - g_soft.ly2) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly3 < 0) {
		if ((g_soft.ly1 - g_soft.ly3) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly2 - g_soft.ly3) > CHKMAX_Y)
			return 1;
	}

	return 0;
}

inline char CheckCoord3() {
	if (g_soft.lx0 < 0) {
		if ((g_soft.lx1 - g_soft.lx0) > CHKMAX_X)
			return 1;
		if ((g_soft.lx2 - g_soft.lx0) > CHKMAX_X)
			return 1;
	}
	if (g_soft.lx1 < 0) {
		if ((g_soft.lx0 - g_soft.lx1) > CHKMAX_X)
			return 1;
		if ((g_soft.lx2 - g_soft.lx1) > CHKMAX_X)
			return 1;
	}
	if (g_soft.lx2 < 0) {
		if ((g_soft.lx0 - g_soft.lx2) > CHKMAX_X)
			return 1;
		if ((g_soft.lx1 - g_soft.lx2) > CHKMAX_X)
			return 1;
	}
	if (g_soft.ly0 < 0) {
		if ((g_soft.ly1 - g_soft.ly0) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly2 - g_soft.ly0) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly1 < 0) {
		if ((g_soft.ly0 - g_soft.ly1) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly2 - g_soft.ly1) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly2 < 0) {
		if ((g_soft.ly0 - g_soft.ly2) > CHKMAX_Y)
			return 1;
		if ((g_soft.ly1 - g_soft.ly2) > CHKMAX_Y)
			return 1;
	}

	return 0;
}

inline char CheckCoord2() {
	if (g_soft.lx0 < 0) {
		if ((g_soft.lx1 - g_soft.lx0) > CHKMAX_X)
			return 1;
	}
	if (g_soft.lx1 < 0) {
		if ((g_soft.lx0 - g_soft.lx1) > CHKMAX_X)
			return 1;
	}
	if (g_soft.ly0 < 0) {
		if ((g_soft.ly1 - g_soft.ly0) > CHKMAX_Y)
			return 1;
	}
	if (g_soft.ly1 < 0) {
		if ((g_soft.ly0 - g_soft.ly1) > CHKMAX_Y)
			return 1;
	}

	return 0;
}

inline char CheckCoordL(short slx0, short sly0, short slx1, short sly1) {
	if (slx0 < 0) {
		if ((slx1 - slx0) > CHKMAX_X)
			return 1;
	}
	if (slx1 < 0) {
		if ((slx0 - slx1) > CHKMAX_X)
			return 1;
	}
	if (sly0 < 0) {
		if ((sly1 - sly0) > CHKMAX_Y)
			return 1;
	}
	if (sly1 < 0) {
		if ((sly0 - sly1) > CHKMAX_Y)
			return 1;
	}

	return 0;
}

////////////////////////////////////////////////////////////////////////
// mask stuff... used in silent hill
////////////////////////////////////////////////////////////////////////

void cmdSTP(unsigned char * baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);

	g_gpu.status_reg &= ~0x1800; // Clear the necessary bits
	g_gpu.status_reg |= ((gdata & 0x03) << 11); // Set the necessary bits

	if (gdata & 1) {
		g_draw.sSetMask = 0x8000;
		g_draw.lSetMask = 0x80008000;
	} else {
		g_draw.sSetMask = 0;
		g_draw.lSetMask = 0;
	}

	if (gdata & 2)
		g_draw.bCheckMask = 1;
	else
		g_draw.bCheckMask = 0;
}

////////////////////////////////////////////////////////////////////////
// cmd: Set texture page infos
////////////////////////////////////////////////////////////////////////

void cmdTexturePage(unsigned char * baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);

	g_gpu.status_reg &= ~0x000007ff;
	g_gpu.status_reg |= (gdata & 0x07ff);

	g_prim.usMirror = (unsigned short) (gdata & 0x3000);

	UpdateGlobalTP((unsigned short) gdata);
	g_soft.GlobalTextREST = (gdata & 0x00ffffff) >> 9;
}

////////////////////////////////////////////////////////////////////////
// cmd: turn on/off texture window
////////////////////////////////////////////////////////////////////////

void cmdTextureWindow(unsigned char *baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);

	uint32_t YAlign, XAlign;

	g_gpu.lGPUInfoVals[INFO_TW] = gdata & 0xFFFFF;

	if (gdata & 0x020)
		g_prim.TWin.Position.y1 = 8; // xxxx1
	else if (gdata & 0x040)
		g_prim.TWin.Position.y1 = 16; // xxx10
	else if (gdata & 0x080)
		g_prim.TWin.Position.y1 = 32; // xx100
	else if (gdata & 0x100)
		g_prim.TWin.Position.y1 = 64; // x1000
	else if (gdata & 0x200)
		g_prim.TWin.Position.y1 = 128; // 10000
	else
		g_prim.TWin.Position.y1 = 256; // 00000

	// Texture window size is determined by the least bit set of the relevant 5 bits

	if (gdata & 0x001)
		g_prim.TWin.Position.x1 = 8; // xxxx1
	else if (gdata & 0x002)
		g_prim.TWin.Position.x1 = 16; // xxx10
	else if (gdata & 0x004)
		g_prim.TWin.Position.x1 = 32; // xx100
	else if (gdata & 0x008)
		g_prim.TWin.Position.x1 = 64; // x1000
	else if (gdata & 0x010)
		g_prim.TWin.Position.x1 = 128; // 10000
	else
		g_prim.TWin.Position.x1 = 256; // 00000

	// Re-calculate the bit field, because we can't trust what is passed in the data


	YAlign = (uint32_t) (32 - (g_prim.TWin.Position.y1 >> 3));
	XAlign = (uint32_t) (32 - (g_prim.TWin.Position.x1 >> 3));

	// Absolute position of the start of the texture window

	g_prim.TWin.Position.y0 = (short) (((gdata >> 15) & YAlign) << 3);
	g_prim.TWin.Position.x0 = (short) (((gdata >> 10) & XAlign) << 3);

	if ((g_prim.TWin.Position.x0 == 0 && // tw turned off
			g_prim.TWin.Position.y0 == 0 && g_prim.TWin.Position.x1 == 0
			&& g_prim.TWin.Position.y1 == 0) || (g_prim.TWin.Position.x1 == 256
			&& g_prim.TWin.Position.y1 == 256)) {
		g_prim.bUsingTWin = 0; // -> just do it
	} else // otherwise
	{
		g_prim.bUsingTWin = 1; // -> tw turned on
	}
}

////////////////////////////////////////////////////////////////////////
// cmd: start of drawing area... primitives will be clipped inside
////////////////////////////////////////////////////////////////////////


void cmdDrawAreaStart(unsigned char * baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);
	g_prim.drawX = gdata & 0x3ff; // for soft drawing
	g_gpu.lGPUInfoVals[INFO_DRAWSTART] = gdata & 0xFFFFF;
	g_prim.drawY = (gdata >> 10) & 0x3ff;
	if (g_prim.drawY >= 512)
		g_prim.drawY = 511; // some security
}

////////////////////////////////////////////////////////////////////////
// cmd: end of drawing area... primitives will be clipped inside
////////////////////////////////////////////////////////////////////////

void cmdDrawAreaEnd(unsigned char * baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);

	g_prim.drawW = gdata & 0x3ff; // for soft drawing

	g_gpu.lGPUInfoVals[INFO_DRAWEND] = gdata & 0xFFFFF;
	g_prim.drawH = (gdata >> 10) & 0x3ff;
	if (g_prim.drawH >= 512)
		g_prim.drawH = 511; // some security
}

////////////////////////////////////////////////////////////////////////
// cmd: draw offset... will be added to prim coords
////////////////////////////////////////////////////////////////////////

void cmdDrawOffset(unsigned char * baseAddr) {
	uint32_t gdata = GETLE32(&((uint32_t*)baseAddr)[0]);

	g_gpu.dsp.DrawOffset.x = (short) (gdata & 0x7ff);

	g_gpu.lGPUInfoVals[INFO_DRAWOFF] = gdata & 0x3FFFFF;
	g_gpu.dsp.DrawOffset.y = (short) ((gdata >> 11) & 0x7ff);

	g_gpu.dsp.DrawOffset.y = (short) (((int) g_gpu.dsp.DrawOffset.y << 21)
			>> 21);
	g_gpu.dsp.DrawOffset.x = (short) (((int) g_gpu.dsp.DrawOffset.x << 21)
			>> 21);
}

////////////////////////////////////////////////////////////////////////
// cmd: load image to vram
////////////////////////////////////////////////////////////////////////

void primLoadImage(unsigned char * baseAddr) {
	unsigned short *sgpuData = ((unsigned short *) baseAddr);

	g_gpu.VRAMWrite.x = GETLEs16(&sgpuData[2]) & 0x3ff;
	g_gpu.VRAMWrite.y = GETLEs16(&sgpuData[3]) & 0x1ff;
	g_gpu.VRAMWrite.Width = GETLEs16(&sgpuData[4]);
	g_gpu.VRAMWrite.Height = GETLEs16(&sgpuData[5]);

	g_gpu.DataWriteMode = DR_VRAMTRANSFER;

	g_gpu.VRAMWrite.ImagePtr = g_gpu.psx_vram.u16 + (g_gpu.VRAMWrite.y << 10)
			+ g_gpu.VRAMWrite.x;
	g_gpu.VRAMWrite.RowsRemaining = g_gpu.VRAMWrite.Width;
	g_gpu.VRAMWrite.ColsRemaining = g_gpu.VRAMWrite.Height;
}

////////////////////////////////////////////////////////////////////////
// cmd: vram -> psx mem
////////////////////////////////////////////////////////////////////////

void primStoreImage(unsigned char * baseAddr) {
	unsigned short *sgpuData = ((unsigned short *) baseAddr);

	g_gpu.VRAMRead.x = GETLEs16(&sgpuData[2]) & 0x03ff;
	g_gpu.VRAMRead.y = GETLEs16(&sgpuData[3]) & 0x01ff;
	g_gpu.VRAMRead.Width = GETLEs16(&sgpuData[4]);
	g_gpu.VRAMRead.Height = GETLEs16(&sgpuData[5]);

	g_gpu.VRAMRead.ImagePtr = g_gpu.psx_vram.u16 + (g_gpu.VRAMRead.y << 10)
			+ g_gpu.VRAMRead.x;
	g_gpu.VRAMRead.RowsRemaining = g_gpu.VRAMRead.Width;
	g_gpu.VRAMRead.ColsRemaining = g_gpu.VRAMRead.Height;

	g_gpu.DataReadMode = DR_VRAMTRANSFER;

	g_gpu.status_reg |= STATUS_READYFORVRAM;
}

////////////////////////////////////////////////////////////////////////
// cmd: blkfill - NO primitive! Doesn't care about draw areas...
////////////////////////////////////////////////////////////////////////

void primBlkFill(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	short sX = GETLEs16(&sgpuData[2]);
	short sY = GETLEs16(&sgpuData[3]);
	short sW = GETLEs16(&sgpuData[4]) & 0x3ff;
	short sH = GETLEs16(&sgpuData[5]) & 0x3ff;

	sW = (sW + 15) & ~15;

	// Increase H & W if they are one short of full values, because they never can be full values
	if (sH >= 1023)
		sH = 1024;
	if (sW >= 1023)
		sW = 1024;

	// x and y of end pos
	sW += sX;
	sH += sY;

	FillSoftwareArea(sX, sY, sW, sH, BGR24to16(GETLE32(&gpuData[0])));

}

////////////////////////////////////////////////////////////////////////
// cmd: move image vram -> vram
////////////////////////////////////////////////////////////////////////

void primMoveImage(unsigned char * baseAddr) {
	short *sgpuData = ((short *) baseAddr);

	short imageY0, imageX0, imageY1, imageX1, imageSX, imageSY, i, j;

	imageX0 = GETLEs16(&sgpuData[2]) & 0x03ff;
	imageY0 = GETLEs16(&sgpuData[3]) & 0x01ff;
	imageX1 = GETLEs16(&sgpuData[4]) & 0x03ff;
	imageY1 = GETLEs16(&sgpuData[5]) & 0x01ff;
	imageSX = GETLEs16(&sgpuData[6]);
	imageSY = GETLEs16(&sgpuData[7]);

	if ((imageX0 == imageX1) && (imageY0 == imageY1))
		return;
	if (imageSX <= 0)
		return;
	if (imageSY <= 0)
		return;

	if ((imageY0 + imageSY) > 512 || (imageX0 + imageSX) > 1024 || (imageY1
			+ imageSY) > 512 || (imageX1 + imageSX) > 1024) {
		int i, j;
		for (j = 0; j < imageSY; j++)
			for (i = 0; i < imageSX; i++)
				g_gpu.psx_vram.u16[(1024 * ((imageY1 + j) & 0x01ff))
						+ ((imageX1 + i) & 0x3ff)] = g_gpu.psx_vram.u16[(1024
						* ((imageY0 + j) & 0x01ff)) + ((imageX0 + i) & 0x3ff)];

		return;
	}

	if (imageSX & 1) // not dword aligned? slower func
	{
		unsigned short *SRCPtr, *DSTPtr;
		unsigned short LineOffset;

		SRCPtr = g_gpu.psx_vram.u16 + (1024 * imageY0) + imageX0;
		DSTPtr = g_gpu.psx_vram.u16 + (1024 * imageY1) + imageX1;

		LineOffset = 1024 - imageSX;

		for (j = 0; j < imageSY; j++) {
			for (i = 0; i < imageSX; i++)
				*DSTPtr++ = *SRCPtr++;
			SRCPtr += LineOffset;
			DSTPtr += LineOffset;
		}
	} else // dword aligned
	{
		uint32_t *SRCPtr, *DSTPtr;
		unsigned short LineOffset;
		int dx = imageSX >> 1;

		SRCPtr = (uint32_t *) (g_gpu.psx_vram.u16 + (1024 * imageY0) + imageX0);
		DSTPtr = (uint32_t *) (g_gpu.psx_vram.u16 + (1024 * imageY1) + imageX1);

		LineOffset = 512 - dx;

		for (j = 0; j < imageSY; j++) {
			for (i = 0; i < dx; i++)
				*DSTPtr++ = *SRCPtr++;
			SRCPtr += LineOffset;
			DSTPtr += LineOffset;
		}
	}

	imageSX += imageX1;
	imageSY += imageY1;

}

////////////////////////////////////////////////////////////////////////
// cmd: draw free-size Tile 
////////////////////////////////////////////////////////////////////////
void primTileS(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t*) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	short sW = GETLEs16(&sgpuData[4]) & 0x3ff;
	short sH = GETLEs16(&sgpuData[5]) & 0x01ff; // mmm... limit tiles to 0x1ff or height?

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	// x and y of start
	g_soft.ly2 = g_soft.ly3 = g_soft.ly0 + sH + g_gpu.dsp.DrawOffset.y;
	g_soft.ly0 = g_soft.ly1 = g_soft.ly0 + g_gpu.dsp.DrawOffset.y;
	g_soft.lx1 = g_soft.lx2 = g_soft.lx0 + sW + g_gpu.dsp.DrawOffset.x;
	g_soft.lx0 = g_soft.lx3 = g_soft.lx0 + g_gpu.dsp.DrawOffset.x;

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	if (!(0 && sH == 32 && GETLE32(&gpuData[0]) == 0x60ffffff)) // special cheat for certain ZiNc games
		FillSoftwareAreaTrans(g_soft.lx0, g_soft.ly0, g_soft.lx2, g_soft.ly2,
				BGR24to16(GETLE32(&gpuData[0])));

}

////////////////////////////////////////////////////////////////////////
// cmd: draw 1 dot Tile (point)
////////////////////////////////////////////////////////////////////////

void primTile1(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t*) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	short sH = 1;
	short sW = 1;

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	// x and y of start
	g_soft.ly2 = g_soft.ly3 = g_soft.ly0 + sH + g_gpu.dsp.DrawOffset.y;
	g_soft.ly0 = g_soft.ly1 = g_soft.ly0 + g_gpu.dsp.DrawOffset.y;
	g_soft.lx1 = g_soft.lx2 = g_soft.lx0 + sW + g_gpu.dsp.DrawOffset.x;
	g_soft.lx0 = g_soft.lx3 = g_soft.lx0 + g_gpu.dsp.DrawOffset.x;

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	FillSoftwareAreaTrans(g_soft.lx0, g_soft.ly0, g_soft.lx2, g_soft.ly2,
			BGR24to16(GETLE32(&gpuData[0]))); // Takes Start and Offset

}

////////////////////////////////////////////////////////////////////////
// cmd: draw 8 dot Tile (small rect)
////////////////////////////////////////////////////////////////////////

void primTile8(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t*) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	short sH = 8;
	short sW = 8;

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	// x and y of start
	g_soft.ly2 = g_soft.ly3 = g_soft.ly0 + sH + g_gpu.dsp.DrawOffset.y;
	g_soft.ly0 = g_soft.ly1 = g_soft.ly0 + g_gpu.dsp.DrawOffset.y;
	g_soft.lx1 = g_soft.lx2 = g_soft.lx0 + sW + g_gpu.dsp.DrawOffset.x;
	g_soft.lx0 = g_soft.lx3 = g_soft.lx0 + g_gpu.dsp.DrawOffset.x;

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	FillSoftwareAreaTrans(g_soft.lx0, g_soft.ly0, g_soft.lx2, g_soft.ly2,
			BGR24to16(GETLE32(&gpuData[0]))); // Takes Start and Offset

}

////////////////////////////////////////////////////////////////////////
// cmd: draw 16 dot Tile (medium rect)
////////////////////////////////////////////////////////////////////////

void primTile16(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t*) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	short sH = 16;
	short sW = 16;

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	// x and y of start
	g_soft.ly2 = g_soft.ly3 = g_soft.ly0 + sH + g_gpu.dsp.DrawOffset.y;
	g_soft.ly0 = g_soft.ly1 = g_soft.ly0 + g_gpu.dsp.DrawOffset.y;
	g_soft.lx1 = g_soft.lx2 = g_soft.lx0 + sW + g_gpu.dsp.DrawOffset.x;
	g_soft.lx0 = g_soft.lx3 = g_soft.lx0 + g_gpu.dsp.DrawOffset.x;

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	FillSoftwareAreaTrans(g_soft.lx0, g_soft.ly0, g_soft.lx2, g_soft.ly2,
			BGR24to16(GETLE32(&gpuData[0]))); // Takes Start and Offset

}

////////////////////////////////////////////////////////////////////////
// cmd: small sprite (textured rect)
////////////////////////////////////////////////////////////////////////

void primSprt8(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	SetRenderMode(GETLE32(&gpuData[0]));

	if (g_prim.bUsingTWin)
		DrawSoftwareSpriteTWin(baseAddr, 8, 8);
	else if (g_prim.usMirror)
		DrawSoftwareSpriteMirror(baseAddr, 8, 8);
	else
		DrawSoftwareSprite(baseAddr, 8, 8, baseAddr[8], baseAddr[9]);

}

////////////////////////////////////////////////////////////////////////
// cmd: medium sprite (textured rect)
////////////////////////////////////////////////////////////////////////

void primSprt16(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	SetRenderMode(GETLE32(&gpuData[0]));

	if (g_prim.bUsingTWin)
		DrawSoftwareSpriteTWin(baseAddr, 16, 16);
	else if (g_prim.usMirror)
		DrawSoftwareSpriteMirror(baseAddr, 16, 16);
	else
		DrawSoftwareSprite(baseAddr, 16, 16, baseAddr[8], baseAddr[9]);

}

////////////////////////////////////////////////////////////////////////
// cmd: free-size sprite (textured rect)
////////////////////////////////////////////////////////////////////////

// func used on texture coord wrap
void primSprtSRest(unsigned char * baseAddr, unsigned short type) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	unsigned short sTypeRest = 0;

	short s;
	short sX = GETLEs16(&sgpuData[2]);
	short sY = GETLEs16(&sgpuData[3]);
	short sW = GETLEs16(&sgpuData[6]) & 0x3ff;
	short sH = GETLEs16(&sgpuData[7]) & 0x1ff;
	short tX = baseAddr[8];
	short tY = baseAddr[9];

	switch (type) {
	case 1:
		s = 256 - baseAddr[8];
		sW -= s;
		sX += s;
		tX = 0;
		break;
	case 2:
		s = 256 - baseAddr[9];
		sH -= s;
		sY += s;
		tY = 0;
		break;
	case 3:
		s = 256 - baseAddr[8];
		sW -= s;
		sX += s;
		tX = 0;
		s = 256 - baseAddr[9];
		sH -= s;
		sY += s;
		tY = 0;
		break;
	case 4:
		s = 512 - baseAddr[8];
		sW -= s;
		sX += s;
		tX = 0;
		break;
	case 5:
		s = 512 - baseAddr[9];
		sH -= s;
		sY += s;
		tY = 0;
		break;
	case 6:
		s = 512 - baseAddr[8];
		sW -= s;
		sX += s;
		tX = 0;
		s = 512 - baseAddr[9];
		sH -= s;
		sY += s;
		tY = 0;
		break;
	}

	SetRenderMode(GETLE32(&gpuData[0]));

	if (tX + sW > 256) {
		sW = 256 - tX;
		sTypeRest += 1;
	}
	if (tY + sH > 256) {
		sH = 256 - tY;
		sTypeRest += 2;
	}

	g_soft.lx0 = sX;
	g_soft.ly0 = sY;

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	DrawSoftwareSprite(baseAddr, sW, sH, tX, tY);

	if (sTypeRest && type < 4) {
		if (sTypeRest & 1 && type == 1)
			primSprtSRest(baseAddr, 4);
		if (sTypeRest & 2 && type == 2)
			primSprtSRest(baseAddr, 5);
		if (sTypeRest == 3 && type == 3)
			primSprtSRest(baseAddr, 6);
	}

}

////////////////////////////////////////////////////////////////////////

void primSprtS(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);
	short sW, sH;

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);

	if (!(g_prim.dwActFixes & 8))
		AdjustCoord1();

	sW = GETLEs16(&sgpuData[6]) & 0x3ff;
	sH = GETLEs16(&sgpuData[7]) & 0x1ff;

	SetRenderMode(GETLE32(&gpuData[0]));

	if (g_prim.bUsingTWin)
		DrawSoftwareSpriteTWin(baseAddr, sW, sH);
	else if (g_prim.usMirror)
		DrawSoftwareSpriteMirror(baseAddr, sW, sH);
	else {
		unsigned short sTypeRest = 0;
		short tX = baseAddr[8];
		short tY = baseAddr[9];

		if (tX + sW > 256) {
			sW = 256 - tX;
			sTypeRest += 1;
		}
		if (tY + sH > 256) {
			sH = 256 - tY;
			sTypeRest += 2;
		}

		DrawSoftwareSprite(baseAddr, sW, sH, tX, tY);

		if (sTypeRest) {
			if (sTypeRest & 1)
				primSprtSRest(baseAddr, 1);
			if (sTypeRest & 2)
				primSprtSRest(baseAddr, 2);
			if (sTypeRest == 3)
				primSprtSRest(baseAddr, 3);
		}

	}

}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Poly4
////////////////////////////////////////////////////////////////////////

void primPolyF4(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[4]);
	g_soft.ly1 = GETLEs16(&sgpuData[5]);
	g_soft.lx2 = GETLEs16(&sgpuData[6]);
	g_soft.ly2 = GETLEs16(&sgpuData[7]);
	g_soft.lx3 = GETLEs16(&sgpuData[8]);
	g_soft.ly3 = GETLEs16(&sgpuData[9]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord4();
		if (CheckCoord4())
			return;
	}

	offsetPSX4();
	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	drawPoly4F(GETLE32(&gpuData[0]));

}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly4
////////////////////////////////////////////////////////////////////////

void primPolyG4(unsigned char * baseAddr) {
	uint32_t *gpuData = (uint32_t *) baseAddr;
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[6]);
	g_soft.ly1 = GETLEs16(&sgpuData[7]);
	g_soft.lx2 = GETLEs16(&sgpuData[10]);
	g_soft.ly2 = GETLEs16(&sgpuData[11]);
	g_soft.lx3 = GETLEs16(&sgpuData[14]);
	g_soft.ly3 = GETLEs16(&sgpuData[15]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord4();
		if (CheckCoord4())
			return;
	}

	offsetPSX4();
	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	drawPoly4G(GETLE32(&gpuData[0]), GETLE32(&gpuData[2]),
			GETLE32(&gpuData[4]), GETLE32(&gpuData[6]));

}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Texture3
////////////////////////////////////////////////////////////////////////

void primPolyFT3(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[6]);
	g_soft.ly1 = GETLEs16(&sgpuData[7]);
	g_soft.lx2 = GETLEs16(&sgpuData[10]);
	g_soft.ly2 = GETLEs16(&sgpuData[11]);

	g_draw.lLowerpart = GETLE32(&gpuData[4]) >> 16;
	UpdateGlobalTP((unsigned short) g_draw.lLowerpart);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord3();
		if (CheckCoord3())
			return;
	}

	offsetPSX3();
	SetRenderMode(GETLE32(&gpuData[0]));

	drawPoly3FT(baseAddr);

}

////////////////////////////////////////////////////////////////////////
// cmd: flat shaded Texture4
////////////////////////////////////////////////////////////////////////

void primPolyFT4(unsigned char * baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[6]);
	g_soft.ly1 = GETLEs16(&sgpuData[7]);
	g_soft.lx2 = GETLEs16(&sgpuData[10]);
	g_soft.ly2 = GETLEs16(&sgpuData[11]);
	g_soft.lx3 = GETLEs16(&sgpuData[14]);
	g_soft.ly3 = GETLEs16(&sgpuData[15]);

	g_draw.lLowerpart = GETLE32(&gpuData[4]) >> 16;
	UpdateGlobalTP((unsigned short) g_draw.lLowerpart);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord4();
		if (CheckCoord4())
			return;
	}

	offsetPSX4();

	SetRenderMode(GETLE32(&gpuData[0]));

	drawPoly4FT(baseAddr);

}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Texture3
////////////////////////////////////////////////////////////////////////

void primPolyGT3(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[8]);
	g_soft.ly1 = GETLEs16(&sgpuData[9]);
	g_soft.lx2 = GETLEs16(&sgpuData[14]);
	g_soft.ly2 = GETLEs16(&sgpuData[15]);

	g_draw.lLowerpart = GETLE32(&gpuData[5]) >> 16;
	UpdateGlobalTP((unsigned short) g_draw.lLowerpart);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord3();
		if (CheckCoord3())
			return;
	}

	offsetPSX3();
	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	if (SHADETEXBIT(GETLE32(&gpuData[0]))) {
		gpuData[0] = (gpuData[0] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
		gpuData[3] = (gpuData[3] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
		gpuData[6] = (gpuData[6] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
	}

	drawPoly3GT(baseAddr);

}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly3
////////////////////////////////////////////////////////////////////////

void primPolyG3(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[6]);
	g_soft.ly1 = GETLEs16(&sgpuData[7]);
	g_soft.lx2 = GETLEs16(&sgpuData[10]);
	g_soft.ly2 = GETLEs16(&sgpuData[11]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord3();
		if (CheckCoord3())
			return;
	}

	offsetPSX3();
	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	drawPoly3G(GETLE32(&gpuData[0]), GETLE32(&gpuData[2]), GETLE32(&gpuData[4]));

}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Texture4
////////////////////////////////////////////////////////////////////////

void primPolyGT4(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[8]);
	g_soft.ly1 = GETLEs16(&sgpuData[9]);
	g_soft.lx2 = GETLEs16(&sgpuData[14]);
	g_soft.ly2 = GETLEs16(&sgpuData[15]);
	g_soft.lx3 = GETLEs16(&sgpuData[20]);
	g_soft.ly3 = GETLEs16(&sgpuData[21]);

	g_draw.lLowerpart = GETLE32(&gpuData[5]) >> 16;
	UpdateGlobalTP((unsigned short) g_draw.lLowerpart);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord4();
		if (CheckCoord4())
			return;
	}

	offsetPSX4();
	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	if (SHADETEXBIT(GETLE32(&gpuData[0]))) {
		gpuData[0] = (gpuData[0] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
		gpuData[3] = (gpuData[3] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
		gpuData[6] = (gpuData[6] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
		gpuData[9] = (gpuData[9] & HOST2LE32(0xff000000))
				|HOST2LE32(0x00808080);
	}

	drawPoly4GT(baseAddr);

}

////////////////////////////////////////////////////////////////////////
// cmd: smooth shaded Poly3
////////////////////////////////////////////////////////////////////////

void primPolyF3(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[4]);
	g_soft.ly1 = GETLEs16(&sgpuData[5]);
	g_soft.lx2 = GETLEs16(&sgpuData[6]);
	g_soft.ly2 = GETLEs16(&sgpuData[7]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord3();
		if (CheckCoord3())
			return;
	}

	offsetPSX3();
	SetRenderMode(GETLE32(&gpuData[0]));

	drawPoly3F(GETLE32(&gpuData[0]));

}

////////////////////////////////////////////////////////////////////////
// cmd: skipping shaded polylines
////////////////////////////////////////////////////////////////////////

void primLineGSkip(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	int iMax = 255;
	int i = 2;

	g_soft.ly1 = (short) ((GETLE32(&gpuData[1]) >> 16) & 0xffff);
	g_soft.lx1 = (short) (GETLE32(&gpuData[1]) & 0xffff);

	while (!(((GETLE32(&gpuData[i]) & 0xF000F000) == 0x50005000) && i >= 4)) {
		i++;
		g_soft.ly1 = (short) ((GETLE32(&gpuData[i]) >> 16) & 0xffff);
		g_soft.lx1 = (short) (GETLE32(&gpuData[i]) & 0xffff);
		i++;
		if (i > iMax)
			break;
	}
}

////////////////////////////////////////////////////////////////////////
// cmd: shaded polylines
////////////////////////////////////////////////////////////////////////

void primLineGEx(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	int iMax = 255;
	uint32_t lc0, lc1;
	short slx0, slx1, sly0, sly1;
	int i = 2;
	char bDraw = 1;

	sly1 = (short) ((GETLE32(&gpuData[1]) >> 16) & 0xffff);
	slx1 = (short) (GETLE32(&gpuData[1]) & 0xffff);

	if (!(g_prim.dwActFixes & 8)) {
		slx1 = (short) (((int) slx1 << SIGNSHIFT) >> SIGNSHIFT);
		sly1 = (short) (((int) sly1 << SIGNSHIFT) >> SIGNSHIFT);
	}

	lc1 = gpuData[0] & 0xffffff;

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;

	while (!(((GETLE32(&gpuData[i]) & 0xF000F000) == 0x50005000) && i >= 4)) {
		sly0 = sly1;
		slx0 = slx1;
		lc0 = lc1;
		lc1 = GETLE32(&gpuData[i]) & 0xffffff;

		i++;

		// no check needed on gshaded polyline positions
		// if((gpuData[i] & 0xF000F000) == 0x50005000) break;

		sly1 = (short) ((GETLE32(&gpuData[i]) >> 16) & 0xffff);
		slx1 = (short) (GETLE32(&gpuData[i]) & 0xffff);

		if (!(g_prim.dwActFixes & 8)) {
			slx1 = (short) (((int) slx1 << SIGNSHIFT) >> SIGNSHIFT);
			sly1 = (short) (((int) sly1 << SIGNSHIFT) >> SIGNSHIFT);
			if (CheckCoordL(slx0, sly0, slx1, sly1))
				bDraw = 0;
			else
				bDraw = 1;
		}

		if ((g_soft.lx0 != g_soft.lx1) || (g_soft.ly0 != g_soft.ly1)) {
			g_soft.ly0 = sly0;
			g_soft.lx0 = slx0;
			g_soft.ly1 = sly1;
			g_soft.lx1 = slx1;

			offsetPSX2();
			if (bDraw)
				DrawSoftwareLineShade(lc0, lc1);
		}
		i++;
		if (i > iMax)
			break;
	}

}

////////////////////////////////////////////////////////////////////////
// cmd: shaded polyline2
////////////////////////////////////////////////////////////////////////

void primLineG2(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[6]);
	g_soft.ly1 = GETLEs16(&sgpuData[7]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord2();
		if (CheckCoord2())
			return;
	}

	if ((g_soft.lx0 == g_soft.lx1) && (g_soft.ly0 == g_soft.ly1)) {
		g_soft.lx1++;
		g_soft.ly1++;
	}

	g_soft.DrawSemiTrans = (SEMITRANSBIT(GETLE32(&gpuData[0]))) ? 1 : 0;
	offsetPSX2();
	DrawSoftwareLineShade(GETLE32(&gpuData[0]), GETLE32(&gpuData[2]));

}

////////////////////////////////////////////////////////////////////////
// cmd: skipping flat polylines
////////////////////////////////////////////////////////////////////////

void primLineFSkip(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	int i = 2, iMax = 255;

	g_soft.ly1 = (short) ((GETLE32(&gpuData[1]) >> 16) & 0xffff);
	g_soft.lx1 = (short) (GETLE32(&gpuData[1]) & 0xffff);

	while (!(((GETLE32(&gpuData[i]) & 0xF000F000) == 0x50005000) && i >= 3)) {
		g_soft.ly1 = (short) ((GETLE32(&gpuData[i]) >> 16) & 0xffff);
		g_soft.lx1 = (short) (GETLE32(&gpuData[i]) & 0xffff);
		i++;
		if (i > iMax)
			break;
	}
}

////////////////////////////////////////////////////////////////////////
// cmd: drawing flat polylines
////////////////////////////////////////////////////////////////////////

void primLineFEx(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	int iMax;
	short slx0, slx1, sly0, sly1;
	int i = 2;
	char bDraw = 1;

	iMax = 255;

	sly1 = (short) ((GETLE32(&gpuData[1]) >> 16) & 0xffff);
	slx1 = (short) (GETLE32(&gpuData[1]) & 0xffff);
	if (!(g_prim.dwActFixes & 8)) {
		slx1 = (short) (((int) slx1 << SIGNSHIFT) >> SIGNSHIFT);
		sly1 = (short) (((int) sly1 << SIGNSHIFT) >> SIGNSHIFT);
	}

	SetRenderMode(GETLE32(&gpuData[0]));

	while (!(((GETLE32(&gpuData[i]) & 0xF000F000) == 0x50005000) && i >= 3)) {
		sly0 = sly1;
		slx0 = slx1;
		sly1 = (short) ((GETLE32(&gpuData[i]) >> 16) & 0xffff);
		slx1 = (short) (GETLE32(&gpuData[i]) & 0xffff);
		if (!(g_prim.dwActFixes & 8)) {
			slx1 = (short) (((int) slx1 << SIGNSHIFT) >> SIGNSHIFT);
			sly1 = (short) (((int) sly1 << SIGNSHIFT) >> SIGNSHIFT);

			if (CheckCoordL(slx0, sly0, slx1, sly1))
				bDraw = 0;
			else
				bDraw = 1;
		}

		g_soft.ly0 = sly0;
		g_soft.lx0 = slx0;
		g_soft.ly1 = sly1;
		g_soft.lx1 = slx1;

		offsetPSX2();
		if (bDraw)
			DrawSoftwareLineFlat(GETLE32(&gpuData[0]));

		i++;
		if (i > iMax)
			break;
	}

}

////////////////////////////////////////////////////////////////////////
// cmd: drawing flat polyline2
////////////////////////////////////////////////////////////////////////

void primLineF2(unsigned char *baseAddr) {
	uint32_t *gpuData = ((uint32_t *) baseAddr);
	short *sgpuData = ((short *) baseAddr);

	g_soft.lx0 = GETLEs16(&sgpuData[2]);
	g_soft.ly0 = GETLEs16(&sgpuData[3]);
	g_soft.lx1 = GETLEs16(&sgpuData[4]);
	g_soft.ly1 = GETLEs16(&sgpuData[5]);

	if (!(g_prim.dwActFixes & 8)) {
		AdjustCoord2();
		if (CheckCoord2())
			return;
	}

	if ((g_soft.lx0 == g_soft.lx1) && (g_soft.ly0 == g_soft.ly1)) {
		g_soft.lx1++;
		g_soft.ly1++;
	}

	offsetPSX2();
	SetRenderMode(GETLE32(&gpuData[0]));

	DrawSoftwareLineFlat(GETLE32(&gpuData[0]));

}

////////////////////////////////////////////////////////////////////////
// cmd: well, easiest command... not implemented
////////////////////////////////////////////////////////////////////////

void primNI(unsigned char *bA) {
	fprintf(stderr, "Unknow GPU func \n");
}

////////////////////////////////////////////////////////////////////////
// cmd func ptr table
////////////////////////////////////////////////////////////////////////


void (*primTableJ[256])(unsigned char *) = {
		/* 00 */
		primNI,
		primNI,
		primBlkFill,
		primNI,
		/* 04 */
		primNI,
		primNI,
		primNI,
		primNI,
		/* 08 */
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		// 10
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		// 18
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		primNI,
		// 20
		primPolyF3,
		primPolyF3,
		primPolyF3,
		primPolyF3,
		primPolyFT3,
		primPolyFT3,
		primPolyFT3,
		primPolyFT3,
		// 28
		primPolyF4,
		primPolyF4,
		primPolyF4,
		primPolyF4,
		primPolyFT4,
		primPolyFT4,
		primPolyFT4,
		primPolyFT4,
		// 30
		primPolyG3,
		primPolyG3,
		primPolyG3,
		primPolyG3,
		primPolyGT3,
		primPolyGT3,
		primPolyGT3,
		primPolyGT3,
		// 38
		primPolyG4,
		primPolyG4,
		primPolyG4,
		primPolyG4,
		primPolyGT4,
		primPolyGT4,
		primPolyGT4,
		primPolyGT4,
		// 40
		primLineF2,
		primLineF2,
		primLineF2,
		primLineF2,
		primNI,
		primNI,
		primNI,
		primNI,
		// 48
		primLineFEx,
		primLineFEx,
		primLineFEx,
		primLineFEx,
		primLineFEx,
		primLineFEx,
		primLineFEx,
		primLineFEx,
		// 50
		primLineG2, primLineG2,
		primLineG2,
		primLineG2,
		primNI,
		primNI,
		primNI,
		primNI,
		// 58
		primLineGEx, primLineGEx, primLineGEx,
		primLineGEx,
		primLineGEx,
		primLineGEx,
		primLineGEx,
		primLineGEx,
		// 60
		primTileS, primTileS, primTileS, primTileS,
		primSprtS,
		primSprtS,
		primSprtS,
		primSprtS,
		// 68
		primTile1, primTile1, primTile1, primTile1, primNI,
		primNI,
		primNI,
		primNI,
		// 70
		primTile8, primTile8, primTile8, primTile8, primSprt8, primSprt8,
		primSprt8,
		primSprt8,
		// 78
		primTile16, primTile16, primTile16, primTile16, primSprt16, primSprt16,
		primSprt16, primSprt16,
		// 80
		primMoveImage, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// 88
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// 90
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// 98
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// a0
		primLoadImage, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// a8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// b0
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// b8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// c0
		primStoreImage, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// c8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// d0
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// d8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// e0
		primNI, cmdTexturePage, cmdTextureWindow, cmdDrawAreaStart,
		cmdDrawAreaEnd, cmdDrawOffset, cmdSTP, primNI,
		// e8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// f0
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI,
		// f8
		primNI, primNI, primNI, primNI, primNI, primNI, primNI, primNI };

