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

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

#include "GB.h"
#include "rawkeys.h"


/** Various variables and short functions ********************/
#define WIDTH  160
#define HEIGHT 144

char *Title = "VGB Amiga V0.33";

int  ExitFlag = 0;
int  SaveCPU  = 1;

byte *XBuf,*LinePtr, *LargePic;

struct Screen *scr;
struct Window *win;
struct RastPort *rp;
UBYTE Palette[4]={1,2,3,4};

extern BOOL ScaleUp;

char HelpReqText[]= "Virtual Gameboy V0.33\n\
Amiga version by\n\
Michael Boese & Matthias Bethke\n\n\
In-game keys:\n\
'x'   -   Button 'A'\n\
'y'   -   Button 'B'\n\
'.'   -   'Select'\n\
'-'   -   'Start'\n\
All 4 buttons simultaneously: RESET\n\n\
Cursorkeys: Joypad";

char NoSoundReqText[] = "Couldn't get my sound channels!\nNo sound usable!";

struct EasyStruct HelpReq =
{
	sizeof(struct EasyStruct),
	0,
	"VGB Amiga Help",
	HelpReqText,
	"Like it!"
};

struct EasyStruct NoSoundReq =
{
	sizeof(struct EasyStruct),
	0,
	"VGB Amiga Error",
	NoSoundReqText,
	"Oops..."
};

struct IOAudio *(ioa[2]);
BOOL SoundOpen;


int InitMachine()
{
	if(scr = LockPubScreen(NULL))
	{
		if(win = OpenWindowTags(NULL,
					WA_Left,(scr->Width - WIDTH)/2,
					WA_Top,(scr->Height - HEIGHT)/2,
					WA_InnerWidth,ScaleUp?WIDTH*2:WIDTH,
					WA_InnerHeight,ScaleUp?HEIGHT*2: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();
			do
			{
				if(SoundOpen = InitSound()) break;
			} while(EasyRequestArgs(NULL,&NoSoundReq,NULL,NULL) != 0);
			if(XBuf=AllocVec(sizeof(byte)*HEIGHT*WIDTH,MEMF_ANY))
			{
				if(ScaleUp)
				{
					if(LargePic = AllocVec(WIDTH*HEIGHT*4,MEMF_ANY)) return 1;
					fprintf(stderr,"Can't allocate buffer for scaled image!\n");
				} else return 1;
				FreeVec(XBuf);
			}
			CloseWindow(win);
		}
		fprintf(stderr,"Can't open window :(((\n");
		UnlockPubScreen(NULL,scr);
	}
	fprintf(stderr,"No default PubScreen!\n");
	return 0;
}

/** TrashMachine *********************************************/  
/** Deallocate all resources taken by InitMachine().        **/
/*************************************************************/
void TrashMachine(void)
{
int i;
	if(LargePic) FreeVec(LargePic);
	FreeVec(XBuf);
	FreeSound();
	CloseWindow(win);
	for(i=0; i<4; i++) ReleasePen(scr->ViewPort.ColorMap,Palette[i]);
}


/** PutImage *************************************************/
/** Put an image on the screen.                             **/
/*************************************************************/
void PutImage(void)
{
int i,j;
byte *p, *q;

	if(ScaleUp)
	{
		p = XBuf;
		for(j=0; j<HEIGHT; j++)
		{
			q = LargePic + WIDTH * 4 * j;
			for(i=0; i<WIDTH; i++)
			{
				*q++ = *p;
				*q++ = *p++;
			}
			CopyMem(LargePic + WIDTH * 4 * j,LargePic + WIDTH * 4 * j + WIDTH * 2, WIDTH * 2);
		}
		WriteChunkyPixels(rp,win->BorderLeft,win->BorderTop,win->BorderLeft+WIDTH*2-1,win->BorderTop+HEIGHT*2-1,LargePic,160*2);
	}
	else WriteChunkyPixels(rp,win->BorderLeft,win->BorderTop,win->BorderLeft+WIDTH-1,win->BorderTop+HEIGHT-1,XBuf,160);
}


/** Joystick *************************************************/
/** Return the current joystick state.                      **/
/*************************************************************/
byte Joystick(void)
{
struct IntuiMessage *imsg;
ULONG imsg_Class;
UWORD Code;
int J;
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);
}



/** AllocatePens *********************************************/
/** Allocate four pens to be used by the emulation          **/
/*************************************************************/
void __inline AllocatePens(void)
{
int i;
struct
{
	LONG r,g,b;
} GB_Colors[4] = {{0xffffffff,0xffffffff,0xffffffff},{0x98ffffff,0x98ffffff,0x98ffffff},
						{0x58ffffff,0x58ffffff,0x58ffffff},{0x00ffffff,0x00ffffff,0x00ffffff}};

	for(i=0; i<4; i++)
	{
		Palette[i] = (UBYTE)(ObtainBestPenA(scr->ViewPort.ColorMap,GB_Colors[i].r,GB_Colors[i].g,GB_Colors[i].b,NULL));
	}
}

/** InitSound ************************************************/
/** Try to get one left and one right audiochannel          **/
/*************************************************************/
BOOL __inline InitSound(void)
{
UBYTE LeftChannels[]={1,8}, RightChannels[]={2,4};

	if(ioa[0] = NewIOBlock(LeftChannels))
	{
		if(ioa[1] = NewIOBlock(RightChannels))
		{
			return TRUE;
		}
		FreeIOBlock(ioa[0]);
		printf("Can't get right channel!\n!");
	}
	printf("Can't get left channel!\n!");
}

/** FreeSound ************************************************/
/** Deallocate all audiochannels                            **/
/*************************************************************/
void __inline FreeSound(void)
{
	if(SoundOpen)
	{
		FreeIOBlock(ioa[0]);
		FreeIOBlock(ioa[1]);
	}
}

/** PlaySound ************************************************/
/** To be called periodically, calculate wave pattern       **/
/** and translate GB audio registers for audio.device       **/
/*************************************************************/
void __inline PlaySound(void)
{
static UBYTE __chip SampleBuffer[256];

	if(SNDREG52 & 0x80)		// all sound on
	{
		if(SNDREG52 & 0x01)	// sound 1 on
		{
		}
		if(SNDREG52 & 0x02)	// sound 2 on
		{
		}
		if(SNDREG52 & 0x04)	// sound 3 on
		{
			if(SNDREG30 & 0x80)
			{
				if(SNDREG34 & 0x80)	//restart
				{
					SNDREG34 &= 0x7f;	//clear restart bit

				}
			}
		}
		if(SNDREG52 & 0x08)	// sound 4 on
		{
		}
	}
}

/** PlaySample ***********************************************/
/** Play a sample on a previously allocated audiochannel    **/
/*************************************************************/
void __inline PlaySample(UBYTE *Sample, UWORD Length, UWORD Period, UBYTE Vol, UBYTE Channel)
{
	ioa[Channel]->ioa_Period = Period;
	ioa[Channel]->ioa_Volume = Vol;
	ioa[Channel]->ioa_Length = Length;
	ioa[Channel]->ioa_Data = Sample;
	ioa[Channel]->ioa_Cycles = 1;
	SendIO((struct IORequest*)(ioa[Channel]));
}

/** NewIOBlock ************************************************/
/** Allocate a messageport + IORequest and open audio.device **/
/**************************************************************/
struct IOAudio *NewIOBlock(UBYTE *channels)
{
static char SoundPortName[] = "VGB Soundport";
struct IOAudio *ioa;
struct MsgPort *port;

	if(port = CreatePort(SoundPortName,0))
	{
		if(ioa = (struct IOAudio*)CreateExtIO(port,sizeof(struct IOAudio)))
		{
			ioa->ioa_Data = channels;
			ioa->ioa_Length = 2;
			ioa->ioa_Request.io_Message.mn_Node.ln_Pri = 0;
			if((OpenDevice(AUDIONAME,0,(struct IORequest*)ioa,0)) == 0)
			{
				ioa->ioa_Request.io_Flags = ADIOF_SYNCCYCLE;
				ioa->ioa_Period = ioa->ioa_Volume = 0;
				ioa->ioa_Request.io_Command = ADCMD_PERVOL;
				DoIO((struct IORequest*)ioa);
				return ioa;
			}
			DeleteExtIO((struct IORequest*)ioa);
		}
		DeletePort(port);
	}
	return NULL;
}

/** FreeIOBlock **********************************************/
/** Close audio.device and free messageport + IORequest     **/
/*************************************************************/
void FreeIOBlock(struct IOAudio *ioa)
{
	AbortIO((struct IORequest*)ioa);
	CloseDevice((struct IORequest*)ioa);
	DeletePort(ioa->ioa_Request.io_Message.mn_ReplyPort);
	DeleteExtIO((struct IORequest*)ioa);
}

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