#include <stdlib.h>
#include <stdio.h>
#include "timer.h"
#include <io.h>
#include <string.h>
#include "antic.h"
#include "gita.h"
#include "pokey.h"
//#include "pia.h"
#include "a6502.h"

#define HSYNC 114
#define MAX_MEM 49152U

int testflag = 0;
//****************GLOBALS*************************************************
//Define a pointer to all the 6502 functions
Pftype func[256];

void init_video(void);

//emulate hardware
m6502 cpu;
ANTIC pf;
GITA pm;
POKEY snd;
//PIA ext;


//******************************atox()******************************
Word atox(char *str)
{
   Word ans = 0;
   if (str[0] == '0' && (str[1] == 'x' || str[1] == 'X'))
      for (int i=2; i<strlen(str);i++)
      {
	 switch (str[i])
	 {
	    case '0': ans = (ans<<4); break;
	    case '1': ans = (ans<<4)+1;break;
	    case '2': ans = (ans<<4)+2;break;
	    case '3': ans = (ans<<4)+3;break;
	    case '4': ans = (ans<<4)+4;break;
	    case '5': ans = (ans<<4)+5;break;
	    case '6': ans = (ans<<4)+6;break;
	    case '7': ans = (ans<<4)+7;break;
	    case '8': ans = (ans<<4)+8; break;
	    case '9': ans = (ans<<4)+9;break;
	    case 'a': ans = (ans<<4)+10;break;
	    case 'b': ans = (ans<<4)+11;break;
	    case 'c': ans = (ans<<4)+12;break;
	    case 'd': ans = (ans<<4)+13;break ;
	    case 'e': ans = (ans<<4)+14;break ;
	    case 'f': ans = (ans<<4)+15;break ;
	    case 'A': ans = (ans<<4)+10;break;
	    case 'B': ans = (ans<<4)+11;break;
	    case 'C': ans = (ans<<4)+12;break;
	    case 'D': ans = (ans<<4)+13;break ;
	    case 'E': ans = (ans<<4)+14;break ;
	    case 'F': ans = (ans<<4)+15;break ;
	    default: break;
	 }
      }
   return ans;
}
//******************MAIN()***********************************************
void main(int argc, char **argv)
{
   unsigned ind, usr;

   if (argc<2) {puts("usage: emu input_file"); exit(0);}

//reserve space for a test program
   Byte huge *testcode;
   testcode = (Byte huge *) farmalloc(MAX_MEM*sizeof(Byte));
   if (testcode ==NULL) exit(0);

  if (argc==3) usr = atox(argv[2]);
   else usr = 0xf125;


//read in test file
   FILE *in;
   in = fopen(argv[1],"rb");
   ind = fread(testcode, sizeof(Byte), MAX_MEM,in);
   printf("\n\nfile length = %d", ind);
   fclose(in);

//compute the address to start on after reset
   Word res = Word(testcode[02])+(Word(testcode[03])<<8);
//total length of code
   Word end = Word(testcode[04])+(Word(testcode[05])<<8);
   Word len = Word(long(end)-long(res)+1);

#ifdef DEBUG
   printf("\nloading program at %x", res);
   printf("\nlength %x", len);
   printf("\nend at %lx\n", long(res)+long(len));
#endif

   init_video();
   snd.key_setup();

//copy the program into the 6502 memory
   cpu.copy_bytes(res,len,&testcode[6]);
//write the reset address to the 6502 reset vector
   cpu.copy_address(0xfffc,0xf125);

   for (long add = 0xa000;add<0x10000L;add++) *(cpu.rom+Word(add)) = 1;

//clear hardware
//   cpu.ram[0xbffc] = 1;
//   cpu.ram[0x9ffc] = 1;

   puts("starting...");
   cpu.setup_print();

//replace code below with assembly language routine
   while (cpu.opcode!= 0)   {
       //call the appropriate 6502 function and update all registers
   #ifdef DEBUG

       if (cpu.pc == usr+1) testflag = 1;
       if (testflag == 1)  {
	 cpu.print_reg();
	 cpu.print_opcode();
       }
   #endif
       (cpu.*func[cpu.opcode])();

       cpu.opcode = cpu.ram[cpu.pc++];
	 //update video hardware

       if (cpu.clock>=HSYNC) {cpu.clock = 0;cpu.hardware_update();}

   }

	 cpu.fRTS();
	 cpu.print_reg();
	 cpu.print_opcode();

}

//Need to define these routines in for handling write to hardware
void m6502::write_ram(Word ram_address, Byte value)
{
   if (rom[ram_address]==0) ram[ram_address] = value;
   if ((ram_address>>12)==0xd) hardware_write(ram_address, value);
}

void m6502::write_ram(void)
{
   if (rom[address] == 0) ram[address] = a;
   if ((address>>12)==0xd) hardware_write(address, a);
}

void m6502::write_ram(Byte value)
{
   if (rom[address]==0) ram[address] = value;
   if ((address>>12)==0xd) hardware_write(address, value);
}

void m6502::hardware_write(Word add, Byte val)
{
   //update the appropriate hardware chip
   switch ((add>>8)&0x0f)
   {
      case 0: pm.update(add,val); break;
      case 2: snd.update(add,val); break;
//      case 3: ext.update(add,val); break;
      case 4: pf.update(add,val); break;
      default:;
   }
}

void m6502::steal_jsr(Word addr)
{
   if (addr == 0xe459) {n = 1; fRTS();}
}

