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

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

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

// defined in isr.c
extern void uart_handler(void *callback);
extern int uart_interrupt_count;
extern char uart_character;



#define W 640
#define H 480
#define VGA_START 0x00800000

#define RED 0xE0
#define GREEN 0x1C
#define BLUE 0x03

#define FONT_WIDTH 8
#define FONT_HEIGHT 8

int pos_x, pos_y;

void draw_byte(char x, int offset) {
  int i=0;
  int MASK = 1;
  for(i=0;i<8;i++) {
    if ((MASK&x)==0) {
      XIo_Out8(VGA_START + offset+(8-i), BLUE|GREEN);
    }
    else {
      XIo_Out8(VGA_START + offset+(8-i), BLUE);
    }
    MASK=MASK<<1;
  }  
}

void write_c(char x) {
  int i=0;
  for(i=0;i<8;i++) {
    draw_byte(fontdata[x*8+i], pos_x*FONT_WIDTH+(pos_y+i)*W);
  }
}

void new_line() {
  write_c(' ');
  pos_x=0;
  pos_y+=FONT_HEIGHT;
}
void new_line_char() {
  write_c(' ');
  pos_y+=FONT_HEIGHT;
}
void return_char() {
  write_c(' ');
  pos_y+=FONT_HEIGHT;
}

void move_cursor() {
  pos_x += 1;
  if(pos_x*FONT_WIDTH == W) {
    pos_y += FONT_HEIGHT;
    pos_x = 0;
  }
}

//debug here!!!
void move_cursor_back() {
  pos_x -= 1;
  if(pos_x*FONT_WIDTH < 0) {
    pos_x = W-16;
    pos_y -= FONT_HEIGHT;
    if(pos_y<0) {
      pos_y = 0;
    }
  }
}


void write_char(char x) {
  switch(x) {
  case '_':
    break;
  case 8: //backspace
    write_c(' ');
    move_cursor_back();
    write_c(' ');
    break;
  case 13: //new line \n
    new_line_char();
    break; 
  case 20: //space
    move_cursor();
    break;
  case '\t':
    write_c(' ');
    move_cursor();
    write_c(' ');
    move_cursor();
    write_c(' ');
    move_cursor();
    write_c(' ');
    move_cursor();
    break;
  default:
    write_c(x);
    move_cursor();
  }

}
void draw_curser() {
  write_c('_');
  //move_cursor();
}


void write_str(char* x, int length) {
  int i=0;
  for(i=0;i<length;i++) {
    write_char(x[i]);
  }
  //draw_curser();
}


 void shift_screen_up() {
  int x, y;
  for(y=0;y<H;y++) {
    for(x=0;x<W;x++) {
      XIo_Out8(VGA_START+x+(y)*W, XIo_In8(VGA_START+x+(y+1)*W));
    }
  }
  for(x=0;x<W;x++) {
    XIo_Out8(VGA_START + x+ H*W, BLUE|GREEN);
  }
  pos_y = pos_y-1;
}




/*
 * setup_interrupts: Initialize the interrupt sources and handlers
 *
 * Should be called once when the system starts
 *
 * The main _interrupt_handler() function from Xilinx
 *
 * Saves and restores CPU context, etc.
 *
 * Sees which interrupts are pending, and for each it
 *     acknowledges the interrupt and
 *     calls a user-defined interrupt handler in Xintc_InterruptVectorTable
 *
 * Place interrupt service routines in isr.c to ensure they are placed in
 * the proper memory segment.
 */
void setup_interrupts()
{
  /*
   * Reset the interrupt controller peripheral
   */

  /* Disable the interrupt signal */
  XIntc_mMasterDisable(XPAR_INTC_SINGLE_BASEADDR);

  /* Disable all interrupt sources */
  XIntc_mEnableIntr(XPAR_INTC_SINGLE_BASEADDR,0);

  /* Acknowledge all possible interrupt sources
     to make sure none are pending */
  XIntc_mAckIntr(XPAR_INTC_SINGLE_BASEADDR, 0xffffffff);

  /*
   * Install the UART interrupt handler
   */

  XIntc_InterruptVectorTable[XPAR_INTC_MYUART_INTERRUPT_INTR].Handler =
    uart_handler;

  /*
   * Enable interrupt sources
   */

  /* Enable CPU interrupts */
  microblaze_enable_interrupts();

  /* Enable interrupts from the interrupt controller */
  XIntc_mMasterEnable(XPAR_INTC_SINGLE_BASEADDR);

  /* Tell the interrupt controller to accept interrupts from the UART */
  XIntc_mEnableIntr(XPAR_INTC_SINGLE_BASEADDR, XPAR_MYUART_INTERRUPT_MASK);
  
  /* Enable UART interrupt generation */
  XUartLite_mEnableIntr(XPAR_MYUART_BASEADDR);
}

int main()
{
  char c;
  int i;
  char buf[2];
  int prev_int_count;
  int x, y;
  pos_x=0;
  pos_y=0;
  prev_int_count=0;
  for(x=0; x<H*W; x++) {
    XIo_Out8(VGA_START + x, BLUE|GREEN);
  }
   

  buf[1] = '\0';

  // Enable the instruction cache: makes the code run 6 times faster
  microblaze_enable_icache();

  print("Hello Lab3!\r\n");

  setup_interrupts();
  draw_curser();
  
  for (;;) {
    if(pos_y == H) {
      for(i=0;i<8;i++) {
	shift_screen_up();
      }
      draw_curser();
    }
    print("Character number ");
    putnum(uart_interrupt_count);
    print(" is ");
    buf[0] = uart_character;
    print(buf);
    if(uart_character!=' ') {
      if(uart_interrupt_count>prev_int_count) {
	write_char(uart_character);
	draw_curser();
	prev_int_count = uart_interrupt_count;
      }
    }
    //move_cursor();
    print("\r\n");
    
    for (i = 0 ; i < 300 ; i++ )  ; /* delay */
  }

  return 0;
}
