/*	
 *	framldir.c
 *
 *	高速切替・16階調表示
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2015 Naoyuki Sawa
 *
 *	* Sun Jun 6 14:11:00 JST 2004 Naoyuki Sawa
 *	- 作成開始。
 *	* Tue Jun 8 19:00:00 JST 2004 Naoyuki Sawa
 *	- 4bit描画のためのカスタムpceLCDDrawObject()をこのモジュールに含めました。
 *	* Thu Jun 10 19:00:00 JST 2004 Naoyuki Sawa
 *	- new_pceLCDTrans()の処理本体を高速RAMへ移し、ldirect_mkbuf()としました。
 *	* Tue Jun 4 21:28:00 JST 2005 Naoyuki Sawa
 *	- 4bit描画ルーチン(PDW_Draw_4BN)を、framldir.cからframdraw.cへ移動しました。
 *	  4bit描画ルーチンを使わない16諧調表示アプリケーション(エミュレータなど)で、
 *	  4bit描画ルーチンが高速RAMを圧迫するのを避けるためです。
 *	- 4bit描画ルーチンを使わない16諧調表示アプリケーションは、
 *	  framldir.oを高速RAMに配置し、framdraw.cはSRAMに配置してください。
 *	* Fri Jan 09 21:26:07 JST 2015 Naoyuki Sawa
 *	- 内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
 */
#include "clip.h"

/****************************************************************************
 *	内部関数
 ****************************************************************************/

#if 0	/*==================================================================*/
void
ldirect_mkbuf(const unsigned char wbuff[/*DISP_X / 2 * DISP_Y*/], const unsigned char* lcdbuff)
{
	const unsigned int* src = (const unsigned int*)lcdbuff;
	      unsigned int* dst = (      unsigned int*)wbuff;
	//
	int i;
	unsigned int pixel0to3;
	unsigned int pixel4to7;

	/* 8ピクセル単位で、先行4ピクセルを上位4bit、後続4ピクセルを下位4bitに重ねて格納します。 */
	for(i = 0; i < DISP_X / 8 * DISP_Y; i++) {
		pixel0to3 = *src++;
		pixel4to7 = *src++;
		*dst++ = pixel0to3 << 4 | pixel4to7;
	}
}
#else	/*==================================================================*/
asm("
	.code
	.global ldirect_mkbuf
ldirect_mkbuf:
	xld.w %r15, 1408		; %r15 = DISP_X / 8 * DISP_Y
ldirect_mkbuf_LOOP:
	ld.w %r10, [%r13]+		; %r10 = pixel0to3
	ld.w %r11, [%r13]+		; %r11 = pixel4to7
	xsll %r10, 4			; %r10 = pixel0to3 << 4 | pixel4to7
	or %r10, %r11
	ld.w [%r12]+, %r10		; store (pixel0to3 << 4 | pixel4to7)
	xsub %r15, %r15, 1
	xjrne ldirect_mkbuf_LOOP
	ret
");
#endif	/*==================================================================*/

#if 0	/*==================================================================*/
void
ldirect_trans(const unsigned char wbuff[/*DISP_X / 2 * DISP_Y*/], const unsigned short cvttbl[/*256*/])
{
	/* コードサイズ節約&高速化のために、I/O領域のベースアドレスをローカル変数に用意します。 */
	#undef plc_IO /* c33209.hでのマクロ定義をキャンセル */
  //{{2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
  //│* Fri Jan 09 21:26:07 JST 2015 Naoyuki Sawa
  //│- S1C33209/221/222テクニカルマニュアル『図4.1 メモリマップ』(s1c33209_221_222j.pdf B-II-4-4)参照
  //│  内蔵I/Oメモリの正式なアドレスは0x40000〜0x4FFFFだが、0x30000〜0x3FFFF,及び,0x50000〜0x5FFFFにもミラーが見える。
  //│  内蔵I/Oメモリのミラー空間を使って命令数を削減する事が出来る。具体的には下記の通り:
  //│- 0x40000〜0x4FFFFのベースアドレスをレジスタに保持してオフセットでアクセスする場合、即値0x40000をレジスタにロードするには3命令かかる。
  //│  しかし0x40000〜0x4FFFFの代りに0x30000〜0x3FFFFのミラー空間を使ってアクセスすれば、即値0x30000をレジスタにロードするには2命令で済む。
  //│- ベースアドレスに限らず、内蔵I/Oメモリの特定のアドレスをレジスタにロードする場合も、上記の工夫を適用可能である。
  //	volatile struct c_IOtag* plc_IO = (volatile struct c_IOtag*)0x40000;
  //↓2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
	volatile struct c_IOtag* plc_IO = (volatile struct c_IOtag*)0x30000;
  //}}2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。

	int v;
	int page;
	int column;

	/* 各ページ(横8×縦88(=DISP_Y)ピクセルの短冊矩形)を転送します。
	 * * P/ECE LCDのページ番号並びは、左から { 9,8,7,6,5,4,3,2,1,0,15,14,13,12,11,10 } です。
	 *   開発中に使われていた旧いLCDとの互換性のための歴史的理由(？)により、
	 *   LCDハードウェアから見ると、左右反転＋9ページス分クロールした状態となっています。
	 *   /usr/PIECE/sysdev/pcekn/lcd.cを参照してください。
	 */
	page = 9;
	do {

		/*** コマンド送信 ***/

		bP2_P2D_P21D = 0; /* RS <= 0:Command */

		v = 0xb0 | page; /* Set Page Address */
		asm volatile ("mirror %0,%0" : "=r"(v) : "0"(v));
		while(!bSIF3_STATUS_TDBE3) { /* 送信バッファ空き待ち */ }
		pSIF3_TXD = v;

		//v = 0x10; /* Set Column Address MSB */
		//asm volatile ("mirror %0,%0" : "=r"(v) : "0"(v));
		//↓0x10のmirrorはあらかじめ計算できるので、次のように変更。
		v = 0x08;
		while(!bSIF3_STATUS_TDBE3) { /* 送信バッファ空き待ち */ }
		pSIF3_TXD = v;

		//v = 0x00; /* Set Column Address LSB */
		//asm volatile ("mirror %0,%0" : "=r"(v) : "0"(v));
		//↓0x00のmirrorはあらかじめ計算できるので、次のように変更。
		v = 0x00;
		while(!bSIF3_STATUS_TDBE3) { /* 送信バッファ空き待ち */ }
		pSIF3_TXD = v;

		while(bSIF3_STATUS_TEND3) { /* 送信完了待ち */ } /* 必要!! */

		/*** データ送信 ***/

		bP2_P2D_P21D = 1; /* RS <= 1:Data */

		for(column = 0; column < DISP_Y; column++) {
			v =          cvttbl[*wbuff++]; /* [4],[0] */
			v = v >> 1 | cvttbl[*wbuff++]; /* [5],[1] */
			v = v >> 1 | cvttbl[*wbuff++]; /* [6],[2] */
			v = v >> 1 | cvttbl[*wbuff  ]; /* [7],[3] */
			wbuff += (DISP_X / 2) - (8 / 2 - 1);

			while(!bSIF3_STATUS_TDBE3) { /* 送信バッファ空き待ち */ }
			pSIF3_TXD = v;
			while(!bSIF3_STATUS_TDBE3) { /* 送信バッファ空き待ち */ }
			pSIF3_TXD = v >> 8;
		}

		/* 次のページへ。 */
		wbuff += (8 / 2) - (DISP_X / 2 * DISP_Y);
		page = (page - 1) & 15;

		while(bSIF3_STATUS_TEND3) { /* 送信完了待ち */ } /* 必要!! */

	} while(page != 9); /* ページ番号が一周したら終了 */
}
#else	/*==================================================================*/
asm("
	.code
	.global ldirect_trans
ldirect_trans:
  ;//{{2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
  ;//│* Fri Jan 09 21:26:07 JST 2015 Naoyuki Sawa
  ;//│- S1C33209/221/222テクニカルマニュアル『図4.1 メモリマップ』(s1c33209_221_222j.pdf B-II-4-4)参照
  ;//│  内蔵I/Oメモリの正式なアドレスは0x40000〜0x4FFFFだが、0x30000〜0x3FFFF,及び,0x50000〜0x5FFFFにもミラーが見える。
  ;//│  内蔵I/Oメモリのミラー空間を使って命令数を削減する事が出来る。具体的には下記の通り:
  ;//│- 0x40000〜0x4FFFFのベースアドレスをレジスタに保持してオフセットでアクセスする場合、即値0x40000をレジスタにロードするには3命令かかる。
  ;//│  しかし0x40000〜0x4FFFFの代りに0x30000〜0x3FFFFのミラー空間を使ってアクセスすれば、即値0x30000をレジスタにロードするには2命令で済む。
  ;//│- ベースアドレスに限らず、内蔵I/Oメモリの特定のアドレスをレジスタにロードする場合も、上記の工夫を適用可能である。
  ;//	xld.w %r4, 0x401f5		; %r4 = pSIF3_TXD
  ;//	xld.w %r5, 0x401f7		; %r5 = pSIF3_STATUS
  ;//	xld.w %r6, 0x402d9		; %r6 = pP2_P2D
  ;//↓2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
	xld.w %r4, 0x301f5		; %r4 = pSIF3_TXD
	xld.w %r5, 0x301f7		; %r5 = pSIF3_STATUS
	xld.w %r6, 0x302d9		; %r6 = pP2_P2D
  ;//}}2015/01/09変更:内蔵I/Oメモリのミラー空間を使って、ldirect_trans()の命令数を削減しました。
	;
	xld.w %r14, 9			; %r14 = page = { 9,8,7,6,5,4,3,2,1,0,15,14,13,12,10 }
ldirect_trans_LOOP_X:
	;
	;	コマンド送信
	;
	bclr [%r6], 1			; bP2_P2D_P21D = 0
	;
	mirror %r10, %r14		; %r10 = mirror(0xb0 | page)
	xoor %r10, %r10, 0x0d		;      = 0x0d | mirror(page)
ldirect_trans_L10:
	btst [%r5], 1			; while(!bSIF3_STATUS_TDBE3) { }
	xjreq ldirect_trans_L10
	ld.b [%r4], %r10		; pSIF3_TXD = %r10
	;
	xld.w %r10, 0x08		; %r10 = mirror(0x10) = 0x08
ldirect_trans_L20:
	btst [%r5], 1			; while(!bSIF3_STATUS_TDBE3) { }
	xjreq ldirect_trans_L20
	ld.b [%r4], %r10		; pSIF3_TXD = %r10
	;
	xld.w %r10, 0x00		; %r10 = mirror(0x00) = 0x00
ldirect_trans_L30:
	btst [%r5], 1			; while(!bSIF3_STATUS_TDBE3) { }
	xjreq ldirect_trans_L30
	ld.b [%r4], %r10		; pSIF3_TXD = %r10
	;
ldirect_trans_L40:
	btst [%r5], 5			; while(bSIF3_STATUS_TEND3) { }
	xjrne ldirect_trans_L40
	;
	;	データ送信
	;
	bset [%r6], 1			; bP2_P2D_P21D = 1
	;
	xld.w %r15, 88			; %r15 = DISP_Y
ldirect_trans_LOOP_Y:
	;
	; ※以下の命令列、インターロック回避しようとしてレジスタ割り当ての変更や命令の並び替えを行うと、
	;   なぜか余計に遅くなります。文書化されていない、外部メモリアクセスパターンの特性でしょうか？
	;   とりあえずこのままで・・・
	;
	ld.ub %r11, [%r12]+		; [4],[0]
	xsll %r11, 1			; !INTERLOCK!
	add %r11, %r13
	ld.uh %r10, [%r11]
	;
	ld.ub %r11, [%r12]+		; [5],[1]
	xsll %r11, 1			; !INTERLOCK!
	add %r11, %r13
	ld.uh %r11, [%r11]
	xsrl %r10, 1			; *anti-interlock*
	or %r10, %r11
	;
	ld.ub %r11, [%r12]+		; [6],[2]
	xsll %r11, 1			; !INTERLOCK!
	add %r11, %r13
	ld.uh %r11, [%r11]
	xsrl %r10, 1			; *anti-interlock*
	or %r10, %r11
	;
	ld.ub %r11, [%r12]		; [7],[3]
	xsll %r11, 1			; !INTERLOCK!
	add %r11, %r13
	ld.uh %r11, [%r11]
	xsrl %r10, 1			; *anti-interlock*
	or %r10, %r11
	;
ldirect_trans_L50:
	btst [%r5], 1			; while(!bSIF3_STATUS_TDBE3) { }
	xjreq ldirect_trans_L50
	ld.b [%r4], %r10		; pSIF3_TXD = %r10
	;
	xadd %r12, %r12, 61		; %r12 += (DISP_X / 2) - (8 / 2 - 1)
	xsrl %r10, 8			; %r10 >>= 8
	;
ldirect_trans_L60:
	btst [%r5], 1			; while(!bSIF3_STATUS_TDBE3) { }
	xjreq ldirect_trans_L60
	ld.b [%r4], %r10		; pSIF3_TXD = %r10
	;
	xsub %r15, %r15, 1
	xjrne ldirect_trans_LOOP_Y
	;
	xsub %r12, %r12, 5628		; wbuff += (8 / 2) - (DISP_X / 2 * DISP_Y)
	xsub %r14, %r14, 1		; page = (page - 1) & 15
	xand %r14, %r14, 15
	;
ldirect_trans_L70:
	btst [%r5], 5			; while(bSIF3_STATUS_TEND3) { }
	xjrne ldirect_trans_L70
	;
	xcmp %r14, 9
	xjrne ldirect_trans_LOOP_X
	;
	ret
");
#endif	/*==================================================================*/

