//==============================================================================
//
//  PIC C software to control T6963 based LCD display 
//  My display is 128 x 64 but you should be able to adapt to other sizes
//  by changing the defines. 
//  
//  Bought the display and had to do something with it. Also trying to learn
//  C. So this was a 'C' learning experience!
//
//  Processor used was 16F874 @ 10.0 Mhz. Should work on 877 -- new .H file.
//  Display used was a surplus MGLS-12864T. 
//
//  Version 0.001      Support not guaranteed. But, if I find any 
//  serious bug's I will repost.
//
//  Written by Art Prewitt  N4PT       artprewitt@earthlink.net
//  Feb. 15, 2002 
//
//
//   Direct comments/bug's to artprewitt@earthlink.net
//
//   Based on information from Steve Lawther 
//   http://ourwrold.compuserve.com/homepages/steve_lawther/t6963c.pdf
//   Also John Beale ... LPT port control of T6963
//   
//   No reads from T6963 have been implemented
//   Ran out of ROM space in the 16F874... Need to buy a 877
//
//   Problems:
//
//   Not guaranteed to be bug free!! But works on my hardware. 
//       
//   Still not happy with line drawing routine, lines are 
//   not smooth enough.
//
//   Drawing routines are pixel by pixel... Could try a buffer then 
//   send a number of bytes in auto mode.
//
//   Compiler:
//  
//   Compiled using WIZ-C V8.0 Forest Electronic Developments compiler
//   Pic Call stack.... Local Optimize bytes 8 
//   Compiles to 3925 program words.  
//   
//   I have combined my T6963.H file into this file for posting
//   split out if you choose.  
//
//   The Page Test and Display test routines should demo most of the
//   functions that have been implemented.
//
//
//   I am not an C programmer so you Guru's can condense
//   the code. Maybe make a WIZ-C element.
//
//  
//   Enjoy !
//
//==============================================================================


#include <P16F874.H>
#include <delays.h>
#include <MATHS.H>


//==============================================================================
//
// LCD Signal       FS     RST   C/D   ~CE   ~RD   ~WR 
//
// PORTB BIT        6       5     4     3      2    1
//==============================================================================
// LCD Data lines to PORTD   PORTD.0 = DB0 etc.
//
//
// if you need schematic send me an email 
//==============================================================================



#define  WR    PB.B1
#define  RD    PB.B2
#define  CE    PB.B3
#define  CD    PB.B4
#define  RST   PB.B5
#define  FS    PB.B6 

#define  Ldata PORTD  // lcd data DB0..DB7 


#define HI 1
#define LO 0

#define DRAW   1
#define ERASE  0

//==============================================================================
//
//  This is really T6963.H 
//
//==============================================================================


#define EightbyEight  0  // fonts
#define SixbyEight    1  


#define STA0STA1 0x03  // Status masks 
#define STA3     0x08

//
// Macros 
// 

#define HiByte(X)  (X >> 8 )
#define LoByte(X)  (X & 0xFF )


//==============================================================================
//
//  T6963 Commands & modifiers
//
//
//==============================================================================

#define PointerSet 0x20
// modifiers
#define CursorPointerSet  0x01
#define OffsetRegisterSet 0x02
#define AddressPointerSet 0x04

#define ControlWordSet 0x40
// Modifiers
#define TextHomeAddress 0x0
#define TextAreaSet     0x1
#define GraphicHomeAddress 0x02
#define GraphicAreaSet  0x03

#define ModeSet  0x80
// modifiers
#define CGROMMode  0x0
#define CGRAMMode  0x8
#define ORMode     0x0
#define EXORMode   0x1
#define ANDMode    0x3
#define Textonly   0x4

#define DisplayModes 0x90
// modifiers
#define GraphicsOff  0x0
#define GraphicsOn   0x8
#define TextOff      0x0
#define TextOn       0x4
#define CursorOff    0x0
#define CursorOn     0x2
#define CursorBlinkOff  0x0
#define CursorBlinkOn 0x1

#define CursorPattern  0xA0
// modifiers 0 to 7 Number lines for cursor 000 = bottom 111 = 8lines
#define BottomLine    0x0

#define DataAuto   0xB0
// Modifiers
#define  DataAutoWrite  0x0
#define  DataAutoRead   0x1
#define  AutoReset      0x2

#define DataRdWr  0xC0
// Modifiers
#define AddressPointerup/down   0x0
#define AddressPointerunchanged 0x4
#define AddressPointerup        0x0
#define AddressPointerdown      0x2
#define DataWrite               0x0
#define DataRead                0x1

#define BitSetReset    0xF0
// Modifiers  or in bit number 0 to 7 
#define BitReset    0x0
#define BitSet      0x8

//==============================================================================
//
// Display Constants 
//
//==============================================================================

#define MemorySize 0x2000       // 8k could be 4k
#define GraphBase  0x0400       // base address of graphics memory
#define TextBase   0x0000       // base address of text memory
#define CGRamBase  0x1C00       // Requires offset = 3 
#define OFFSET     0x03         // CG rom 1800h - 1FFFh 
                                // char80h Starts at 1C00h
                                // 1800h + 80h*8 = 1C00  
                                
// These chars are at 0x80h 
// They are in a 6x8 field so work at both fonts
// 80h is an up arrow
// 81h is a square with a dot in the center
//

char MyChars[] = {0x00,0x04,0x0E,0x15,0x04,0x04,0x04,0x00,
                  0x00,0x3E,0x22,0x2A,0x22,0x3E,0x00,0x00};

#define NumMyChars 2 // change if chars added  
                                
#define DisplayX   128 
#define DisplayY    64 

#define ROWS       ((DisplayY / 8) - 1)

#define XMAX  (DisplayX -1)  // limits of (x,y) LCD graphics drawing
#define XMIN    0
#define YMAX  (DisplayY - 1)
#define YMIN    0

//==============================================================================
//
//  End of H file 
//
//==============================================================================

#asmline __CONFIG 0x3F3A ;  // Set 16F874 Fuses


//==============================================================================
//
//  Program Globals
//
//==============================================================================
 
BYTE FontSize ;
unsigned int BytesPerRow ;
BYTE Paged = 0 ;
int NumPages = 0 ;

// Used in Displaytest & Pagetest

int i ;
int xx ;
int yy ;
BYTE bb ;

// Used in lcd_line

int t , distance ;  // these were moved here to debug the line drawing routine 
int xerr , yerr  ;  // Not happy with line drawing routine
int Dx,Dy ;         // Still working on it.. Maybe float's ?
int incx,incy ;

 

           
//==============================================================================
//
// Function Proto's 
//
//==============================================================================

void ProcInit();
void lcd_init();
void DisplayTest();
void PageTest();
void StatSTA01(void);
void StatSTA3(void);
void DataOut(BYTE b);
void CmdOut(BYTE b);
void lcd_clear_graph();
void lcd_clear_text();
void lcd_char(char b);
void lcd_char_raw(char b);
void lcd_print(char *string);
void lcd_print_page( char *string, int page);
void lcd_pixel(int X, int Y, BYTE SetReset);
void lcd_xy(int X, int Y);
void lcd_xy_page(int X, int Y , int page);
void lcd_changeFont(char NewFont);
void SetAutoMode();
void AutoOut(BYTE B);
void ResetAutoMode();
void lcd_clear_textline(BYTE Line);
void lcd_cursorXY(BYTE X, BYTE Y);
void DataOut2(int b);
void lcd_plot( int X,int Y, BYTE SetReset);
void lcd_box(int X1, int Y1, int X2, int Y2, BYTE SetReset);
void lcd_line(int X1,int Y1, int X2, int Y2, BYTE SetReset);
void AutoZero(unsigned int Terminate);
void load_CGRAM(char *p, int NumBytes);
void lcd_changepage(int page);
void lcd_setup_page( BYTE Pages);


//==============================================================================
//
//  End Proto's
//
//==============================================================================


void main()
{
   ProcInit();

   lcd_init(); 
   PageTest() ; // Run once at start 
   while(1)
   {  
    DisplayTest();
     if ( FS == EightbyEight)  
       lcd_changeFont(SixbyEight);
     else
       lcd_changeFont(EightbyEight);
   }          
}

//=============================================================================
//
//
//  Start of Test program 
//
//
//=============================================================================

void PageTest()
{
  if (FS == SixbyEight)
    lcd_changeFont(EightbyEight);  
  lcd_setup_page(3);
  lcd_clear_text();
  CmdOut(DisplayModes | GraphicsOff | TextOn | CursorOff | CursorBlinkOff);
  // Page 1 
  lcd_xy(0,0);
  lcd_print(" Page Test" );
  lcd_xy(0,1) ;
  lcd_print("This is page 1");
  lcd_xy(0,2);
  lcd_print("0123456789012345");
  
  // Page 2
  lcd_xy_page(0,0,2);  // write to page 2... not displayed yet 
  lcd_print("This is page 2");
  lcd_xy_page(0,1,2);
  lcd_print("Page 2 line 2");
  lcd_xy_page(0,3,2);
  lcd_print("0123456789012345");
  
  // Page 3            // write to page 3... not displayed yet
  lcd_xy_page(0,0,3);
  lcd_print("This is page 3");
  lcd_xy_page(0,1,3);
  lcd_print("Page 3 line 2");
  lcd_xy_page(0,4,3);
  lcd_print("0123456789012345");
 
  Wait(5000);
  lcd_changepage(2);  // display page 2
  Wait(5000);
  
  lcd_changepage(3); //  display page 3
  Wait(5000);
  lcd_changepage(1);
  lcd_xy(0,0);
  lcd_print(" Shift Pages"); // shift left through pages
  Wait(4000);
  for(i=0;i<=32;i++)
   {
    DataOut2(TextBase+i) ;  // To start of page 3 
    CmdOut(ControlWordSet | TextHomeAddress); 
    Wait(300);
   } 
   Wait(5000);
 
  DataOut2(TextBase);			
  CmdOut(ControlWordSet | TextHomeAddress); 
  lcd_clear_text();
  lcd_xy(0,7);
  lcd_print(" End Page Demo");
  Wait(5000);
  
  CmdOut(DisplayModes | GraphicsOff | TextOff | CursorOff | CursorBlinkOff);
  lcd_changeFont(EightbyEight); // quick & dirty exit from page mode 
}  
  
  
void DisplayTest()
{
   lcd_clear_graph();
   lcd_clear_text(); 
   CmdOut(DisplayModes | GraphicsOff | TextOn | CursorOff | CursorBlinkOff); 
 
   lcd_xy(0,0); 
   lcd_print("Hello World");
   lcd_xy(0, 1); 
   if ( FS == EightbyEight)
    { 
     lcd_print("0123456789012345");
     lcd_xy(0, 2);
     lcd_print("8 X 8 Font");
    } 
   else
     {
      lcd_print("012345678901234567890"; 
      lcd_xy(0, 2);
      lcd_print("6 X 8 Font");
     } 
   lcd_xy(0, 3);
   lcd_print("Line 4");
   lcd_xy(0, 4);
   lcd_print("Line 5");
   lcd_xy(0,5);
   lcd_print("Line 6");
   lcd_xy(0,6);
   lcd_print("Line 7");
   lcd_xy(0, 7);
   lcd_print("More to come");
   Wait(7000);
   
   lcd_clear_textline(3);
   lcd_clear_textline(4);
   lcd_xy(0,3);
   lcd_print("lcd_char ");
   lcd_xy(0,4);
   lcd_char('A');
   lcd_char('B');
   Wait(4000);
   
   lcd_clear_textline(0);
   lcd_print(" Erase lines ");
   for(i=1;i<7;i++)
     {
       lcd_clear_textline(i);
       Wait(200);
     }  
   Wait(2000);
  
   lcd_clear_text();
   lcd_cursorXY(0,4);
   lcd_xy(0,0);
   lcd_print("CURSOR ON");
   CmdOut(DisplayModes | GraphicsOff | TextOn |CursorOn | CursorBlinkOff);
   Wait(2000);
 
   lcd_clear_textline(0);
   lcd_xy(0,0);
   lcd_print("Cursor Blink");
   CmdOut(DisplayModes | GraphicsOff | TextOn | CursorOn | CursorBlinkOn);
   Wait(2000);
      
   lcd_clear_textline(0);
   lcd_xy(0,0);
   lcd_print(" Move Cursor ");
   lcd_cursorXY(0,0);
   CmdOut(CursorPattern | 7); 
   CmdOut(DisplayModes | GraphicsOff | TextOn |CursorOn | CursorBlinkOff);
   for(i=0;i<=15;i++)
    {
     lcd_cursorXY(i,0);
     Wait(200);
    } 
   for(i=1;i<=4;i++)
    {
     lcd_cursorXY(15,i);
     Wait(200);
    }
   Wait(1000);
   lcd_cursorXY(7,3);  
   lcd_xy(0,1);
   lcd_print(" Cursor Size ");
   for(i=0;i<=7;i++)
     {
       CmdOut(CursorPattern | (BYTE)i );
       Wait(200);
     }  
   Wait(1000);
  
  
   CmdOut(DisplayModes | GraphicsOff | TextOn | CursorOff | CursorBlinkOff); 
   lcd_clear_text();
   lcd_xy(0,0); 
   lcd_print("Font Test");
   Wait(1000);
   
   lcd_xy(0,0);
   
   if ( FS == EightbyEight)
    {
      SetAutoMode();
      for(xx=0;xx<=0x7F;xx++)
        AutoOut(xx);
       ResetAutoMode();
     }
    else
     { // only 21 chars in 22 char field in 6x8 
      SetAutoMode();
      bb =21 ;
      for(xx=0;xx<=0x7f;xx++)
       {  
         if ( xx == bb)
          {
            bb += 21 ;
            AutoOut(0); // 0 to char 22 
          }  
         AutoOut(xx);
       } 
      ResetAutoMode(); 
     } 
   Wait(4000);
  
   lcd_clear_text();
   lcd_xy(0,0);
   lcd_print("My Chars");
   lcd_print("  ");
   lcd_char_raw(0x80);  // user chars direct 
   lcd_char_raw(0x81);
   lcd_xy(7,3);
   lcd_char(0x80 + 0x20); // add 20h to user char 
   lcd_char(0x81 + 0x20);
   
   Wait(4000);
   lcd_clear_text();
   lcd_xy(0,0);
   lcd_print("   Graphic Test ");
   CmdOut(DisplayModes | GraphicsOn | TextOn | CursorOff);
   lcd_line(XMAX/2,0,XMAX/2,YMAX,DRAW); // draw axis
   lcd_line(0,YMAX/2,XMAX,YMAX/2,DRAW);
   Wait(2000);
  
   CmdOut(DisplayModes | GraphicsOn | TextOff | CursorOff);
   Wait(3000);
  
   lcd_line(XMAX/2,0,XMAX/2,YMAX,ERASE);  // erase axis 
   lcd_line(0,YMAX/2,XMAX,YMAX/2,ERASE);
   Wait(1000);
  
   lcd_box(10,10,50,50,DRAW); // draw box 
   Wait(1000);
   
   lcd_box(10,10,50,50,ERASE); // erase box 
   Wait(1000); 
 
   lcd_line(XMAX/2,YMAX/2,0,0,DRAW);      //  crisscross  
   lcd_line(XMAX/2,YMAX/2,0,YMAX,DRAW);  
   lcd_line(XMAX/2,YMAX/2,XMAX,YMAX,DRAW);  
   lcd_line(XMAX/2,YMAX/2,XMAX,0,DRAW);
   Wait(4000);
      
   lcd_clear_graph();
   for(i=0;i<=XMAX;i+=10)                // pinwheel 
      lcd_line(XMAX/2,YMAX/2,i,YMAX,DRAW);
   for(i = YMAX;i>=0;i-=10)
      lcd_line(XMAX/2,YMAX/2,XMAX,i,DRAW);
   for(i = XMAX ;i>=0;i-=10)
       lcd_line(XMAX/2,YMAX/2,i,0,DRAW); 
    for(i=0;i<=YMAX;i+=10)
       lcd_line(XMAX/2,YMAX/2,0,i,DRAW);    
 
   Wait(4000);  
   lcd_clear_graph();
   for(i=5;i<60;i+=10)                  // shrinking boxes 
     lcd_box(i,i/2,126-i,63-i/2,DRAW);
   Wait(4000);  
}

//====================================================================================
//
//
//   End of Test 
//
//====================================================================================


void ProcInit()
{
   T1CON = 1 ;
   ADCON0 = 0;			// Kill the pesky A/D
   ADCON1 = 7 ;			// All Digital i/o									
   PORTB = 0x1F;                // FS low all else high
   TRISB = 0;			// output 
   PORTD = 0;
   TRISD = 0;			// output 
   PORTA = 0 ;
   TRISA = 0x3F ;
   CE = HI;			// All control lines HI
   RD = HI;
   WR = HI;
   CD = HI;
   RST = LO;			// Reset LCD
   Wait(1);                     // Delay 1 ms for reset could be shorter
   RST = HI;			// Release Reset
   Wait(1);                     // unnecessary 
   FS = EightbyEight ;		// Set font 8x8 
   if ( FS == EightbyEight)     // Set Font Size 
    {
      FontSize = 8 ; 
      BytesPerRow = 16 ;
     }         
   else
    {
      FontSize = 6 ;
      BytesPerRow = 22 ;       // only 21 chars per line but need 
                               // extra byte for last 2 bits in 
                               // graph mode
    }   
  }

//==============================================================================
//
//
// Initalize Display 
//
//
//==============================================================================


void lcd_init()
{
   CmdOut(DisplayModes | GraphicsOff | TextOff  | CursorOff );
   
   DataOut2(GraphBase);  		// Graphic Base Address
   CmdOut(ControlWordSet | GraphicHomeAddress); 
   
   DataOut2(BytesPerRow);		// Graphic Area 
   CmdOut(ControlWordSet | GraphicAreaSet); 
   
   DataOut2(TextBase);			// Text Base Address
   CmdOut(ControlWordSet | TextHomeAddress); 

   Paged = 0 ;                          // page mode off 
   DataOut2(BytesPerRow);		// Text Area 
   CmdOut(ControlWordSet | TextAreaSet); 
   
   DataOut2(OFFSET);                    // User CGRAM 
   CmdOut(PointerSet | OffsetRegisterSet);
   
   load_CGRAM(MyChars,(NumMyChars*8));

   CmdOut(ModeSet | ORMode | CGROMMode); // mode set

   CmdOut(CursorPattern | 3);		 // cursor is 8 lines high
   
   lcd_clear_graph();
   lcd_clear_text();
 
   lcd_cursorXY(0,0);        		// Move Cursor to (x,y)
}


//==============================================================================
//
//
//  MidLevel  Routines 
//
//==============================================================================



//==============================================================================
//
//   Clear Graph Display ... Writes 0's (blanks) to graphic ram 
//                           Uses Auto Mode 
//==============================================================================

void lcd_clear_graph()
{
     DataOut2(GraphBase);
     CmdOut(PointerSet | AddressPointerSet); 
     AutoZero( BytesPerRow * DisplayY  );
}

//==============================================================================
//
//   Clear Text Display .... Writes Zeros( blanks)  to Text Ram 
//                           Uses Auto Mode 
//
//==============================================================================                                

void lcd_clear_text()
{
   DataOut2(TextBase);
   CmdOut(PointerSet | AddressPointerSet);
   AutoZero( BytesPerRow * ( DisplayY /8 ));
}

//==============================================================================
//
// Clear the Text at specified line & set addr ptr to start of line 
//
//==============================================================================

void lcd_clear_textline(BYTE Line)
{
  lcd_xy(0,Line);
  AutoZero(BytesPerRow);
  lcd_xy(0,Line);
}

//==============================================================================
//
//  Write char b to display... Use lcd_xy to set location 
//  Also ROM generator is offset. Space (20h) is at 00 
//  So subtrace 0x20 from character 
//
//==============================================================================
        
void lcd_char(char b)
{
   DataOut(b - 0x20);
   CmdOut(DataRdWr);
}

// write raw data to screen for use with user defined chars

void lcd_char_raw(char b)
{
  DataOut(b);
  CmdOut(DataRdWr);
}  

//==============================================================================
//
//  Print String   Uses Auto Mode
//  Use lcd_xy to set location 
//  subtract 0x20 hex from char
//==============================================================================

void lcd_print(char *string)
{
   char *p = string ;
   
   SetAutoMode();
   while (*p)
      AutoOut(*p++ - 0x20);
   ResetAutoMode();
}

//==============================================================================
//
//  Set or Reset the Pixel at X,Y ; SetReset = DRAW or ERASE  
//
//==============================================================================

void lcd_pixel(int X, int Y, BYTE SetReset)
{
   BYTE Bit = (FontSize-1) - (X % FontSize);
 
   DataOut2(GraphBase + (Y * BytesPerRow) + (X / FontSize));
   CmdOut(PointerSet | AddressPointerSet);
   if ( SetReset )
     CmdOut(BitSetReset | BitSet   | Bit);
   else
     CmdOut(BitSetReset | BitReset | Bit );   
}

//==============================================================================
//
//  Set Text Ram address to X,Y  
//   
//==============================================================================


void lcd_xy(int X, int Y)
{
   DataOut2(TextBase + (Y * BytesPerRow) + X);
   CmdOut(PointerSet | AddressPointerSet);
}

//==============================================================================
//
//       PAGE DEMO STUFF 
// Page version of lcd_xy .. This is only good for 8x8 font
//    1 < page < x 
// I did NOT work all the features for paged display
// Just enough to learn how to do it.
//==============================================================================

void lcd_xy_page(int X, int Y , int page)
{
 if ( Paged)
   {
    DataOut2(TextBase + ( ( Y * BytesPerRow) + X ) + (page-1) * 16);
    CmdOut(PointerSet | AddressPointerSet);
   } 
} 

//
// Change pages... Move TextHome address to Base + page offset 
//

void lcd_changepage(int page)
{
 if ( (Paged) && ( page <= NumPages) )
   {
     DataOut2(TextBase + ((page-1) * 16)); // Text Base Address + page offset
     CmdOut(ControlWordSet | TextHomeAddress); 
   }  
}

//
// Setup pages. Assumes 16 bytes/page.
// Careful not to overwrite Graphic area
//

void lcd_setup_page( BYTE Pages)
{
   Paged = 1 ;
   NumPages = Pages ;
   BytesPerRow = 16 * NumPages ;     // only good for 8x8 font 
   DataOut2(TextBase);			// Text Base Address
   CmdOut(ControlWordSet | TextHomeAddress); 

   DataOut2(BytesPerRow);		//  Text Area 
   CmdOut(ControlWordSet | TextAreaSet); 
}   
        
//==============================================================================
//
//   Position cursor at column, row
//   No value checking is done .. watch out in 6x8 mode
//==============================================================================

void lcd_cursorXY(BYTE X, BYTE Y)
{
   DataOut(X);
   DataOut(Y);
   CmdOut(PointerSet | CursorPointerSet); 
}

//==============================================================================
//
// Change Font  either 8x8 or 6x8
//
//==============================================================================

void lcd_changeFont(char NewFont)
{
  if ( NewFont == EightbyEight)
   {
     FS = EightbyEight ;
     FontSize = 8 ;
     BytesPerRow = 16;
     lcd_init();
   }
  else
  if ( NewFont == SixbyEight)
   {
     FS = SixbyEight ;
     FontSize = 6 ;
     BytesPerRow = 22 ;
     lcd_init(); 
   }
}   

//==============================================================================    
//
//   Load User CGRAM  Note ADP is changed  
//   User must point to correct text or graph address before next write 
//   Should really put the user chars in eeprom -- next time.
//
//==============================================================================            
void load_CGRAM(char *p, int NumBytes)
{
  DataOut2(CGRamBase);  
  CmdOut(PointerSet | AddressPointerSet );
  SetAutoMode();
  while(NumBytes--)
    AutoOut(*p++);
  ResetAutoMode();
}             
    
//==============================================================================
//
// Primitive plot routines flips the Y axis 
// 0,0 located at lower left corner 
//
//==============================================================================    
  
  
void lcd_plot( int X,int Y, BYTE SetReset)
{
  BYTE Bit = (BYTE) ((FontSize -1 ) - ( X % FontSize)) ; // bit in byte
  
  DataOut2(GraphBase +  (YMAX - Y) *  BytesPerRow + ( X / FontSize ));
  CmdOut(PointerSet | AddressPointerSet);
   if ( SetReset  )
     CmdOut(BitSetReset | BitSet   | Bit);
   else
     CmdOut(BitSetReset | BitReset | Bit);   
}

//
// Draw a box lower left corner.. upper right corner 
//

void lcd_box(int X1, int Y1, int X2, int Y2, BYTE SetReset)
{
   lcd_line(X1,Y1,X1,Y2, SetReset);
   lcd_line(X1,Y2,X2,Y2, SetReset);
   lcd_line(X2,Y2,X2,Y1, SetReset);
   lcd_line(X2,Y1,X1,Y1, SetReset);
}       

//
// Line drawing routine using Bersenham's Algorithm
// Could be better!
// 

void lcd_line(int X1,int Y1, int X2, int Y2, BYTE SetReset)
{
// int t , distance ;    made global for debug 
// int xerr , yerr = 0 ;
// int Dx,Dy ;
// int incx,incy ;
// int XO,YO ;
// Bersenham's algorithm
 Dx = X2 - X1 ;
 Dy = Y2 - Y1 ;
 if ( Dx > 0 ) 
   incx = 1 ;
 else
  if (Dx == 0 )
    incx = 0 ;
  else
   incx = -1 ;
 if ( Dy > 0 ) 
   incy = 1 ;
 else
   if ( Dy == 0 ) 
     incy = 0 ;
   else
     incy = -1 ;
 Dy = fabs(Dy); 
 Dx = fabs(Dx);
 
 if ( Dx > Dy ) 
   distance = Dx ;
 else
  distance = Dy ;
  
 yerr = 0;
 xerr = 0;
 for(t=0;t<=distance+1;t++)
 {
  lcd_plot(X1,Y1,SetReset);
  xerr += Dx ;
  yerr += Dy ;
  if ( xerr > distance )
   {
     xerr -= distance ;
     X1 += incx ;
     if ( X1 < 0 )
        X1 = 0 ;
     if ( X1 > XMAX )
        X1 = XMAX ;   
   }
  if(yerr > distance )
   {
     yerr -= distance ;
     Y1 += incy;
     if ( Y1 < 0 )
       Y1 = 0 ;
     if ( Y1 > YMAX ) 
       Y1 = YMAX ;  
   }
 }                                                                    
}


//==============================================================================
//
// Send Terminate number of Zeros ( same as 20h) in Auto Mode. Used for 
// clear ram routines 
//
//==============================================================================

void AutoZero( unsigned int Terminate )
{
  SetAutoMode();
  while(Terminate--)
    AutoOut(0);
  ResetAutoMode() ;
}    
                                      
//==============================================================================
//
// Low Level Control Routines 
//
//
//
//==============================================================================


//
// Read status STA0 and STA1 return when not busy 
// It will get stuck here if signals are bad 
// 

void StatSTA01()
{
   BYTE lcd_status;

   TRISD = 0xFF;  // Input Mode
   do
   {
      CE = LO;
      RD = LO;
      lcd_status = Ldata; 
      CE = HI;
      RD = HI;
   }
   while ((lcd_status & STA0STA1) != STA0STA1);
   TRISD = 0; // output mode 

}

//
// Read status of STA3 for auto mode return when not busy 
// Can get stuck here !!

void StatSTA3()
{
   BYTE lcd_status;

   TRISD = 0xFF;   // Input Mode 
   do
   {
      CE = LO;
      RD = LO;
      lcd_status = Ldata; // read LCD status byte
      CE = HI;
      RD = HI;
   }
   while ((lcd_status & STA3) != STA3);
   TRISD = 0; // output mode 

}

//
// Check STA1 & STA2 and send data byte when not busy
//

void DataOut(BYTE b)
{
   StatSTA01();
   CD = LO;
   Ldata = b;
   CE = LO; // low for > 80 ns 
   WR = LO ;
   CE = HI;
   WR = HI;
   CD = HI ;
}

//
// Send 2 data bytes 
//

void DataOut2(int b)
{
    DataOut(LoByte(b));
    DataOut(HiByte(b));
}    

//
//  check STA1 & STA2 and send command when not busy
//

void CmdOut(BYTE b)
{
   StatSTA01();
   Ldata = b;
   CE = LO;
   WR = LO; // > 80 ns 
   CE = HI;
   WR = HI;
}

//
// Set Auto Mode 
//


void SetAutoMode()
{
  CmdOut(DataAuto | DataAutoWrite);
}

//
// Check STA3 Send byte Auto Mode when not busy
//

void AutoOut(BYTE b)
{
   StatSTA3();
   CD = LO;
   Ldata = b;
   CE = LO; // low for > 80 ns 
   WR = LO ;
   CE = HI;
   WR = HI;
   CD = HI ;
}

//
// Check STA3 Exit Auto Mode when not busy 
// Same as CmdOut but checks STA3 first 
//

void ResetAutoMode()
{
   StatSTA3();
   Ldata = DataAuto | AutoReset;
   CE = LO;
   WR = LO; // > 80 ns 
   CE = HI;
   WR = HI;
}



