/** VGB: portable GameBoy emulator ***************************/
/**                                                         **/
/**                           Amiga.c                       **/
/**                                                         **/
/** Amiga-specific graphics/joystick routines  drivers.     **/
/**                                                         **/
/** Copyright (C) Lars Malmborg 1996                        **/
/** based on a version by Michael Boese & Matthias Bethke   **/
/** and the Unix/X version.                                 **/
/**                                                         **/
/** Copyright (C) Marat Fayzullin 1994,1995,1996            **/
/**               Elan Feingold   1995                      **/
/**     You are not allowed to distribute this software     **/
/**     commercially. Please, notify me, if you make any    **/
/**     changes to this file.                               **/
/*************************************************************/

#define REVISION "rev. 2"
#define USE_XPAL  /* We are using XPal[] to determine colors */

/* Amiga includes */
#include <proto/exec.h>
#include <proto/dos.h>
#include <proto/intuition.h>
#include <proto/graphics.h>
#include <proto/asl.h>
#include <dos/rdargs.h>
#include <exec/memory.h>

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

#ifdef CYBER
#include <proto/cybergraphics.h>
#include <cybergraphics/cybergraphics.h>
#endif

void __inline AllocatePens(void);

#include "GB.h"
#include "AmigaRawkeys.h"


/** Various variables and short functions ********************/
#define WIDTH_OFFS 8
#define WIDTH  (160+2*WIDTH_OFFS)
#define HEIGHT 144

char *Title = "Virtual GameBoy 0.7";

char *AmigaVersion="$VER: Virtual GameBoy Amiga 0.7 "REVISION" "__AMIGADATE__;

int  ExitFlag = 0;

byte *XBuf=NULL,*ZBuf=NULL;
LONG XPal[12]={-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1};

struct Screen *scr=NULL;
struct Window *win=NULL;
struct RastPort *rp=NULL;

char ReqText[]= "Virtual Gameboy V0.7 by\n\
Marat Fayzullin <fms@freeflight.com>\n\
Amiga version "REVISION" "__AMIGADATE__" by\n\
Lars Malmborg <glue@df.lth.se>\n\n\
In-game keys:\n\
Left Alt    or '+' on key pad   - 'A'\n\
Left Shift  or '6' on key pad   - 'B'\n\
Right Shift or '4' on key pad   - 'Select'\n\
Return      or '5' on key pad   - 'Start'\n\
Cursorkeys   - Joypad\n\n\
ESC          - Quit emulation\n"
#ifdef DEBUG
"\
F1           - Go into built-in debugger\n\
F2           - Show LCD controller registers\n"
F3           - Show sprite registers\n"
#endif
; //Gremlins!

struct EasyStruct HelpReq =
{
	sizeof(struct EasyStruct),
	0,
	"VGB Amiga Help",
	ReqText,
	"OK"
};

#ifndef CYBER
struct RastPort temprp;
/*
struct BitMap tempbm;
char tempp[WIDTH*8];
*/
#endif
BOOL ScaleUp;
STRPTR PubScreen;

int InitMachine(void)
{
	int scale;
	if (ScaleUp)
		scale=2;
	else
		scale=1;

	if (scr = LockPubScreen(PubScreen))
	{
		if(win = OpenWindowTags(NULL,
					WA_Left,(scr->Width - scale*(WIDTH-2*WIDTH_OFFS))/2,
					WA_Top,(scr->Height - scale*HEIGHT)/2,
					WA_InnerWidth,scale*(WIDTH-2*WIDTH_OFFS),
					WA_InnerHeight,scale*HEIGHT,
					WA_Title,Title,
					WA_CloseGadget,TRUE,
					WA_DepthGadget,TRUE,
					WA_DragBar,TRUE,
					WA_SizeGadget,FALSE,
					WA_RMBTrap,TRUE,
					WA_Activate,TRUE,
					WA_IDCMP,IDCMP_RAWKEY|IDCMP_CLOSEWINDOW,
					WA_PubScreenName,PubScreen,
					TAG_END
			))
		{
			UnlockPubScreen(PubScreen,scr);
			rp = win->RPort;
			AllocatePens();
			if(!(XBuf=AllocVec(sizeof(byte)*HEIGHT*WIDTH,MEMF_ANY))) return(0);
			if(!(ZBuf=AllocVec(sizeof(byte)*2*WIDTH*HEIGHT/8,MEMF_ANY))) return(0);

#ifndef CYBER
			// init temprp
			CopyMem(rp,&temprp,sizeof(struct RastPort));
			temprp.Layer=NULL;
			if (!(temprp.BitMap=AllocBitMap(scale*(WIDTH-2*WIDTH_OFFS),1,rp->BitMap->Depth,0,NULL)))
			{
				fprintf(stderr,"Error allocating temporary rastport.\n");
				return(0);
			}
#endif
		}
		else
		{
			fprintf(stderr,"Can't open window.\n");
			return(0);
		}
	}
	else
	{
		fprintf(stderr,"Screen not found.\n");
		return(0);
	}
	return(1);
}

/** TrashMachine *********************************************/
/** Deallocate all resources taken by InitMachine().        **/
/*************************************************************/
void TrashMachine(void)
{
	int i;
#ifndef CYBER
	// release temprp
	if (temprp.BitMap)
		FreeBitMap(temprp.BitMap);
#endif

	if (ZBuf)
		FreeVec(ZBuf);
	if (XBuf)
		FreeVec(XBuf);
	if (win)
		CloseWindow(win);
	for(i=0; i<12; i++)
		if (XPal[i]!=-1)
			ReleasePen(scr->ViewPort.ColorMap,XPal[i]);
}


/** PutImage *************************************************/
/** Put an image on the screen.                             **/
/*************************************************************/
void PutImage(void)
{
#ifdef CYBER
	// Render with cybergraphics.library
	int scale;
	if (ScaleUp)
		scale=2;
	else
		scale=1;

	WritePixelArray(XBuf,WIDTH_OFFS,0,scale*WIDTH,rp,win->BorderLeft,win->BorderTop,scale*(WIDTH-2*WIDTH_OFFS),HEIGHT,RECTFMT_LUT8);
#else
	// Render with graphics.library
	if (ScaleUp)
	{
		register int i;
		for(i=0;i<144;i++)
		{
			UBYTE drawarray[2*(WIDTH-2*WIDTH_OFFS)];
			register int n;
			for (n=0;n<(WIDTH-2*WIDTH_OFFS);n++)
			{
				drawarray[n*2]=(UBYTE)(XBuf[(i*WIDTH)+n+WIDTH_OFFS]);
				drawarray[n*2+1]=drawarray[n*2];
			}
			WritePixelLine8(rp,win->BorderLeft,win->BorderTop+i*2,2*(WIDTH-2*WIDTH_OFFS),drawarray,&temprp);
			WritePixelLine8(rp,win->BorderLeft,win->BorderTop+i*2+1,2*(WIDTH-2*WIDTH_OFFS),drawarray,&temprp);
		}
	}
	else
	{
		register int i;
		for(i=0;i<144;i++)
		{
			WritePixelLine8(rp,win->BorderLeft,win->BorderTop+i,WIDTH-2*WIDTH_OFFS,(UBYTE*)(&XBuf[(i*WIDTH)+WIDTH_OFFS]),&temprp);
		}
	}
}

/** Joystick *************************************************/
/** Return the current joystick state.                      **/
/*************************************************************/
byte Joystick(void)
{
struct IntuiMessage *imsg;
ULONG imsg_Class;
UWORD Code;
#ifdef DEBUG
int J;
#endif
static byte JoyState=0xff;

		if(imsg = (struct IntuiMessage*)GetMsg(win->UserPort))
		{
			imsg_Class = imsg->Class;
			Code  = imsg->Code;
			ReplyMsg((struct Message*)imsg);
			switch(imsg_Class)
			{
				case IDCMP_CLOSEWINDOW:
					ExitFlag = 1;
					break;
				case IDCMP_RAWKEY:
					if(Code & 0x80)
					{
						switch(Code & 0x7f)
						{
#ifdef DEBUG
							case KEY_F1:
								Trace=!Trace;
								break;
							case KEY_F2:
								puts("\n*** REGISTERS: ***");		//F2 up
								for(J=0xFF40;J<0xFF50;J++)
									printf("(%Xh) = %Xh\n",J,RAM[J]);
								printf("ISWITCH = %Xh\n",ISWITCH);
								break;
        			case KEY_F3:
								puts("\n*** SPRITES: ***");
								for(J=0xFE9C;J<0xFE9C+4*40;J+=4)
								printf("SPRITE %d: %d,%d   Pat %d   Attr %d\n",(J-0xFE9C)/4,RAM[J+1],RAM[J],RAM[J+2],RAM[J+3]);
								break;
#endif
							case KEY_HELP:
								EasyRequestArgs(win,&HelpReq,NULL,NULL);
								break;
							case KEY_RETURN:	//start
							case KEY_P5:
								JoyState|=0x80;
								break;
							case KEY_RSHIFT:	//select
							case KEY_P4:
								JoyState|=0x40;
								break;
							case KEY_LSHIFT:	//B
							case KEY_P6:
								JoyState|=0x20;
								break;
							case KEY_LALT:		//A
							case KEY_PPLUS:
								JoyState|=0x10;
								break;
							case KEY_CDOWN:		//Down
								JoyState|=0x08;
								break;
							case KEY_CUP:			//Up
								JoyState|=0x04;
								break;
							case KEY_CLEFT:		//Left
								JoyState|=0x02;
								break;
							case KEY_CRIGHT:	//Right
								JoyState|=0x01;
								break;
						}
					}
					else
					{
						switch(Code)
						{
							case KEY_ESC:
								ExitFlag = 1;
								break;
							case KEY_RETURN:
							case KEY_P5:
								JoyState&=0x7F;
								break;
							case KEY_RSHIFT:
							case KEY_P4:
								JoyState&=0xBF;
								break;
							case KEY_LSHIFT:
							case KEY_P6:
								JoyState&=0xDF;
								break;
							case KEY_LALT:
							case KEY_PPLUS:
								JoyState&=0xEF;
								break;
							case KEY_CDOWN:
								JoyState&=0xF7;
								break;
							case KEY_CUP:
								JoyState&=0xFB;
								break;
							case KEY_CLEFT:
								JoyState&=0xFD;
								break;
							case KEY_CRIGHT:
								JoyState&=0xFE;
								break;
						}
					}
					break;
			}
		}

  if(ExitFlag)
  { TrashGB();TrashMachine();exit(0); }

  return(JoyState);
}

struct
{
	LONG r,g,b;
} GB_Colors[12]={
					{0xff000000,0xff000000,0xff000000},{0x98000000,0x98000000,0x98000000},
					{0x58000000,0x58000000,0x58000000},{0x00000000,0x00000000,0x00000000},

					{0xff000000,0xff000000,0xff000000},{0x98000000,0x98000000,0x98000000},
					{0x58000000,0x58000000,0x58000000},{0x00000000,0x00000000,0x00000000},

					{0xff000000,0xff000000,0xff000000},{0x98000000,0x98000000,0x98000000},
					{0x58000000,0x58000000,0x58000000},{0x00000000,0x00000000,0x00000000}};

void SetColors(int mode, char* color)
{
	long tcol[4];
	int i=0;
	while (!isdigit(color[i])) i++;
	sscanf(color,"%lx,%lx,%lx,%lx",&tcol[0],&tcol[1],&tcol[2],&tcol[3]);

	GB_Colors[mode+0].r=(tcol[0]&0x00ff0000)<<8;
	GB_Colors[mode+0].g=(tcol[0]&0x0000ff00)<<16;
	GB_Colors[mode+0].b=(tcol[0]&0x000000ff)<<24;

	GB_Colors[mode+1].r=(tcol[1]&0x00ff0000)<<8;
	GB_Colors[mode+1].g=(tcol[1]&0x0000ff00)<<16;
	GB_Colors[mode+1].b=(tcol[1]&0x000000ff)<<24;

	GB_Colors[mode+2].r=(tcol[2]&0x00ff0000)<<8;
	GB_Colors[mode+2].g=(tcol[2]&0x0000ff00)<<16;
	GB_Colors[mode+2].b=(tcol[2]&0x000000ff)<<24;

	GB_Colors[mode+3].r=(tcol[3]&0x00ff0000)<<8;
	GB_Colors[mode+3].g=(tcol[3]&0x0000ff00)<<16;
	GB_Colors[mode+3].b=(tcol[3]&0x000000ff)<<24;
}

void AllocatePens(void)
{
	int i;
	for(i=0; i<12; i++)
	{
		XPal[i] = ObtainBestPenA(scr->ViewPort.ColorMap,GB_Colors[i].r,GB_Colors[i].g,GB_Colors[i].b,NULL);
	}
}


/*** SIOSend ****************************************************/
/*** Send a byte onto the serial line.                        ***/
/****************************************************************/
byte SIOSend(register byte V) { return(0); }


/*** SIOReceive *************************************************/
/*** Receive a byte from the serial line. Returns 1 on        ***/
/*** success, 0 otherwise.                                    ***/
/****************************************************************/
byte SIOReceive(register byte *V) { return(0); }


/****************************************************************/
/*** Write value into sound chip register (Reg #0 at FF10h).  ***/
/****************************************************************/
void Sound(byte R,byte V) { return; }


/** Common.h ****************************************************/
/** Parts of the drivers common for Unix/X and MSDOS.          **/
/****************************************************************/
#include "Common.h"
