/****************************************************************************
 ■ HoneyBox
   2010(C) Mr.Honey
****************************************************************************/

#include "lcd.h"
#include "ZY-FGD1442701V1.h"
#include "ff.h"

//=================================================================

static void WriteArea(const rect_t* rc);
static UINT WriteBits(const BYTE* buf, UINT btf);
static void WriteCmd(U8 cmd);
static void WriteDat(U8 data);
static U8 ReadDat(void);

#define FastOut(x) \
	LCD_DATA = (x); \
	LCD_WR = 0; \
	LCD_WR = 1;

//=================================================================

const rect_t g_Scr = { 0, 0, LCD_CY, LCD_CX };
static FIL s_Font;
static U8 s_nSize;
static U8 s_nColor;

//=================================================================

// ●初期化
extern void InitLCD(void)
{
	s_nSize = 0;
	s_nColor = 0;

	DisableINT();
	LCD_CS = 0;

	// Software Reset
	WriteCmd(SWRESET);
	DelayMs(120);

	// Sleep Out
	WriteCmd(SLPOUT);
	DelayMs(120);

	// Memory Data Access Control
	WriteCmd(MADCTL);
	WriteDat(0x7C);	// MV:1 MX:1 RGB:1 MH:1(X-Y Exchange, X-Mirror)

	// Interface Pixel Format
	WriteCmd(COLMOD);
	WriteDat(0x05);	// 16-bit/pixel

	// Frame Rate Control (In normal mode/ Full colors)
	WriteCmd(FRMCTR1);
	WriteDat(0x04);
	WriteDat(0x25);
	WriteDat(0x18);
	
	// Frame Rate Control (In Idle mode/ 8-colors)
	WriteCmd(FRMCTR2);
	WriteDat(0x04);
	WriteDat(0x25);
	WriteDat(0x18);
	
	// Frame Rate Control (In Partial mode/ full colors)
	WriteCmd(FRMCTR3);
	WriteDat(0x04);
	WriteDat(0x25);
	WriteDat(0x18);
	WriteDat(0x04);
	WriteDat(0x25);
	WriteDat(0x18);

	// Display Function set 5
	WriteCmd(DISSET5);
	WriteDat(0x15);
	WriteDat(0x02);

	// Power Control 1
	WriteDat(PWCTR1);
	WriteDat(0x02);	// VRH:4.7V
	WriteDat(0x70);	// IB:1uA

	// Power Control 2
	WriteCmd(PWCTR2);
	WriteDat(0x07);	// VGHH:6*VCI1 14.7 VGLL:-5*VCI1 -12.25

	// Power Control 3 (in Normal mode/ Full colors)
	WriteCmd(PWCTR3);
	WriteDat(0x01);	// Amount of Current: Small
	WriteDat(0x01);	// Step-up cycle: BCLK/1 BCLK/2
	
	// Power Control 4 (in Idle mode/ 8-colors)
	WriteCmd(PWCTR4);			
	WriteDat(0x02);	// Amount of Current: Medium Low
	WriteDat(0x07);	// Step-up cycle: BCL/4 BCLK/16

	// Power Control 5 (in Partial mode/ full-colors)
	WriteCmd(PWCTR5);		
	WriteDat(0x02);	// Amount of Current: Medium Low
	WriteDat(0x04);	// Step-up cycle: BCLK/2 BCLK/4
	
	// Power Control 5 (in Partial mode + Idle mode)
	WriteCmd(PWCTR6);
	WriteDat(0x11);
	WriteDat(0x17);

	// VCOM Control 1
	WriteCmd(VMCTR1);
	WriteDat(0x3C);	// VCOMH:4.000
	WriteDat(0x4F);	// VCOML:-0.525

	// Gamma (‘+’polarity) Correction Characteristics Setting
	WriteCmd(GMCTRP1);
	WriteDat(0x00); // High level adjustment
	WriteDat(0x22); // Low level adjustment
	WriteDat(0x05); // The voltage of V3 grayscale is selected by the 64 to 1 selector
	WriteDat(0x20); // The voltage of V6 grayscale is selected by the 64 to 1 selector
	WriteDat(0x27); // The voltage of V11 grayscale is selected by the 64 to 1 selector
	WriteDat(0x23); // The voltage of V19 grayscale is selected by the 64 to 1 selector
	WriteDat(0x1C); // The voltage of V27 grayscale is selected by the 64 to 1 selector
	WriteDat(0x21); // The voltage of V36 grayscale is selected by the 64 to 1 selector
	WriteDat(0x20); // The voltage of V44 grayscale is selected by the 64 to 1 selector
	WriteDat(0x1C); // The voltage of V52 grayscale is selected by the 64 to 1 selector
	WriteDat(0x26); // The voltage of V57 grayscale is selected by the 64 to 1 selector
	WriteDat(0x2F); // The voltage of V60 grayscale is selected by the 64 to 1 selector
	WriteDat(0x00); // The voltage of V0 grayscale is selected by the 64 to 1 selector
	WriteDat(0x03); // The voltage of V1 grayscale is selected by the 64 to 1 selector
	WriteDat(0x00); // The voltage of V62 grayscale is selected by the 64 to 1 selector
	WriteDat(0x24); // The voltage of V63 grayscale is selected by the 64 to 1 selector
	
	// Gamma ‘-’polarity Correction Characteristics Setting
	WriteCmd(GMCTRN1);		
	WriteDat(0x00); // High level adjustment
	WriteDat(0x24); // Low level adjustment
	WriteDat(0x05); // The voltage of V3 grayscale is selected by the 64 to 1 selector
	WriteDat(0x21); // The voltage of V6 grayscale is selected by the 64 to 1 selector
	WriteDat(0x27); // The voltage of V11 grayscale is selected by the 64 to 1 selector
	WriteDat(0x22); // The voltage of V19 grayscale is selected by the 64 to 1 selector
	WriteDat(0x1C); // The voltage of V27 grayscale is selected by the 64 to 1 selector
	WriteDat(0x21); // The voltage of V36 grayscale is selected by the 64 to 1 selector
	WriteDat(0x1F); // The voltage of V44 grayscale is selected by the 64 to 1 selector
	WriteDat(0x1D); // The voltage of V52 grayscale is selected by the 64 to 1 selector
	WriteDat(0x27); // The voltage of V57 grayscale is selected by the 64 to 1 selector
	WriteDat(0x2F); // The voltage of V60 grayscale is selected by the 64 to 1 selector
	WriteDat(0x05); // The voltage of V0 grayscale is selected by the 64 to 1 selector
	WriteDat(0x03); // The voltage of V1 grayscale is selected by the 64 to 1 selector
	WriteDat(0x00); // The voltage of V62 grayscale is selected by the 64 to 1 selector
	WriteDat(0x3F); // The voltage of V63 grayscale is selected by the 64 to 1 selector
	
	// Vcom 4 Level Control
	WriteCmd(VCOM4L);
	WriteDat(0x40);	// TC1: 0 clock TC2: 4 clock
	WriteDat(0x03);	// TC3: 3 clock
	WriteDat(0x1A);

	// Display On
	WriteCmd(DISPON);

	LCD_CS = 1;
	LCD_CD = 0;
	LCD_DATA = 0;
	EnableINT();

	DelayMs(1);
}

// ●領域の塗りつぶし
extern void LCD_Fill(const rect_t* rc, U16 c)
{
	U8 w = rc->w;
	U8 h = rc->h;
	U8 Ch = HIBYTE(c);
	U8 Cl = LOBYTE(c);

	DisableINT();
	LCD_CS = 0;

	WriteArea(rc);
	LCD_CD = 0;
	FastOut(RAMWR);
	LCD_CD = 1;
	do {
		do {
			FastOut(Ch); FastOut(Cl);
		} while(--w > 0);
	} while(--h > 0);

	LCD_CS = 1;
	LCD_CD = 0;
	LCD_DATA = 0;
	EnableINT();
}

// ●イメージ転送
extern void LCD_Blit(const rect_t* rc, U8 id)
{
	static char fname[7] = { '/', 'S', '/', 'I', 0, 0, '\0' };
	FIL file;

	DisableINT();
	LCD_CS = 0;

	fname[4] = (id / 10) + '0';
	fname[5] = (id % 10) + '0';

	if (f_open(&file, fname, FA_READ) == FR_OK)
	{
		UINT cb;
		
		WriteArea(rc);
		LCD_CD = 0;
		FastOut(RAMWR);
		LCD_CD = 1;
		f_forward(&file, WriteBits, file.fsize, &cb);

		f_close(&file);
	}

	LCD_CS = 1;
	LCD_CD = 0;
	LCD_DATA = 0;
	EnableINT();
}

// ●テキスト描画
extern U8 LCD_Text(const text_t* info)
{
	U8 h = info->size;
	U16 u = (h + 2) * (U16)h * 4 + 2;
	char buf[16], *p;
	rect_t rc;

	const char rom * rp = info->roms;
	if (rp == 0)
	{
		if (info->rams == 0)
		{
			// 数値->符号なし10進数文字列
			U16 n = info->num;
			U8 r = (info->flags & TXT_HEX)? 16 : 10;
			S8 i = (info->flags & TXT_2DIG)? 2 : 1;

			char* s = buf + sizeof(buf) - 1;
			*s = '\0';

			do { --s; *s = (n % r) + '0'; --i; }
			while ((n /= r) != 0 || i > 0);
			p = s;
		}
		else {
			p = info->rams;
		}
	}
	else
	{
		// プログラム領域からデータ領域へ
		p = buf;
		do *p = *rp++;
		while (*p++ != 0);
		p = buf;
	}

	// フォントファイルの切替
	if (s_nSize != h || s_nColor != info->c)
	{
		static char fname[10] = { '/', 'S', '/', 'F', '/', 0, 0, '-', 0, '\0' };

		if (s_nSize != 0)
		{
			f_close(&s_Font);
			s_nSize = 0;
		}

		fname[5] = (h / 10) + '0';
		fname[6] = (h % 10) + '0';
		fname[8] = info->c + '0';

		if (f_open(&s_Font, fname, FA_READ) != FR_OK)
			return 0;

		s_nSize = h;
		s_nColor = info->c;
	}

	// 文字ループ
	DisableINT();
	LCD_CS = 0;

	rc.x = info->x;
	rc.y = info->y;
	rc.h = h;
	for (; *p != 0; p++)
	{
		// 文字位置へシーク
		DWORD i = (U8)*p - (U8)' ';
		if (f_lseek(&s_Font, i * u) == FR_OK)
		{
			UINT cb;
			U8 w;
		
			// 文字幅取得
			if (f_read(&s_Font, &w, 1, &cb) == FR_OK && cb == 1)
			{
				rc.w = w;
				if ((rc.x + w) > LCD_CX)
					break;

				WriteArea(&rc);
				LCD_CD = 0;
				FastOut(RAMWR);
				LCD_CD = 1;
				f_forward(&s_Font, WriteBits, (U16)w * (U16)h * 2, &cb);

				rc.x += w;
			}
		}
	}

	LCD_CS = 1;
	LCD_CD = 0;
	LCD_DATA = 0;
	EnableINT();

	return rc.x;
}

// ●アンマウント処理
extern void LCD_OnUnmount(void)
{
	if (s_nSize != 0)
	{
		f_close(&s_Font);
		s_nSize = 0;
	}
}

//=================================================================

// ●アドレス送信定する
static void WriteArea(const rect_t* rc)
{
	// Column Address Set
	LCD_CD = 0;
	FastOut(CASET);
	LCD_CD = 1;
	FastOut(0);
	FastOut(rc->x + 1);
	FastOut(0);
	FastOut(rc->x + rc->w);

	// Row Address Set
	LCD_CD = 0;
	FastOut(RASET);
	LCD_CD = 1;
	FastOut(0);
	FastOut(rc->y + 2);
	FastOut(0);
	FastOut(rc->y + rc->h + 1);
}

// ●転送コールバック
static UINT WriteBits(const BYTE* buf, UINT btf)
{
	if (btf > 0)
	{
		UINT i = btf;
		do { FastOut(*buf++); }
		while(--i > 0);
		return btf;
	}
	return 1;
}

// ●コマンド送信
static void WriteCmd(U8 cmd)
{
	LCD_DATA = cmd;
	LCD_CD = 0;
	LCD_WR = 0;
	LCD_WR = 1;
}

// ●データ送信
static void WriteDat(U8 data)
{
	LCD_DATA = data;
	LCD_CD = 1;
	LCD_WR = 0;
	LCD_WR = 1;
}

// ●データ受信
static U8 ReadDat(void)
{
	U8 data;
	DIR_LCD_DATA = 1;

	LCD_CD = 1;
	LCD_RD = 0;
	data = LCD_DATA;
	LCD_RD = 1;

	DIR_LCD_DATA = 0;
	return data;
}
