/*
 *****************************************************************************
 * Module for Microchip Graphics Library
 * Solomon SSD1928 external LCD controller driver
 * 8/16-bit 8080 mode, 16bpp mode only
*
 * Author: John Leung
 * File: SSD1928.c 
 * Company: TechToys Company
 * Web presence: www.TechToys.com.hk
 * Date: 11 June 2008
 *
 * Note:			Original source code from Microchip Graphic Library 
 *					The original software license agreement as stated below:
 *
 * Software License Agreement
 *
 * Copyright ?2007 Microchip Technology Inc.  All rights reserved.
 * Microchip licenses to you the right to use, modify, copy and distribute
 * Software only when embedded on a Microchip microcontroller or digital
 * signal controller, which is integrated into your product or third party
 * product (pursuant to the sublicense terms in the accompanying license
 * agreement).  
 *
 * You should refer to the license agreement accompanying this Software
 * for additional information regarding your rights and obligations.
 *
 * SOFTWARE AND DOCUMENTATION ARE PROVIDED “AS IS?WITHOUT WARRANTY OF ANY
 * KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
 * OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
 * PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR
 * OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION,
 * BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT
 * DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL,
 * INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
 * COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY
 * CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
 * OR OTHER SIMILAR COSTS.
 *****************************************************************************
*/

/*
******************************************************************************
* 1st Aug 08
* Add _gpioStatus for ssd1928¡¦s SPI
******************************************************************************
*/

/*
******************************************************************************
* 11st Aug 2008
* Introduction of 
* 1) BYTE _page for SetActivePage(page) & SetVisualPage(page)
* 2) DWORD _line_mem_pitch for LINE_MEM_PITCH, change all LINE_MEM_PITCH
*		constants to variable _line_mem_picth
* 3) Change PutPixel(x,y) for _line_mem_pitch
* 4) SetLinePitch(pitch) for InitGraph() under primitive.c
* 5) Change Bar() for _line_mem_pitch
* 6) PAGE_MEM_SIZE for SCREEN_HOR_SIZE*SCREEN_VER_SIZE*2 under ssd1928.h
******************************************************************************
*/

/*
******************************************************************************
* 24th Sept 2008
* 1) Modify GetReg(index) for 8-bit operation by using 0xFF instead of 0xFFFF
*	 for data port operation
******************************************************************************
*/

/*
******************************************************************************
* 28th Sept 2008
* 1) Add GetRegW(WORD index), but you need to test it before use.
******************************************************************************
*/

/*
******************************************************************************
* 30th Sept 2008
* 1) Tested GetRegW(WORD index) with the register at REG_FLOAT_WIN_X_END_POS0
*		which seems OK.
*
******************************************************************************
*/

/*
******************************************************************************
* 28th Nov 2008
* 1) Move low level I/O function prototype incl. 
*		SetRegW(WORD index, WORD value);
*		SetRegL(WORD index, DWORD value);
*		GetRegL(WORD index);
*		to SSD1928.h to solve the pecular error for ssd1928_sdhc.c
******************************************************************************
*/

#include "Graphics\Graphics.h"

// Color
WORD  _color;
// Page
BYTE  _page;
// line memory pitch for main window & floating windows. 
// For main window, it is the default value LINE_MEM_PITCH
DWORD _line_mem_pitch;

// Clipping region control
SHORT _clipRgn;
// Clipping region borders
SHORT _clipLeft;
SHORT _clipTop;
SHORT _clipRight;
SHORT _clipBottom;

// SSD1928 specific
BYTE  _gpioStatus = 0;

/////////////////////// LOCAL FUNCTIONS PROTOTYPES ////////////////////////////

#ifdef USE_8BIT_8080
// 8-bit 8080 access mode
void SetAddress(DWORD address);
void WriteData(WORD value);
BYTE GetData(void);
void SetRegB(WORD index, BYTE value);		//set register value for 8-bit length (BYTE)
void SetMemB(DWORD mem, BYTE value);		//set memory value for 8-bit length (BYTE)
BYTE GetReg(WORD index);
WORD GetRegW(WORD index);
#endif


void PutImage1BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch);
void PutImage4BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch);
void PutImage8BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch);
void PutImage16BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch);

void PutImage1BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch);
void PutImage4BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch);
void PutImage8BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch);
void PutImage16BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch);


/**************** LOCAL FUNCTIONS PROTOTYPES (SSD1928 SPECIFIC) ****************/
void GPIO_SPRESET(BOOL state);
void GPIO_SPENA(BOOL state);
void GPIO_SPCLK(BOOL state);
void GPIO_SPDAT(BOOL state);

void SPI_Write(BYTE byte);
void SPI_SetReg(BYTE reg, WORD cmd);


/*********************************************************************
* Function:  void  DelayMs(WORD time)
*
* PreCondition: none
*
* Input: time - delay in ms
*
* Output: none
*
* Side Effects: none
*
* Overview: delays execution on time specified in ms
*
* Note: delay is defined for 16MIPS
*
********************************************************************/
#define DELAY_1MS 16000/5  // for 16MIPS
void  DelayMs(WORD time){
unsigned delay;
	while(time--)
		for(delay=0; delay<DELAY_1MS; delay++);	
}


#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  void SetAddress(DWORD address)
*
* PreCondition: #define USE_8BIT_8080 or #USE_16BIT_8080 under SSD1928.h
*
* Input: address - address in 32bit DWORD
*
* Output: none
*
* Side Effects: none
*
* Overview: sets the address for read/write operations
*
* Note: The range of address from 0x00000 to 0x3FFFF.
*		The min. CS# pulse low is 82ns, and min. pulse high is 82ns.
*		For PIC24 at 16MIPS, one instruction cycle takes 62.5ns.
*		Therefore it may be necessary to pause between CS strobes.
********************************************************************/
void SetAddress(DWORD address)
{
 RS_LAT_BIT = 0;	// write command
 WR_LAT_BIT = 0;	// prepare for write action
 RD_LAT_BIT = 1;

 DAT_LAT_BIT = 0x80|(((DWORD_VAL)address).v[2]);	// M/R#=1 => memory access, and this bitwise OR with the address content AB18:16 
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;
	
 DAT_LAT_BIT = ((DWORD_VAL)address).v[1];
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;

 DAT_LAT_BIT = ((DWORD_VAL)address).v[0];
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;

 WR_LAT_BIT = 1;
}
#else
#ifdef USE_16BIT_8080

void SetAddress(DWORD address)
{
	//16-bit 8080 accessing mode
}
#else
#error ACCESSING MODE NOT DEFINED UNDER SSD1928.h
#endif
#endif

#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  void  WriteData(WORD value)
*
* PreCondition: #define USE_8BIT_8080 or #USE_16BIT_8080 under SSD1928.h
*
* Input:  value - value to be written in WORD format
*
* Output: none
*
* Side Effects: none
*
* Overview: writes 16-bit word into video buffer at current address.
*           The address pointer is incremented after the operation.
*
* Note: The min. CS# pulse low is 82ns, and min. pulse high is 82ns.
*		For PIC24 at 16MIPS, one instruction cycle takes 62.5ns.
*		Therefore it may be necessary to pause between CS strobes.
********************************************************************/
void WriteData(WORD value)
{
	RS_LAT_BIT = 1;	// DC set high to write data
 	WR_LAT_BIT = 0;
 	RD_LAT_BIT = 1;
 
 	DAT_LAT_BIT = ((WORD_VAL)value).v[1];
 	PMPDelay();
 	CS_LAT_BIT = 0;
 	CS_LAT_BIT = 1;

 	DAT_LAT_BIT = ((WORD_VAL)value).v[0];
 	PMPDelay();
 	CS_LAT_BIT = 0;
 	CS_LAT_BIT = 1;

 	WR_LAT_BIT = 1;
}
#else
#ifdef USE_16BIT_8080
void WriteData(WORD value)
{}
#endif
#endif

#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  BYTE  ReadData(void)
*
* PreCondition: #define USE_8BIT_8080 under SSD1928.h
*
* Input:  none
*
* Output: BYTE read from IO bus
*
* Side Effects: none
*
* Overview:
*
* Note: The min. CS# pulse low is 82ns, and min. pulse high is 82ns.
*		For PIC24 at 16MIPS, one instruction cycle takes 62.5ns.
*		Therefore it may be necessary to pause between CS strobes.
********************************************************************/
BYTE GetData(void)
{
	BYTE value;

	RS_LAT_BIT = 1;	// DC set high to get data
	RD_LAT_BIT = 0;
	WR_LAT_BIT = 1;
	DAT_TRIS_BIT = 0xFF;
	CS_LAT_BIT = 0;
	PMPDelay(); PMPDelay();
	value = DAT_PORT_BIT;
	CS_LAT_BIT = 1;
	RD_LAT_BIT = 1;
	
}
#endif


#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  void  SetRegB(WORD index, BYTE value)
*
* PreCondition: #define USE_8BIT_8080 or #USE_16BIT_8080 under SSD1928.h
*
* Input: index - register number in WORD format
*        value - byte value to be set
*
* Output: none
*
* Side Effects: none
*
* Overview: sets graphics controller register(WORD index)
*	    	with a particular value(BYTE)
*
* Note: The min. CS# pulse low is 82ns, and min. pulse high is 82ns.
*		For PIC24 at 16MIPS, one instruction cycle takes 62.5ns.
*		Therefore it may be necessary to pause between CS strobes.
********************************************************************/
void SetRegB(WORD index, BYTE value)
{
 RS_LAT_BIT = 0;		// write command
 WR_LAT_BIT = 0;
 RD_LAT_BIT = 1;
 
 DAT_LAT_BIT = 0x00;	// M/R#=0 => register
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;


 DAT_LAT_BIT = ((WORD_VAL)index).v[1];
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;


 DAT_LAT_BIT = ((WORD_VAL)index).v[0];
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;

 RS_LAT_BIT = 1;		// write data
 DAT_LAT_BIT = value;
 PMPDelay();
 CS_LAT_BIT = 0;
 CS_LAT_BIT = 1;

 WR_LAT_BIT = 1;
}

#else
#ifdef USE_16BIT_8080
void SetRegB(WORD index, BYTE value)
{}
#endif
#endif

#ifdef USE_8BIT_8080
void SetMemB(DWORD mem, BYTE value)
{
	SetAddress(mem);
	RS_LAT_BIT = 1;	// DC set high to write data
 	WR_LAT_BIT = 0;
 	RD_LAT_BIT = 1;

 	DAT_LAT_BIT = value;
 	PMPDelay();
 	CS_LAT_BIT = 0;
 	CS_LAT_BIT = 1;
}
#endif

#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  BYTE GetReg(WORD index)
*
* PreCondition: #define USE_8BIT_8080 or #USE_16BIT_8080 under SSD1928.h
*
* Input: index - register number
*
* Output: returns graphics controller register value in byte format
*
* Side Effects: none
*
* Note: No read burst termination applied yet!
*
********************************************************************/
BYTE GetReg(WORD index)
{
	BYTE value;

	RS_LAT_BIT = 0;		// write command
 	WR_LAT_BIT = 0;
 	RD_LAT_BIT = 1;

 	DAT_LAT_BIT = 0x00;	// M/R#=0 => register
 	PMPDelay();
 	CS_LAT_BIT = 0;		// write M/R#
 	CS_LAT_BIT = 1;


	DAT_LAT_BIT = ((WORD_VAL)index).v[1];
 	PMPDelay();
 	CS_LAT_BIT = 0;		// write AB15:8
 	CS_LAT_BIT = 1;


 	DAT_LAT_BIT = ((WORD_VAL)index).v[0];
	PMPDelay();
 	CS_LAT_BIT = 0;			// write AB7:0
 	CS_LAT_BIT = 1;

	DAT_TRIS_BIT = 0xFF;	// important! Set data port to be input here

	RD_LAT_BIT = 0;			// setup for read command
	RS_LAT_BIT = 1;		
	WR_LAT_BIT = 1;

 	CS_LAT_BIT = 0;			// dummy read
 	CS_LAT_BIT = 1;
	PMPDelay();

 	CS_LAT_BIT = 0;			// real read strobe
 	PMPDelay();				// It is around 30ns after a negative edge of CS to data ready 
 	CS_LAT_BIT = 1;			// at the data port. Because we are using PMPDelay() and an extra cycle to raise
							// the CS bit, we have got a lot of time for data ready.
	PMPDelay();

	value = DAT_PORT_BIT;	

	DAT_TRIS_BIT = 0x00;	// reset the port direction for write action
	RD_LAT_BIT = 1;			// reset the RD pin to high

	return value;
}
#else
#ifdef USE_16BIT_8080
BYTE GetReg(WORD index)
{}
#endif
#endif

#ifdef USE_8BIT_8080
/*********************************************************************
* Function:  WORD GetReg(WORD index)
*
* PreCondition: #define USE_8BIT_8080 or #USE_16BIT_8080 under SSD1928.h
*
* Input: index - register number
*
* Output: returns the word read from register index
*
* Side Effects: none
*
********************************************************************/
WORD GetRegW(WORD index)
{
	WORD wvalue;
	BYTE bvalue;

	RS_LAT_BIT = 0;		// write command
 	WR_LAT_BIT = 0;
 	RD_LAT_BIT = 1;

 	DAT_LAT_BIT = 0x00;	// M/R#=0 => register
 	PMPDelay();
 	CS_LAT_BIT = 0;		// write M/R#
 	CS_LAT_BIT = 1;


	DAT_LAT_BIT = ((WORD_VAL)index).v[1];
 	PMPDelay();
 	CS_LAT_BIT = 0;		// write AB15:8
 	CS_LAT_BIT = 1;


 	DAT_LAT_BIT = ((WORD_VAL)index).v[0];
	PMPDelay();
 	CS_LAT_BIT = 0;			// write AB7:0
 	CS_LAT_BIT = 1;

	DAT_TRIS_BIT = 0xFF;	// important! Set data port to be input here

	RD_LAT_BIT = 0;			// setup for read command
	RS_LAT_BIT = 1;		
	WR_LAT_BIT = 1;

 	CS_LAT_BIT = 0;			// dummy read
 	CS_LAT_BIT = 1;
	PMPDelay();

 	CS_LAT_BIT = 0;			// real read strobe
 	PMPDelay();				// It is around 30ns after a negative edge of CS to data ready 
 	CS_LAT_BIT = 1;			// at the data port. Because we are using PMPDelay() and an extra cycle to raise
							// the CS bit, we have got a lot of time for data ready.
	PMPDelay();

	bvalue = DAT_PORT_BIT;	// it is the register at N

 	CS_LAT_BIT = 0;			// 2nd read strobe
 	PMPDelay();				// It is around 30ns after a negative edge of CS to data ready 
 	CS_LAT_BIT = 1;			// at the data port. Because we are using PMPDelay() and an extra cycle to raise
							// the CS bit, we have got a lot of time for data ready.
	PMPDelay();
	wvalue = (BYTE)DAT_PORT_BIT;
	wvalue = wvalue<<8;
	wvalue = wvalue|bvalue;

	DAT_TRIS_BIT = 0x00;	// reset the port direction for write action
	RD_LAT_BIT = 1;			// reset the RD pin to high

	return wvalue;
}
#else
#ifdef USE_16BIT_8080
WORD GetRegW(WORD index)
{}
#endif
#endif



/*
*************************************************************************************
* Function: 	Send a value in 16-bit word to a register 'index'
*
* PreCondition: dependency on SetRegB(WORD index, BYTE value)
*				
*
* Arguments: 	'WORD' index - register number in WORD format
*				'WORD' value - in 16-bit format
* Return: 		none
*
* Note:			Address of index register denotes the lower starting address
*************************************************************************************
*/
void SetRegW(WORD index, WORD value)
{
	BYTE byte2write;
	
	byte2write = (BYTE)(value&0xFF);
	SetRegB(index,byte2write);

	byte2write = (BYTE)((value>>8)&0xFF);
	SetRegB(index+1, byte2write);
}

/*
*************************************************************************************
* Function: 	Send a 32-bit DWORD value to a register 'index'
*
* PreCondition: dependency on SetRegW(WORD index, WORD value)
*
* Arguments:	'WORD' index - register number in WORD format
*				'DWORD' value in 32-bit format
* Return: none
*
* Note:			Address of index register denotes the lower starting address.
*				Registers of SSD1928 have been arranged such that 
*				no register will be set for the 4th most significant byte
*				when the width of the register is 24-bit wide.
*				For example, REG_DV_DV0_START_ADDR_X occupy 0x1b0:2
*				REG_DV_DV0_START_ADDR_0		   	0x1b0
*				REG_DV_DV0_START_ADDR_1		   	0x1b1
*				REG_DV_DV0_START_ADDR_2   		0x1b2
*				
*				There is nothing at memory 0x1b3 and it skips to 0x1b4 for 
*				REG_DV_DV1_START_ADDR_0.
*				This makes SetRegL(WORD index, DWORD value)
*				work perfectly for a complete 16-bit access in upper and lower words
*				format. Otherwise, this function will set something for the register at
*				the highest byte location with a wrong value
*************************************************************************************
*/
void SetRegL(WORD index, DWORD value)
{
	WORD word2write;
	
	word2write = (WORD)(value&0xFFFF);	//write lower word for register 'index'
	SetRegW(index,word2write);

	word2write = (WORD)((value>>16)&0xFFFF);
	SetRegW(index+2, word2write);		//write upper word for register 'index+2'
}


/*
*************************************************************************************
* Function: 	Get a 32-bit DWORD value from a register at 'index'
*
* PreCondition: dependency on GetRegW(WORD index)
*
* Arguments:	'WORD' index - register number in WORD format
* Return: 		The DWORD value from register at 'index'
*
* Note:			
*************************************************************************************
*/
DWORD GetRegL(WORD index)
{
	DWORD lval;

	lval = GetRegW(index+2);
	lval  = (lval<<16) | GetRegW(index);

	return lval;
}

/*********************************************************************
* Function:  void ResetDevice()
*
* PreCondition: none
*
* Input: none
*
* Output: none
*
* Side Effects: none
*
* Overview: Initialize low level IO port for mcu,
*			configure LCD via SPI, initialize SSD1928 for PCLK, HYSNC, VSYNC etc
*
* Note: none
*
********************************************************************/
void ResetDevice(void){

	//Set LED backlight on
	LED_TRIS_BIT = 0;
	LED_LAT_BIT = 1;	//ON
	
    // Set LCD CS pin as output
    CS_TRIS_BIT = 0;
    // Disable LCD 
    CS_LAT_BIT = 1;
    
    // Set reset pin as output
    RST_TRIS_BIT = 0;
    // Hold in reset
    RST_LAT_BIT = 0;
    
	// Set RS pin as output
	RS_TRIS_BIT = 0;
	RS_LAT_BIT = 0;

	// Set RD pin as output
	RD_TRIS_BIT = 0;
	RD_LAT_BIT = 1;

	// Set WR pin as output
	WR_TRIS_BIT = 0;
	WR_LAT_BIT = 1;

	// Set SLEEP pin as output
	SLEEP_TRIS_BIT = 0;
	SLEEP_LAT_BIT  = 0;							// non-sleep operation

	// Set DATA BUS as output
	DAT_TRIS_BIT = 0x0000;

    // Reset controller (SSD1928)
    RST_LAT_BIT = 0;
    DelayMs(10);
    RST_LAT_BIT = 1;
    DelayMs(10);

	SetRegB(REG_POWER_SAVE_CONFIG, 0x00);				// Reg[A0h] disable power save mode to start oscillation here!!!   
	DelayMs(200);
	/*** Setup display for SSD1928 external controller ***/

	/* Set GPIO to configure the Himax controller */
	SetRegB(REG_GPIO_CONFIG0, 0x0F);						// REG[A8h] defines GPIO[4:0], in this case, we need to set GPIO[3:0] as output for himax controller
	SetRegB(REG_GPIO_CONFIG1, 0x80);						// REG[A9h] turns on GPIO control
	// LL Reset to LCD!!!
	GPIO_SPENA(1);
	GPIO_SPCLK(1);
	GPIO_SPDAT(1);
	GPIO_SPRESET(1);
	GPIO_SPRESET(0);
	DelayMs(1);
	GPIO_SPRESET(1);
	SPI_SetReg(0x01, 0x7300);					// 0x73 gives the best result by trial-an-error
	SPI_SetReg(0x02, 0x0200);
	SPI_SetReg(0x03, 0x6364);
	SPI_SetReg(0x04, 0x040f);					// Set himax for 8-bit serial TFT mode
	SPI_SetReg(0x05, 0xBCC4);
	SPI_SetReg(0x0A, 0x4008);
	SPI_SetReg(0x0B, 0xD400);
	SPI_SetReg(0x0D, 0x3229);
	SPI_SetReg(0x0E, 0x3200);
	SPI_SetReg(0x0F, 0x0000);
	SPI_SetReg(0x16, 0x9F80);
	SPI_SetReg(0x17, 0x2212);
	SPI_SetReg(0x1E, 0x0052);
	SPI_SetReg(0x30, 0x0000);
	SPI_SetReg(0x31, 0x0407);
	SPI_SetReg(0x32, 0x0202);
	SPI_SetReg(0x33, 0x0000);
	SPI_SetReg(0x34, 0x0505);
	SPI_SetReg(0x35, 0x0003);
	SPI_SetReg(0x36, 0x0707);
	SPI_SetReg(0x37, 0x0000);
	SPI_SetReg(0x3A, 0x0904);
	SPI_SetReg(0x3B, 0x0904);

	/*** PLL configuration for 4MHz crystal ***/
	SetRegB(REG_PLL_CLK_CONFIG0, 0x0A);			// Reg 126h to N value of 0x0A for input crystal freq 4MHz
	SetRegB(REG_PLL_CLK_CONFIG1, 0xB4);			// Reg 127h to 0xB4, PLL output clock = 4MHz(input) * (M/N) = 4*180/10 = 72MHz
	SetRegB(REG_PLL_CLK_CONFIG2, 0xAE);			// Reg 12Bh to 0xAE, this is a standard procedure
	SetRegB(REG_PLL_CLK_CONFIG0, 0x8A);			// Reg 126h to N value of 0x0A and bit 7 set to 1 to enable PLL
	SetRegB(REG_MEMCLK_CONFIG, 0x00);			// Reg 04h defines the MCLK divide ratio to 0, i.e. MCLK = PLL output = 72MHz
	DelayMs(20);
	/*** PLL configuration up to here ***/

	/*** hardware initialization starts here ***/
	SetRegL(REG_PCLK_FREQ_RATIO_0, 0x13FFF);

	//SetRegB(REG_PCLK_FREQ_RATIO_0, 0xFF);		// Reg 158h is the PCLK divide ratio register0 PCLK[7:0]
	//SetRegB(REG_PCLK_FREQ_RATIO_1, 0x3F);		// Reg 159h is the PCLK divide ratio register1 PCLK[15:8]
	//SetRegB(REG_PCLK_FREQ_RATIO_2, 0x01);		// Reg 15Ah  configures PCLK[19:16]
												// Altogether, PCLK freq = MCLK freq.*(PCLK freq ratio +1)/(2^18)
												// i.e. PCLK freq = 80MHz * (0x13FFF+1) / 2^18 = 25MHz. We need to 3x the speed of 24-bit parallel RGB display
	//Aging mode trial
	//SetRegB(REG_POWER_SAVE_CONFIG, 0x00);		// 0xa0 disable power save mode to start oscillation here!!!	

	SetRegB(REG_PANEL_TYPE, 0x72);				// Reg 10h : Set panel type to serial TFT, color LCD
	SetRegB(REG_MOD_RATE, 0x00);				// Reg 11h to 0 for LDEN toggles every LFRAME

	/* Set Horizontal total by REG[12h] & REG[13h] */
	SetRegB(REG_HORIZ_TOTAL_0, 0x32);			// Reg 12h for horizontal total bits[10:3]
	SetRegB(REG_HORIZ_TOTAL_1, 0x07);			// Reg 13h for horizontal total bits[2:0], i.e. its range is 0 ~ 7; therefore an input of 0x0A as the argument will be eq. 0b00000010
												// Horizontal total in pixel no. = (REG[12h])*8 + REG[13] + 1
												// RLM779-1 panel has Hcycle=408 in landscape position, therefore (bits[10:3])*8 + bits[2:0] + 1 = 408
												// By observation, "50*8 + 7 + 1" fits; so bits[10:3] = 0x32, bits[2:0] = 0x07 to make the total 408 pixels
												// For serial TFT display, 1 pixel = 3 tDOTCLKs, and we need to bear in mind that, one cycle out of every four 
												// DOTCLKs is off in horizontal display period. In horizontal non-display period, all DOTCLKs are on.
												// So, the total number of DOTCLKs would be like 3x that of 24-bit parallel RGB display
	/* Set LLINE Pulse Start Position by REG[23h] & REG[22h] */
	SetRegB(REG_HSYNC_PULSE_START_POS0, 0x00);			//REG[0x22] not needed
	SetRegB(REG_HSYNC_PULSE_START_POS1, 0x00);			//REG[0x23] not needed

	/* Set LLINE Pulse width by REG[20h] */
	SetRegB(REG_HSYNC_PULSE_WIDTH, 0x00);					// Reg 20h defines LLINE Pulse width, i.e. it defines the LP polarity and width
															// REG[20h] bit 7 is the pulse polarity, 0 for active low for RLM779
															// bits[6:0] define the LLINE pulse width in PCLK = bits[6:0] + 1
    SetRegB(REG_LLINE_PULSE_START_SUBPIXEL_POS, 0x00);		// REG[0x21] defines exactly where display period starts down to sub-pixel accuracy, but it is not needed here  

	/* REG[17h] & REG[16h] define the Horizontal display start position relative to the horizontal sync pulse edge */
	SetRegB(REG_HDP_START_POS0, 0x44);			// Reg 16h defines period start position timing in pixel no. bits[7:0], RLM779's tHBP=68
	SetRegB(REG_HDP_START_POS1, 0x00);			// Reg 17h defines period start position timing bits[10:8]

	/* Finally, we need to define the effective horizontal display pixel by REG[14h] */
	SetRegB(REG_HDP, 0x27);						// Reg 14h defines the effective display pixel no. Horizontal display period = (bits[6:0]+1)*8
												// therefore, Horizontal display period = (0x27+1)*8 = 40*8 = 320 for landscape

	// Set the total number of lines in vertical orentation 
	SetRegB(REG_VERT_TOTAL0, 0x05);				// Reg 18h defines bits[7:0] of the vertical total period
	SetRegB(REG_VERT_TOTAL1,0x01);				// Reg 19h defineds bits[10:8] of the vertical total period
												// Vertical total in number of lines = bits[10:0] + 1 = 0x105+1; for RLM779 it is 262
	// The display vertical is 262 lines 
	SetRegB(REG_VDP0, 0xEF);					// Reg 1Ch defines vertical display period bits [7:0]
	SetRegB(REG_VDP1, 0x00);					// Reg 1Dh defines vertical display period bits [9:8]
												// Vertical display period = bits [9:0] + 1 = 00ef + 1 = 239 + 1
	// Vertical display starts from tVBP=18
	SetRegB(REG_VDP_START_POS0, 0x12);			// Reg 1Eh defines bits[7:0] of the vertical display period starts
	SetRegB(REG_VDP_START_POS1, 0x00);			// Reg 1Fh defines bits[10:8] of the vertical display period starts
												// For RLM779, it is 18 (tVBP)

	SetRegB(REG_VSYNC_PULSE_WIDTH, 0x01);		//Reg 0x24 defines the vertical sync pulse width & polarity. We need a width of two hsync pulses for the v-sync signal
												//LFRAME Pulse Width in no. of pixels(hsync) = (Bits[2:0]+1)xHorizontal Total + offset
	SetRegB(REG_VSYNC_PULSE_START_POS0, 0x00);	//Reg 0x26 sets zero
	SetRegB(REG_VSYNC_PULSE_START_POS1, 0x00);	//Reg 0x27 sets zero

	SetRegB(REG_POWER_SAVE_CONFIG, 0x00);		//Start oscillation here for PCK etc
	
}

/*********************************************************************
* Function: void PutPixel(SHORT x, SHORT y)
*
* PreCondition: none
*
* Input: x,y - pixel coordinates
*
* Output: none
*
* Side Effects: none
*
* Overview: puts pixel
*
* Note: This function modified for 8-bit accessing mode for SSD1928
*		Please note that in byte mode access, the address is auto-incr
*		by a single byte instead, therefore we need to make increment
*		on x for an extra byte for advancing the address by 1 byte
*
********************************************************************/
void PutPixel(SHORT x, SHORT y){
DWORD address;

    if(_clipRgn){
        if(x<_clipLeft)
            return;
        if(x>_clipRight)
            return;
        if(y<_clipTop)
            return;
        if(y>_clipBottom)
            return;
    }

	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*y + (x<<1);
	
    SetAddress(address);

    WriteData(_color);
}


/*********************************************************************
* Function: void Bar(SHORT left, SHORT top, SHORT right, SHORT bottom)
*
* PreCondition: none
*
* Input: left,top - top left corner coordinates,
*        right,bottom - bottom right corner coordinates
*
* Output: none
*
* Side Effects: none
*
* Overview: draws rectangle filled with current color
*
* Note: none
*
********************************************************************/
void Bar(SHORT left, SHORT top, SHORT right, SHORT bottom){
DWORD address;
register SHORT  x,y;

    if(_clipRgn){
        if(left<_clipLeft)
           left = _clipLeft;
        if(right>_clipRight)
           right= _clipRight;
        if(top<_clipTop)
           top = _clipTop;
        if(bottom>_clipBottom)
           bottom = _clipBottom;
    }

    //address = (DWORD)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    for(y=top; y<bottom+1; y++){
        SetAddress(address);

        for(x=left; x<right+1; x++){
            WriteData(_color);
        }
        address += (_line_mem_pitch<<1);
    }
}

/*********************************************************************
* Function: 	This function clears the Main Window with starting
*				address defined in register REG_MAIN_WIN_DISP_START_ADDR0
*
* PreCondition: none
*
* Input: none
*
* Output: none
*
* Side Effects: none
*
* Overview: 	clears screen with current color 
*
* Note: none
*
*********************************************************************/
void ClearDevice(void){

	DWORD   counter;

    SetAddress(GetRegL(REG_MAIN_WIN_DISP_START_ADDR0)<<2);

    for(counter=0; counter<(DWORD)(GetMaxX()+1)*(GetMaxY()+1); counter++){
        WriteData(_color);
    }

}

/*********************************************************************
* Function: void PutImage(SHORT left, SHORT top, void* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage(SHORT left, SHORT top, void* bitmap, BYTE stretch){
FLASH_BYTE* flashAddress;
BYTE colorDepth;
WORD colorTemp;

    // Save current color
    colorTemp = _color;

    switch(*((SHORT*)bitmap))
    {
#ifdef USE_BITMAP_FLASH
        case FLASH:
            // Image address
            flashAddress = ((BITMAP_FLASH*)bitmap)->address;
            // Read color depth
            colorDepth = *(flashAddress+1);
            // Draw picture
            switch(colorDepth){
                case 1:
                    PutImage1BPP(left, top, flashAddress, stretch);
                    break;
                case 4:
                    PutImage4BPP(left, top, flashAddress, stretch);
                    break;
                case 8:
                    PutImage8BPP(left, top, flashAddress, stretch);
                    break;
                case 16:
                    PutImage16BPP(left, top, flashAddress, stretch);
                    break;
            }
            break;
#endif
#ifdef USE_BITMAP_EXTERNAL
        case EXTERNAL:
            // Get color depth
            ExternalMemoryCallback(bitmap, 1, 1, &colorDepth);
            // Draw picture
            switch(colorDepth){
                case 1:
                    PutImage1BPPExt(left, top, bitmap, stretch);
                    break;
                case 4:
                    PutImage4BPPExt(left, top, bitmap, stretch);
                    break;
                case 8:
                    PutImage8BPPExt(left, top, bitmap, stretch);
                    break;
                case 16:
                    PutImage16BPPExt(left, top, bitmap, stretch);
                    break;
                default:
                    break;
            }
            break;
#endif
        default:
            break;
    }

    // Restore current color
    _color = colorTemp;
}

#ifdef USE_BITMAP_FLASH
/*********************************************************************
* Function: void PutImage1BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs monochrome image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage1BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch){
register DWORD address;
register FLASH_BYTE* flashAddress;
register FLASH_BYTE* tempFlashAddress;
BYTE temp;
WORD sizeX, sizeY;
WORD x,y;
BYTE stretchX,stretchY;
WORD pallete[2];
BYTE mask;

    // Move pointer to size information
    flashAddress = bitmap + 2;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top+ (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Read image size
    sizeY = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;
    sizeX = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;
    pallete[0] = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;
    pallete[1] = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;

    for(y=0; y<sizeY; y++){
        tempFlashAddress = flashAddress;
        for(stretchY = 0; stretchY<stretch; stretchY++){
            flashAddress = tempFlashAddress;
            SetAddress(address);
            mask = 0;
            for(x=0; x<sizeX; x++){

                // Read 8 pixels from flash
                if(mask == 0){
                    temp = *flashAddress;
                    flashAddress++;
                    mask = 0x80;
                }
                
                // Set color
                if(mask&temp){
                    SetColor(pallete[1]);
                }else{
                    SetColor(pallete[0]);
                }

                // Write pixel to screen
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }

                // Shift to the next pixel
                mask >>= 1;
           }
           address += (_line_mem_pitch<<1); 
        }
    }
}

/*********************************************************************
* Function: void PutImage4BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs 16 color image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage4BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch){
register DWORD address;
register FLASH_BYTE* flashAddress;
register FLASH_BYTE* tempFlashAddress;
WORD sizeX, sizeY;
register WORD x,y;
BYTE temp;
register BYTE stretchX,stretchY;
WORD pallete[16];
WORD counter;

    // Move pointer to size information
    flashAddress = bitmap + 2;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top+ (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Read image size
    sizeY = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;
    sizeX = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;

    // Read pallete
    for(counter=0;counter<16;counter++){
        pallete[counter] = *((FLASH_WORD*)flashAddress);
        flashAddress += 2;
    }

    for(y=0; y<sizeY; y++){
        tempFlashAddress = flashAddress;
        for(stretchY = 0; stretchY<stretch; stretchY++){
            flashAddress = tempFlashAddress;
            SetAddress(address);
            for(x=0; x<sizeX; x++){
                // Read 2 pixels from flash
                if(x&0x0001){
                    // second pixel in byte
                    SetColor(pallete[temp>>4]);
                }else{
                    temp = *flashAddress;
                    flashAddress++;
                    // first pixel in byte
                    SetColor(pallete[temp&0x0f]);
                }

                // Write pixel to screen       
                 for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }

                // Shift to the next pixel
                //temp >>= 4;
            }
            address += (_line_mem_pitch<<1);
        }
    }
}

/*********************************************************************
* Function: void PutImage8BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs 256 color image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage8BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch){
register DWORD address;
register FLASH_BYTE* flashAddress;
register FLASH_BYTE* tempFlashAddress;
WORD sizeX, sizeY;
WORD x,y;
BYTE temp;
BYTE stretchX, stretchY;
WORD pallete[256];
WORD counter;

    // Move pointer to size information
    flashAddress = bitmap + 2;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Read image size
    sizeY = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;
    sizeX = *((FLASH_WORD*)flashAddress);
    flashAddress += 2;

    // Read pallete
    for(counter=0;counter<256;counter++){
        pallete[counter] = *((FLASH_WORD*)flashAddress);
        flashAddress += 2;
    }
    
    for(y=0; y<sizeY; y++){
        tempFlashAddress = flashAddress;
        for(stretchY = 0; stretchY<stretch; stretchY++){
            flashAddress = tempFlashAddress;
            SetAddress(address);
            for(x=0; x<sizeX; x++){
                // Read pixels from flash
                temp = *flashAddress;
                flashAddress++;

                // Set color
                SetColor(pallete[temp]);

                // Write pixel to screen      
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }
            }
            address += (_line_mem_pitch<<1);
        }
    }
}

/*********************************************************************
* Function: void PutImage16BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs hicolor image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage16BPP(SHORT left, SHORT top, FLASH_BYTE* bitmap, BYTE stretch){
register DWORD address;
register FLASH_WORD* flashAddress;
register FLASH_WORD* tempFlashAddress;
WORD sizeX, sizeY;
register WORD x,y;
WORD temp;
register BYTE stretchX,stretchY;

    // Move pointer to size information
    flashAddress = (FLASH_WORD*)bitmap + 1;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Read image size
    sizeY = *flashAddress;
    flashAddress++;
    sizeX = *flashAddress;
    flashAddress++;
    
    for(y=0; y<sizeY; y++){
        tempFlashAddress = flashAddress;
        for(stretchY = 0; stretchY<stretch; stretchY++){
            flashAddress = tempFlashAddress;
            SetAddress(address);
            for(x=0; x<sizeX; x++){
                // Read pixels from flash
                temp = *flashAddress;
                flashAddress++;

                // Set color
                SetColor(temp);

                // Write pixel to screen       
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }
            }
            address += (_line_mem_pitch<<1);
        }
    }
}
#endif

#ifdef USE_BITMAP_EXTERNAL
/*********************************************************************
* Function: void PutImage1BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs monochrome image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage1BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch){
register DWORD      address;
register DWORD      memOffset;
BITMAP_HEADER       bmp;
WORD                pallete[2];
BYTE                lineBuffer[(SCREEN_HOR_SIZE/8)+1];
BYTE*               pData; 
SHORT               byteWidth;

BYTE                temp;
BYTE                mask;
WORD                sizeX, sizeY;
WORD                x,y;
BYTE                stretchX, stretchY;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Get bitmap header
    ExternalMemoryCallback(bitmap, 0, sizeof(BITMAP_HEADER), &bmp);

    // Get pallete (2 entries)
    ExternalMemoryCallback(bitmap, sizeof(BITMAP_HEADER), 2*sizeof(WORD), pallete);

    // Set offset to the image data
    memOffset = sizeof(BITMAP_HEADER) + 2*sizeof(WORD);

    // Line width in bytes
    byteWidth = bmp.width>>3;
    if(bmp.width&0x0007)
        byteWidth++;

    // Get size
    sizeX = bmp.width;
    sizeY = bmp.height;

    for(y=0; y<sizeY; y++){
        // Get line
        ExternalMemoryCallback(bitmap, memOffset, byteWidth, lineBuffer);
        memOffset += byteWidth;

        for(stretchY = 0; stretchY<stretch; stretchY++){
            pData = lineBuffer;
            SetAddress(address);
            mask = 0;
            for(x=0; x<sizeX; x++){

                // Read 8 pixels from flash
                if(mask == 0){
                    temp = *pData++;
                    mask = 0x80;
                }
                
                // Set color
                if(mask&temp){
                    SetColor(pallete[1]);
                }else{
                    SetColor(pallete[0]);
                }

                // Write pixel to screen
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }

                // Shift to the next pixel
                mask >>= 1;
           }
           address += (_line_mem_pitch<<1); 
        }
    }

}

/*********************************************************************
* Function: void PutImage4BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs monochrome image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage4BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch){
register DWORD      address;
register DWORD      memOffset;
BITMAP_HEADER       bmp;
WORD                pallete[16];
BYTE                lineBuffer[(SCREEN_HOR_SIZE/2)+1];
BYTE*               pData; 
SHORT               byteWidth;

BYTE                temp;
WORD                sizeX, sizeY;
WORD                x,y;
BYTE                stretchX, stretchY;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Get bitmap header
    ExternalMemoryCallback(bitmap, 0, sizeof(BITMAP_HEADER), &bmp);

    // Get pallete (16 entries)
    ExternalMemoryCallback(bitmap, sizeof(BITMAP_HEADER), 16*sizeof(WORD), pallete);

    // Set offset to the image data
    memOffset = sizeof(BITMAP_HEADER) + 16*sizeof(WORD);

    // Line width in bytes
    byteWidth = bmp.width>>1;
    if(bmp.width&0x0001)
        byteWidth++;

    // Get size
    sizeX = bmp.width;
    sizeY = bmp.height;

    for(y=0; y<sizeY; y++){

        // Get line
        ExternalMemoryCallback(bitmap, memOffset, byteWidth, lineBuffer);
        memOffset += byteWidth;

        for(stretchY = 0; stretchY<stretch; stretchY++){

            pData = lineBuffer;
            SetAddress(address);

            for(x=0; x<sizeX; x++){

                // Read 2 pixels from flash
                if(x&0x0001){
                    // second pixel in byte
                    SetColor(pallete[temp>>4]);
                }else{
                    temp = *pData++;
                    // first pixel in byte
                    SetColor(pallete[temp&0x0f]);
                }

                // Write pixel to screen 
 
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }
           }
           address += (_line_mem_pitch<<1); 
        }
    }
}

/*********************************************************************
* Function: void PutImage8BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs monochrome image starting from left,top coordinates
*
* Note: image must be located in flash
*
********************************************************************/
void PutImage8BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch){
register DWORD      address;
register DWORD      memOffset;
BITMAP_HEADER       bmp;
WORD                pallete[256];
BYTE                lineBuffer[SCREEN_HOR_SIZE];
BYTE*               pData; 

BYTE                temp;
WORD                sizeX, sizeY;
WORD                x,y;
BYTE                stretchX, stretchY;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Get bitmap header
    ExternalMemoryCallback(bitmap, 0, sizeof(BITMAP_HEADER), &bmp);

    // Get pallete (256 entries)
    ExternalMemoryCallback(bitmap, sizeof(BITMAP_HEADER), 256*sizeof(WORD), pallete);

    // Set offset to the image data
    memOffset = sizeof(BITMAP_HEADER) + 256*sizeof(WORD);

    // Get size
    sizeX = bmp.width;
    sizeY = bmp.height;

    for(y=0; y<sizeY; y++){

        // Get line
        ExternalMemoryCallback(bitmap, memOffset, sizeX, lineBuffer);
        memOffset += sizeX;

        for(stretchY = 0; stretchY<stretch; stretchY++){

            pData = lineBuffer;
            SetAddress(address);

            for(x=0; x<sizeX; x++){

                temp = *pData++;
                SetColor(pallete[temp]);                    

                // Write pixel to screen
     
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }
           }
           //address += (LINE_MEM_PITCH<<1); 
			 address += (_line_mem_pitch<<1);
        }
    }
}

/*
*************************************************************************************
* Function: void PutImage8BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch)
*
* PreCondition: none
*
* Input: left,top - left top image corner, bitmap - image pointer,
*        stretch - image stretch factor
*
* Output: none
*
* Side Effects: none
*
* Overview: outputs monochrome image starting from left,top coordinates
*
* Note: image must be located in flash
*
*************************************************************************************
*/
void PutImage16BPPExt(SHORT left, SHORT top, void* bitmap, BYTE stretch){
register DWORD      address;
register DWORD      memOffset;
BITMAP_HEADER       bmp;
WORD                lineBuffer[SCREEN_HOR_SIZE];
WORD*               pData; 
WORD                byteWidth;

WORD                temp;
WORD                sizeX, sizeY;
WORD                x,y;
BYTE                stretchX, stretchY;

    // Set start address
    // address = (long)(LINE_MEM_PITCH<<1)*top + (left<<1);
	address = PAGE_MEM_SIZE*_page + (_line_mem_pitch<<1)*top + (left<<1);

    // Get bitmap header
    ExternalMemoryCallback(bitmap, 0, sizeof(BITMAP_HEADER), &bmp);

    // Set offset to the image data
    memOffset = sizeof(BITMAP_HEADER);

    // Get size
    sizeX = bmp.width;
    sizeY = bmp.height;

    byteWidth = sizeX<<1;

    for(y=0; y<sizeY; y++){
        // Get line
        ExternalMemoryCallback(bitmap, memOffset, byteWidth, lineBuffer);
        memOffset += byteWidth;

        for(stretchY = 0; stretchY<stretch; stretchY++){

            pData = lineBuffer;
            SetAddress(address);

            for(x=0; x<sizeX; x++){

                temp = *pData++;
                SetColor(temp);                    

                // Write pixel to screen
      
                for(stretchX=0; stretchX<stretch; stretchX++){
                    WriteData(_color);
                }

           }

           //address += (LINE_MEM_PITCH<<1); 
			address += (_line_mem_pitch<<1);
        }
    }
}



#endif

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPRESET(BOOL state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_RESET;
	else
		_gpioStatus = _gpioStatus&(~LCD_RESET);
	
	SetRegB(REG_GPIO_STATUS_CONTROL0, _gpioStatus);	
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPENA(BOOL state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPENA;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPENA);
	
	SetRegB(REG_GPIO_STATUS_CONTROL0, _gpioStatus);

}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPCLK(BOOL state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPCLK;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPCLK);
	
	SetRegB(REG_GPIO_STATUS_CONTROL0, _gpioStatus);	
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void GPIO_SPDAT(BOOL state)
{
	if(state==TRUE)
		_gpioStatus = _gpioStatus|LCD_SPDAT;
	else
		_gpioStatus = _gpioStatus&(~LCD_SPDAT);
	
	SetRegB(REG_GPIO_STATUS_CONTROL0, _gpioStatus);	
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void SPI_Write(BYTE byte)
{
	BYTE bit_ctr;

	for(bit_ctr=0; bit_ctr<8; bit_ctr++)
	{
		if(byte&0x80)
			GPIO_SPDAT(1);
		else
			GPIO_SPDAT(0);

		GPIO_SPCLK(0);
		GPIO_SPCLK(1);
		byte=(byte<<1);
	}
}

/*
*************************************************************************************
* Function: 
*
* PreCondition: none
*
* Arguments:
*
* Return: none
*
* Note:
*
*************************************************************************************
*/
void SPI_SetReg(BYTE reg, WORD cmd)
{
	GPIO_SPENA(0);		// RS=0 for index read/write, RS=1 for instruction read/write
	SPI_Write(0x70);	// This is a bitwise for (Device ID|RS|RW), therefore 0b011100|RS|RW, with RS=0, RW=0
	SPI_Write(0x00);	// register in 0x00[reg]
	SPI_Write(reg);
	GPIO_SPENA(1);

	GPIO_SPENA(0);	
	SPI_Write(0x72);	// Now RS=1 & RW=0 for command set
	SPI_Write(((WORD_VAL)cmd).v[1]);
	SPI_Write(((WORD_VAL)cmd).v[0]);
	GPIO_SPENA(1);
}

/*
void ssd1928_SetPage(DWORD startaddr)
{
	SetRegB(REG_DISPLAY_MODE,GetReg(REG_DISPLAY_MODE)|0x80); 	// Blank the display

	SetRegL(REG_MAIN_WIN_DISP_START_ADDR0, startaddr>>2);		// REG_MAIN_WIN_DISP_START_ADDR0==0x74

	SetRegB(REG_DISPLAY_MODE,GetReg(REG_DISPLAY_MODE)&0x7F);
}
*/


/*
*************************************************************************************
* Function: 	Float window initialization function
*
* Arguments:	DWORD startaddr - 	it is the starting address on the image buffer.
*									There is 256KB buffer in SSD1928
*				WORD  linewidth - 	line address offset. This variable sets the display
*									width of the Floating Window.
*				WORD x			-	It is the location for x-coordinate of the Floating Window
*				WORD y			-	It is the location for y-coordinate of the Floating Window
*									is supported in this version. No effect in this version
*				WORD width		- 	Width of the Floating Window
*				WORD height		-	Height of the Floating Window
*				BOOL rgb		-	0 for YUV mode
*									1 for RGB mode
*
* Return:		None	
*
* Note: 		For 16bpp only. Float window initialization is a critical routine for
*				definition on linewidth which is very important for memory map.
*************************************************************************************
*/
void ssd1928_FloatWndInit(DWORD startaddr, WORD linewidth, WORD x, WORD y,  WORD width, WORD height, BOOL rgb)
{
	SetRegW(REG_FLOAT_WIN_X_START_POS0, x>>1);
	SetRegW(REG_FLOAT_WIN_X_END_POS0,((x+width)>>1)-1);

	SetRegW(REG_FLOAT_WIN_Y_START_POS0,y);
	SetRegW(REG_FLOAT_WIN_Y_END_POS0,y+height-1);

	SetRegL(REG_FLOAT_WIN_DISP_START_ADDR0,startaddr>>2);
	SetRegW(REG_FLOAT_WIN_ADDR_OFFSET0,linewidth>>1);

	//Set BPP
	SetRegB(REG_DISPLAY_MODE,GetReg(REG_DISPLAY_MODE)|4);

	if (rgb)
		SetRegB(REG_DV_OFORMAT,GetReg(REG_DV_OFORMAT)|(1<<7));	//RGB mode, set bit7 '1 
	else	
		SetRegB(REG_DV_OFORMAT,GetReg(REG_DV_OFORMAT)&0x40);	//YUV mode, set bit7 '0

}

/*
*************************************************************************************
* Function: 	Enable/disable Floating window
*
* PreCondition:	We need to enable the Main Window prior to enable the Float Window
*
* Arguments: 	BOOL enable	- 	TRUE to enable the Float Window
*								FALSE to disable the Float Window, Float window will be blank
*
* Return:		
* Note: 		For 16bpp only
*************************************************************************************
*/
void ssd1928_FloatWndEnable(BOOL enable)
{
	if(enable==TRUE)
		SetRegB(REG_SPECIAL_EFFECTS,GetReg(REG_SPECIAL_EFFECTS)|(1<<4));	//Float window enable
	else
		SetRegB(REG_SPECIAL_EFFECTS,GetReg(REG_SPECIAL_EFFECTS)&(~(1<<4)));		
}

/*
*************************************************************************************
* Function: 	Main window initialization function
*
* PreCondition:	InitGraph(), inside we need ResetDevice()
*
* Arguments:	DWORD startaddr - 	it is the starting address on the image buffer.
*									There is 256KB buffer in SSD1928
*				WORD  linewidth - 	line address offset. This variable sets the display
*									width. It is possible to set this parameter larger
*									or smaller than the physical display width 
*									(i.e. 320 for Landscape, 240 for portrait) for special
*									effect like display pan or jpeg file display with 
*									smaller image width
*				WORD bpp		-	It is the bit per pixel, for this version, only 16-bit
*									color is supported, therefore it has been fixed to 16
*				BYTE orient		-	Orientation of the display. Only zero degree rotation
*									is supported in this version. No effect in this version
*				BOOL rgb		-	color mode of the display, either '1' for RGB, '0' for YUV
*									YUV mode is used for jpeg decode display.
*									RGB mode is used for video, and normal display
*
* Return:		None
*
* Note:			Main Window Display Start Address Register0, 1, & 2 occupy
*				REG[74h], REG[75h], & REG[76h]. These bits from the 17-bit address
*				for starting double-word of the LCD image in the display buffer
*				for the Main Window. 
*************************************************************************************
*/
void ssd1928_MainWndInit(DWORD startaddr, WORD linewidth, WORD bpp, BYTE orient, BOOL rgb)
{
	//SetRegB(REG_DISPLAY_MODE,GetReg(REG_DISPLAY_MODE)|0x80); // turn OFF for now	//REG_DISPLAY_MODE 0x70.

	//SetRegB(REG_MAIN_WIN_DISP_START_ADDR0,0x00); 			// Reg[74h] defines bits[7:0] for the Main Window display start address
	//SetRegB(REG_MAIN_WIN_DISP_START_ADDR1,0x00); 			// Reg[75h]	defines bits[15:0] for the Main Window display start address
	//SetRegB(REG_MAIN_WIN_DISP_START_ADDR2,0x00); 			// Reg[76h] defines bits[16] for the Main Window display start address
															// These bits form the 17-bit address for the starting double-word of the LCD image in the display
															// buffer for the main window. 
	SetRegL(REG_MAIN_WIN_DISP_START_ADDR0, startaddr>>2);	// default to be 0 deg orientation

	SetRegB(REG_DISPLAY_MODE,GetReg(REG_DISPLAY_MODE)|4);	// 16bpp for now

	bpp = LINE_MEM_PITCH;

	SetRegW(REG_MAIN_WIN_ADDR_OFFSET0,(WORD)bpp>>1); 		// Reg[78h] = Display width in pixels/(32/bpp)

	//Set orientation: rotate angel=oritent*90
	SetRegW(REG_SPECIAL_EFFECTS, ((GetReg(REG_SPECIAL_EFFECTS)|0x40) & ~0x03));
	SetRegB(REG_DV_OFORMAT,GetReg(REG_DV_OFORMAT)|0x40);	//REG_DV_OFORMAT==0x1a4, only RGB mode support for now	

	if(rgb)
		SetRegB(REG_DV_OFORMAT,GetReg(REG_DV_OFORMAT)|0x40);//Set Main Window to RGB mode
	else
		SetRegB(REG_DV_OFORMAT,GetReg(REG_DV_OFORMAT)&0x80);//Set Main Window to YUV mode
}

/*
*************************************************************************************
* Function:		Set focus on Main Window or Floating Window
*
* PreCondition:	None
*
* Arguments: 	BYTE wnd		0 for Main Window
*								1 for Floating Window
* Return:		None
*	
* Note:
*************************************************************************************
*/
void ssd1928_FocusWnd(BYTE wnd)
{
	WORD linewidth;

	if(wnd)
	{
		// it is a Floating window
		_page = 1;
		linewidth = (WORD)GetReg(REG_FLOAT_WIN_ADDR_OFFSET1)<<8;
		linewidth = linewidth |(GetReg(REG_FLOAT_WIN_ADDR_OFFSET0)&0x00FF);
		linewidth = linewidth<<1;
		_line_mem_pitch = linewidth;
		
	} else
	{
		// it is the main window
		_page = 0;
		_line_mem_pitch = LINE_MEM_PITCH;
	}
}

/*
*************************************************************************************
* Function:		Enable/disable the Main Window
*
* PreCondition:	None
*
* Arguments: 	BOOL enable	- 	TRUE to enable the Main Window
*								FALSE to disable the Main Window, display will be blank
* Return:		None
*	
* Note:
*************************************************************************************
*/
void ssd1928_MainWndEnable(BOOL enable)
{
	if(enable == TRUE)
	{
		SetRegB(REG_DISPLAY_MODE, GetReg(REG_DISPLAY_MODE)&0x7F);
	} else
	{
		SetRegB(REG_DISPLAY_MODE, GetReg(REG_DISPLAY_MODE)|0x80);
	}
	
}
