/*
 * Copyright (C) 2010 Benoit Gschwind
 * Inspired by original author : Pete Bernert
 *
 * 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 3 of the
 * License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
 * 02111-1307, USA.
 */

#include "config.h"

#include <stdlib.h>
#include <stdio.h>
#include <string.h>

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

#define CALLBACK

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

#define SHADETEXBIT(x) ((x>>24) & 0x1)
#define SEMITRANSBIT(x) ((x>>25) & 0x1)
#define PSXRGB(r,g,b) ((g<<10)|(b<<5)|r)

#define DATAREGISTERMODES unsigned short

#define DR_NORMAL        0
#define DR_VRAMTRANSFER  1

#define MIN(a,b) ((a) > (b) ? (b) : (a))
#define MAX(a,b) ((a) < (b) ? (b) : (a))

#ifdef ENABLE_NLS
#include <libintl.h>
#include <locale.h>
#define _(x)  gettext(x)
#define N_(x) (x)
#else
#define _(x)  (x)
#define N_(x) (x)
#endif

////////////////////////////////////////////////////////////////////////
// PPDK developer must change libraryName field and can change revision and build
////////////////////////////////////////////////////////////////////////

unsigned char const version = 1; // do not touch - library for PSEmu 1.x
unsigned char const revision = 0;
unsigned char const build = 0; // increase that with each version

static char const * libraryName = N_("GXVideo Driver");
static char const * libraryInfo = N_("GXvideo Driver v1.00");
//static char const * PluginAuthor =
//		N_("gschwind (rewrite from Pete Bernert and the P.E.Op.S. team)");

////////////////////////////////////////////////////////////////////////
// some misc external display funcs
////////////////////////////////////////////////////////////////////////

#include <time.h>

//void CALLBACK GPUdisplayText(char * pText) {
//
//}

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

//void CALLBACK GPUdisplayFlags(unsigned long dwFlags) // some info func
//{
//dwCoreFlags = dwFlags;
//BuildDispMenu(0);
//}

////////////////////////////////////////////////////////////////////////
// stuff to make this a true PDK module
////////////////////////////////////////////////////////////////////////

char * CALLBACK PSEgetLibName(void) {
	return _(libraryName);
}

unsigned long CALLBACK PSEgetLibType(void) {
	//return PSE_LT_GPU;
	return 2; /* GPU plugin */
}

unsigned long CALLBACK PSEgetLibVersion(void) {
	return version << 16 | revision << 8 | build;
}

char * GPUgetLibInfos(void) {
	return _(libraryInfo);
}

////////////////////////////////////////////////////////////////////////
// Snapshot func
////////////////////////////////////////////////////////////////////////

//char * pGetConfigInfos(int iCfg) {
//	char szO[2][4] = { "off", "on " };
//	char szTxt[256];
//	char * pB = (char *) malloc(32767);
//
//	if (!pB)
//		return NULL;
//	*pB = 0;
//	//----------------------------------------------------//
//	sprintf(szTxt, "Plugin: %s %d.%d.%d\r\n", libraryName, version, revision,
//			build);
//	strcat(pB, szTxt);
//	sprintf(szTxt, "Author: %s\r\n\r\n", PluginAuthor);
//	strcat(pB, szTxt);
//	//----------------------------------------------------//
//
//	sprintf(szTxt, "Resolution/Color:\r\n- %dx%d ", g_cfg.ResX, g_cfg.ResY);
//
//	strcat(pB, szTxt);
//	if (iWindowMode && iCfg)
//		strcpy(szTxt, "Window mode\r\n");
//	else if (iWindowMode)
//		sprintf(szTxt, "Window mode - [%d Bit]\r\n", iDesktopCol);
//	else
//		sprintf(szTxt, "Fullscreen - [%d Bit]\r\n", iColDepth);
//	strcat(pB, szTxt);
//
//	sprintf(szTxt, "Stretch mode: %d\r\n", iUseNoStretchBlt);
//	strcat(pB, szTxt);
//	sprintf(szTxt, "Dither mode: %d\r\n\r\n", iUseDither);
//	strcat(pB, szTxt);
//	//----------------------------------------------------//
//	sprintf(szTxt, "Framerate:\r\n- FPS limit: %s\r\n", szO[UseFrameLimit]);
//	strcat(pB, szTxt);
//	sprintf(szTxt, "- Frame skipping: %s", szO[UseFrameSkip]);
//	strcat(pB, szTxt);
//	if (iFastFwd)
//		strcat(pB, " (fast forward)");
//	strcat(pB, "\r\n");
//	if (iFrameLimit == 2)
//		strcpy(szTxt, "- FPS limit: Auto\r\n\r\n");
//	else
//		sprintf(szTxt, "- FPS limit: %.1f\r\n\r\n", fFrameRate);
//	strcat(pB, szTxt);
//	//----------------------------------------------------//
//#if !defined (_MACGL) && !defined (_WINDOWS)
//	strcpy(szTxt, "Misc:\r\n- MaintainAspect: ");
//	if (iMaintainAspect == 0)
//		strcat(szTxt, "disabled");
//	else if (iMaintainAspect == 1)
//		strcat(szTxt, "enabled");
//	strcat(szTxt, "\r\n");
//	strcat(pB, szTxt);
//#endif
//	sprintf(szTxt, "- Game fixes: %s [%08x]\r\n", szO[iUseFixes], dwCfgFixes);
//	strcat(pB, szTxt);
//	//----------------------------------------------------//
//	return pB;
//	return 0;
//}

void CALLBACK GPUmakeSnapshot(void) {
	//	FILE *bmpfile;
	//	char filename[256];
	//	unsigned char header[0x36];
	//	long size, height;
	//	unsigned char line[1024 * 3];
	//	short i, j;
	//	unsigned char empty[2] = { 0, 0 };
	//	unsigned short color;
	//	unsigned long snapshotnr = 0;
	//	unsigned char *pD;
	//
	//	height = gpu_ctx.PreviousPSXDisplay.DisplayMode.y;
	//
	//	size = height * PreviousPSXDisplay.Range.x1 * 3 + 0x38;
	//
	//	// fill in proper values for BMP
	//
	//	// hardcoded BMP header
	//	memset(header, 0, 0x36);
	//	header[0] = 'B';
	//	header[1] = 'M';
	//	header[2] = size & 0xff;
	//	header[3] = (size >> 8) & 0xff;
	//	header[4] = (size >> 16) & 0xff;
	//	header[5] = (size >> 24) & 0xff;
	//	header[0x0a] = 0x36;
	//	header[0x0e] = 0x28;
	//	header[0x12] = gpu_ctx.PreviousPSXDisplay.Range.x1 % 256;
	//	header[0x13] = gpu_ctx.PreviousPSXDisplay.Range.x1 / 256;
	//	header[0x16] = height % 256;
	//	header[0x17] = height / 256;
	//	header[0x1a] = 0x01;
	//	header[0x1c] = 0x18;
	//	header[0x26] = 0x12;
	//	header[0x27] = 0x0B;
	//	header[0x2A] = 0x12;
	//	header[0x2B] = 0x0B;
	//
	//	// increment snapshot value & try to get filename
	//	do {
	//		snapshotnr++;
	//#ifdef _WINDOWS
	//		sprintf(filename,"snap\\pcsxr%04ld.bmp",snapshotnr);
	//#else
	//		sprintf(filename, "%s/pcsxr%04ld.bmp", getenv("HOME"), snapshotnr);
	//#endif
	//
	//		bmpfile = fopen(filename, "rb");
	//		if (bmpfile == NULL)
	//			break;
	//
	//		fclose(bmpfile);
	//	} while (TRUE);
	//
	//	// try opening new snapshot file
	//	if ((bmpfile = fopen(filename, "wb")) == NULL)
	//		return;
	//
	//	fwrite(header, 0x36, 1, bmpfile);
	//	for (i = height + PSXDisplay.DisplayPosition.y - 1; i
	//			>= PSXDisplay.DisplayPosition.y; i--) {
	//		pD = (unsigned char *) &psxVuw[i * 1024 + PSXDisplay.DisplayPosition.x];
	//		for (j = 0; j < PreviousPSXDisplay.Range.x1; j++) {
	//			if (PSXDisplay.RGB24) {
	//				uint32_t lu = *(uint32_t *) pD;
	//				line[j * 3 + 2] = (unsigned char) RED(lu);
	//				line[j * 3 + 1] = (unsigned char) GREEN(lu);
	//				line[j * 3 + 0] = (unsigned char) BLUE(lu);
	//				pD += 3;
	//			} else {
	//				color = GETLE16(pD);
	//				line[j * 3 + 2] = (color << 3) & 0xf1;
	//				line[j * 3 + 1] = (color >> 2) & 0xf1;
	//				line[j * 3 + 0] = (color >> 7) & 0xf1;
	//				pD += 2;
	//			}
	//		}
	//		fwrite(line, PreviousPSXDisplay.Range.x1 * 3, 1, bmpfile);
	//	}
	//	fwrite(empty, 0x2, 1, bmpfile);
	//	fclose(bmpfile);
	//
	//	DoTextSnapShot(snapshotnr);
}

////////////////////////////////////////////////////////////////////////
// INIT, will be called after lib load... well, just do some var init...
////////////////////////////////////////////////////////////////////////

long CALLBACK GPUinit() {
	fprintf(stderr, "%s\n", __FUNCTION__);
	memset(g_gpu.ulStatusControl, 0, 256 * sizeof(uint32_t)); // init save state scontrol field
	g_gpu.psxVSecure.u8 = (uint8_t *) malloc((512 * 2) * 1024 + (1024 * 1024)); // always alloc one extra MB for soft drawing funcs security
	if (!g_gpu.psxVSecure.u8)
		return -1;

	//!!! ATTENTION !!!
	g_gpu.psx_vram.u8 = g_gpu.psxVSecure.u8 + 512 * 1024; // security offset into double sized psx vram!

	g_gpu.psxVuw_eom.u16 = g_gpu.psx_vram.u16 + 1024 * 512; // pre-calc of end of vram

	memset(g_gpu.psxVSecure.s8, 0x00, (512 * 2) * 1024 + (1024 * 1024));
	memset(g_gpu.lGPUInfoVals, 0x00, 16 * sizeof(uint32_t));

	g_gpu.dsp.range.x0 = 0;
	g_gpu.dsp.range.x1 = 0;
	g_gpu.lGPUdataRet = 0x400;

	g_gpu.DataWriteMode = DR_NORMAL;

	// Reset transfer values, to prevent mis-transfer of data
	memset(&g_gpu.VRAMWrite, 0, sizeof(gxv_vram_load_t));
	memset(&g_gpu.VRAMRead, 0, sizeof(gxv_vram_load_t));

	// device initialised already !
	g_gpu.status_reg = 0x14802000;
	return 0;
}

/* Here starts all... */
long GPUopen(unsigned long * disp, char * CapText, char * CfgFile) {
	unsigned long d;

	ReadConfig();

	g_draw.bIsFirstFrame = 1;

	d = init_display(); // setup x

	if (disp)
		*disp = d; // wanna x pointer? ok

	if (d)
		return 0;
	return -1;
}

////////////////////////////////////////////////////////////////////////
// time to leave...
////////////////////////////////////////////////////////////////////////

long CALLBACK GPUclose() {
	fprintf(stderr, "%s\n", __FUNCTION__);
	//ReleaseKeyHandler(); // de-subclass window
	CloseDisplay(); // shutdown direct draw
	return 0;
}

////////////////////////////////////////////////////////////////////////
// I shot the sheriff
////////////////////////////////////////////////////////////////////////

long CALLBACK GPUshutdown() {
	free(g_gpu.psxVSecure.s8);
	return 0;
}

////////////////////////////////////////////////////////////////////////
// roughly emulated screen centering bits... not complete !!!
////////////////////////////////////////////////////////////////////////
//
//void ChangeDispOffsetsX(void) // X CENTER
//{
//	long lx, l;
//
//	if (!g_gpu.dsp.range.x1)
//		return;
//
//	l = g_gpu.prev_dsp.DisplayMode.x;
//
//	l *= (long) g_gpu.dsp.range.x1;
//	l /= 2560;
//	lx = l;
//	l &= 0xfffffff8;
//
//	if (l == g_gpu.prev_dsp.range.y1)
//		return; // abusing range.y1 for
//	g_gpu.prev_dsp.range.y1 = (short) l; // storing last x range and test
//
//	if (lx >= g_gpu.prev_dsp.DisplayMode.x) {
//		g_gpu.prev_dsp.range.x1 = (short) g_gpu.prev_dsp.DisplayMode.x;
//		g_gpu.prev_dsp.range.x0 = 0;
//	} else {
//		g_gpu.prev_dsp.range.x1 = (short) l;
//
//		g_gpu.prev_dsp.range.x0 = (g_gpu.dsp.range.x0 - 500) / 8;
//
//		if (g_gpu.prev_dsp.range.x0 < 0)
//			g_gpu.prev_dsp.range.x0 = 0;
//
//		if ((g_gpu.prev_dsp.range.x0 + lx) > g_gpu.prev_dsp.DisplayMode.x) {
//			g_gpu.prev_dsp.range.x0 = (short) (g_gpu.prev_dsp.DisplayMode.x
//					- lx);
//			g_gpu.prev_dsp.range.x0 += 2; //???
//
//			g_gpu.prev_dsp.range.x1 += (short) (lx - l);
//
//			g_gpu.prev_dsp.range.x1 -= 2; // makes linux stretching easier
//		}
//
//		// some linux alignment security
//		g_gpu.prev_dsp.range.x0 = g_gpu.prev_dsp.range.x0 >> 1;
//		g_gpu.prev_dsp.range.x0 = g_gpu.prev_dsp.range.x0 << 1;
//		g_gpu.prev_dsp.range.x1 = g_gpu.prev_dsp.range.x1 >> 1;
//		g_gpu.prev_dsp.range.x1 = g_gpu.prev_dsp.range.x1 << 1;
//
//		DoClearScreenBuffer();
//	}
//
//	g_prim.bDoVSyncUpdate = 1;
//}

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

//void ChangeDispOffsetsY(void) // Y CENTER
//{
//	int iT, iO = g_gpu.prev_dsp.range.y0;
//	int iOldYOffset = g_gpu.prev_dsp.DisplayModeNew.y;
//
//	// new
//
//	if ((g_gpu.prev_dsp.DisplayModeNew.x + g_gpu.dsp.DisplayModeNew.y) > 512) {
//		int dy1 = 512 - g_gpu.prev_dsp.DisplayModeNew.x;
//		int dy2 =
//				(g_gpu.prev_dsp.DisplayModeNew.x + g_gpu.dsp.DisplayModeNew.y)
//						- 512;
//
//		if (dy1 >= dy2) {
//			g_gpu.prev_dsp.DisplayModeNew.y = -dy2;
//		} else {
//			g_gpu.dsp.position.y = 0;
//			g_gpu.prev_dsp.DisplayModeNew.y = -dy1;
//		}
//	} else
//		g_gpu.prev_dsp.DisplayModeNew.y = 0;
//
//	// eon
//
//	if (g_gpu.prev_dsp.DisplayModeNew.y != iOldYOffset) // if old offset!=new offset: recalc height
//	{
//		g_gpu.dsp.Height = g_gpu.dsp.range.y1 - g_gpu.dsp.range.y0
//				+ g_gpu.prev_dsp.DisplayModeNew.y;
//		g_gpu.dsp.DisplayModeNew.y = g_gpu.dsp.Height * g_gpu.dsp.Double;
//	}
//
//	//
//
//	if (g_gpu.dsp.PAL)
//		iT = 48;
//	else
//		iT = 28;
//
//	if (g_gpu.dsp.range.y0 >= iT) {
//		g_gpu.prev_dsp.range.y0 = (short) ((g_gpu.dsp.range.y0 - iT - 4)
//				* g_gpu.dsp.Double);
//		if (g_gpu.prev_dsp.range.y0 < 0)
//			g_gpu.prev_dsp.range.y0 = 0;
//		g_gpu.dsp.DisplayModeNew.y += g_gpu.prev_dsp.range.y0;
//	} else
//		g_gpu.prev_dsp.range.y0 = 0;
//
//	if (iO != g_gpu.prev_dsp.range.y0) {
//		DoClearScreenBuffer();
//	}
//}

////////////////////////////////////////////////////////////////////////
// check if update needed
////////////////////////////////////////////////////////////////////////


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

// TOGGLE FULLSCREEN - WINDOW DISABLED
//void ChangeWindowMode(void) {
//	extern Display *display;
//	extern Window window;
//	extern int root_window_id;
//	Screen *screen;
//	XSizeHints hints;
//	MotifWmHints mwmhints;
//	Atom mwmatom;
//
//	screen = DefaultScreenOfDisplay(display);
//	iWindowMode = !iWindowMode;
//
//	if (!iWindowMode) // fullscreen
//	{
//		mwmhints.flags = MWM_HINTS_DECORATIONS;
//		mwmhints.functions = 0;
//		mwmhints.decorations = 0;
//		mwmhints.input_mode = 0;
//		mwmatom = XInternAtom(display, "_MOTIF_WM_HINTS", 0);
//		XChangeProperty(display, window, mwmatom, mwmatom, 32, PropModeReplace,
//				(unsigned char *) &mwmhints, 5);
//
//		XResizeWindow(display, window, screen->width, screen->height);
//
//		hints.min_width = hints.max_width = hints.base_width = screen->width;
//		hints.min_height = hints.max_height = hints.base_height
//				= screen->height;
//
//		XSetWMNormalHints(display, window, &hints);
//
//		{
//			XEvent xev;
//
//			memset(&xev, 0, sizeof(xev));
//			xev.xclient.type = ClientMessage;
//			xev.xclient.serial = 0;
//			xev.xclient.send_event = 1;
//			xev.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", 0);
//			xev.xclient.window = window;
//			xev.xclient.format = 32;
//			xev.xclient.data.l[0] = 1;
//			xev.xclient.data.l[1] = XInternAtom(display,
//					"_NET_WM_STATE_FULLSCREEN", 0);
//			xev.xclient.data.l[2] = 0;
//			xev.xclient.data.l[3] = 0;
//			xev.xclient.data.l[4] = 0;
//
//			XSendEvent(display, root_window_id, 0, SubstructureRedirectMask
//					| SubstructureNotifyMask, &xev);
//		}
//	} else {
//		{
//			XEvent xev;
//
//			memset(&xev, 0, sizeof(xev));
//			xev.xclient.type = ClientMessage;
//			xev.xclient.serial = 0;
//			xev.xclient.send_event = 1;
//			xev.xclient.message_type = XInternAtom(display, "_NET_WM_STATE", 0);
//			xev.xclient.window = window;
//			xev.xclient.format = 32;
//			xev.xclient.data.l[0] = 0;
//			xev.xclient.data.l[1] = XInternAtom(display,
//					"_NET_WM_STATE_FULLSCREEN", 0);
//			xev.xclient.data.l[2] = 0;
//			xev.xclient.data.l[3] = 0;
//			xev.xclient.data.l[4] = 0;
//
//			XSendEvent(display, root_window_id, 0, SubstructureRedirectMask
//					| SubstructureNotifyMask, &xev);
//		}
//
//		mwmhints.flags = MWM_HINTS_DECORATIONS;
//		mwmhints.functions = 0;
//		mwmhints.decorations = 1;
//		mwmhints.input_mode = 0;
//		mwmatom = XInternAtom(display, "_MOTIF_WM_HINTS", 0);
//
//		//This shouldn't work on 64 bit longs, but it does...in fact, it breaks when I change all the mwmhints to int.
//		//I don't pretend to understand it.
//		XChangeProperty(display, window, mwmatom, mwmatom, 32, PropModeReplace,
//				(unsigned char *) &mwmhints, 5);
//
//		hints.flags = USPosition | USSize;
//		hints.base_width = iResX;
//		hints.base_height = iResY;
//		XSetWMNormalHints(display, window, &hints);
//
//		XResizeWindow(display, window, iResX, iResY);
//	}
//
//	DoClearScreenBuffer();
//
//	bChangeWinMode = 0;
//	bDoVSyncUpdate = 1;
//}

////////////////////////////////////////////////////////////////////////
// gun cursor func: player=0-7, x=0-511, y=0-255
////////////////////////////////////////////////////////////////////////

/* disabled */
void CALLBACK GPUcursor(int iPlayer, int x, int y) {
	//	if (iPlayer < 0)
	//		return;
	//	if (iPlayer > 7)
	//		return;
	//
	//	usCursorActive |= (1 << iPlayer);
	//
	//	if (x < 0)
	//		x = 0;
	//	if (x > 511)
	//		x = 511;
	//	if (y < 0)
	//		y = 0;
	//	if (y > 255)
	//		y = 255;
	//
	//	ptCursorPoint[iPlayer].x = x;
	//	ptCursorPoint[iPlayer].y = y;
}

////////////////////////////////////////////////////////////////////////
// update lace is called evry VSync
////////////////////////////////////////////////////////////////////////

void CALLBACK GPUupdateLace(void) // VSYNC
{

	g_gpu.status_reg ^= STATUS_ODDLINES;

	if (g_gpu.status_reg & STATUS_DISPLAYDISABLED) {
		do_clear_screen_buffer();
		return;
	}

	/* I do not roughly emulate interlace, I just draw 1/2 frame
	 * to save CPU, and improve render */
	if (!(g_gpu.status_reg & STATUS_INTERLACED) || (g_gpu.status_reg
			& STATUS_ODDLINES))
		do_buffer_swap();

	frame_cap(g_gpu.fps);
}

////////////////////////////////////////////////////////////////////////
// process read request from GPU status register
////////////////////////////////////////////////////////////////////////


uint32_t CALLBACK GPUreadStatus(void) // READ STATUS
{
	return g_gpu.status_reg;
}

////////////////////////////////////////////////////////////////////////
// processes data send to GPU status register
// these are always single packet commands.
////////////////////////////////////////////////////////////////////////

void CALLBACK GPUwriteStatus(uint32_t gdata) {
	uint32_t lCommand = (gdata >> 24) & 0xff;
	g_gpu.ulStatusControl[lCommand] = gdata; // store command for freezing
	switch (lCommand) {
	case 0x00:
		/* reset GPU */
		memset(g_gpu.lGPUInfoVals, 0x00, 16 * sizeof(uint32_t));
		/* STATUS_READYFORCOMMANDS | STATUS_IDLE | STATUS_DISPLAYDISABLED | UNKNOW13 */
		g_gpu.status_reg = 0x14802000;
		g_gpu.DataWriteMode = DR_NORMAL;
		g_gpu.DataReadMode = DR_NORMAL;
		g_prim.drawX = 0;
		g_prim.drawY = 0;
		g_prim.drawW = 0;
		g_prim.drawH = 0;
		g_draw.sSetMask = 0;
		g_draw.lSetMask = 0;
		g_draw.bCheckMask = 0;
		g_prim.usMirror = 0;
		g_soft.GlobalTextAddrX = 0;
		g_soft.GlobalTextAddrY = 0;
		g_soft.GlobalTextTP = 0;
		g_soft.GlobalTextABR = 0;
		g_prim.bUsingTWin = 0;
		return;
	case 0x01:
		/* reset command buffer */
		//fprintf(stderr, "Reset command buffer not implemented\n");
		return;
	case 0x02:
		/* reset IRQ */
		//fprintf(stderr, "Reset IRQ not implemented\n");
		return;
	case 0x03:
		/* Enable or disable the display */
		if (gdata & 0x01)
			g_gpu.status_reg |= STATUS_DISPLAYDISABLED;
		else
			g_gpu.status_reg &= ~STATUS_DISPLAYDISABLED;
		return;
	case 0x04:
		/* Set the transfering mode */
		gdata &= 0x03; // Only want the lower two bits
		g_gpu.DataWriteMode = DR_NORMAL;
		g_gpu.DataReadMode = DR_NORMAL;
		if (gdata == 0x02)
			g_gpu.DataWriteMode = DR_VRAMTRANSFER;
		if (gdata == 0x03)
			g_gpu.DataReadMode = DR_VRAMTRANSFER;
		g_gpu.status_reg &= ~STATUS_DMABITS; // Clear the current settings of the DMA bits
		g_gpu.status_reg |= (gdata << 29); // Set the DMA bits according to the received data
		return;
	case 0x05:
		/* setting display position */
		g_gpu.dsp.position.y = (short) ((gdata >> 10) & 0x1ff);
		g_gpu.dsp.position.x = (short) (gdata & 0x3ff);
		//fprintf(stderr, "Update display position X=%d,Y=%d\n",
		//		g_gpu.dsp.position.x, g_gpu.dsp.position.y);
		return;
	case 0x06:
		/* Set width */
		g_gpu.dsp.range.x0 = (short) (gdata & 0x03ff);
		g_gpu.dsp.range.x1 = (short) ((gdata >> 12) & 0x0fff);
		//fprintf(stderr, "Setrange x0 : %d, x1 : %d\n", g_gpu.dsp.range.x0,
		//		g_gpu.dsp.range.x1);
		return;
	case 0x07:
		/* Set height */
		g_gpu.dsp.range.y0 = (short) (gdata & 0x3ff);
		g_gpu.dsp.range.y1 = (short) ((gdata >> 10) & 0x3ff);
		//fprintf(stderr, "Setrange y0 : %d, y1 : %d\n", g_gpu.dsp.range.y0,
		//		g_gpu.dsp.range.y1);
		return;
	case 0x08:
		/* setting display infos */
		//fprintf(stderr, "command %x\n", gdata&0x000043);
		switch (gdata & 0x000043) {
		case 0x00:
			g_gpu.dsp.mode.x = 10;
			break;
		case 0x01:
			g_gpu.dsp.mode.x = 8;
			break;
		case 0x02:
			g_gpu.dsp.mode.x = 5;
			break;
		case 0x03:
			g_gpu.dsp.mode.x = 4;
			break;
		case 0x40:
			g_gpu.dsp.mode.x = 7;
			break;
		default:
			g_gpu.dsp.mode.x = 0;
		}

		/* clear width status */
		g_gpu.status_reg &= ~STATUS_WIDTHBITS;
		/* update status reg */
		g_gpu.status_reg |= ((gdata & 0x00000003) << 17)
				| ((gdata & 0x00000040) << 10);

		switch (gdata & 0x000004) {
		case 0x00:
			g_gpu.status_reg &= ~STATUS_DOUBLEHEIGHT;
			g_gpu.dsp.mode.y = 1;
			break;
		case 0x04:
			g_gpu.status_reg |= STATUS_DOUBLEHEIGHT;
			g_gpu.dsp.mode.y = 2;
			break;
		}

		switch (gdata & 0x08) {
		case 0x00:
			g_gpu.status_reg &= ~STATUS_PAL;
			g_gpu.fps = 60;
			break;
		case 0x08:
			g_gpu.status_reg |= STATUS_PAL;
			g_gpu.fps = 50;
			break;
		}
		//fprintf(stderr, "video mode %dx%d\n", g_gpu.dsp.mode.x, g_gpu.dsp.mode.y);

		if ((gdata & 0x10)) {
			g_gpu.status_reg |= STATUS_RGB24;
		} else {
			g_gpu.status_reg &= ~STATUS_RGB24;
		}

		if ((gdata & 0x20)) {
			g_gpu.status_reg |= STATUS_INTERLACED;
		} else {
			g_gpu.status_reg &= ~STATUS_INTERLACED;
		}
		return;
	case 0x10:
		/* ask about GPU version and other stuff */
		gdata &= 0xff;
		switch (gdata) {
		case 0x02:
			g_gpu.lGPUdataRet = g_gpu.lGPUInfoVals[INFO_TW]; // tw infos
			return;
		case 0x03:
			g_gpu.lGPUdataRet = g_gpu.lGPUInfoVals[INFO_DRAWSTART]; // draw start
			return;
		case 0x04:
			g_gpu.lGPUdataRet = g_gpu.lGPUInfoVals[INFO_DRAWEND]; // draw end
			return;
		case 0x05:
		case 0x06:
			g_gpu.lGPUdataRet = g_gpu.lGPUInfoVals[INFO_DRAWOFF]; // draw offset
			return;
		case 0x07:
			if (0)
				g_gpu.lGPUdataRet = 0x01;
			else
				g_gpu.lGPUdataRet = 0x02; // gpu type
			return;
		case 0x08:
		case 0x0F: // some bios addr?
			g_gpu.lGPUdataRet = 0xBFC03720;
			return;
		}
		return;
	default:
		fprintf(stderr, "Unknow command %02x\n", lCommand);
	}
}

////////////////////////////////////////////////////////////////////////
// vram read/write helpers, needed by LEWPY's optimized vram read/write :)
////////////////////////////////////////////////////////////////////////

inline void FinishedVRAMWrite(void) {
	// Set register to NORMAL operation
	g_gpu.DataWriteMode = DR_NORMAL;
	// Reset transfer values, to prevent mis-transfer of data
	g_gpu.VRAMWrite.x = 0;
	g_gpu.VRAMWrite.y = 0;
	g_gpu.VRAMWrite.Width = 0;
	g_gpu.VRAMWrite.Height = 0;
	g_gpu.VRAMWrite.ColsRemaining = 0;
	g_gpu.VRAMWrite.RowsRemaining = 0;
}

inline void FinishedVRAMRead(void) {
	// Set register to NORMAL operation
	g_gpu.DataReadMode = DR_NORMAL;
	// Reset transfer values, to prevent mis-transfer of data
	g_gpu.VRAMRead.x = 0;
	g_gpu.VRAMRead.y = 0;
	g_gpu.VRAMRead.Width = 0;
	g_gpu.VRAMRead.Height = 0;
	g_gpu.VRAMRead.ColsRemaining = 0;
	g_gpu.VRAMRead.RowsRemaining = 0;

	// Indicate GPU is no longer ready for VRAM data in the STATUS REGISTER
	g_gpu.status_reg &= ~STATUS_READYFORVRAM;
}

////////////////////////////////////////////////////////////////////////
// core read from vram
////////////////////////////////////////////////////////////////////////

void CALLBACK GPUreadDataMem(uint32_t * pMem, int iSize) {
	int i;

	if (g_gpu.DataReadMode != DR_VRAMTRANSFER)
		return;

	g_gpu.status_reg &= ~STATUS_IDLE;

	// adjust read ptr, if necessary

	while (g_gpu.VRAMRead.ImagePtr >= g_gpu.psxVuw_eom.u16)
		g_gpu.VRAMRead.ImagePtr -= 512 * 1024;
	while (g_gpu.VRAMRead.ImagePtr < g_gpu.psx_vram.u16)
		g_gpu.VRAMRead.ImagePtr += 512 * 1024;

	for (i = 0; i < iSize; i++) {
		// do 2 seperate 16bit reads for compatibility (wrap issues)
		if ((g_gpu.VRAMRead.ColsRemaining > 0) && (g_gpu.VRAMRead.RowsRemaining
				> 0)) {
			// lower 16 bit
			g_gpu.lGPUdataRet = (uint32_t) GETLE16(g_gpu.VRAMRead.ImagePtr);

			g_gpu.VRAMRead.ImagePtr++;
			if (g_gpu.VRAMRead.ImagePtr >= g_gpu.psxVuw_eom.u16)
				g_gpu.VRAMRead.ImagePtr -= 512 * 1024;
			g_gpu.VRAMRead.RowsRemaining--;

			if (g_gpu.VRAMRead.RowsRemaining <= 0) {
				g_gpu.VRAMRead.RowsRemaining = g_gpu.VRAMRead.Width;
				g_gpu.VRAMRead.ColsRemaining--;
				g_gpu.VRAMRead.ImagePtr += 1024 - g_gpu.VRAMRead.Width;
				if (g_gpu.VRAMRead.ImagePtr >= g_gpu.psxVuw_eom.u16)
					g_gpu.VRAMRead.ImagePtr -= 512 * 1024;
			}

			// higher 16 bit (always, even if it's an odd width)
			g_gpu.lGPUdataRet |= (uint32_t) GETLE16(g_gpu.VRAMRead.ImagePtr)
					<< 16;
			PUTLE32(pMem, g_gpu.lGPUdataRet);
			pMem++;

			if (g_gpu.VRAMRead.ColsRemaining <= 0) {
				FinishedVRAMRead();
				goto ENDREAD;
			}

			g_gpu.VRAMRead.ImagePtr++;
			if (g_gpu.VRAMRead.ImagePtr >= g_gpu.psxVuw_eom.u16)
				g_gpu.VRAMRead.ImagePtr -= 512 * 1024;
			g_gpu.VRAMRead.RowsRemaining--;
			if (g_gpu.VRAMRead.RowsRemaining <= 0) {
				g_gpu.VRAMRead.RowsRemaining = g_gpu.VRAMRead.Width;
				g_gpu.VRAMRead.ColsRemaining--;
				g_gpu.VRAMRead.ImagePtr += 1024 - g_gpu.VRAMRead.Width;
				if (g_gpu.VRAMRead.ImagePtr >= g_gpu.psxVuw_eom.u16)
					g_gpu.VRAMRead.ImagePtr -= 512 * 1024;
			}
			if (g_gpu.VRAMRead.ColsRemaining <= 0) {
				FinishedVRAMRead();
				goto ENDREAD;
			}
		} else {
			FinishedVRAMRead();
			goto ENDREAD;
		}
	}

	ENDREAD: g_gpu.status_reg |= STATUS_IDLE;
}

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

uint32_t CALLBACK GPUreadData(void) {
	uint32_t l;
	GPUreadDataMem(&l, 1);
	return g_gpu.lGPUdataRet;
}

////////////////////////////////////////////////////////////////////////
// processes data send to GPU data register
// extra table entries for fixing polyline troubles
////////////////////////////////////////////////////////////////////////

const unsigned char primTableCX[256] = {
// 00
		0, 0, 3, 0, 0, 0, 0, 0,
		// 08
		0, 0, 0, 0, 0, 0, 0, 0,
		// 10
		0, 0, 0, 0, 0, 0, 0, 0,
		// 18
		0, 0, 0, 0, 0, 0, 0, 0,
		// 20
		4, 4, 4, 4, 7, 7, 7, 7,
		// 28
		5, 5, 5, 5, 9, 9, 9, 9,
		// 30
		6, 6, 6, 6, 9, 9, 9, 9,
		// 38
		8, 8, 8, 8, 12, 12, 12, 12,
		// 40
		3, 3, 3, 3, 0, 0, 0, 0,
		// 48
		//  5,5,5,5,6,6,6,6,    // FLINE
		254, 254, 254, 254, 254, 254, 254, 254,
		// 50
		4, 4, 4, 4, 0, 0, 0, 0,
		// 58
		//  7,7,7,7,9,9,9,9,    // GLINE
		255, 255, 255, 255, 255, 255, 255, 255,
		// 60
		3, 3, 3, 3, 4, 4, 4, 4,
		// 68
		2, 2, 2, 2, 3, 3, 3, 3, // 3=SPRITE1???
		// 70
		2, 2, 2, 2, 3, 3, 3, 3,
		// 78
		2, 2, 2, 2, 3, 3, 3, 3,
		// 80
		4, 0, 0, 0, 0, 0, 0, 0,
		// 88
		0, 0, 0, 0, 0, 0, 0, 0,
		// 90
		0, 0, 0, 0, 0, 0, 0, 0,
		// 98
		0, 0, 0, 0, 0, 0, 0, 0,
		// a0
		3, 0, 0, 0, 0, 0, 0, 0,
		// a8
		0, 0, 0, 0, 0, 0, 0, 0,
		// b0
		0, 0, 0, 0, 0, 0, 0, 0,
		// b8
		0, 0, 0, 0, 0, 0, 0, 0,
		// c0
		3, 0, 0, 0, 0, 0, 0, 0,
		// c8
		0, 0, 0, 0, 0, 0, 0, 0,
		// d0
		0, 0, 0, 0, 0, 0, 0, 0,
		// d8
		0, 0, 0, 0, 0, 0, 0, 0,
		// e0
		0, 1, 1, 1, 1, 1, 1, 0,
		// e8
		0, 0, 0, 0, 0, 0, 0, 0,
		// f0
		0, 0, 0, 0, 0, 0, 0, 0,
		// f8
		0, 0, 0, 0, 0, 0, 0, 0 };

void CALLBACK GPUwriteDataMem(uint32_t * pMem, int iSize) {
	unsigned char command;
	uint32_t gdata = 0;
	int i = 0;

	g_gpu.status_reg &= ~STATUS_IDLE;
	g_gpu.status_reg &= ~STATUS_READYFORCOMMANDS;

	STARTVRAM:

	if (g_gpu.DataWriteMode == DR_VRAMTRANSFER) {
		char bFinished = 0;

		// make sure we are in vram
		while (g_gpu.VRAMWrite.ImagePtr >= g_gpu.psxVuw_eom.u16)
			g_gpu.VRAMWrite.ImagePtr -= 512 * 1024;
		while (g_gpu.VRAMWrite.ImagePtr < g_gpu.psx_vram.u16)
			g_gpu.VRAMWrite.ImagePtr += 512 * 1024;

		// now do the loop
		while (g_gpu.VRAMWrite.ColsRemaining > 0) {
			while (g_gpu.VRAMWrite.RowsRemaining > 0) {
				if (i >= iSize) {
					goto ENDVRAM;
				}
				i++;

				gdata = GETLE32(pMem);
				pMem++;

				PUTLE16(g_gpu.VRAMWrite.ImagePtr, (unsigned short)gdata);
				g_gpu.VRAMWrite.ImagePtr++;
				if (g_gpu.VRAMWrite.ImagePtr >= g_gpu.psxVuw_eom.u16)
					g_gpu.VRAMWrite.ImagePtr -= 512 * 1024;
				g_gpu.VRAMWrite.RowsRemaining--;

				if (g_gpu.VRAMWrite.RowsRemaining <= 0) {
					g_gpu.VRAMWrite.ColsRemaining--;
					if (g_gpu.VRAMWrite.ColsRemaining <= 0) // last pixel is odd width
					{
						gdata
								= (gdata & 0xFFFF)
										| (((uint32_t) GETLE16(g_gpu.VRAMWrite.ImagePtr))
												<< 16);
						FinishedVRAMWrite();
						goto ENDVRAM;
					}
					g_gpu.VRAMWrite.RowsRemaining = g_gpu.VRAMWrite.Width;
					g_gpu.VRAMWrite.ImagePtr += 1024 - g_gpu.VRAMWrite.Width;
				}

				PUTLE16(g_gpu.VRAMWrite.ImagePtr, (unsigned short)(gdata>>16));
				g_gpu.VRAMWrite.ImagePtr++;
				if (g_gpu.VRAMWrite.ImagePtr >= g_gpu.psxVuw_eom.u16)
					g_gpu.VRAMWrite.ImagePtr -= 512 * 1024;
				g_gpu.VRAMWrite.RowsRemaining--;
			}

			g_gpu.VRAMWrite.RowsRemaining = g_gpu.VRAMWrite.Width;
			g_gpu.VRAMWrite.ColsRemaining--;
			g_gpu.VRAMWrite.ImagePtr += 1024 - g_gpu.VRAMWrite.Width;
			bFinished = 1;
		}

		FinishedVRAMWrite();
	}

	ENDVRAM:

	if (g_gpu.DataWriteMode == DR_NORMAL) {
		void (* *primFunc)(unsigned char *);
		primFunc = primTableJ;

		for (; i < iSize;) {
			if (g_gpu.DataWriteMode == DR_VRAMTRANSFER)
				goto STARTVRAM;

			gdata = GETLE32(pMem);
			pMem++;
			i++;

			if (g_gpu.gpuDataC == 0) {
				command = (unsigned char) ((gdata >> 24) & 0xff);

				//if(command>=0xb0 && command<0xc0) auxprintf("b0 %x!!!!!!!!!\n",command);

				if (primTableCX[command]) {
					g_gpu.gpuDataC = primTableCX[command];
					g_gpu.gpuCommand = command;
					PUTLE32(&g_gpu.gpuDataM[0], gdata);
					g_gpu.gpuDataP = 1;
				} else {
					//fprintf(stderr, "unknow command %02x \n", command);
					continue;
				}
			} else {
				PUTLE32(&g_gpu.gpuDataM[g_gpu.gpuDataP], gdata);
				if (g_gpu.gpuDataC > 128) {
					if ((g_gpu.gpuDataC == 254 && g_gpu.gpuDataP >= 3)
							|| (g_gpu.gpuDataC == 255 && g_gpu.gpuDataP >= 4
									&& !(g_gpu.gpuDataP & 1))) {
						if ((g_gpu.gpuDataM[g_gpu.gpuDataP] & 0xF000F000)
								== 0x50005000)
							g_gpu.gpuDataP = g_gpu.gpuDataC - 1;
					}
				}
				g_gpu.gpuDataP++;
			}

			if (g_gpu.gpuDataP == g_gpu.gpuDataC) {
				g_gpu.gpuDataC = g_gpu.gpuDataP = 0;
				primFunc[g_gpu.gpuCommand]((unsigned char *) g_gpu.gpuDataM);
			}
		}
	}

	g_gpu.lGPUdataRet = gdata;

	g_gpu.status_reg |= STATUS_READYFORCOMMANDS;
	g_gpu.status_reg |= STATUS_IDLE;
}

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

void CALLBACK GPUwriteData(uint32_t gdata) {
	PUTLE32(&gdata, gdata);
	GPUwriteDataMem(&gdata, 1);
}

////////////////////////////////////////////////////////////////////////
// this functions will be removed soon (or 'soonish')... not really needed, but some emus want them
////////////////////////////////////////////////////////////////////////

void CALLBACK GPUsetMode(unsigned long gdata) {
	// Peops does nothing here...
	// DataWriteMode=(gdata&1)?DR_VRAMTRANSFER:DR_NORMAL;
	// DataReadMode =(gdata&2)?DR_VRAMTRANSFER:DR_NORMAL;
}

long CALLBACK GPUgetMode(void) {
	long iT = 0;

	if (g_gpu.DataWriteMode == DR_VRAMTRANSFER)
		iT |= 0x1;
	if (g_gpu.DataReadMode == DR_VRAMTRANSFER)
		iT |= 0x2;
	return iT;
}

////////////////////////////////////////////////////////////////////////
// call config dlg
////////////////////////////////////////////////////////////////////////

long CALLBACK GPUconfigure(void) {
	SoftDlgProc();
	return 0;
}

////////////////////////////////////////////////////////////////////////
// sets all kind of act fixes
////////////////////////////////////////////////////////////////////////

void SetFixes(void) {

}

////////////////////////////////////////////////////////////////////////
// process gpu commands
////////////////////////////////////////////////////////////////////////

unsigned long lUsedAddr[3];

inline char CheckForEndlessLoop(unsigned long laddr) {
	if (laddr == lUsedAddr[1])
		return 1;
	if (laddr == lUsedAddr[2])
		return 1;

	if (laddr < lUsedAddr[0])
		lUsedAddr[1] = laddr;
	else
		lUsedAddr[2] = laddr;
	lUsedAddr[0] = laddr;
	return 0;
}

long CALLBACK GPUdmaChain(uint32_t * baseAddrL, uint32_t addr) {
	uint32_t dmaMem;
	unsigned char * baseAddrB;
	short count;
	unsigned int DMACommandCounter = 0;

	g_gpu.status_reg &= ~STATUS_IDLE;

	lUsedAddr[0] = lUsedAddr[1] = lUsedAddr[2] = 0xffffff;

	baseAddrB = (unsigned char*) baseAddrL;

	do {
		if (1)
			addr &= 0x1FFFFC;
		if (DMACommandCounter++ > 2000000)
			break;
		if (CheckForEndlessLoop(addr))
			break;

		count = baseAddrB[addr + 3];

		dmaMem = addr + 4;

		if (count > 0)
			GPUwriteDataMem(&baseAddrL[dmaMem >> 2], count);

		addr = GETLE32(&baseAddrL[addr>>2]) & 0xffffff;
	} while (addr != 0xffffff);

	g_gpu.status_reg |= STATUS_IDLE;
	return 0;
}

////////////////////////////////////////////////////////////////////////
// show about dlg
////////////////////////////////////////////////////////////////////////


void CALLBACK GPUabout(void) // ABOUT
{
	AboutDlgProc();
	return;
}

////////////////////////////////////////////////////////////////////////
// We are ever fine ;)
////////////////////////////////////////////////////////////////////////

long CALLBACK GPUtest(void) {
	// if test fails this function should return negative value for error (unable to continue)
	// and positive value for warning (can continue but output might be crappy)
	return 0;
}

////////////////////////////////////////////////////////////////////////
// Freeze
////////////////////////////////////////////////////////////////////////

typedef struct GPUFREEZETAG {
	uint32_t ulFreezeVersion; // should be always 1 for now (set by main emu)
	uint32_t ulStatus; // current gpu status
	uint32_t ulControl[256]; // latest control register values
	unsigned char psxVRam[1024 * 1024 * 2]; // current VRam image (full 2 MB for ZN)
} GPUFreeze_t;

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

long CALLBACK GPUfreeze(uint32_t ulGetFreezeData, GPUFreeze_t * pF) {
	//----------------------------------------------------//
	if (ulGetFreezeData == 2) // 2: info, which save slot is selected? (just for display)
	{
		long lSlotNum = *((long *) pF);
		if (lSlotNum < 0)
			return 0;
		if (lSlotNum > 8)
			return 0;
		//g_gpu.lSelectedSlot = lSlotNum + 1;
		//BuildDispMenu(0);
		return 1;
	}
	//----------------------------------------------------//
	if (!pF)
		return 0; // some checks
	if (pF->ulFreezeVersion != 1)
		return 0;

	if (ulGetFreezeData == 1) // 1: get data
	{
		pF->ulStatus = g_gpu.status_reg;
		memcpy(pF->ulControl, g_gpu.ulStatusControl, 256 * sizeof(uint32_t));
		memcpy(pF->psxVRam, g_gpu.psx_vram.u8, 1024 * 512 * 2);

		return 1;
	}

	if (ulGetFreezeData != 0)
		return 0; // 0: set data

	g_gpu.status_reg = pF->ulStatus;
	memcpy(g_gpu.ulStatusControl, pF->ulControl, 256 * sizeof(uint32_t));
	memcpy(g_gpu.psx_vram.u8, pF->psxVRam, 1024 * 512 * 2);

	// RESET TEXTURE STORE HERE, IF YOU USE SOMETHING LIKE THAT

	GPUwriteStatus(g_gpu.ulStatusControl[0]);
	GPUwriteStatus(g_gpu.ulStatusControl[1]);
	GPUwriteStatus(g_gpu.ulStatusControl[2]);
	GPUwriteStatus(g_gpu.ulStatusControl[3]);
	GPUwriteStatus(g_gpu.ulStatusControl[8]); // try to repair things
	GPUwriteStatus(g_gpu.ulStatusControl[6]);
	GPUwriteStatus(g_gpu.ulStatusControl[7]);
	GPUwriteStatus(g_gpu.ulStatusControl[5]);
	GPUwriteStatus(g_gpu.ulStatusControl[4]);

	return 1;
}

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

////////////////////////////////////////////////////////////////////////
// SAVE STATE DISPLAY STUFF
////////////////////////////////////////////////////////////////////////

// font 0-9, 24x20 pixels, 1 byte = 4 dots
// 00 = black
// 01 = white
// 10 = red
// 11 = transparent

unsigned char cFont[10][120] = {
// 0
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x54, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80,
				0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 1
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x05, 0x50, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x50, 0x00, 0x00, 0x80, 0x00, 0x00, 0x50, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x00, 0x50, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x50, 0x00, 0x00, 0x80, 0x00, 0x05, 0x55, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 2
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x54, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x00, 0x14, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x01, 0x40, 0x00,
				0x00, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x15, 0x55, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 3
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x54, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x01, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 4
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x14, 0x00, 0x00, 0x80, 0x00, 0x00, 0x54, 0x00,
				0x00, 0x80, 0x00, 0x01, 0x54, 0x00, 0x00, 0x80, 0x00, 0x01,
				0x54, 0x00, 0x00, 0x80, 0x00, 0x05, 0x14, 0x00, 0x00, 0x80,
				0x00, 0x14, 0x14, 0x00, 0x00, 0x80, 0x00, 0x15, 0x55, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x14, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x14, 0x00, 0x00, 0x80, 0x00, 0x00, 0x55, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 5
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x15, 0x55, 0x00, 0x00, 0x80, 0x00, 0x14, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x00, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x15, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 6
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x01, 0x54, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x00, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x15, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x15, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 7
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x15, 0x55, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x14, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x14, 0x00, 0x00, 0x80, 0x00, 0x00, 0x50, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x50, 0x00, 0x00, 0x80, 0x00, 0x01, 0x40, 0x00,
				0x00, 0x80, 0x00, 0x01, 0x40, 0x00, 0x00, 0x80, 0x00, 0x05,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x05, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 8
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x54, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x05, 0x54, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa },
		// 9
		{ 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x54, 0x00, 0x00, 0x80, 0x00, 0x14, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x14, 0x05, 0x00, 0x00, 0x80, 0x00, 0x14,
				0x05, 0x00, 0x00, 0x80, 0x00, 0x14, 0x15, 0x00, 0x00, 0x80,
				0x00, 0x05, 0x55, 0x00, 0x00, 0x80, 0x00, 0x00, 0x05, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x05, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x14, 0x00, 0x00, 0x80, 0x00, 0x05, 0x50, 0x00, 0x00, 0x80,
				0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00, 0x00, 0x00,
				0x00, 0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x80, 0x00, 0x00,
				0x00, 0x00, 0x00, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa } };

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

void PaintPicDot(unsigned char * p, unsigned char c) {

	if (c == 0) {
		*p++ = 0x00;
		*p++ = 0x00;
		*p = 0x00;
		return;
	} // black
	if (c == 1) {
		*p++ = 0xff;
		*p++ = 0xff;
		*p = 0xff;
		return;
	} // white
	if (c == 2) {
		*p++ = 0x00;
		*p++ = 0x00;
		*p = 0xff;
		return;
	} // red
	// transparent
}

////////////////////////////////////////////////////////////////////////
// the main emu allocs 128x96x3 bytes, and passes a ptr
// to it in pMem... the plugin has to fill it with
// 8-8-8 bit BGR screen data (Win 24 bit BMP format 
// without header). 
// Beware: the func can be called at any time,
// so you have to use the frontbuffer to get a fully
// rendered picture
// LINUX version:

//extern char * Xpixels;

void GPUgetScreenPic(unsigned char * pMem) {
	/*
	 unsigned short c;unsigned char * pf;int x,y;

	 float XS=(float)iResX/128;
	 float YS=(float)iResY/96;

	 pf=pMem;
	 memset(pMem, 0, 128*96*3);

	 if(Xpixels)
	 {
	 unsigned char * ps=(unsigned char *)Xpixels;
	 {
	 long lPitch=iResX<<2;
	 uint32_t sx;

	 for(y=0;y<96;y++)
	 {
	 for(x=0;x<128;x++)
	 {
	 sx=*((uint32_t *)((ps)+
	 (((int)((float)y*YS))*lPitch)+
	 ((int)((float)x*XS))*4));
	 *(pf+0)=(sx&0xff);
	 *(pf+1)=(sx&0xff00)>>8;
	 *(pf+2)=(sx&0xff0000)>>16;
	 pf+=3;
	 }
	 }
	 }
	 }


	 /////////////////////////////////////////////////////////////////////
	 // generic number/border painter

	 pf=pMem+(103*3);                                      // offset to number rect

	 for(y=0;y<20;y++)                                     // loop the number rect pixel
	 {
	 for(x=0;x<6;x++)
	 {
	 c=cFont[lSelectedSlot][x+y*6];                    // get 4 char dot infos at once (number depends on selected slot)
	 PaintPicDot(pf,(c&0xc0)>>6);pf+=3;                // paint the dots into the rect
	 PaintPicDot(pf,(c&0x30)>>4);pf+=3;
	 PaintPicDot(pf,(c&0x0c)>>2);pf+=3;
	 PaintPicDot(pf,(c&0x03));   pf+=3;
	 }
	 pf+=104*3;                                          // next rect y line
	 }

	 pf=pMem;                                              // ptr to first pos in 128x96 pic
	 for(x=0;x<128;x++)                                    // loop top/bottom line
	 {
	 *(pf+(95*128*3))=0x00;*pf++=0x00;
	 *(pf+(95*128*3))=0x00;*pf++=0x00;                   // paint it red
	 *(pf+(95*128*3))=0xff;*pf++=0xff;
	 }
	 pf=pMem;                                              // ptr to first pos
	 for(y=0;y<96;y++)                                     // loop left/right line
	 {
	 *(pf+(127*3))=0x00;*pf++=0x00;
	 *(pf+(127*3))=0x00;*pf++=0x00;                      // paint it red
	 *(pf+(127*3))=0xff;*pf++=0xff;
	 pf+=127*3;                                          // offset to next line
	 }
	 */
}

////////////////////////////////////////////////////////////////////////
// func will be called with 128x96x3 BGR data.
// the plugin has to store the data and display
// it in the upper right corner.
// If the func is called with a NULL ptr, you can
// release your picture data and stop displaying
// the screen pic

void CALLBACK GPUshowScreenPic(unsigned char * pMem) {
	DestroyPic(); // destroy old pic data
	if (pMem == 0)
		return; // done
	CreatePic(pMem); // create new pic... don't free pMem or something like that... just read from it
}

void CALLBACK GPUsetfix(uint32_t dwFixBits) {
	g_prim.dwEmuFixes = dwFixBits;
}

void CALLBACK GPUvBlank(int val) {
	//fprintf(stderr, "Vblanc %d\n", val);
	//if(val)
	//	g_gpu.status_reg |= STATUS_ODDLINES;
	//else
	//	g_gpu.status_reg &= ~STATUS_ODDLINES;
}
