/*
 *Leo Gertsenshteyn  -- lpg2006@columbia.edu
 *Russel Santillanes -- res2049@columbia.edu
 *Kathryn Hagan      -- kmh2124@columbia.edu / wirehead@notapattern.net
 *
 *CSEE W4840 Lab 2 (modified into project code)
 *04/12/2006
*/


#include "xbasic_types.h"
#include "xio.h"

#include "xintc_l.h"
#include "xuartlite_l.h"
#include "xparameters.h"

/* Defined in mymicroblaze/xparameters.h */
/* #define XPAR_VGA_BASEADDR 0xFEFF1000 */
/* #define XPAR_VGA_HIGHADDR 0xFEFF1fff */


//#define BRAM XPAR_BRAM_PERIPHERAL_BASEADDR


#define FONT_OFFSET 0xA00

/* Address of a particular character on the screen (rows are 80) */
#define CHAR(r,c) \
   (((unsigned char *)(XPAR_VGA_BASEADDR))[(c) + ((r) << 6) + ((r) << 4)])

/* Start of font memory */
#define FONT ((unsigned char *)XPAR_VGA_BASEADDR + FONT_OFFSET)

int cursorx = 0, cursory = 0;    // cursor position
unsigned char saved_cursor;  // character sharing space with the cursor

int blink_delay = 0;

int BUFFER_SIZE = 256;
char buffer[256];        // character buffer
int volatile next_empty = 0;  // pointer to next empty slot in buffer
int volatile next_data = 0;   // pointer to next character to be read from buffer

 
/*
 * Interrupt service routine for the UART
 */
void uart_handler(void *callback)
{
  Xuint32 IsrStatus;
   
  Xuint8 incoming_character;

  /* Check the ISR status register so we can identify the interrupt source */
  IsrStatus = XIo_In32(XPAR_MYUART_BASEADDR + XUL_STATUS_REG_OFFSET);
   
  if ((IsrStatus & (XUL_SR_RX_FIFO_FULL | XUL_SR_RX_FIFO_VALID_DATA)) != 0) {
    /* The input FIFO contains data: read it */
    incoming_character =
      (Xuint8) XIo_In32( XPAR_MYUART_BASEADDR + XUL_RX_FIFO_OFFSET );

    // TODO: check for full buffer ?

    // add new character to buffer
    buffer[next_empty] = incoming_character;

    // advance the buffer pointer
    ++next_empty;

    // check for overflow
    if(next_empty >= BUFFER_SIZE) next_empty = 0;
  }
  
  if ((IsrStatus & XUL_SR_TX_FIFO_EMPTY) != 0) {
    /* The output FIFO is empty: we can send another character */
  }
    
}

/* Write a text string on the display */
void put_string(char *string, int row, int column)
{
  char *p = &(CHAR(row, column));
  while (*p++ = *string++)
    ;
}

void write_fortune(char *string, int startrow, int startcol) {
  char *p = &(CHAR(startrow, startcol));
  int i=0;
  int j;
  int col=startcol;
  while(string[i] != '\0') {
    *p++ = string[i++];
    for (j=0; j<100000000 ; j++)  ; /* delay (this mimics typing) */
  }
}

void generate_fortune(char* fortune) {
  strcpy(fortune, "generated fortune");
  //int test = rand(10);
}


void draw_graphic() {
  int offset=10;
  /* put_string("..;;..", 0, offset+22);
  put_string("..;;;;;;ii;;..", 1, offset+18);
  put_string(";;;;iiiiiiii;;ii..", 2, offset+16);
  put_string(";;iiii..  ;;::....;;;;", 3, offset+12);
  put_string(";;ii..  ....;;....;;;;..", 4, offset+12);
  put_string("..ii;;::  ;;....ii  ,,;;;;", 5, offset+10);
  put_string("..;;;;;;;;;;;;..........;;", 6, offset+10);
  put_string("..;;;;LLEEff..  ;;LL..::..", 7, offset+10);
  put_string(";;LLWWWWLL;;    ..tt..ttWW;;", 8, offset+10);
  put_string(";;ii####GG;;ttLL....tt;;ttKK..", 9, offset+8);
  put_string("ii;;KKWWDDii;;tt..::iittjjKKii", 10, offset+8);
  put_string(";;;;iiWWWWGG;;ii..    ....;;KKLLii", 11, offset+6);
  put_string(";;ii;;EE##LL....        ..;;KKEE..", 12, offset+6);
  put_string("..;;;;EEWWGG......iiii......WWKKDD..ii", 13, offset+6);
  put_string(";;  ;;ffWWKK;;  ;;;;ii;;  GGKK##WWKKWW..", 14, offset+6);
  put_string("....  iiiiKKDDWW..;;LLGG,,tt##DDEEEELL::", 15, offset+4);
  put_string(";;  ..;;;;WW####GG;;    ..GG##KKWWWWGG,,", 16, offset+4);
  put_string(";;  ....;;DDLLff##GGLLffjjffGGttDD##ff", 17, offset+4);
  put_string("....  ....ii;;ttKKGGffttttttiiWW;;iiGGGG", 18, offset+2);
  put_string("....  ;;ii;;;;LLDDttii;;;;....ttLLtttttt..", 19, offset+2);
  put_string(";;    ;;jjiiiiLLii;;;;ii;;..;;ttttii;;ttff", 20, offset+2);
  put_string(";;..  ;;LLLLttii..;;....    ..::ttttttiiii..", 21, offset+2);
  put_string("ii..;;ii;;ii;;..            ..iiiiiiLLiiLL", 22, offset+4);
  put_string("    ;;ffii;;iitt;;..              ..tt;;;;ii;;;;  ", 23, 10);
  put_string("  ttKK;;;;..;;ffii..              ..ii;;  ..GGff  ", 24, 10);
  put_string("  LLLL  ....iiii;;;;              ..ii;;..  jjKK;;", 25, 10);
  put_string("  GGLL..iiiiKKLL;;;;..          ..;;iiii..  ttLLjj", 26, 10);
  put_string(";;ffKK  EE..::;;iiii....      ..;;fftt;;iiiiLLGGff", 27, 10);
  put_string("    LLLL;;      iitt;;........;;ffii..  ,,GGDD;;tt", 28, 10);
  put_string("                  ;;ttiiiittttttii..    ..GGLLii;;", 29, 10); */

  put_string("~~ Madame Xilinx knows all! ~~", 4, 40);
  put_string("(press a key to see your fortune)", 10, 40);
}

int main()
{
  volatile unsigned char *p;
  unsigned char v, vv;
  int i, j, k;
  unsigned char *to, *from;

  /* display initial graphic */
  //print("Hello World!\r\n");
  draw_graphic();

  /* test BRAM access */
  /*int addr;
  for ( i = 0 ; i < 512 ; i++ ) {
    addr = XPAR_BRAM_PERIPHERAL_BASEADDR + (i << 2);
    XIo_Out8(addr, j);
    j += 17;
  }

  j = 0;
  for ( i = 0 ; i < 512 ; i++ ) {
    addr = XPAR_BRAM_PERIPHERAL_BASEADDR + (i << 2);
    k = XIo_In8(addr);
    putnum(addr);
    print(" got ");
    putnum(k);
    if ( k != j ) {
      print(" expected ");
      putnum(j);
    }
    print("\r\n");
    j += 17;
  */


#if 0
  v = 0;
  for ( p = (unsigned char *) XPAR_VGA_BASEADDR ;
        p <= (unsigned char *) XPAR_VGA_HIGHADDR ;
        ++p ) {
#if 0
    print("writing to ");
    putnum((int) p);
    print(" value ");
    putnum(v);
    print("\r\n");
#endif
    *p = v;
    v += 1;
  }
  print("wrote to memory\r\n");
 
  v = 0;
  for ( p = (unsigned char *) XPAR_VGA_BASEADDR ;
        p <= (unsigned char *) XPAR_VGA_HIGHADDR ;
        ++p ) {
    vv = *p;
    if (vv != v) {
      print("read from ");
      putnum((int) p);
      print(" returned ");
      putnum(vv);
      print(" expected ");
      putnum(v);
      print("\r\n");
    }
    v += 1;
  }
 
  print("memory test completed. from ");
  putnum(XPAR_VGA_BASEADDR);
  print(" to ");
  putnum(XPAR_VGA_HIGHADDR);
  print("\r\n");
#endif

  /* Enable UART interrupts and register uart_handler as the ISR */
  XIntc_RegisterHandler( XPAR_INTC_BASEADDR, XPAR_MYUART_DEVICE_ID,
	 		 (XInterruptHandler)uart_handler, (void *)0);
  XIntc_mEnableIntr( XPAR_INTC_BASEADDR, XPAR_MYUART_INTERRUPT_MASK);
  XIntc_mMasterEnable( XPAR_INTC_BASEADDR );
  XIntc_Out32(XPAR_INTC_BASEADDR + XIN_MER_OFFSET, XIN_INT_MASTER_ENABLE_MASK);
  microblaze_enable_interrupts();
  XUartLite_mEnableIntr(XPAR_MYUART_BASEADDR);

  for (;;) {
    unsigned char new_data;
    char buf[2];
    buf[1] = '\0';

    //print("waiting for input...\r\n");

    // get any new characters from the buffer
    if(next_empty != next_data) {
      // get character from buffer
      new_data = buffer[next_data];

      // update pointer and check for overflow
      ++next_data;
      if(next_data >= BUFFER_SIZE) next_data = 0;

      //print("got character");

      // wait for space, newline or CR
      if ((new_data == ' ') || (new_data == '\n') || (new_data == '\r'))
	{
	  print("Displaying message\r\n");
          char phrase[255];
          generate_fortune(phrase);
	  write_fortune(phrase, 27, 5);
          new_data = NULL;
	}
    }
      
      // adjust for end of line
      /*if(newx > 79) { ...

      // adjust for bottom of screen
      /*if(newy > 29) { ...
    
    // update cursor position
    /*if(cursorx != newx || cursory != newy) {
      cursorx = newx;
      cursory = newy;
      saved_cursor = CHAR(cursory, cursorx);
      //      saved_cursor = CHAR(newy, newx);
      buf[0] = saved_cursor;
      print("Saved cursor is now: ");
      print(buf);
      print("\r\n");
      }*/


    /*if(blink_delay == 40 ) {
      blink_delay = 0;

      //replace the _ with the stored character for the blink_delay
      if(CHAR(cursory, cursorx) == '_') {
	CHAR(cursory, cursorx) = saved_cursor;
      }
      else {
      CHAR(cursory, cursorx) = '_';
      }
      }

    else
    ++blink_delay;*/

    //CHAR(cursory, cursorx) = CHAR(cursory, cursorx);
         
    for (i = 0 ; i < 10000000 ; i++ )  ; /* delay */
  }

  return 0;
}



