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

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

#define W 640
#define H 480
#define VGA_START 0x00800000
#define RED 0xE0
#define GREEN 0x1C
#define BLUE 0x03

#define BUFFSIZE 15000

// defined in isr.c

extern void uart_handler(void *callback);
extern volatile int uart_interrupt_count;
extern volatile char uart_character;
extern volatile char buffer[BUFFSIZE];
extern volatile char *last_in;
extern volatile char *last_out;
extern volatile int last_in_loop_count;

int z=0;
int init_count; 
char color;
char counter;
int last_out_loop_count = 0;


/*
 * 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);
}

void clear_screen()
{
  int q;
  for (q=0; q<H*W; q++)
    {
      XIo_Out8(VGA_START + q, 0);
    }
}

void write_pixel()
{
  char j;

  color = *last_out; // take color byte
  
  last_out++; // increment pointer value
  
  counter = *last_out; // take a byte that says 
                       //how many times to repeat the color byte
  
  if (last_out == buffer + BUFFSIZE) // loop around 
    {
      last_out = buffer;
      last_out_loop_count++;
    }
  else 
    last_out++;
  
  for (j=0; j<counter; j++)  // display color byte counter times
    {
      XIo_Out8(VGA_START + z, color);
      z++;
    }
}


int main()
{
  microblaze_enable_icache();
  
  setup_interrupts();
  
  clear_screen();

  /* in the next few lines
   * skipping first two bytes
   * initializing stuff (can solve it with header, minor thing)*/

  init_count = uart_interrupt_count;
  while (init_count == uart_interrupt_count){}

  color = uart_character;
  
  init_count = uart_interrupt_count;
  while (init_count == uart_interrupt_count){}
  
  counter = uart_character;


  
  for (;;)
    {
      
      if (last_out == buffer + BUFFSIZE)   // loop around
	{
	  last_out = buffer;
	  last_out_loop_count++;
	}

      if (last_in_loop_count > last_out_loop_count)
	{
	  if (last_in < last_out)
	    {
	      write_pixel();
	    }
	}

      if (last_out_loop_count == last_in_loop_count)
	{
	  if (last_in > last_out + 1)        // write as long as we have fresh data
	    write_pixel();   
	}
      else {}

    }
  
  return 0; 
}
