/*	
 *	clipgpio.c
 *
 *	汎用I/Oルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Wed Sep 11 00:35:50 JST 2013 Naoyuki Sawa
 *	- 1stリリース。
 *	* Wed Sep 11 23:15:01 JST 2013 Naoyuki Sawa
 *	- ChatteringCanceller_Update()をASM化し、約32byteメモリ節約しました。
 *	* Sat Apr 02 23:34:06 JST 2016 Naoyuki Sawa
 *	- ARRAY_SIZE()マクロの変更に伴い、VC++6.0でintとunsignedの比較に対する警告が厳密に出るようになったので、警告が出ないように明示的なキャストを追加しました。
 *	  動作自体はこれまでと変わりません。
 *	  詳細はclipmisc.hの同日のコメントを参照して下さい。
 *	* Wed Aug 17 21:19:23 JST 2016 Naoyuki Sawa
 *	- 構造体の名前を、ST_ChatteringCanceller⇒ST_Gpioに変更しました。
 *	  関数の名前を、ChatteringCanceller_〜⇒Gpio_〜に変更しました。
 *	  名前を変更した理由は、変更前の名前は長過ぎてアプリケーションで使い辛かったからです。
 *	  名前以外は変更しておらず、当モジュールの動作は変わっていません。
 */
#include "clip.h"
/****************************************************************************
 *	チャタリング除去
 ****************************************************************************/
//構造体を初期化します。
//[in]
//	pGpio		構造体へのポインタ
//	StableCount	チャタリング除去回数(0〜4)
void Gpio_Init(ST_Gpio* pGpio, int StableCount) {
	if((unsigned)StableCount > (unsigned)ARRAY_SIZE(pGpio->PortStatus)) { DIE(); }	//チャタリング除去回数が有効な範囲であることを確認する。
	memset(pGpio, 0, sizeof(ST_Gpio));						//構造体をクリアする。
	pGpio->StableCount = StableCount;						//チャタリング除去回数を格納する。
}
/*--------------------------------------------------------------------------*/
//安定状態を更新します。
//[in]
//	pGpio		構造体へのポインタ
//	PortStatus	今回のポート状態
//[note]
//	- ○ms間隔のタイマ割り込みから呼び出して、更新処理を行うことを想定しています。
//	  タイマ割り込みを使用せずに、メインループから呼び出して更新処理を行っても構いませんが、正確さが低下するおそれがあります。
#ifndef PIECE
void Gpio_Update(ST_Gpio* pGpio, int PortStatus) {
	int UnstableBit, Tmp1, Tmp2, i;
	UnstableBit = 0;	//今回のポート状態と、(1〜pGpio->StableCount)回前のポート状態が、一回でも異なるビットを、ここに抽出する。以下、不安定ビットと称する。
	Tmp1 = PortStatus;	//(1〜pGpio->StableCount)回前のポート状態を、一回分づつ古い方へシフトするための、一時変数。
	for(i = 0; i < pGpio->StableCount; i++) {
		Tmp2 = pGpio->PortStatus[i];					//(i+1)回前のポート状態を取得する。
		       pGpio->PortStatus[i] = Tmp1;				//┬ポート状態を、一回分づつ古い方へシフトする。
		                              Tmp1 = Tmp2;			//┘
		                     UnstableBit |= (Tmp2 ^ PortStatus);	//不安定ビットを抽出する。
	}
	Tmp1 = ( UnstableBit & pGpio->StableStatus) |	//不安定ビットの部分については前回の安定状態を，
	       (~UnstableBit & PortStatus);		//安定ビットの部分については今回のポート状態を組み合わせ、新しい安定状態とする。
	Tmp2 = (pGpio->StableStatus ^ Tmp1);		//前回の安定状態と、新しい安定状態で、エッジを抽出する。
	pGpio->StableStatus = Tmp1;			//新しい安定状態を格納する。
	pGpio->StableOnEdge  |= ( Tmp1 & Tmp2);		//安定状態のOnエッジを蓄積する。
	pGpio->StableOffEdge |= (~Tmp1 & Tmp2);		//安定状態のOffエッジを蓄積する。
}
#else //PIECE
asm("
		.code
		.align		1
		.global		Gpio_Update
Gpio_Update:
		;// %r12 := pGpio
		;// %r13 := PortStatus
		xadd		%r9, %r12, 12			;//%r9  :=                             &pGpio->StableCount
		ld.w		%r4, [%r9]+			;//%r4  := i = StableCount, %r9 := p = &pGpio->PortStatus[0]
	;//	ld.w		%r5, 0				;//%r5  := UnstableBit = 0	────┐
		cmp		%r4, 0				;//if(i) {				│
		jreq.d		Gpio_Update_L20			;//  					│
		ld.w		%r5, 0				;//%r5  := UnstableBit = 0	←───┘						*delay*
		ld.w		%r6, %r13			;//  %r6  := Tmp1 = PortStatus
Gpio_Update_L10:						;//  do {
		;// %r4  := i
		;// %r5  := UnstableBit
		;// %r6  := Tmp1
		;// %r9  := p
		;// %r12 := pGpio
		;// %r13 := PortStatus
		ld.w		%r7, [%r9]			;//    %r7  := Tmp2 = *p
		ld.w		[%r9]+, %r6			;//                   *p++ = Tmp1
		ld.w		%r6, %r7			;//    %r6  :=               Tmp1 = Tmp2
		xor		%r7, %r13			;//    %r7  :=                     (Tmp2 ^ PortStatus)
	;//	or		%r5, %r7			;//    %r5  :=      UnstableBit |= (Tmp2 ^ PortStatus)	────┐
		sub		%r4, 1				;//    %r4  := i--						│
		jrne.d		Gpio_Update_L10			;//  } while(i)							│
		or		%r5, %r7			;//    %r5  :=      UnstableBit |= (Tmp2 ^ PortStatus)	←───┘			*delay*
Gpio_Update_L20:						;//}
		;// %r5  := UnstableBit
		;// %r12 := pGpio
		;// %r13 := PortStatus
		ld.w		%r7, [%r12]			;//%r7  :=                        pGpio->StableStatus
		not		%r6, %r5			;//%r6  :=                                                ~UnstableBit			*anti-interlock*
		and		%r5, %r7			;//%r5  :=         (UnstableBit & pGpio->StableStatus)
		and		%r6, %r13			;//%r6  :=                                               (~UnstableBit & PortStatus)
		or		%r6, %r5			;//%r6  := Tmp1 := (UnstableBit & pGpio->StableStatus) | (~UnstableBit & PortStatus)
		xor		%r7, %r6			;//%r7  := Tmp2 =                (pGpio->StableStatus ^ Tmp1)
		ld.w		[%r12]+, %r6			;//pGpio->StableStatus  = Tmp1
		not		%r4, %r6			;//%r4  :=                                 ~Tmp1
		ld.w		%r5, [%r12]			;//%r5  :=                StableOnEdge  = pGpio->StableOnEdge
		and		%r6, %r7			;//%r6  :=                                ( Tmp1 & Tmp2)				*anti-interlock*
		or		%r5, %r6			;//%r5  :=                StableOnEdge  | ( Tmp1 & Tmp2)
		ld.w		[%r12]+, %r5			;//pGpio->StableOnEdge  = StableOnEdge  | ( Tmp1 & Tmp2)
		ld.w		%r5, [%r12]			;//%r5  :=                StableOffEdge = pGpio->StableOffEdge
		and		%r4, %r7			;//%r6  :=                                (~Tmp1 & Tmp2)				*anti-interlock*
		or		%r5, %r4			;//%r5  :=                StableOffEdge | (~Tmp1 & Tmp2)
		ld.w		[%r12], %r5			;//pGpio->StableOffEdge = StableOnEdge  | (~Tmp1 & Tmp2)
		ret
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
//安定状態を取得します。
//[in]
//	pGpio		構造体へのポインタ
//	pOnEdge		安定状態のOnエッジを格納する変数へのポインタ  (NULL可)
//	pOffEdge	安定状態のOffエッジを格納する変数へのポインタ (NULL可)
//[out]
//	戻り値		安定状態
//[note]
//	- 安定状態のOnエッジ,及び,安定状態のOffエッジが不要ならば、引数にNULLポインタを指定してください。
//	- 安定状態のOnエッジ,及び,安定状態のOffエッジは、蓄積された値を返すことに注意してください。
//	  前回のGpio_GetStatus()呼び出し後に、一回でもエッジが検出されたビットが"1"になります。
//	  従って、特定のビットに対応する、OnエッジとOffエッジの値が、両方とも"1"になることも有り得ます。
int Gpio_GetStatus(ST_Gpio* pGpio, int* pOnEdge/*NULL可*/, int* pOffEdge/*NULL可*/) {
	int Status, Dummy;
	if(!pOnEdge)  { pOnEdge  = &Dummy; }	//安定状態のOnエッジが要求されていない場合は、ダミー変数を指す。
	if(!pOffEdge) { pOffEdge = &Dummy; }	//安定状態のOffエッジが要求されていない場合は、ダミー変数を指す。
ENTER_CS;//Gpio_Update()がタイマ割り込みから呼び出されることを想定して、Gpio_GetStatus()とGpio_Update()の排他制御を行う。
	Status    = pGpio->StableStatus;	//安定状態を取得する。
	*pOnEdge  = pGpio->StableOnEdge;	//安定状態のOnエッジを取得する。
	            pGpio->StableOnEdge  = 0;	//安定状態のOnエッジをクリアする。
	*pOffEdge = pGpio->StableOffEdge;	//安定状態のOffエッジを取得する。
	            pGpio->StableOffEdge = 0;	//安定状態のOffエッジをクリアする。
LEAVE_CS;//Gpio_Update()がタイマ割り込みから呼び出されることを想定して、Gpio_GetStatus()とGpio_Update()の排他制御を行う。
	return Status;
}
/****************************************************************************
 *	
 ****************************************************************************/
#if 0
■説明

	ST_Gpio構造体1個で、32ポートまで管理できます。
	32ポートを超える入力を管理する場合は、ST_Gpio構造体を複数個使用してください。

	生のポート状態が、(pGpio->StableCount+1)回連続で同じ状態であった場合に、安定状態と見なします。
	実際のチャタリング除去時間は、アプリケーションがGpio_Update()を呼び出す間隔に依存します。
	適切な呼び出し間隔は、約1〜10msです。(スイッチの回路次第なので、チャタリング継続時間によって要調整)

		                  ﾁｬﾀﾘﾝｸﾞ   ﾁｬﾀﾘﾝｸﾞ
		                  |<--->|   |<--->|
		PortStatus   0000 1010 1111 0101 0000 0000 0000 0000
		StableStatus 0000 0000 0011 1111 1100 0000 0000 0000	StableCount=2で初期化した場合の例。PortStatusが(2+1)回連続で同じ時に安定状態と見なす。
		OnEdge       0000 0000 0011 1111 1111 1111 1100 0000
		OffEdge      0000 0000 0000 0000 0011 1111 1100 0000
		             |||| |||| |||| ････             |
		             Gpio_Update()                   Gpio_GetStatus()
	
	当機能は、P/ECE拡張端子に何か外付けスイッチを接続した場合のために用意したものです。
	P/ECE本体のボタン入力は、当機能を使用する必要はありません。
	P/ECE本体のボタン入力は、これまでどおりpcePadGet()等,又は,CLiPライブラリのpad_get()を使用してください。

■使用例

	ST_Gpio stGpio;
	void app_isr(void* context) {
		Gpio_Update(&stGpio,~(	//負論理⇒正論理変換
			pP0_P0D<< 0|	//[ 7: 0] P0X	P06=拡張端子[6],P05=拡張端子[7],P04=拡張端子[8]
			pP1_P1D<< 8|	//[15: 8] P1X	P14=拡張端子[1],P12=拡張端子[4]
			pK5_K5D<<16|	//[23:16] K5X	K54=ﾎﾞﾀﾝSTART,K53=ﾎﾞﾀﾝSELECT				P/ECE本体のﾎﾞﾀﾝ入力でﾃｽﾄ
			pK6_K6D<<24));	//[31:24] K6X	K65=ﾎﾞﾀﾝA,K64=ﾎﾞﾀﾝB,K63=ﾎﾞﾀﾝ上,K62=ﾎﾞﾀﾝ下,K61=ﾎﾞﾀﾝ左K60	P/ECE本体のﾎﾞﾀﾝ入力でﾃｽﾄ
	}
	void app_main() {
		int Status, OnEdge, OffEdge, OnEdgeCount = 0, OffEdgeCount = 0;
		Gpio_Init(&stGpio, 4);
		timer_start(1/*ms*/, app_isr, NULL);
		for(;;) {
			schedule();
			surface_clear(&surface, 0);
			Status = Gpio_GetStatus(&stGpio, &OnEdge, &OffEdge);
			if(OnEdge  &  (1<<(24+5)/*K65*/)) { OnEdgeCount++;  }
			if(OffEdge &  (1<<(24+5)/*K65*/)) { OffEdgeCount++; }
			render_printf(&render,0, 0,0,3, "K65 Status      : %d", (Status>>(24+5)/*K65*/)&1);
			render_printf(&render,0,10,0,3, "K65 OnEdgeCount : %d", OnEdgeCount);
			render_printf(&render,0,20,0,3, "K65 OffEdgeCount: %d", OffEdgeCount);
		}
	}
#endif
