#include <90s8515.h> 
#include <stdio.h>
#include <string.h>
#include <delay.h>
  
#asm
   .equ __lcd_port=0x1b  // set LCD to PORTA
#endasm

#include <lcd.h>         // LCD driver routines
             
// Set if you want the game board to start
#define START
// Set if you want to use the prototype board
//#define BOARD

// states for game play state machine
#define sendCoord 1
#define receiveCoord 2
#define start 3
#define sendCheck 4
#define enterCoord 5
#define release 6
#define debounce 7
#define stillPressed 8
#define debounceRelease 9
#define detectTerm 10
#define stillPressedTerm 11     
#define receiveResult 12
#define checkCoord 13
#define sendResult 14
#define pause 15   
#define waitOpponentReady 16
#define displayWait 17
#define dbStart 18        
#define waitAttack 19

// states for getting boats
#define promptBoat 1
#define waitBoat 2
#define getBoat 3
#define debounceBoat 4
#define stillPressedBoat 5

#define t1 30                // timer1: enter state machine every 30ms
#define BUFFER_LENGTH 2      // UART receive buffer length
#define MAX_KEYS 8           // number of valid keys on keypad
#define MAX_LENGTH 2         //number of coordintes
#define enter 8              // enter key '#'

#define NUM_BOATS 2          // number of boats
#define MAX_BOAT_LENGTH 4    // maximum boat length
#define BOARD_SIZE 4	     // max board size is 4x4

// Structure to hold the coordinates                          
struct coordinate {
  unsigned har coord;
  unsigned char hit;
};

// Structure to hold the boats
struct boat {
  struct coordinate position[MAX_BOAT_LENGTH];  // Array of all the coordinates for the boat
  unsigned char length;                         // length of the boat
  unsigned char status;
};

struct boat boatMap[NUM_BOATS];  // map to store boat positions

unsigned char state;             // state machine variable
unsigned char time1, reload;     // timer 1 variables
unsigned char butnum, maybe, keyCount, keyStr;  //keypad scan variables
unsigned char temp;   
unsigned char result;            // result of attack
unsigned char numBoatsLeft;      // number of boats left to be sunk  
unsigned int t;                  // counter
unsigned char mapReady;         
unsigned char startGame;

// UART variables
unsigned char r_index;       
unsigned char r_buffer[BUFFER_LENGTH];   
unsigned char r_ready;      
unsigned char r_char;        

// Game state machine
void _displayWait(void);          //        
void _waitOpponentReady(void);    // wait for opponent to finish entering boats
void _start(void);                // start the turn by pushing '9'
void _dbStart(void);              // debounce start

// Attack states
void _enterCoord(void);           // enter coordinates for attack 
void _sendCoord(void);            // send coordinates to other mcu
void _sendCheck(void);            // check transmission complete
void _pause(void);                // wait for results from other mcu
void _receiveResult(void);        // receive result from other mcu

// Defense states
void _receiveCoord(void);         // recieve atatck coordinates
void _checkCoord(void);           // check coordinates against map
void _sendResult(void);           // send result to other mcu
void _waitAttack(void);           // pause before switching to attack

// Read map state machine
void readMapControl(void);		// control for map reading state machine
void _promptBoat(void);	   	// prompt user for boat input
void _waitBoat(void);			// wait for user input		
void _getBoat(void);			// calls boardScan to read boat
void _debounceBoat(void);		
void _stillPressedBoat(void);

// Keypad states
void _release(void);
void _debounce(void);
void _detectTerm(void);
void _stillPressed(void);
void _stillPressedTerm(void);
void _debounceRelease(void);          

void control(void);                  // state machine control
void initialize(void);               // initialization
void keypadScan();                   // keypad scan routine
char convertToA(char);               // convert to ascii
void boardScan(void);                // board scan routine
void insertBoat(char, char *, char); // insert boat into map
void initMap(void);                  // debug function for creating a map
char findBoat(char, char);           // find a boat in the map


//key pad scan table
//{1, 2, 3, 4, 5, 6, 7, 9}
flash unsigned char keytbl[MAX_KEYS]={0xf6, 0xee, 0xbe, 0xf5, 0xed, 0xbd, 0xf3, 0xbb};

// Values used for boardScan 
flash unsigned char DDRDtbl[7] = {0, 4, 8, 16, 32, 64, 128};
flash unsigned char masktbl[7] = {2, 4, 8, 16, 32, 64, 128};  //2^(i)
					
//**********************************************************
//timer 0 overflow ISR
interrupt [TIM0_OVF] void timer0_overflow(void)
{
    
  //reload to force 1 mSec overflow
  TCNT0=reload;
  
  //Decrement the three times if they are not already zero
  if (time1>0)	--time1;

}  

//**********************************************************
//interrupt when we have something to get from UDR
interrupt [UART_RXC] void uart_rec(void) 
{
  r_char = UDR;   // get a char
  if(r_char == 0xbe)
  {
  	startGame = 1;
  	return;
  }
  // build the input string
  if ((r_char != 0x00) && (r_index < BUFFER_LENGTH)) 
    r_buffer[r_index++]=r_char;
  else
  {
    r_buffer[r_index]=0x00;    // zero terminate
    r_ready=1;                 // signal cmd processor
    UCR.7=0;                   // disable recieve	
  }
}  

//**********************************************************
// Main task scheduler loop
void main(void)
{    
  initialize();
  //initMap();
  
  while(1)
  {
    if (time1==0)
    {
      if(mapReady == 0)
        readMapControl();
      else
        control();
    }
  }
}

//*********************************************************
// State machine control
void control(void)
{
  time1 = t1;   
  
  switch(state)
  {          
  case displayWait:
    _displayWait();
    break;
  case waitOpponentReady:
    _waitOpponentReady();
    break;
  case start:
    _start();
    break;
  case dbStart:
    _dbStart();
    break;
  case enterCoord:
    _enterCoord();
    break;
  case sendCoord:
    _sendCoord();
    break;
  case sendCheck:
    _sendCheck();
    break;         
  case pause:
    _pause();
    break;
  case receiveResult:
    _receiveResult();
    break;
  case receiveCoord:
    _receiveCoord();   
    break;     
  case checkCoord:
    _checkCoord();
    break;
  case sendResult:
    _sendResult();
    break;    
  case waitAttack:
    _waitAttack();
    break;
  case release:
    _release();
    break;
  case debounce:
    _debounce();
    break;
  case detectTerm:
    _detectTerm();
    break;
  case stillPressedTerm:
    _stillPressedTerm();
    break;
  case stillPressed:
    _stillPressed();
    break;
  case debounceRelease:
    _debounceRelease();
    break;
  default:
    state = start;    
    _start();
    break;
  }

}

void _displayWait(void)
{
  lcd_clear();
  lcd_putsf("Waiting!"); 
  UCR.7 = 1;
  state = waitOpponentReady;
}

void _waitOpponentReady(void)
{
  if(startGame == 1)
  {  
    //let the games begin!
    lcd_clear();

#ifdef START
    state = start;
    lcd_putsf("Go Go Go!");		
    UCR.7 = 0; 
#else  
    state = receiveCoord;  
    lcd_putsf("Look out!");		
    UCR.7 = 1;      
#endif 
    }
}

void _start(void)
{                
  if (butnum == enter)
    state = dbStart;  
  else 
    keypadScan();  
}

void _dbStart(void)
{
  if(butnum != enter)
    state = enterCoord;
  else
    keypadScan();
}

void _enterCoord(void)
{
  //prompt user to enter xy coordinates
  lcd_clear();
  lcd_gotoxy(0,0);
  lcd_putsf("xy=");

  keypadScan();
  keyCount = 0;
  state = release;
}

void _release(void)
{
  if (butnum==0)
    keypadScan();
  else
  {
    maybe = butnum;
    keypadScan();
    state = debounce;
  }
}

void _debounce(void)
{
  if (butnum==maybe)
  {
     state = detectTerm;
  }
  else
  {
    keypadScan();
    state = release;
  }
}

void _detectTerm(void)
{
  if (butnum == enter)
  {
    if (keyCount == MAX_LENGTH)
      state = stillPressedTerm;  
    else
    {
      state = release;
      keypadScan();
    }
  } 
  else
  {
    if (keyCount < MAX_LENGTH) 
    { 
      if (keyCount == 0)
        keyStr = butnum;
      else 
        keyStr = (keyStr << 4) | butnum;

      // display coordinate on LCD
      lcd_gotoxy(keyCount+3, 0);            
      lcd_putchar(convertToA(butnum));    
      keyCount++;                             
    }

    keypadScan();
    state = stillPressed;
  }
}

void _stillPressedTerm(void)
{
  if (butnum == enter) 
  {
    keypadScan();
  }
  else 
  {
    keyCount=0;
    state = sendCoord;
  }
}

void _stillPressed(void)
{
  if (butnum == maybe)
  {
    keypadScan();
  }
  else 
  {
    keypadScan();
    state = debounceRelease;
  }
}

void _debounceRelease(void)
{
  if (butnum == maybe)
  {
    keypadScan();
    state = stillPressed;
  }
  else
  {
    keypadScan();
    state = release;
  }
}
    
void _sendCoord(void)
{      
  //Send x,y, and term    
  putchar(keyStr);
  putchar('\0');
  
  state = sendCheck;
}    

void _sendCheck(void)
{
  if (USR.6 == 1)
  {
    state = pause;
    UCR.7 = 1;
                   
    lcd_clear();
    temp = 0;
    t = 10;    
  }	
}

void _pause(void)
{   
  if (t==0) 
  {
    lcd_gotoxy(temp++,0);
    lcd_putsf(".");
    t = 10;
  }
  else
    t--;
    
  if (temp == 7)
    state = receiveResult;

}

void _receiveResult(void)
{       
  if(r_ready==1)
  {             
    lcd_clear(); 
    lcd_gotoxy(0,0);
    if(r_buffer[0] != 0xff) 
    {  
      temp = r_buffer[0] & 0x07;
      switch (temp) 
      {
      case 1:
        lcd_putsf("*hit*");
        break;
      case 3:
        lcd_putsf("*sunk*");
        break;
      case 7:
        lcd_putsf("You win!");
        break;
      }  
    }
    else 
      lcd_putsf("miss :p");
    
    r_ready = 0;
    r_index = 0;  
    UCR.7 = 1;
    state = receiveCoord;  
  }
}

void _receiveCoord(void)
{        
    
  if(r_ready==1)  
  { 
    lcd_clear();
    lcd_putsf("bomb@ "); 
    
    // display on LCD
    temp = r_buffer[0] >> 4;
    lcd_gotoxy(5,0);
    lcd_putchar(convertToA(temp));
    temp = r_buffer[0] & 0x0f;
    lcd_gotoxy(6,0);
    lcd_putchar(convertToA(temp));
    
    state = checkCoord;    
    r_ready = 0;
    r_index = 0;
  }
}   

void _checkCoord(void)
{
  char index;
  index = findBoat(r_buffer[0], 1);  // look for boat

  // is boat found?
  if(index != -1)
  {
    result = 0x01;
    boatMap[index].status--;
  	
    // boat sunk
    if(boatMap[index].status == 0)
    {
      result = 0x03;
      numBoatsLeft--;
  		
      // all boats sunk
      if(numBoatsLeft == 0)
        result = 0x07;
    }
  }
  else 
    result = 0xff;
  
  state = sendResult;  
}

void _sendResult(void)
{
  putchar(result);
  putchar('\0');
  t = 75;
  state = waitAttack;
}  

void _waitAttack(void)
{
  if (t==0)
  {
     //display result
    lcd_clear(); 
    lcd_gotoxy(0,0);
    if(result != 0xff) 
    {  
      temp = result & 0x07;
      switch (temp) 
      {
      case 1:
        lcd_putsf("ouch :o");
        break;
      case 3:
        lcd_putsf("bye boat");
        break;
      case 7:
        lcd_putsf("You lost!");
        break;
      }  
    }
    else 
      lcd_putsf("phew :)");     
     
    state = start;    
    keypadScan();  
  }
  else
     t--;

}   
 
void initialize(void)
{
  UCR = 0x10 + 0x08 ;   //enable serial transmit and receive
  UBRR = 25 ;           //set baud rate to 9600      
  
  UCR.7 = 1;
  
  //set up timer 0     
  //62.5 x (64x.25) microSec = 1.0 mSec, so prescale 64, and count 62 times.
  reload=256-62; 
  TCNT0=reload;	 
  TCCR0=3;	 
  TIMSK=2;	 
  
  //task1 timer
  time1 = t1;
  
  lcd_init(16);     
  lcd_clear();  
  
  r_ready = 0; // buffer not ready
  r_index = 0; // initialize buffer index

  // Read map first
  numBoatsLeft = 0;
  /*initMap();
  mapReady = 1;
  startGame = 1;
  state = waitOpponentReady;
  */
  state = promptBoat;
  mapReady = 0;
  startGame = 0;
  
  //crank up the ISRs  
  #asm
    sei
  #endasm
}  


//**********************************************************
// scan keypad                             
void keypadScan(void) 
{
  char key; //current key

  //get upper nibble
  DDRB = 0x27;
  PORTB = 0xd8; 
  delay_us(5);
  key = PINB;
      
  //get lower nibble
  DDRB = 0xd8;
  PORTB = 0x27; 
  delay_us(5);
  key = key | PINB;
      
  //find matching keycode in keytbl
  if (key != 0xff)
  {   
    for (butnum=0; butnum<MAX_KEYS; butnum++)   
      if (keytbl[butnum]==key)  break;  
      
      if (butnum==MAX_KEYS) butnum=0;
      else butnum++;	            
  }  
  else 
    butnum=0;     
        
}

//*********************************************************
// convertToA subroutine
char convertToA(char i)
{
  if(i < 8)
    return i + 48;
  else 
    return '?';
}

//*********************************************************
// Insert boat into map
void insertBoat(char index, char *pos, char numPos)
{
	int i;
	boatMap[index].length = numPos;
	boatMap[index].status = numPos;
	
	for(i=0; i<numPos; i++)
	{
		boatMap[index].position[i].coord = pos[i];
	 	boatMap[index].position[i].hit = 0;
	}
}

// Looks to see if a boat exists at coordinate pos.
// If there is a boat, set the coordinate at pos to hit 
// and return the index of the boat. 
char findBoat(char pos, char hit)
{
	char i, j;
	char boatLength;
	
	// Loop through all the boats in boatMap
	for(i=0; i<NUM_BOATS; i++)
	{
		boatLength = boatMap[i].length;
		// Loop through the length of the boat
		for(j=0; j<boatLength; j++)
		{                                     
			// Check to see if pos is contained in the boat
			if(pos == boatMap[i].position[j].coord)
			{
				// if we're trying to hit.. but spot already hit..
				// return -1 (miss)
				if((hit != 0) && (boatMap[i].position[j].hit == 1))
					return -1;
					
				// set the hit value and return index of boat
				boatMap[i].position[j].hit = hit;
				return i;
			}
		}		
	}
    // didn't find coordinate.. so return -1
	return -1;
}




//**********************************************************
// Scan the game board for a boat                        
void boardScan(void) 
{
  char key;
  char row, col, pos, coord;
  char boatCoord[MAX_BOAT_LENGTH];
  char foundBoat[MAX_BOAT_LENGTH];
  char fb, i;
  lcd_clear();
  
  DDRC = 0x01;
  
#ifdef BOARD
  PORTC = 0x00;                  
#else 
  DDRD = 0xff;      
#endif

  pos = 0;
  fb = 0;
  //loop through x coord
  for (row=1; row <= BOARD_SIZE; row++) 
  {
#ifdef BOARD
    DDRD = DDRDtbl[row-1];
    PORTD = ~DDRD; 
#else
    if (row==1) PORTC = 0xFE;     
    else PORTC = 0xFF;
    PORTD = ~DDRDtbl[row-1];  
#endif    
 
    delay_us(5);
    
    key = ~PINC;
    
    //figure out y coord(s)    
    if ((key != 0) && (key != 1)) //code this better
    {  
      for (col=1; col <= BOARD_SIZE; col++)
      {
        if ( (key & masktbl[col-1]) != 0 )
        {
          coord = (row << 4) | col;    
          
          //check if boat already exists
          if ( findBoat(coord, 0) == -1 ) 
          {
            //if not save coordinate position
            boatCoord[pos++] = coord;          
          }
          else
          {
          	foundBoat[fb++] = coord;
          }
        }
      }
    }  
  }   
  
  if (pos != 0)
    insertBoat(numBoatsLeft++, boatCoord, pos);    
    
  /*if (fb != 0)
  {
  	for(i=0; i<fb; i++)
  	{
  		row = foundBoat[i] >> 4;
  		col = foundBoat[i] & 0x0f;
  		lcd_gotoxy((i)*3,0);
    	lcd_putchar(convertToA(row));
     	lcd_gotoxy((i)*3+1,0);
      	lcd_putchar(convertToA(col));
  	}
  } 
  else
  {     
  		lcd_gotoxy(2, 0);
  		lcd_putchar('$');    
  */      
  
  	for(i=0; i<pos; i++)
  	{
  	
  		row = boatCoord[i] >> 4;
  		col = boatCoord[i] & 0x0f;
  		lcd_gotoxy((i)*3,0);
    	lcd_putchar(convertToA(row));
     	lcd_gotoxy((i)*3+1,0);
      	lcd_putchar(convertToA(col));
  	}
                                            

}       

void readMapControl(void)
{
	time1 = t1;

	switch(state)
	{
	case promptBoat:
		_promptBoat();
		break;
	case waitBoat:
		_waitBoat();
		break;
	case getBoat:
		_getBoat();
		break;
	case debounceBoat:
		_debounceBoat();
		break;
	case stillPressedBoat:
		_stillPressedBoat();
		break;
	default:
		state = promptBoat;
	}

}

void _promptBoat(void)
{
	if(numBoatsLeft != NUM_BOATS)
	{
		lcd_clear();
		lcd_gotoxy(0,0);
		lcd_putsf("Boat");
		lcd_gotoxy(5, 0);
		lcd_putchar(convertToA(numBoatsLeft));

		state = waitBoat;
		keypadScan();
	}
	else
	{   
		// map is now ready...
		// let the other board know you're ready!!!
		putchar(0xbe);
		mapReady = 1;
		state = displayWait;
		//putchar('\0');
	}
}
void _waitBoat(void)
{
	if (butnum==0)
		keypadScan();
	else if (butnum==enter)
	{
		keypadScan();
		state = debounceBoat;
	}
}

void _debounceBoat(void)
{
	if (butnum==enter)
	{
		state = getBoat;
	}
	else
	{
		state = debounceBoat;
		keypadScan();
	}
}

void _getBoat(void)
{
	state = stillPressedBoat;
	boardScan();
	keypadScan();
}

void _stillPressedBoat(void)
{
	if (butnum != enter)
		state = promptBoat;
	
	keypadScan();
}

/***
* Used for debugging purposes... creates a default map with boats already in place
*/
void initMap(void)
{
	unsigned char blah[4];
  /*	blah[0] = 0x77;
	blah[1] = 0x76;
	blah[2] = 0x75;
	bl