/** 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 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 "__AMIGADATE__;

int  ExitFlag = 0;
int  SaveCPU  = 1;

byte *XBuf,*ZBuf,*LinePtr,XPal[12];

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

extern BOOL ScaleUp;

char ReqText[]= "Virtual Gameboy V0.7 by\n\
Marat Fayzullin <fms@freeflight.com>\n\
Amiga version "__AMIGADATE__" by\n\
Lars Malmborg <glue@df.lth.se>\n\n\
In-game keys:\n\
'x'        - Button 'A'\n\
'y'        - Button 'B'\n\
'.'        - 'Select'\n\
'-'        - '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"
#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];
*/
int Interleave;
#endif

int InitMachine()
{
	if (scr = LockPubScreen(NULL))
	{
		if(win = OpenWindowTags(NULL,
					WA_Left,(scr->Width - WIDTH-2*WIDTH_OFFS)/2,
					WA_Top,(scr->Height - HEIGHT)/2,
					WA_InnerWidth,WIDTH-2*WIDTH_OFFS,
					WA_InnerHeight,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,
					TAG_END
			))
		{
			UnlockPubScreen(NULL,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(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,"No default PubScreen!\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);
	CloseWindow(win);
	for(i=0; i<12; i++) ReleasePen(scr->ViewPort.ColorMap,XPal[i]);
}


/** PutImage *************************************************/
/** Put an image on the screen.                             **/
/*************************************************************/
void PutImage(void)
{
#ifdef CYBER
	// Render with cybergraphics.library
	WritePixelArray(XBuf,WIDTH_OFFS,0,WIDTH,rp,win->BorderLeft,win->BorderTop,WIDTH-2*WIDTH_OFFS,HEIGHT,RECTFMT_LUT8);
#else
	// Render with graphics.library
	register int i;
	register int il;
	il=Interleave;
	for(i=0;i<144;i+=il)
	{
		WritePixelLine8(rp,win->BorderLeft,win->BorderTop+i,WIDTH-2*WIDTH_OFFS,(UBYTE*)(&XBuf[i*WIDTH]+WIDTH_OFFS),&temprp);
	}
#endif
}

/** 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) = $%X\n",J,*(RAM+J));
												printf("\n");
												break;
#endif
							case KEY_HELP:		EasyRequestArgs(NULL,&HelpReq,NULL,NULL);
							case KEY_DASH:		JoyState|=0x80;break;	//start
							case KEY_PERIOD:	JoyState|=0x40;break;	//select
							case KEY_Y:			JoyState|=0x20;break;	//B
							case KEY_X:			JoyState|=0x10;break;	//A
							case KEY_CDOWN:	JoyState|=0x08;break;	//down
							case KEY_CUP:		JoyState|=0x04;break;	//up
							case KEY_CLEFT:	JoyState|=0x02;break;	//left
							case KEY_CRIGHT:	JoyState|=0x01;break;	//right
						}
					} else
					{
						switch(Code)
						{
							case KEY_ESC:		ExitFlag = 1; break;
							case KEY_DASH:		JoyState&=0x7F;break;
							case KEY_PERIOD:	JoyState&=0xBF;break;
							case KEY_Y:			JoyState&=0xDF;break;
							case KEY_X:			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;
						}
					}
			}
		}

  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] = (UBYTE)(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"
