#include "antic.h"
#include "a6502.h"
#include "gita.h"

#define Hsize 	320
#define LB	0x1f

extern Byte far *video_page;

extern m6502 cpu;
extern ANTIC pf;
extern GITA pm;

//******************setup everything************************************
ANTIC::ANTIC(void)
{
   dlist = 0;
   pc = 0;
   evcount = 0;
   memscan = 0;
   wait = 1;
   vscrol = 0;
   sc = 0;
   amodes[0] = &ANTIC::mode0;
   amodes[1] = &ANTIC::mode1;
   amodes[2] = &ANTIC::mode2;
   amodes[3] = &ANTIC::mode3;
   amodes[4] = &ANTIC::mode4;
   amodes[5] = &ANTIC::mode5;
   amodes[6] = &ANTIC::mode6;
   amodes[7] = &ANTIC::mode7;
   amodes[8] = &ANTIC::mode8;
   amodes[9] = &ANTIC::mode9;
   amodes[10] = &ANTIC::mode10;
   amodes[11] = &ANTIC::mode11;
   amodes[12] = &ANTIC::mode12;
   amodes[13] = &ANTIC::mode13;
   amodes[14] = &ANTIC::mode14;
   amodes[15] = &ANTIC::mode15;
}

void ANTIC::update(Word addr, Byte data)
{
   ram[addr&LB] = data;
   //check for special cases
   if (addr == NMIRES) cpu.ram[NMIST] = 0; //reset nmi flags
   if (addr == WSYNC) cpu.clock = 107;	//advance counter
   if (addr == DLISTH || addr == DLISTL)
      dlist = (Word(pf.ram[DLISTH&LB])<<8) + Word(pf.ram[DLISTL&LB]);
   //That's all the special cases I know
}

//**********************ANTIC routines***********************************
void ANTIC::mode0(void)
{
   //This instruction adds blank lines to the screen

   Byte blank = ((dinstr>>4)&0x7)+1; //number of blank lines
   evcount += blank;
   sc = blank;                	//for gita
   dliflag = ((dinstr&MDLI)!=0);
   ++pc;
}

void ANTIC::mode1(void)
{
   //this mode reloads the display list address
   dlist = Word(cpu.ram[dlist+pc+1]) + (Word(cpu.ram[dlist+pc+2])<<8);
   sc = 0;			//nothing displayed
   pc = 0;			//reset instruction counter
   if ((dinstr&b6) != 0) wait = 1;	//set JVB flag
   dliflag = ((dinstr&MDLI)!=0);
}

/*//###################short version########################
void ANTIC::mode2(void)
{
   //this mode is the 40x24 text mode

   //Read in display data location if bit 6 set
   if (dinstr&MMSCAN)
	 memscan = Word(cpu.ram[dlist+(++pc)]) +
		  (Word(cpu.ram[dlist+(++pc)])<<8);

   //start display at evcount = 32
   if (evcount>224)	//off display
   {
      evcount++;
      return;
   }
   evcount += sc;

   //get the character display control byte
   Byte chactl = ram[CHACTL&LB];

   //ch1 and ch2 hold the pixel data to be written to the screen.
   //ch1 (ach1) is the current character,
   Byte ch1[8], ach1;
   //ascii characters to be displayed

   Byte huge *pchbase = &cpu.ram[(Word(ram[CHBASE&LB])<<8)];

   //Display entire line of text
   for (int k=0;k<40;k++)
   {
   //take care of horizontal shift by reading in current and
   //previous characters.
      ach1 = cpu.ram[memscan];
      memscan++;

      //construct the kth character to display
      for (int i=0;i<8;i++)
	 ch1[i] = pchbase[(ach1&0x7f)*8+i];

	 for (int j=0;j<8;j++)
	    for (int ii=0;ii<8;ii++)
	    video_page[(evcount - 40 + j)*Hsize + (k<<3) + ii] =
	       ((ch1[j]>>(8-ii-1))&0x01)?pm.ram[COLPF1&LB]:
		  pm.ram[COLPF2&LB];
   } //end k

   ++pc;
   dliflag = ((dinstr&MDLI)!=0);
}
*/
//**************************commented out******************************

void ANTIC::mode2(void)
{
   //this mode is the 40x24 text mode

   Byte h = 8;			//character height
   Byte w = 8;			//character width
   Byte chcount = 40;		//number of characters to display on
				//a line.

   //screen width int bits used for horizontal scrolling
   Byte mw = ((dinstr&MHSCROL)!=0)*32 + 96 + 32*(ram[DMACTL&LB]&0x3);

   if (mw>192) mw = 192;	//force screen width to be <=192 or >=128
   else if (mw<128) mw = 0;
   mw /= (w/2);			//screen width in characters

   //Read in display data location if bit 6 set
   if (dinstr&MMSCAN)
	 memscan = Word(cpu.ram[dlist+(++pc)]) +
		  (Word(cpu.ram[dlist+(++pc)])<<8);

   //start display at evcount = 32
   if (evcount>224)	//off display
   {
      evcount++;
      return;
   }
   evcount += sc;

   //get the character display control byte
   Byte chactl = ram[CHACTL&LB];

   //ch1 and ch2 hold the pixel data to be written to the screen.
   //ch1 (ach1) is the current character,
   //ch2 (ach2) is the previous character.
   Byte ch1[8], ch2[8];
   //ascii characters to be displayed
   Byte ach1, ach2;

   //Number of pixels to scroll horizontally
   //=0 if hscrol not enabled
   Byte hs = ((dinstr&MHSCROL) !=0) * (ram[HSCROL&LB]&0x0f);

   //determine the number of whole characters is shifted
   Byte ihs = hs/4;
   //get number of pixels to shift
   Byte shift = hs - ihs*4;

   Byte huge *pchbase = &cpu.ram[(Word(ram[CHBASE&LB])<<8)];

   //Display entire line of text
   for (int k=0;k<chcount;k++)
   {
   //take care of horizontal shift by reading in current and
   //previous characters.
      ach1 = cpu.ram[memscan - ihs];
      ach2 = cpu.ram[memscan - ihs - 1];
      memscan++;

      //construct the kth character to display
      for (int i=0;i<h;i++)
      {
	 ch1[i] = pchbase[(ach1&0x7f)*h+i];
	 ch2[i] = pchbase[(ach2&0x7f)*h+i];
	 if (ach1&b7)
	 {
	    if (chactl&b0) ch1[i] = 0;
	    if (chactl&b1) ch1[i] = ~ch1[i];
	    if (chactl&b2) ch1[h-i-1] = ch1[i];
	 }
	 if (ach2&b7)
	 {
	    if (chactl&b0) ch2[i] = 0;
	    if (chactl&b1) ch2[i] = ~ch2[i];
	    if (chactl&b2) ch2[h-i-1] = ch2[i];
	 }
	 ch1[i] = (ch2[i]<<(h-shift)) + (ch1[i]>>shift);

      } //end i

      //deal with vertical scrolling and display caharacter
      if (!vscrol && dinstr&MVSCROL)
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 1;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	       video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j+h-sc]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];

      }
      else if (vscrol && ((dinstr&MVSCROL)==0))
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 0;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];
      }
      else
      {
	 sc = h;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 40 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:
		  pm.ram[COLPF2&LB];
      }
   } //end k

   memscan += mw-chcount;
   ++pc;
   dliflag = ((dinstr&MDLI)!=0);
}
//******************end commented out*************************************/

//********************mode3********************************************
void ANTIC::mode3(void)
{
   //this mode is the 40x19 text mode

   Byte h = 10;			//character height
   Byte w = 8;			//character width
   Byte chcount = 40;		//number of characters to display on
				//a line.
   Byte mw = ((dinstr&MHSCROL)!=0)*32 + 128 + 32*ram[DMACTL&LB]&0x3;

   if (mw>192) mw = 192;	//memory width
   else if (mw<128) mw = 0;
   mw /= w;			//width in characters

   if (dinstr&MMSCAN)
   {
	 memscan = Word(cpu.ram[dlist+(++pc)]) + (Word(cpu.ram[dlist+(++pc)])<<8);
	 pc +=2;
   }


   if (evcount<32 || evcount>224)	//off display
   {
      evcount+=sc;
      memscan += w;
      return;
   }
   evcount += sc;

   Byte chactl = ram[CHACTL&LB];

   Byte ch1[8], ch2[8];
   Byte ach1, ach2;
   Byte hs = ((dinstr&MHSCROL) !=0) * (ram[HSCROL&LB]&0x0f);
   Byte ihs = hs/4;
   Byte shift = hs - ihs;

   Byte huge *pchbase = &cpu.ram[(ram[CHBASE&LB]<<8)];

   //Display entire line of text
   for (int k=0;k<chcount;k++)
   {
   //take care of horizontal shift by reading in current and
   //previous characters.
      ach1 = cpu.ram[memscan++ - ihs];
      ach2 = cpu.ram[memscan++ - ihs - 1];

      for (int i=0;i<h;i++) {ch1[i] = ch2[i] = 0;}

      //construct character to display
      for (i=0;i<h-2;i++)
      {
	 if (ach1&0x60) ch1[i+2] = pchbase[(ach1&0x7f)*h+(i+1)%8];
	 else ch1[i] = pchbase[(ach1&0x7f)*h+i];
	 if (ach2&0x60) ch2[i+2] = pchbase[(ach2&0x7f)*h+(i+1)%8];
	 else ch2[i] = pchbase[(ach2&0x7f)*h+i];

	 if (ach1&b7)
	 {
	    if (chactl&b0) ch1[i] = 0;
	    if (chactl&b1) ch1[i] = ~ch1[i];
	    if (chactl&b2) ch1[h-i-1] = ch1[i];
	 }
	 if (ach2&b7)
	 {
	    if (chactl&b0) ch2[i] = 0;
	    if (chactl&b1) ch2[i] = ~ch2[i];
	    if (chactl&b2) ch2[h-i-1] = ch2[i];
	 }
	 ch1[i] = (ch2[i]<<(h-shift)) + (ch1[i]>>shift);

      } //end i

      //deal with vertical scrolling and display caharacter
      if (!vscrol && dinstr&MVSCROL)
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 1;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	       video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j+h-sc]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];

      }
      else if (vscrol && ((dinstr&MVSCROL)==0))
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 0;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];
      }
      else
      {
	 sc = h;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];
      }
   } //end k
   evcount += sc;
   memscan += mw - chcount;
   ++pc;
   dliflag = ((dinstr&MDLI)!=0);
}

//********************mode4********************************************
void ANTIC::mode4(void)
{
   //this mode is the 40x24 text mode with

   Byte h = 10;			//character height
   Byte w = 8;			//character width
   Byte chcount = 40;		//number of characters to display on
				//a line.
   Byte mw = ((dinstr&MHSCROL)!=0)*32 + 128 + 32*ram[DMACTL&LB]&0x3;

   if (mw>192) mw = 192;	//memory width
   else if (mw<128) mw = 0;
   mw /= w;			//width in characters

   if (dinstr&MMSCAN)
   {
	 memscan = cpu.ram[dlist+(++pc)] + (cpu.ram[dlist+(++pc)]<<8);
	 pc +=2;
   }


   if (evcount<32 || evcount>224)	//off display
   {
      evcount+=sc;
      memscan += w;
      return;
   }
   evcount += sc;

   Byte chactl = ram[CHACTL&LB];

   Byte ch1[8], ch2[8];
   Byte ach1, ach2;
   Byte hs = ((dinstr&MHSCROL) !=0) * (ram[HSCROL&LB]&0x0f);
   Byte ihs = hs/4;
   Byte shift = hs - ihs;

   Byte huge *pchbase = &cpu.ram[(ram[CHBASE&LB]<<8)];

   //Display entire line of text
   for (int k=0;k<chcount;k++)
   {
   //take care of horizontal shift by reading in current and
   //previous characters.
      ach1 = cpu.ram[memscan++ - ihs];
      ach2 = cpu.ram[memscan++ - ihs - 1];

      for (int i=0;i<h;i++) {ch1[i] = ch2[i] = 0;}

      //construct character to display
      for (i=0;i<h-2;i++)
      {
	 if (ach1&0x60) ch1[i+2] = pchbase[(ach1&0x7f)*h+(i+1)%8];
	 else ch1[i] = pchbase[(ach1&0x7f)*h+i];
	 if (ach2&0x60) ch2[i+2] = pchbase[(ach2&0x7f)*h+(i+1)%8];
	 else ch2[i] = pchbase[(ach2&0x7f)*h+i];

	 if (ach1&b7)
	 {
	    if (chactl&b0) ch1[i] = 0;
	    if (chactl&b1) ch1[i] = ~ch1[i];
	    if (chactl&b2) ch1[h-i-1] = ch1[i];
	 }
	 if (ach2&b7)
	 {
	    if (chactl&b0) ch2[i] = 0;
	    if (chactl&b1) ch2[i] = ~ch2[i];
	    if (chactl&b2) ch2[h-i-1] = ch2[i];
	 }
	 ch1[i] = (ch2[i]<<(h-shift)) + (ch1[i]>>shift);

      } //end i

      //deal with vertical scrolling and display caharacter
      if (!vscrol && dinstr&MVSCROL)
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 1;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	       video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j+h-sc]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];

      }
      else if (vscrol && ((dinstr&MVSCROL)==0))
      {
	 sc = h - (ram[VSCROL&LB]&0x7);
	 vscrol = 0;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];
      }
      else
      {
	 sc = h;
	 for (int j=0;j<sc;j++)
	    for (int ii=0;ii<w;ii++)
	    video_page[(evcount - 32 + j)*Hsize + k*w + ii] =
	       ((ch1[j]>>(w-ii-1))&0x01)?pm.ram[COLPF1&LB]:pm.ram[COLPF2&LB];
      }
   } //end k
   evcount += sc;
   memscan += mw - chcount;
   ++pc;
   dliflag = ((dinstr&MDLI)!=0);
}

void ANTIC::mode5(void){}
void ANTIC::mode6(void){}
void ANTIC::mode7(void){}
void ANTIC::mode8(void){}
void ANTIC::mode9(void){}
void ANTIC::mode10(void){}
void ANTIC::mode11(void){}
void ANTIC::mode12(void){}
void ANTIC::mode13(void){}
void ANTIC::mode14(void){}
void ANTIC::mode15(void){}
