#include "xparameters.h"
#include "xintc.h"
#include "stdio.h"
#include "stdlib.h"
#include "xps2.h"
#include "xbasic_types.h"
#include "xtmrctr.h"
#include "tmrctr_header.h"
#include "uartlite_header.h"
#include "graph_acc.h"
#include "scancodes.h"
#include "xil_exception.h"
#include <stdint.h>
#include <stdbool.h>
#include "xstatus.h"
#include "my_timer.h"

//====================================================

#define PS2_DEVICE_ID		XPAR_PS2_0_DEVICE_ID
#define INTC_DEVICE_ID		XPAR_INTC_0_DEVICE_ID
#define INTR_ID			XPAR_INTC_0_PS2_0_VEC_ID


/************************** Function Prototypes *****************************/

int Ps2IntrExample(XIntc* IntcInstPtr, XPs2* Ps2InstPtr,
			u16 Ps2DeviceId, u16 Ps2IntrId);
			
static int Ps2SetupIntrSystem(XIntc* IntcInstPtr, XPs2 *Ps2Ptr, u16 IntrId);

void MY_TIMER_Intr_Handler(void * baseaddr_p);

int TimerSetupIntrSystem();

void add_bomb(int player, int iPos, int jPos);

void explode(int iPos, int jPos, int clear);

char decode(uint8_t );

struct bomb {
	int player;
	int timer;
	int iPos;
	int jPos;
	int explosion;
	struct bomb* next;
} bomb_head;

/**************/
static XPs2 Ps2Inst; 		/* Ps2 driver instance */
static XIntc IntcInst; 		/* Instance of the XIntc driver. */

Xuint32 my_timer;
unsigned int *my_timer_p =
         (unsigned int *) XPAR_MY_TIMER_0_BASEADDR;

#define TOTAL_TEST_BYTES	18 	/* Total Number of bytes to be
						transmitted/received */
						
#define TIMER_RESET   0x00000000
#define TIMER_RUN     0x40000000
#define TIMER_EXPIRED 0x80000000
#define TIMER_HALFSEC 0x02FAF080
#define TIMER_BOMB	 3
#define TIMER_EXPLODE 2

#define GROUND_TILE	1
#define STONE_TILE	2
#define BRICK_TILE	3
#define BOMB_TILE		4
#define FIRE_C_TILE	5
#define FIRE_H_TILE	6
#define FIRE_V_TILE	7

#define FIRE_REACH	5

/*
 * Shared variables used to test the callbacks.
 */
volatile static int RxDataRecv = FALSE; /* Flag to indicate Receive Data */
volatile static int RxError = FALSE; 	/* Flag to indicate Receive Error */
volatile static int RxOverFlow = FALSE; /* Flag to indicate Receive Overflow */
volatile static int TxDataSent = FALSE; /* Flag to indicate Tx Data sent */
volatile static int TxNoAck = FALSE;    /* Flag to indicate Tx No Ack */
volatile static int TimeOut = FALSE;    /* Flag to indicate Watchdog Timeout */
volatile static int TxNumBytes = 0;	/* Number of bytes transmitted */
volatile static int RxNumBytes = 0;     /* Number of bytes received */

/* Game board */
int gameboard[15][20];
int level;

/*
 * Transmit Buffer.
 */
u8 TxBuffer[TOTAL_TEST_BYTES];
/*
 * Receive Buffer.
 */
u8 RxBuffer[TOTAL_TEST_BYTES];

/* player positions */
int p1pos,p2pos;
int p1dead, p2dead;

void init(){ //int test;
	int i;
	int j;
	int h;
	int a;
	int seed = 0x9d658ac6;
	level++;
	p1pos = 0x80080060 - 0x1000 - 1;
	p2pos = 0xC02001A0 - 0x1000 - 1;
	p1dead = 0;
	p2dead = 0;
	GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
	GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
	for (i = 0; i <= 14; i++) {
		for (j = 0; j <= 19; j++) {
			if( i<2 | i>12 | j<3 | j>15 ) {
				gameboard[i][j] = STONE_TILE;
			} else {
				if ((i%2 != 0) && (j%2 == 0)) {
					gameboard[i][j] = STONE_TILE;
				} else {
					if (((i == 2) && (j == 3)) ||
							((i == 12) && (j == 15))) {
						gameboard[i][j] = GROUND_TILE;
					} else {
						if (((seed*31*level/i) >> j) % 2 == 0) {
							gameboard[i][j] = BRICK_TILE;
						} else {
							gameboard[i][j] = GROUND_TILE;
						}
					}	
				}
			}
			a = ((i << 8) & 0xF00) | ((j << 4) & 0xF0) | gameboard[i][j];
			GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
		}
	}	
}

void win(int player) {
	int i,j,a;
	for (i = 2; i <= 12; i++) {
		for (j = 3; j <= 15; j++) {
			gameboard[i][j] = GROUND_TILE;
			a = ((i << 8) & 0xF00) | ((j << 4) & 0xF0) | gameboard[i][j];
			GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
		}
	}
	if (player == 1) {
		p1pos = 0x80080060 - 0x1000 - 1 + (5*32);
		p2pos = 0xC02001A0 - 0x1000 - 1 + 32;
		GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
		for (i = 0; i < 500; i++) {
			xil_printf("%d",i);
			GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos+(i*0x1000));
		}
	} else {
		p1pos = 0x80080060 - 0x1000 - 1 - 32;
		p2pos = 0xC02001A0 - 0x1000 - 1 - (5*32);
		GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
		for (i = 0; i < 500; i++) {
			xil_printf("%d",i);
			GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos-(i*0x1000));
		}
	}

}

int main (void) {


   /* Enable and initialize cache */
   #if XPAR_MICROBLAZE_0_USE_ICACHE
      Xil_ICacheInvalidate();
      Xil_ICacheEnable();
   #endif

   #if XPAR_MICROBLAZE_0_USE_DCACHE
      Xil_DCacheInvalidate();
      Xil_DCacheEnable();
   #endif

	while (1) {
		
		bomb_head.next = NULL;
		level = 1;
		init();	
		
		Ps2IntrExample(&IntcInst, &Ps2Inst, PS2_DEVICE_ID, INTR_ID);
		TimerSetupIntrSystem();
		
		while (1) { 
			if( p1dead == 1 ) {
				win(2);
				p1dead = 0;
				init();
			}
			if( p2dead == 1 ) {
				p2dead = 0;
				win(1);
				init();
			}
		}
	}
	
   /*
    * Disable cache and reinitialize it so that other
    * applications can be run with no problems
    */
   #if XPAR_MICROBLAZE_0_USE_DCACHE
      Xil_DCacheDisable();
      Xil_DCacheInvalidate();
   #endif

   #if XPAR_MICROBLAZE_0_USE_ICACHE
      Xil_ICacheDisable();
      Xil_ICacheInvalidate();
   #endif
   return 0;
}

int Ps2IntrExample(XIntc* IntcInstPtr, XPs2* Ps2InstPtr,
			u16 Ps2DeviceId, u16 Ps2IntrId)
{
	int Status;
	XPs2_Config *ConfigPtr;
	u32 Index;
	u32 BytesSent;
	u32 BytesReceived;

	// Initialize the PS/2 driver.

	ConfigPtr = XPs2_LookupConfig(Ps2DeviceId);
	if (ConfigPtr == NULL) {
		return XST_FAILURE;
	}
	XPs2_CfgInitialize(Ps2InstPtr, ConfigPtr, ConfigPtr->BaseAddress);

	// Self Test the PS/2 device.
	Status = XPs2_SelfTest(Ps2InstPtr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	// Setup the interrupt system.
	Status = Ps2SetupIntrSystem(&IntcInst, &Ps2Inst, INTR_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	// Set the Handler.

	// Enable the Receive interrupts and the global interrupt in the PS/2 device.
	XPs2_IntrEnable(&Ps2Inst, XPS2_IPIXR_RX_FULL);
	XPs2_IntrGlobalEnable(&Ps2Inst);

	// Initialize the flags that are set in the Interrupt Callback function.
	TxDataSent = FALSE;
	TxNumBytes = 0;

	// Prepare the Transmit Buffer.
	for (Index = 0; Index < TOTAL_TEST_BYTES; Index++) {
		TxBuffer[Index] = Index;
	}

	/*
	 * Send the data to a PS/2 keyboard.
	 */
	return XST_SUCCESS;
}

void MY_TIMER_Intr_Handler(void * baseaddr_p)
{
  static Xuint32 led_data;
  Xuint32 baseaddr;
  Xuint32 IpStatus;
  struct bomb* curr;
  struct bomb* prev;
  baseaddr = (Xuint32) baseaddr_p;

  // Get the timer interrupt status
  IpStatus = MY_TIMER_mReadReg(baseaddr, MY_TIMER_INTR_IPISR_OFFSET);

  // If timer caused the interrupt then switch LEDs
  if (IpStatus) {
	curr = bomb_head.next;	
	prev = &bomb_head;
	while (curr != NULL) {
		curr->timer--;
		
		if (curr->timer <= 0) {
			if (curr->explosion == 0) {
				curr->timer = TIMER_EXPLODE;
				curr->explosion = 1;
				explode(curr->iPos,curr->jPos,0);
			} else {
				explode(curr->iPos,curr->jPos,1);
				prev->next = curr->next;
				free(curr);
				curr = prev->next;	
			}
		} else {
			prev = curr;
			curr = curr->next;
		}
	}
    // Reset the timer and hence the interrupt
    MY_TIMER_mWriteSlaveReg1(baseaddr, 0, TIMER_RESET);
    MY_TIMER_mWriteSlaveReg1(baseaddr, 0, TIMER_RUN);
  }
}

void explode(int iPos, int jPos, int clear) {
	int a;
	int i;
	int j;
	int tmp;
	if (clear == 0) {		
		
		gameboard[iPos][jPos] = FIRE_C_TILE;
		a = ((iPos << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos][jPos];
		GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
			
		for (j = 1; j < FIRE_REACH; j++) {
			if ( ((p2pos & 0xFFF) >> 5) == iPos && (((p2pos) & 0xFFF000) >> 17) == (jPos+j)) {
				p2dead = 1;
			}
			if ( ((p1pos & 0xFFF) >> 5) == iPos && (((p1pos) & 0xFFF000) >> 17) == (jPos+j)) {
				p1dead = 1;
			}
			
			switch (gameboard[iPos][jPos+j]) {
			case FIRE_H_TILE:
			case FIRE_C_TILE:
			case FIRE_V_TILE:
			case GROUND_TILE:
				gameboard[iPos][jPos+j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos+j) << 4) & 0xF0) | gameboard[iPos][jPos+j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BOMB_TILE:
				gameboard[iPos][jPos+j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos+j) << 4) & 0xF0) | gameboard[iPos][jPos+j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BRICK_TILE:
				gameboard[iPos][jPos+j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos+j) << 4) & 0xF0) | gameboard[iPos][jPos+j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				j = FIRE_REACH;
				break;
			case STONE_TILE:
				j = FIRE_REACH;
				break;	
			default:
				break;
			}
		}
			
		for (j = 1; j < FIRE_REACH; j++) {
			if ( ((p2pos & 0xFFF) >> 5) == iPos && (((p2pos) & 0xFFF000) >> 17) == (jPos-j)) {
				p2dead = 1;
			}
			if ( (((p1pos & 0xFFF) >> 5) == iPos) && ((((p1pos) & 0xFFF000) >> 17) == (jPos-j))) {
				p1dead = 1;
			}
			switch (gameboard[iPos][jPos-j]) {
			case FIRE_H_TILE:
			case FIRE_C_TILE:
			case FIRE_V_TILE:
			case GROUND_TILE:
				gameboard[iPos][jPos-j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos-j) << 4) & 0xF0) | gameboard[iPos][jPos-j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BOMB_TILE:
				gameboard[iPos][jPos-j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos-j) << 4) & 0xF0) | gameboard[iPos][jPos-j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BRICK_TILE:
				gameboard[iPos][jPos-j] = FIRE_H_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos-j) << 4) & 0xF0) | gameboard[iPos][jPos-j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				j = FIRE_REACH;
				break;
			case STONE_TILE:
				j = FIRE_REACH;
				break;	
			default:
				break;
			}
		}
			
		for (j = 1; j < FIRE_REACH; j++) {
			
			if ( ((p2pos & 0xFFF) >> 5) == (iPos+j) && (((p2pos) & 0xFFF000) >> 17) == (jPos)) {
				p2dead = 1;
			}
			if ( ((p1pos & 0xFFF) >> 5) == (iPos+j) && (((p1pos) & 0xFFF000) >> 17) == (jPos)) {
				p1dead = 1;
			}
			
			switch (gameboard[iPos+j][jPos]) {
			case FIRE_H_TILE:
			case FIRE_C_TILE:
			case FIRE_V_TILE:
			case GROUND_TILE:
				gameboard[iPos+j][jPos] = FIRE_V_TILE;
				a = (((iPos+j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos+j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BOMB_TILE:
				gameboard[iPos+j][jPos] = FIRE_V_TILE;
				a = (((iPos+j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos+j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BRICK_TILE:
				gameboard[iPos+j][jPos] = FIRE_V_TILE;
				a = (((iPos+j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos+j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				j = FIRE_REACH;
				break;
			case STONE_TILE:
				j = FIRE_REACH;
				break;	
			default:
				break;
			}
		}
			
		for (j = 1; j < FIRE_REACH; j++) {
		
			if ( ((p2pos & 0xFFF) >> 5) == (iPos-j) && (((p2pos) & 0xFFF000) >> 17) == (jPos)) {
				p2dead = 1;
			}
			if ( ((p1pos & 0xFFF) >> 5) == (iPos-j) && (((p1pos) & 0xFFF000) >> 17) == (jPos)) {
				p1dead = 1;
			}
		
			switch (gameboard[iPos-j][jPos]) {
			case FIRE_H_TILE:
			case FIRE_C_TILE:
			case FIRE_V_TILE:
			case GROUND_TILE:
				gameboard[iPos-j][jPos] = FIRE_V_TILE;
				a = (((iPos-j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos-j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BOMB_TILE:
				gameboard[iPos-j][jPos] = FIRE_V_TILE;
				a = (((iPos-j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos-j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				break;
			case BRICK_TILE:
				gameboard[iPos-j][jPos] = FIRE_V_TILE;
				a = (((iPos-j) << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos-j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
				j = FIRE_REACH;
				break;
			case STONE_TILE:
				j = FIRE_REACH;
				break;	
			default:
				break;
			}
		}
	} else {
		
		gameboard[iPos][jPos] = GROUND_TILE;
		a = ((iPos << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos][jPos];
		GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
		
		for (j = 1; j < FIRE_REACH; j++) {
			tmp = gameboard[iPos][jPos+j];
			if((tmp == FIRE_V_TILE) || (tmp == FIRE_H_TILE)) {
				gameboard[iPos][jPos+j] = GROUND_TILE;
				a = ((iPos << 8) & 0xF00) | (((jPos+j) << 4) & 0xF0) | gameboard[iPos][jPos+j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
			} else if (tmp != GROUND_TILE){
				j = FIRE_REACH;
			}
		}
		
		for (j = 1; j < FIRE_REACH; j++) {
			tmp = gameboard[iPos][jPos-j];
			if ((tmp == FIRE_V_TILE) || (tmp == FIRE_H_TILE)) {
				gameboard[iPos][jPos-j] = GROUND_TILE;
				a = ((iPos << 8) & 0xF00) | ((jPos-j << 4) & 0xF0) | gameboard[iPos][jPos-j];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
			} else if (tmp != GROUND_TILE){
				j = FIRE_REACH;
			}
		}
		
		for (j = 1; j < FIRE_REACH; j++) {
			tmp = gameboard[iPos+j][jPos];
			if((tmp == FIRE_V_TILE) || (tmp == FIRE_H_TILE)) {
				gameboard[iPos+j][jPos] = GROUND_TILE;
				a = ((iPos+j << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos+j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
			} else if (tmp != GROUND_TILE){
				j = FIRE_REACH;
			}
		}

		for (j = 1; j < FIRE_REACH; j++) {
			tmp = gameboard[iPos-j][jPos];
			if((tmp == FIRE_V_TILE) || (tmp == FIRE_H_TILE)) {
				gameboard[iPos-j][jPos] = GROUND_TILE;
				a = ((iPos-j << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos-j][jPos];
				GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
			} else if (tmp != GROUND_TILE){
				j = FIRE_REACH;
			}
		}
	}
}

void XPs2_IntrHandler(XPs2 *InstancePtr)
{
	u32 IntrStatus;
	char ch;
	int a;
	int i;
	int j;
	
	Xil_AssertVoid(InstancePtr != NULL);

	/*
	 * Read the interrupt status register to determine which
	 * interrupt is active.
	 */
	
  	Xuint32 baseaddr;
	Xuint32 IpStatus;

	baseaddr = (Xuint32) InstancePtr;
	 
	 
	IntrStatus = XPs2_IntrGetStatus(InstancePtr);

	//This is the only XPS2 interupt we care about.
	if (IntrStatus & XPS2_IPIXR_RX_FULL) {
		/*
		 * If there are bytes still to be received in the specified
		 * buffer go ahead and receive them
		 */
		 ch = decode(XPs2_RecvByte(XPAR_XPS_PS2_0_0_BASEADDR));
		if ((ch == 'p')) {
			decode(XPs2_RecvByte(XPAR_XPS_PS2_0_0_BASEADDR));
		} else {
			switch (ch) {
			case 's':
				a = gameboard[((p1pos+32) & 0xFFF) >> 5][(p1pos & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p1pos += 32;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
				}
				if (a > BOMB_TILE)
					p1dead = 1;
				break;
			case 'w':
				a = gameboard[((p1pos-32) & 0xFFF) >> 5][(p1pos & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p1pos -= 32;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
				}
				if (a > BOMB_TILE)
					p1dead = 1;
				break;
			case 'd':
				a = gameboard[(p1pos & 0xFFF) >> 5][((p1pos+32*0x1000) & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p1pos += 32*0x1000;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
				}
				if (a > BOMB_TILE)
					p1dead = 1;
				break;
			case 'a':
				a = gameboard[(p1pos & 0xFFF) >> 5][((p1pos-32*0x1000) & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p1pos -= 32*0x1000;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p1pos);
				}
				if (a > BOMB_TILE)
					p1dead = 1;
				break;
			case 'k':
				a = gameboard[((p2pos+32) & 0xFFF) >> 5][(p2pos & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p2pos += 32;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
				}
				if (a > BOMB_TILE)
					p2dead = 1;
				break;
			case 'i':
				a = gameboard[((p2pos-32) & 0xFFF) >> 5][(p2pos & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p2pos -= 32;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
				}
				if (a > BOMB_TILE)
					p2dead = 1;
				break;
			case 'l':
				a = gameboard[(p2pos & 0xFFF) >> 5][((p2pos+32*0x1000) & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p2pos += 32*0x1000;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
				}
				if (a > BOMB_TILE)
					p2dead = 1;
				break;
			case 'j':
				a = gameboard[(p2pos & 0xFFF) >> 5][((p2pos-32*0x1000) & 0xFFF000) >> 17];
				if (a == GROUND_TILE) {
					p2pos -= 32*0x1000;
					GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,p2pos);
				}
				if (a > BOMB_TILE)
					p2dead = 1;
				break;
			case 'e':
				i = (p1pos & 0xFFF) >> 5;
				j = (p1pos & 0xFFF000) >> 17;
				add_bomb(1,i,j);
				break;
			case 'u':
				i = (p2pos & 0xFFF) >> 5;
				j = (p2pos & 0xFFF000) >> 17;
				add_bomb(2,i,j);
				break;
			default:
				break;
			}
		}
	
		// Clear the Interrupt Status Register
		XPs2_IntrClear(InstancePtr, IntrStatus);
	}
}

void add_bomb(int player, int iPos, int jPos) {
	struct bomb* curr;
	int a;
	int noOfBombs = 0;
	
	curr = bomb_head.next;	
	while (curr != NULL) {
	
		if ((iPos == curr->iPos) && (jPos == curr->jPos))
			return;
			
		if (player == curr->player)
				noOfBombs++;
	
		curr = curr->next;
	}
	if (noOfBombs > 1)
		return;
	
	curr = malloc(sizeof(struct bomb));
	if (curr == NULL) {
		xil_printf("\n\rME");
		return;
	} 
	
	curr->player = player;
	curr->iPos = iPos;
	curr->jPos = jPos;
	curr->timer = TIMER_BOMB;
	curr->explosion = 0;
	curr->next = bomb_head.next;
	bomb_head.next = curr;
	
	gameboard[iPos][jPos] = BOMB_TILE;
	a = ((iPos << 8) & 0xF00) | ((jPos << 4) & 0xF0) | gameboard[iPos][jPos];
	GRAPH_ACC_mWriteSlaveReg0(XPAR_GRAPH_ACC_0_BASEADDR,0,a);
}

int TimerSetupIntrSystem() {
	Xuint32 status;

  //----------------------------------------------------
  // REGISTER INTERRUPT HANDLER
  //----------------------------------------------------
  XIntc_RegisterHandler(XPAR_XPS_INTC_0_BASEADDR,
    XPAR_XPS_INTC_0_MY_TIMER_0_IP2INTC_IRPT_INTR,
    MY_TIMER_Intr_Handler,
    (void *)XPAR_MY_TIMER_0_BASEADDR);

  //----------------------------------------------------
  // INITIALIZE THE TIMER PERIPHERAL
  //----------------------------------------------------
  // Check that the my_timer peripheral exists
  XASSERT_NONVOID(my_timer_p != XNULL);
  my_timer = (Xuint32) my_timer_p;
  // Load the delay register with the delay time of 0.5s
  MY_TIMER_mWriteSlaveReg0(my_timer, 0, TIMER_HALFSEC);

  //----------------------------------------------------
  // ENABLE INTERRUPTS
  //----------------------------------------------------
  // Enable INTC interrupts
  XIntc_MasterEnable(XPAR_XPS_INTC_0_BASEADDR);
  XIntc_EnableIntr(XPAR_XPS_INTC_0_BASEADDR,
    (XPAR_MY_TIMER_0_IP2INTC_IRPT_MASK | XPAR_XPS_PS2_0_IP2INTC_IRPT_1_MASK));
  // Enable timer interrupts
  MY_TIMER_EnableInterrupt(my_timer_p);
  // Enable Microblaze interrupts
  microblaze_enable_interrupts();

  // Start the timer
  MY_TIMER_mWriteSlaveReg1(my_timer, 0, TIMER_RUN);
}

static int Ps2SetupIntrSystem(XIntc* IntcInstPtr, XPs2 *Ps2Ptr, u16 IntrId )
{
	int Status;
	/*
	 * Initialize the interrupt controller driver so that it's ready to use.
	 */
	Status = XIntc_Initialize(IntcInstPtr, INTC_DEVICE_ID);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Connect the handler that will be called when an interrupt
	 * for the device occurs, the handler defined above performs the
	 * specific interrupt processing for the device.
	 */
	Status = XIntc_Connect(IntcInstPtr,IntrId,
				(XInterruptHandler) XPs2_IntrHandler,Ps2Ptr);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Start the interrupt controller so interrupts are enabled for all
	 * devices that cause interrupts. Specify real mode so that the PS/2
	 * device can cause interrupts through the interrupt controller.
	 */
	Status = XIntc_Start(IntcInstPtr, XIN_REAL_MODE);
	if (Status != XST_SUCCESS) {
		return XST_FAILURE;
	}

	/*
	 * Enable the interrupt for the Ps2 device.
	 */
	XIntc_Enable(IntcInstPtr, IntrId);

	/*
	 * Initialize the exception table.
	 */
	Xil_ExceptionInit();

	/*
	 * Register the interrupt controller handler with the exception table.
	 */
	Xil_ExceptionRegisterHandler(XIL_EXCEPTION_ID_INT,
				(Xil_ExceptionHandler) XIntc_InterruptHandler, IntcInstPtr);

	/*
	 * Enable the exceptions.
	 */
	Xil_ExceptionEnable();
	return XST_SUCCESS;
}

/*
 * Decodes scancodes, returns character represented or -1 if the
 * scancode has no char representation (unknown code, part of 
 * break code, or special key (shift, ctr, etc))
 * Does not handle multi character constansts (å,ä,ö and some other special characters)
*/
char decode(volatile uint8_t sc) {
    
	static volatile bool break_code = false;
	static volatile bool shift = false;
	static volatile bool special_key = false; // We ignore scan codes beloning to special keys
	static const volatile char * table =  &(unshifted[0]);
	char volatile ret = -1;

	switch (sc) {
		case 0xF0: // Break code identifier
			break_code = true;
			break;
		case 0xE0: // Special key identifier
			special_key = true;
			break;
	}
	
	if(special_key) {		// we have either a make code or a break code from a
		switch(sc){			// special key, either way ignore them and the next scan code. 
		case 0xE0: 
		case 0xF0: 
			break;
		default:	
			special_key = false;
			break;
		}
	} else if(break_code){
		switch(sc){
			case 0xF0:
				break;
			case 0x12:	 
			case 0x59:	
				shift = false;
				table = &(unshifted[0]);
			default :
				break_code = false;
				break;
		}
		ret = 'p';
		

	} else {	
		switch(sc){
			case 0x12: //make codes for shift, change table.
			case 0x59:
				shift = true;
				table = &(shifted[0]);
				break;
			default : //normal case, collect char representation from table
				ret = table[sc];
				break;
		}
	}
	return ret;
}

