/*	
 *	clipgpio.c
 *
 *	汎用I/Oルーチン
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2013 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メモリ節約しました。
 */
#include "clip.h"

/****************************************************************************
 *	チャタリング除去
 ****************************************************************************/

/* 構造体を初期化します。
 * [in]
 *	pChatteringCanceller	構造体へのポインタ
 *	StableCount		チャタリング除去回数 (0〜4)
 */
void ChatteringCanceller_Init(ST_ChatteringCanceller* pChatteringCanceller, int StableCount) {
	if((unsigned)StableCount > ARRAY_SIZE(pChatteringCanceller->PortStatus)) { DIE(); }	/* チャタリング除去回数が有効な範囲であることを確認する */
	memset(pChatteringCanceller, 0, sizeof(ST_ChatteringCanceller));			/* 構造体をクリアする */
	pChatteringCanceller->StableCount = StableCount;					/* チャタリング除去回数を格納する */
}

/* 安定状態を更新します。
 * [in]
 *	pChatteringCanceller	構造体へのポインタ
 *	PortStatus		今回のポート状態
 * [note]
 *	- ○ms間隔のタイマ割り込みから呼び出して、更新処理を行うことを想定しています。
 *	  タイマ割り込みを使用せずに、メインループから呼び出して更新処理を行っても構いませんが、正確さが低下するおそれがあります。
 */
#ifndef PIECE
void ChatteringCanceller_Update(ST_ChatteringCanceller* pChatteringCanceller, int PortStatus) {
	int UnstableBit, Tmp1, Tmp2, i;
	UnstableBit = 0;	/* 今回のポート状態と、(1〜ChatteringCanceller_StableCount)回前のポート状態が、一回でも異なるビットを、ここに抽出する。以下、不安定ビットと称する */
	Tmp1 = PortStatus;	/* (1〜ChatteringCanceller_StableCount)回前のポート状態を、一回分づつ古い方へシフトするための、一時変数 */
	for(i = 0; i < pChatteringCanceller->StableCount; i++) {
		Tmp2 = pChatteringCanceller->PortStatus[i];				/* (i+1)回前のポート状態を取得する */
		       pChatteringCanceller->PortStatus[i] = Tmp1;			/* ┳ポート状態を、一回分づつ古い方へシフトする */
		                                             Tmp1 = Tmp2;		/* ┛ */
		                                    UnstableBit |= (Tmp2 ^ PortStatus);	/* 不安定ビットを抽出する */
	}
	Tmp1 = ( UnstableBit & pChatteringCanceller->StableStatus) |	/* 不安定ビットの部分については前回の安定状態を， */
	       (~UnstableBit & PortStatus);				/* 安定ビットの部分については今回のポート状態を組み合わせ、新しい安定状態とする */
	Tmp2 = (pChatteringCanceller->StableStatus ^ Tmp1);		/* 前回の安定状態と、新しい安定状態で、エッジを抽出する */
	pChatteringCanceller->StableStatus = Tmp1;			/* 新しい安定状態を格納する */
	pChatteringCanceller->StableOnEdge  |= ( Tmp1 & Tmp2);		/* 安定状態のOnエッジを蓄積する */
	pChatteringCanceller->StableOffEdge |= (~Tmp1 & Tmp2);		/* 安定状態のOffエッジを蓄積する */
}
#else /*PIECE*/
asm("
		.code
		.align		1
		.global		ChatteringCanceller_Update
ChatteringCanceller_Update:
		;// %r12 := pChatteringCanceller
		;// %r13 := PortStatus
		xadd		%r9, %r12, 12			;// %r9  :=                             &pChatteringCanceller->StableCount
		ld.w		%r4, [%r9]+			;// %r4  := i = StableCount, %r9 := p = &pChatteringCanceller->PortStatus[0]
	;//	ld.w		%r5, 0				;// %r5  := UnstableBit = 0	────┐
		cmp		%r4, 0				;// if(i) {				│
		jreq.d		ChatteringCanceller_Update_L20	;//   					│
		ld.w		%r5, 0				;// %r5  := UnstableBit = 0	←───┘								*delay*
		ld.w		%r6, %r13			;//   %r6  := Tmp1 = PortStatus
ChatteringCanceller_Update_L10:					;//   do {
		;// %r4  := i
		;// %r5  := UnstableBit
		;// %r6  := Tmp1
		;// %r9  := p
		;// %r12 := pChatteringCanceller
		;// %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		ChatteringCanceller_Update_L10	;//   } while(i)						│
		or		%r5, %r7			;//     %r5  :=      UnstableBit |= (Tmp2 ^ PortStatus)	←───┘					*delay*
ChatteringCanceller_Update_L20:					;// }
		;// %r5  := UnstableBit
		;// %r12 := pChatteringCanceller
		;// %r13 := PortStatus
		ld.w		%r7, [%r12]			;// %r7  :=                        pChatteringCanceller->StableStatus
		not		%r6, %r5			;// %r6  :=                                                               ~UnstableBit			*anti-interlock*
		and		%r5, %r7			;// %r5  :=         (UnstableBit & pChatteringCanceller->StableStatus)
		and		%r6, %r13			;// %r6  :=                                                              (~UnstableBit & PortStatus)
		or		%r6, %r5			;// %r6  := Tmp1 := (UnstableBit & pChatteringCanceller->StableStatus) | (~UnstableBit & PortStatus)
		xor		%r7, %r6			;// %r7  := Tmp2 =                (pChatteringCanceller->StableStatus ^ Tmp1)
		ld.w		[%r12]+, %r6			;// pChatteringCanceller->StableStatus  = Tmp1
		not		%r4, %r6			;// %r4  :=                                                ~Tmp1
		ld.w		%r5, [%r12]			;// %r5  :=                               StableOnEdge  = pChatteringCanceller->StableOnEdge
		and		%r6, %r7			;// %r6  :=                                               ( Tmp1 & Tmp2)				*anti-interlock*
		or		%r5, %r6			;// %r5  :=                               StableOnEdge  | ( Tmp1 & Tmp2)
		ld.w		[%r12]+, %r5			;// pChatteringCanceller->StableOnEdge  = StableOnEdge  | ( Tmp1 & Tmp2)
		ld.w		%r5, [%r12]			;// %r5  :=                               StableOffEdge = pChatteringCanceller->StableOffEdge
		and		%r4, %r7			;// %r6  :=                                               (~Tmp1 & Tmp2)				*anti-interlock*
		or		%r5, %r4			;// %r5  :=                               StableOffEdge | (~Tmp1 & Tmp2)
		ld.w		[%r12], %r5			;// pChatteringCanceller->StableOffEdge = StableOnEdge  | (~Tmp1 & Tmp2)
		ret
");
#endif/*PIECE*/

/* 安定状態を取得します。
 * [in]
 *	pChatteringCanceller	構造体へのポインタ
 *	pOnEdge			安定状態のOnエッジを格納する変数へのポインタ  (NULL可)
 *	pOffEdge		安定状態のOffエッジを格納する変数へのポインタ (NULL可)
 * [out]
 *	戻り値			安定状態
 * [note]
 *	- 安定状態のOnエッジ,及び,安定状態のOffエッジが不要ならば、引数にNULLポインタを指定してください。
 *	- 安定状態のOnエッジ,及び,安定状態のOffエッジは、蓄積された値を返すことに注意してください。
 *	  前回のChatteringCanceller_GetStatus()呼び出し後に、一回でもエッジが検出されたビットが"1"になります。
 *	  従って、特定のビットに対応する、OnエッジとOffエッジの値が、両方とも"1"になることも有り得ます。
 */
int ChatteringCanceller_GetStatus(ST_ChatteringCanceller* pChatteringCanceller, int* pOnEdge/*NULL可*/, int* pOffEdge/*NULL可*/) {
	int Status, Dummy;
	if(!pOnEdge)  { pOnEdge  = &Dummy; }	/* 安定状態のOnエッジが要求されていない場合は、ダミー変数を指す */
	if(!pOffEdge) { pOffEdge = &Dummy; }	/* 安定状態のOffエッジが要求されていない場合は、ダミー変数を指す */
ENTER_CS;/* ChatteringCanceller_Update()がタイマ割り込みから呼び出されることを想定 */
	Status    = pChatteringCanceller->StableStatus;		/* 安定状態を取得する */
	*pOnEdge  = pChatteringCanceller->StableOnEdge;		/* 安定状態のOnエッジを取得する */
	            pChatteringCanceller->StableOnEdge  = 0;	/* 安定状態のOnエッジをクリアする */
	*pOffEdge = pChatteringCanceller->StableOffEdge;	/* 安定状態のOffエッジを取得する */
	            pChatteringCanceller->StableOffEdge = 0;	/* 安定状態のOffエッジをクリアする */
LEAVE_CS;/* ChatteringCanceller_Update()がタイマ割り込みから呼び出されることを想定 */
	return Status;
}

/****************************************************************************
 *	
 ****************************************************************************/

//■説明
//	
//	ST_ChatteringCanceller構造体1個で、32ポートまで管理できます。
//	32ポートを超える入力を管理する場合は、ST_ChatteringCanceller構造体を複数個使用してください。
//	
//	生のポート状態が、(pChatteringCanceller->StableCount+1)回連続で同じ状態であった場合に、安定状態と見なします。
//	実際のチャタリング除去時間は、アプリケーションがChatteringCanceller_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
//		             |||| |||| |||| ････             |
//		             ChatteringCanceller_Update()    ChatteringCanceller_GetStatus()
//	
//	当機能は、P/ECE拡張端子に何か外付けスイッチを接続した場合のために用意したものです。
//	P/ECE本体のボタン入力は、当機能を使用する必要はありません。
//	P/ECE本体のボタン入力は、これまでどおりpcePadGet()等,又は,CLiPライブラリのpad_get()を使用してください。
//	
//■使用例
//	
//	ST_ChatteringCanceller stChatteringCanceller;
//	void app_isr(void* context) {
//		ChatteringCanceller_Update(&stChatteringCanceller,~(	//負論理⇒正論理変換
//			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;
//		ChatteringCanceller_Init(&stChatteringCanceller, 4);
//		timer_start(1/*ms*/, app_isr, NULL);
//		for(;;) {
//			schedule();
//			surface_clear(&surface, 0);
//			Status = ChatteringCanceller_GetStatus(&stChatteringCanceller, &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);
//		}
//	}

