/** VGB: portable GameBoy emulator ***************************/
/**                                                         **/
/**                           Amiga.c                       **/
/**                                                         **/
/** Amiga-specific graphics and joystick 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 0.2";

int  ExitFlag = 0;
int  SaveCPU  = 1;

byte *XBuf,*LinePtr;

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

char ReqText[]= "Virtual Gameboy V0.2\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";

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

/** PUTIMAGE: macro copying image buffer into a window ********/
#define PutImage WriteChunkyPixels(rp,0,0,WIDTH,HEIGHT,XBuf,160);


int InitMachine()
{
	if(scr = LockPubScreen(NULL))
	{
		if(win = OpenWindowTags(NULL,
					WA_Left,(scr->Width - WIDTH)/2,
					WA_Top,(scr->Height - HEIGHT)/2,
					WA_InnerWidth,WIDTH,
					WA_InnerHeight,HEIGHT,
					WA_Title,Title,
					WA_GimmeZeroZero,TRUE,
					WA_CloseGadget,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(LinePtr=XBuf=AllocVec(sizeof(byte)*HEIGHT*WIDTH,MEMF_ANY)) return(1);
		} else
		{
			fprintf(stderr,"Can't open window :(((\n");
		}
	} else
	{
		fprintf(stderr,"No default PubScreen!\n");
	}
}

void TrashMachine()
{
int i;
	FreeVec(XBuf);
	CloseWindow(win);
	for(i=0; i<4; i++) ReleasePen(scr->ViewPort.ColorMap,Palette[i]);
}

void RefreshScreen()
{
/*
  printf("\033[H");
  for(P=RAM+0xFF40;P<RAM+0xFF4F;P++) printf("%X ",*P);
  printf("\nIFLAGS:%X   ISWITCH:%X\n",IFLAGS,ISWITCH);
*/

  LinePtr=XBuf;PutImage;
}

void RefreshSprites()
{
  register byte *P,*T,*R,*S,I,J,K,N,D0,D1;
  register int DX,DY;

	N=LCDCONT&0x04? 16:8;

  for(S=RAM+0xFE9C,J=40;J;S-=4,J--)
  {
    I=S[3]&0x40? S[0]-1:S[0]-16;
    K=S[3]&0x20? S[1]-1:S[1]-8;
    if((S[0]>=16)&&(S[1]>=8)&&(S[0]<=160-N)&&(S[1]<=160))
    {
      DY=(S[3]&0x40? -WIDTH:WIDTH)+(S[3]&0x20? 8:-8);
      DX=(S[3]&0x20? -1:1);
      P=XBuf+I*WIDTH+K;
      T=RAM+0x8000+((long)S[2]<<4);
      R=(S[3]&0x10)? SPal0:SPal1;
      for(I=N;I;I--,P+=DY)
        for(K=0x80,D0=*T++,D1=*T++;K;K>>=1,P+=DX)
          if((D0|D1)&K) *P=Palette[R[D0&K? (D1&K? 0:2):(D1&K? 1:3)]];
    }
  }
}

void RefreshLine()
{
  register byte Offset,Mask,*P,*T,D0,D1,X1,X2;

  if((LCDCONT&0x81)!=0x81)
    for(X1=0;X1<WIDTH;X1++) *LinePtr++=Palette[3];
  else
  {
    Offset=CURLINE+SCROLLY;
    T=BgdTab+((word)(Offset&0xF8)<<2);
    Offset=(Offset&0x07)<<1;

    D0=*(T+(SCROLLX>>3));
    if(~LCDCONT&0x10) D0+=0x80;
    P=ChrGen+(D0<<4)+Offset;
    D0=*P++;D1=*P;Mask=0x80>>(SCROLLX&0x07);

    X1=SCROLLX;X2=SCROLLX+WIDTH;
    while(X1!=X2)
    {
      *LinePtr=Palette[BPal[D0&Mask? (D1&Mask? 0:2):(D1&Mask? 1:3)]];
      LinePtr++;X1++;
      if(!(Mask>>=1))
      {
        D0=*(T+(X1>>3));
        if(~LCDCONT&0x10) D0+=0x80;
        P=ChrGen+(D0<<4)+Offset;
        D0=*P++;D1=*P;Mask=0x80;
      }
    }
  }

  X1=WNDPOSX-7;X2=LCDCONT&0x02;
  if(((LCDCONT&0xA0)==0xA0)&&(WNDPOSY<CURLINE)&&(X1<WIDTH))
  {
    LinePtr-=WIDTH-X1;
    Offset=CURLINE-WNDPOSY;
    T=WndTab+((word)(Offset&0xF8)<<2);
    Offset=(Offset&0x07)<<1;

    D0=*T+0x80;
    P=RAM+0x8800+((long)D0<<4)+Offset;
    D0=*P++;D1=*P;Mask=0x80;

    for(;X1<WIDTH;X1++,LinePtr++)
    {
      *LinePtr=Palette[BPal[D0&Mask? (D1&Mask? 0:2):(D1&Mask? 1:3)]];
      if(!(Mask>>=1))
      {
        D0=*(++T)+0x80;
        P=RAM+0x8800+((long)D0<<4)+Offset;
        D0=*P++;D1=*P;Mask=0x80;
      }
    }
  }
}

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("\033[H\033[2J*** REGISTERS: ***");		//F2 up
												for(J=0xFF40;J<0xFF50;J++)
													printf("(%Xh) = $%X\n",J,*(RAM+J));
												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);
}

void AllocatePens(void)
{
int i;
struct
{
	LONG r,g,b;
} GB_Colors[4] = {{0x00ffffff,0x00ffffff,0x00ffffff},{0x58ffffff,0x58ffffff,0x58ffffff},
						{0x98ffffff,0x98ffffff,0x98ffffff},{0xffffffff,0xffffffff,0xffffffff}};

	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));
	}
}