#include <msp430x16x.h>
#include "display.h"

#define LCD_PORT_INIT   {LDSEL = 0x0; LCDIR |= (LCD_RS + LCD_RW + LCD_E); LCSEL &= ~(LCD_RS + LCD_RW + LCD_E);}
#define LCD_READ(x)     {SET_BIT(LCD_E); LCD_PDELAY; (x) = LDIN; RST_BIT(LCD_E); LCD_PDELAY;} 
#define LCD_WRITE(x)    {LDDIR = 0xff; LDOUT = x; LCD_ENABLE(); LDDIR = 0;}

#define LCD_PDELAY      {asm("NOP"); asm("NOP"); asm("NOP"); asm("NOP");}

#define SET_BIT(x)      (LCOUT |= (x))   
#define RST_BIT(x)      (LCOUT &= ~(x))

#define LCD_ENABLE()    {SET_BIT(LCD_E); LCD_PDELAY; RST_BIT(LCD_E);}
#define LCD_WAIT()      udelay(120) 

unsigned char wait_BF(void);
int lcd_copy(unsigned char, unsigned char, int);
void lcd_clear(int, int);
unsigned char get_pos(void);
void set_pos(unsigned char );
unsigned char lcd_addr(int line);
void send_cmd(unsigned char value);
void new_line(void);
int putchar(int );

//4.3
int lines = 0;
int aux = 0;

void udelay(unsigned int x) // retardo de x [us] debido a k asm demora 125 ns *8 es 1us
    {
    unsigned int i;
    for (i = 0; i < x; i++) 
    {
        LCD_PDELAY;
        //LCD_PDELAY;
    }
    }
void udelaymicro(unsigned int x) // retardo de x [ms] debido a k asm demora 125 ns *8 es 1us
    {
    unsigned int i;
    for (i = 0; i < x; i++) 
    {
        LCD_PDELAY;
        udelay(1000);
        //LCD_PDELAY;
    }
    }

////////////////////////////////////////
//     RUTINAS NO IMPLEMENTADAS       //
//                                    //
#pragma inline = forced
unsigned char wait_BF(void)
{
    unsigned char add;     // segun la tabla 
    RST_BIT(LCD_RS);       // deja en 0 lcd_rs
    SET_BIT(LCD_RW);       //LCD_RW =1 
    do
    {LCD_READ(add);}       //lee 
    while (add&BIT7);         //lee el bit 7 
    return add;
}

unsigned char get_pos(void)//quiere saber la direccion
{
    return (wait_BF());
}

char get_data(void)
{
    char value;
    wait_BF();
    SET_BIT(LCD_RS);
    SET_BIT(LCD_RW);
    LCD_READ(value);
    return value;
}
//                                    //
////////////////////////////////////////

void set_pos(unsigned char add)
{
    send_cmd(0x80 + add);
}

// dado una línea (0, 1, etc) retorna la dirección de memoria
unsigned char lcd_addr(int line)
{
    switch (LCD_LINES) {
    case 2:
        return (line > 0) ? 0x40 : 0x0;

    case 4:
        if (line == 0) return 0x0;
        else if (line == 1) return 0x40;
        else if (line == 2) return LCD_WIDTH;
        return (0x40 + LCD_WIDTH);

    default: return 0;
    }
}

void send_cmd(unsigned char value)//envia el commando 'value' al display
{
    wait_BF(); 
    RST_BIT(LCD_RS + LCD_RW);
    LCD_WRITE(value);
}

void send_data(char value)
{
    wait_BF(); 
    SET_BIT(LCD_RS); RST_BIT(LCD_RW);
    LCD_WRITE(value);
}

//////////////PUTCHAR/////////////
int putchar(int c)
{
   if(get_pos()== 0x10) 
    set_pos(0x40);
    
   if(get_pos()== 0x50)
   {
    lcd_scroll_up();
    set_pos(0x40);
   }
   
   if(c=='\b')
   {
    lcd_clear((get_pos()-0x1),0x1);
    return(1);
   }
   
   if(c=='\c')
   {
    lcd_clear(0x40,0x10);
    return 1;
   }
 
   if(c=='\n')
   {
    aux++;
    lcd_scroll_up();
    set_pos(0x40);
   }else
    send_data((unsigned char)c);
   return (1);
}

// dado una dirección retorna el número de línea
int lcd_line(unsigned char addr)
{
    int line;

    for (line = 0; line < LCD_LINES; line++)
        if (addr >= lcd_addr(line) &&
                addr < lcd_addr(line) + LCD_WIDTH) return line;

    return -1;
}

// limpia - len - caracteres desde from
void lcd_clear(int from, int len)
{
    int i;

    set_pos(from);
    for (i = 0; i < len; i++)
        send_data(' ');
    set_pos(from);
}

// copia - len - caracteres desde - from - a - to -
int lcd_copy(unsigned char from, unsigned char to, int len)
{
    char data[LCD_WIDTH];
    int i;

    if ((len > LCD_WIDTH) || (from > (0x7f - len))
            || (to > (0x7f - len))) return -1;

    set_pos(from);
    for (i = 0; i < len; i++)
        data[i] = get_data();
    set_pos(to);
    for (i = 0; i < len; i++)
        send_data(data[i]);

    return 0;
}

// copia línea i en línea i - 1 para todo el display
void lcd_scroll_up()
{
    int line;
    for (line = 0; line < LCD_LINES - 1; line++)
        lcd_copy(lcd_addr(line + 1), lcd_addr(line), LCD_WIDTH);
    lcd_clear(lcd_addr(line), LCD_WIDTH);
}


// inicializacion del display
void init_display(void)
{
    udelay(15000);

    LCD_PORT_INIT;
    RST_BIT(LCD_RS + LCD_RW);

    LCD_WRITE(0x38);
    udelay(5);

    LCD_WRITE(0x30);
    udelay(120);

    LCD_WRITE(0x30);
    udelay(200);

    LCD_WRITE(0x38);
    udelay(200);
    LCD_WRITE(0x0F); // display on
    udelay(200);
    LCD_WRITE(0x1);  // clear
    udelay(5000);
    LCD_WRITE(0x06); // shift right
    udelay(200);
    //

}

//4.4
void show_time(unsigned char p, unsigned int s )
{
  unsigned char pos;
  pos = get_pos();
  set_pos(p);
  printf("%d :%2d",s/60,s%60);
  set_pos(pos);
}
//4.3
void new_line(void){
  printf("\nLinea %d",lines++);
}

