#include <p89v51rx2.h>
#include <glcd.h>

//function prototypes
void LcdSelectSide(unsigned char u8LcdSide);
void LcdDataWrite(unsigned char u8Data);
void LcdInstructionWrite(unsigned char u8Instruction);
unsigned char LcdDataRead();
void LcdWaitBusy();
void GLCD_ClearScreen();
void LcdSetDot(unsigned char u8Xaxis,unsigned char u8Yaxis);

/* PROGRAM CODE */


/* CODE TO INVERT AN UNSIGNED BYTE */
unsigned char ByteInvert(unsigned char u8Byte)
{
	unsigned char u8InvertedByte=0;
	unsigned char u8Counter,u8Temporary;
	
	for(u8Counter=0;u8Counter<8;u8Counter++)
	{
		u8Temporary = u8Byte & 0x80;
		u8InvertedByte = u8InvertedByte | u8Temporary;
		u8Byte = u8Byte << 1;
		if(u8Counter != 7)
			u8InvertedByte = u8InvertedByte >> 1;
	}
	
	return u8InvertedByte;
}

/* CODE TO INITIALIIZE LCD */
void GLCD_LcdInit()
{
	LCD_RS = 0;
	LCD_RW = 0;
	LCD_E = 0;
	LCD_DATA = 0x00;

	LCD_RST = 1;
	
	LCD_RST = 0;

	LCD_RST = 1;


	LcdSelectSide(LEFT);
	LcdInstructionWrite(DISPLAY_OFF);	/* DISPLAY OFF */
	LcdInstructionWrite(START_LINE);
	LcdInstructionWrite(Y_ADDRESS);
	LcdInstructionWrite(X_ADDRESS);
	LcdInstructionWrite(DISPLAY_ON);	/* DISPLAY ON */

	LcdSelectSide(RIGHT);
	LcdInstructionWrite(DISPLAY_OFF);	/* DISPLAY OFF */
	LcdInstructionWrite(START_LINE);
	LcdInstructionWrite(Y_ADDRESS);
	LcdInstructionWrite(X_ADDRESS);
	LcdInstructionWrite(DISPLAY_ON);	/* DISPLAY ON */
	
	GLCD_ClearScreen();		
}

/* CODE TO SELECT SIDE OF LCD */
void LcdSelectSide(unsigned char u8LcdSide)
{
	switch(u8LcdSide)
	{
		case RIGHT:
				LCD_CS1=0;
				LCD_CS2=1;
				break;
		case LEFT:	
				LCD_CS1=1;
				LCD_CS2=0;
				break;
	}	//what else is there to do?
}


/* CODE TO SEND DATA TO LCD */
void LcdDataWrite(unsigned char u8Data)
{
	unsigned char u8InvertedData;

	u8InvertedData = ByteInvert(u8Data);

	LcdWaitBusy();						/* wait until LCD becomes free */

	LCD_RS = 1;						/* data mode */
	LCD_RW = 0;						/* write mode */
	LCD_DATA = u8InvertedData;				/* outbyte */	
	LCD_E = 1;						/* strobe to start write cycle*/

	LCD_E = 0;						/* end write cycle */
}	

/* CODE TO SEND INSTURCTION TO LCD */
void LcdInstructionWrite(unsigned char u8Instruction)
{
	unsigned char u8InvertedInstruction;

	u8InvertedInstruction = ByteInvert(u8Instruction);

	LcdWaitBusy();						/* wait until LCD becomes free */
	
	LCD_RS = 0;						/* instruction mode */
	LCD_RW = 0;						/* write mode */
	LCD_DATA = u8InvertedInstruction; 			/* outbyte */
	LCD_E = 1;						/* strobe to start write cycle*/

	LCD_E = 0;						/* end write cycle */
}


/* CODE TO READ DATA FROM LCD */
unsigned char LcdDataRead()
{
	unsigned char u8InvertedLcdData, u8LcdData;

	LCD_E = 0;						/* clear enable pin */
	LCD_RS = 1;						/* data mode */
	LCD_RW = 1;						/* read mode */
	LCD_DATA = 0xFF;					/* set LCD_DATA port in input mode */
	LCD_E = 1;						/* strobe to start read cycle */
	u8InvertedLcdData = LCD_DATA;				/* read data from controller */
	LCD_E = 0;						/* end read cycle */
	
	u8LcdData = ByteInvert(u8InvertedLcdData);
	return u8LcdData;
}


/* CODE TO WAIT AS LONG AS THE LCD IS BUSY */
void LcdWaitBusy()
{
	unsigned char u8InvertedLcdStatus, u8LcdStatus;

	do
	{
		LCD_E = 0;					/* clear enable pin */
		LCD_RS = 0;					/* instruction mode */
		LCD_RW = 1;					/* read mode */
		LCD_DATA = 0xFF;				/* set LCD_DATA in input mode */
		LCD_E = 1;					/* clock out command to LCD */
		u8InvertedLcdStatus = LCD_DATA;			/* read the return value */
		u8LcdStatus = ByteInvert(u8InvertedLcdStatus);
	}while((u8LcdStatus & 0x80) == BUSY);			/* mask the other bits and test the BUSY bit */

	LCD_E = 0;						/* finish the command */
	LCD_RW = 0;						/* turn off RW for future commands */
}


/* CODE TO CLEAR THE LCD SCREEN */
void GLCD_ClearScreen()
{
	unsigned char u8Page = 0;
	unsigned char u8Column = 0;
	
	for(u8Page = 0x0; u8Page < 0x08; u8Page = u8Page + 0x01)
	{
		LcdSelectSide(LEFT);					/* Select left side */
		LcdInstructionWrite(X_ADDRESS | u8Page);		/* set the page number */
		LcdInstructionWrite(Y_ADDRESS);				/* set column to 0 */
	
		for(u8Column = 0; u8Column < 128 ; u8Column++)
		{
			if(u8Column==64)
			{
				LcdSelectSide(RIGHT);			/* select right side */
				LcdInstructionWrite(X_ADDRESS|u8Page);	/* set the page number */
				LcdInstructionWrite(Y_ADDRESS);		/* set to column 0 */
			}
			LcdDataWrite(0x00);				/* erase a column */
		}	
	}
}	

/* CODE TO DRAW A DOT ON THE LCD */
void LcdSetDot(unsigned char u8Xaxis,unsigned char u8Yaxis)
{
	unsigned char u8DataRead=0;	

	if(u8Xaxis > 127 | u8Xaxis < 0 | u8Yaxis > 63 | u8Yaxis < 0)
		return;

	if(u8Xaxis < 64)
		LcdSelectSide(LEFT);
	else
	{
		LcdSelectSide(RIGHT);
		u8Xaxis -= 64;
	}

	LcdInstructionWrite(X_ADDRESS + (u8Yaxis / 8));		/* select page number */
	LcdInstructionWrite(Y_ADDRESS + u8Xaxis);		/* select column */
	u8DataRead = LcdDataRead();				/* dummy read */
	u8DataRead = LcdDataRead();				/* read the current location */

	LcdInstructionWrite(X_ADDRESS + (u8Yaxis / 8));		/* select page number */
	LcdInstructionWrite(Y_ADDRESS + u8Xaxis);		/* select column */
	LcdDataWrite(u8DataRead | (1<<(u8Yaxis % 8)));		/* plot the dot */
}

/* CODE TO DRAW A RECTANGLE ON THE LCD */

void GLCD_Rectangle(unsigned char u8Xaxis1,unsigned char u8Yaxis1, unsigned char u8Xaxis2,unsigned char u8Yaxis2)
{
	unsigned char u8CurrentValue=0;	

	/* Draw two horizontal lines */
	for(u8CurrentValue = 0 ; u8CurrentValue < ( u8Xaxis2 - u8Xaxis1 + 1 ) ; u8CurrentValue++)
	{	
		LcdSetDot(u8Xaxis1 + u8CurrentValue, u8Yaxis1);
		LcdSetDot(u8Xaxis1 + u8CurrentValue, u8Yaxis2); 
	}

	/* draw two vertical lines */
	for(u8CurrentValue = 0 ; u8CurrentValue < ( u8Yaxis2 - u8Yaxis2 + 1 ) ; u8CurrentValue++)
	{
		LcdSetDot(u8Xaxis1,u8Yaxis1 + u8CurrentValue);
		LcdSetDot(u8Xaxis2,u8Yaxis1 + u8CurrentValue);
	}
}

/* CODE TO DRAW A VERTICAL LINE */
void GLCD_VLine(unsigned char u8X1, unsigned char u8X2, unsigned char u8Y)
{
	unsigned char u8Counter;

	for(u8Counter = u8X1 ; u8Counter < u8X2 ; u8Counter++)
		LcdSetDot(u8Counter,u8Y);			
}

/* CODE TO DRAW A HORIZONTAL LINE */
void GLCD_HLine(unsigned char u8X, unsigned char u8Y1, unsigned char u8Y2)
{
	unsigned char u8Counter;

	for(u8Counter = u8Y1 ; u8Counter < u8Y2 ; u8Counter++)
		LcdSetDot(u8X,u8Counter);			
}

