/*
 *	clippsrl.c
 *
 *	パチスロゲーム用リールシミュレータ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2017 Naoyuki Sawa
 *
 *	* Mon Jan 30 21:41:16 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	* Tue Jan 31 22:45:54 JST 2017 Naoyuki Sawa
 *	- PslRelCh_Intr()の「等速中(センサ未検出)」のセンサ検出時に、センサ未検出カウンタの最小有効値未満でも、再加速へ移行しないように修正しました。
 *	  修正した理由は、そうしないと、リール回転開始後の初回の等速中(センサ未検出)にセンサ検出をした時に、間違って再加速へ移行する問題が生じるからです。
 *	  なぜならば、リール回転開始後の初回に等速中(センサ未検出)へ移行した時点ではセンサまでの距離は不定であり、すぐにセンサを検出しても正常だからです。
 *	* Wed Feb 01 21:56:57 JST 2017 Naoyuki Sawa
 *	- PslRelCh_Intr()にて全相励磁の出力パターンを間違えていたのを修正しました。
 *	  修正前は「fOut = -1」としていて、PslRel_Intr()にて他のリールの出力パターンとマージする際に、他のリールの出力パターンを破壊してしまっていました。
 *	  修正後は「fOut = (1<<0)|(1<<1)|(1<<2)|(1<<3)」として、他のリールの出力パターンを破壊してしないようになりました。
 */
#include "clip.h"
/****************************************************************************
 *	ローカル定数
 ****************************************************************************/
//出力パターンテーブル
static const uint8_t TBL_PslRel_OutPat[8]={	//   X Y~X~Y
	(1<<0)|(0<<1)|(0<<2)|(0<<3),	//0 1相励磁 ●○○○
	(1<<0)|(1<<1)|(0<<2)|(0<<3),	//1 2相励磁 ●●○○
	(0<<0)|(1<<1)|(0<<2)|(0<<3),	//2 1相励磁 ○●○○
	(0<<0)|(1<<1)|(1<<2)|(0<<3),	//3 2相励磁 ○●●○
	(0<<0)|(0<<1)|(1<<2)|(0<<3),	//4 1相励磁 ○○●○
	(0<<0)|(0<<1)|(1<<2)|(1<<3),	//5 2相励磁 ○○●●
	(0<<0)|(0<<1)|(0<<2)|(1<<3),	//6 1相励磁 ○○○●
	(1<<0)|(0<<1)|(0<<2)|(1<<3),	//7 2相励磁 ●○○●
};
/*--------------------------------------------------------------------------*/
//加速パターンテーブル
static const uint8_t TBL_PslRel_AccPat[][2/*0=分周,1=回数*/]={
	{10, 4},	//┐		//例えばこの行の場合、等速中の10分の1の速度で、4パルス出力するという意味です。
	{ 7, 8},	//├調整可
	{ 2, 5},	//┘
};
/****************************************************************************
 *	ローカル関数
 ****************************************************************************/
#ifdef  __RENESAS__
#pragma section ERAM
#endif//__RENESAS__
static int PslRelCh_Intr(ST_PslRelCh* pPslRelCh, int fSns) {
	int OnEdge, fOut = 0;	//この変数に、今回の出力値を格納する。
#ifdef  PslRel_UseVirtualReel
	//仮想リールならば、引数fSnsは無視し、理想的な位置でセンサを検出したものと見なす。
	static_assert(PslRel_Sns_Ofs, "以下の判定式はPslRel_Sns_Ofsが0でない事に依存しています。もしPslRel_Sns_Ofsを0にした場合は判定式も変更して下さい。");
	fSns = (pPslRelCh->iOfs == (PslRel_Sns_Ofs - 1)) && (pPslRelCh->iPos == PslRel_Sns_Pos);
	//                                         ~~~今回の動いた後の位置で判定するため。
#endif//PslRel_UseVirtualReel
	//センサ状態を更新する。
	OnEdge = (pPslRelCh->fSns ^ fSns) & fSns;	//今回のOnエッジ
	pPslRelCh->fSns = (pPslRelCh->fSns << 1) | fSns;
	//状態によって…
	switch(pPslRelCh->iStat) {
	default:DIE();
	//-------------------------------------------------------------------------------------
	//非励磁中
	case PslRelCh_Stat_Off:
		/** no job **/
		break;
	//-------------------------------------------------------------------------------------
	//加速中(再加速中も含む)
	case PslRelCh_Stat_Accel:
		//今回の出力値を取得する。
		fOut = TBL_PslRel_OutPat[pPslRelCh->iOutPat & 7];
		//加速パターンカウンタ1を進めて、現在の加速パターンの分周に達したら…
		if(++pPslRelCh->nAccCnt1 >= TBL_PslRel_AccPat[pPslRelCh->iAccPat][0/*分周*/]) {
			//加速パターンカウンタ1をリセットする。
			pPslRelCh->nAccCnt1 = 0;
			//出力パターンインデクスを進める。
			pPslRelCh->iOutPat++;
#ifdef  PslRel_UseVirtualReel
			//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
			if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
				//オフセットをリセットする。
				pPslRelCh->iOfs = 0;
				//絵柄位置を減らす,又は,折り返す。
				pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
			}
#endif//PslRel_UseVirtualReel
			//加速パターンカウンタ2を進めて、現在の加速パターンの回数に達したら…
			if(++pPslRelCh->nAccCnt2 >= TBL_PslRel_AccPat[pPslRelCh->iAccPat][1/*回数*/]) {
				//加速パターンカウンタ2をリセットする。
				pPslRelCh->nAccCnt2 = 0;
				//加速パターンインデクスを進めて、加速パターンの終端に達したら…
				if(++pPslRelCh->iAccPat >= ARRAY_SIZE(TBL_PslRel_AccPat)) {
					//加速パターンインデクスをリセットする。
					pPslRelCh->iAccPat = 0;
					//センサ未検出カウンタをリセットする。
					pPslRelCh->nNdCnt = 0;
					//次回の状態を、等速中(センサ未検出)とする。
					pPslRelCh->iStat = PslRelCh_Stat_Nd;
				}
			}
		}
		break;
	//-------------------------------------------------------------------------------------
	//等速中(センサ未検出)
	case PslRelCh_Stat_Nd:
		//今回の出力値を取得する。
		fOut = TBL_PslRel_OutPat[pPslRelCh->iOutPat & 7];
		//出力パターンインデクスを進める。
		pPslRelCh->iOutPat++;
#ifdef  PslRel_UseVirtualReel
		//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
		if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
			//オフセットをリセットする。
			pPslRelCh->iOfs = 0;
			//絵柄位置を減らす,又は,折り返す。
			pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
		}
#endif//PslRel_UseVirtualReel
		//センサを検出したら…
		if(OnEdge) {
//{{2017/01/31削除:PslRelCh_Intr()の「等速中(センサ未検出)」のセンサ検出時に、センサ未検出カウンタの最小有効値未満でも、再加速へ移行しないように修正しました。
//			//センサ未検出カウンタが、センサ未検出カウンタの最小有効値以上ならば…
//			if(pPslRelCh->nNdCnt >= PslRel_NdCnt_Min) {
//}}2017/01/31削除:PslRelCh_Intr()の「等速中(センサ未検出)」のセンサ検出時に、センサ未検出カウンタの最小有効値未満でも、再加速へ移行しないように修正しました。
				//センサ未検出カウンタをリセットする。
				pPslRelCh->nNdCnt = 0;
				//次回の状態を、等速中(センサ検出済み)とする。
				pPslRelCh->iOfs = PslRel_Sns_Ofs;
				pPslRelCh->iPos = PslRel_Sns_Pos;
				pPslRelCh->iStat = PslRelCh_Stat_Cruise;
//{{2017/01/31削除:PslRelCh_Intr()の「等速中(センサ未検出)」のセンサ検出時に、センサ未検出カウンタの最小有効値未満でも、再加速へ移行しないように修正しました。
//			//センサ未検出カウンタが、センサ未検出カウンタの最小有効値未満ならば…
//			} else {
//				//次回の状態を、加速中(再加速中も含む)とする。
//			//不要	pPslRelCh->nAccCnt1 = 0;	//┬PslRelCh_Stat_AccelからPslRelCh_Stat_Ndへ移行する時に、nAccCnt1もnAccCnt2もリセットされているはず。
//			//不要	pPslRelCh->nAccCnt2 = 0;	//┘
//				ASSERT(!pPslRelCh->nAccCnt1 && !pPslRelCh->nAccCnt2);
//				pPslRelCh->iStat = PslRelCh_Stat_Accel;
//			}
//}}2017/01/31削除:PslRelCh_Intr()の「等速中(センサ未検出)」のセンサ検出時に、センサ未検出カウンタの最小有効値未満でも、再加速へ移行しないように修正しました。
		//センサを検出しなければ…
		} else {
			//センサ未検出カウンタを進めて、センサ未検出カウンタの最大有効値を超えたら…
			if(++pPslRelCh->nNdCnt > PslRel_NdCnt_Max) {
				//次回の状態を、加速中(再加速中も含む)とする。
			//不要	pPslRelCh->nAccCnt1 = 0;	//┬PslRelCh_Stat_AccelからPslRelCh_Stat_Ndへ移行する時に、nAccCnt1もnAccCnt2もリセットされているはず。
			//不要	pPslRelCh->nAccCnt2 = 0;	//┘
				ASSERT(!pPslRelCh->nAccCnt1 && !pPslRelCh->nAccCnt2);
				pPslRelCh->iStat = PslRelCh_Stat_Accel;
			}
		}
		break;
	//-------------------------------------------------------------------------------------
	//等速中(センサ検出済み)
	case PslRelCh_Stat_Cruise:
		//今回の出力値を取得する。
		fOut = TBL_PslRel_OutPat[pPslRelCh->iOutPat & 7];
		//出力パターンインデクスを進める。
		pPslRelCh->iOutPat++;
		//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
		if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
			//オフセットをリセットする。
			pPslRelCh->iOfs = 0;
			//絵柄位置を減らす,又は,折り返す。
			pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
		}
		//センサを検出したら…
		if(OnEdge) {
			//センサ未検出カウンタが、センサ未検出カウンタの最小有効値以上ならば…
			if(pPslRelCh->nNdCnt >= PslRel_NdCnt_Min) {
				//センサ未検出カウンタをリセットする。
				pPslRelCh->nNdCnt = 0;
				//オフセットと絵柄位置を設定する。
				pPslRelCh->iOfs = PslRel_Sns_Ofs;
				pPslRelCh->iPos = PslRel_Sns_Pos;
			//センサ未検出カウンタが、センサ未検出カウンタの最小有効値未満ならば…
			} else {
				//次回の状態を、加速中(再加速中も含む)とする。
			//不要	pPslRelCh->nAccCnt1 = 0;	//┬PslRelCh_Stat_AccelからPslRelCh_Stat_Ndへ移行する時に、nAccCnt1もnAccCnt2もリセットされているはず。
			//不要	pPslRelCh->nAccCnt2 = 0;	//┘
				ASSERT(!pPslRelCh->nAccCnt1 && !pPslRelCh->nAccCnt2);
				pPslRelCh->iStat = PslRelCh_Stat_Accel;
			}
		//センサを検出しなければ…
		} else {
			//センサ未検出カウンタを進めて、センサ未検出カウンタの最大有効値を超えたら…
			if(++pPslRelCh->nNdCnt > PslRel_NdCnt_Max) {
				//次回の状態を、加速中(再加速中も含む)とする。
			//不要	pPslRelCh->nAccCnt1 = 0;	//┬PslRelCh_Stat_AccelからPslRelCh_Stat_Ndへ移行する時に、nAccCnt1もnAccCnt2もリセットされているはず。
			//不要	pPslRelCh->nAccCnt2 = 0;	//┘
				ASSERT(!pPslRelCh->nAccCnt1 && !pPslRelCh->nAccCnt2);
				pPslRelCh->iStat = PslRelCh_Stat_Accel;
			}
		}
		break;
	//-------------------------------------------------------------------------------------
	//停止中
	case PslRelCh_Stat_Brake:
		//今回の出力値を取得する。
		fOut = TBL_PslRel_OutPat[pPslRelCh->iOutPat & 7];
		//出力パターンインデクスを進める。
		pPslRelCh->iOutPat++;
#if 0	//①厳密にはこちらの方が正しいのですが・・・
		//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
		if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
			//オフセットをリセットする。
			pPslRelCh->iOfs = 0;
			//絵柄位置を減らす,又は,折り返す。
			pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
			//絵柄位置が、停止位置に達したら…
			if(pPslRelCh->iPos == pPslRelCh->iStpPos) {
				//上で出力パターンインデクスを進めた結果が2相励磁ならば…(=今回の出力値が1相励磁ならば…)	//←┐
				if(pPslRelCh->iOutPat & 1) {									//　│
					//次回の状態を、1ステップ遅延中とする。							//　├この条件を逆にしても構わない。リールユニットの特性次第です。
					pPslRelCh->iStat = PslRelCh_Stat_Brake2;						//　│
				//上で出力パターンインデクスを進めた結果が1相励磁ならば…(=今回の出力値が2相励磁ならば…)	//←┘
				} else {
					//次回の状態を、全相励磁中とする。
					pPslRelCh->iStat = PslRelCh_Stat_Stop;
				}
			}
		}
#else	//②・・・実用上はこちらの方が安全です。もし、(iOfs==PslRel_Ofs_Max)のタイミングで停止位置を判断して次のコマでビタ止まりの指令を出す時、その間に割り込み処理が発生して次のコマへ進んでしまっていた場合、①だと一周回る挙動になってしまうからです。②ならば1パルスずれて止まるだけで済みます。
		//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
		if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
			//オフセットをリセットする。
			pPslRelCh->iOfs = 0;
			//絵柄位置を減らす,又は,折り返す。
			pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
		}
		//絵柄位置が、停止位置に達したら…
		if(pPslRelCh->iPos == pPslRelCh->iStpPos) {
			//上で出力パターンインデクスを進めた結果が2相励磁ならば…(=今回の出力値が1相励磁ならば…)	//←┐
			if(pPslRelCh->iOutPat & 1) {									//　│
				//次回の状態を、1ステップ遅延中とする。							//　├この条件を逆にしても構わない。リールユニットの特性次第です。
				pPslRelCh->iStat = PslRelCh_Stat_Brake2;						//　│
			//上で出力パターンインデクスを進めた結果が1相励磁ならば…(=今回の出力値が2相励磁ならば…)	//←┘
			} else {
				//次回の状態を、全相励磁中とする。
				pPslRelCh->iStat = PslRelCh_Stat_Stop;
			}
		}
#endif
		break;
	//-------------------------------------------------------------------------------------
	//1ステップ遅延中
	case PslRelCh_Stat_Brake2:
		//今回の出力値を取得する。
		fOut = TBL_PslRel_OutPat[pPslRelCh->iOutPat & 7];
		//出力パターンインデクスを進める。
		pPslRelCh->iOutPat++;
#ifdef  PslRel_UseVirtualReel
		//オフセットを進めて、(一絵柄のパルス数-1)を超えたら…
		if(++pPslRelCh->iOfs > PslRel_Ofs_Max) {
			//オフセットをリセットする。
			pPslRelCh->iOfs = 0;
			//絵柄位置を減らす,又は,折り返す。
			pPslRelCh->iPos = pPslRelCh->iPos ? (pPslRelCh->iPos - 1) : PslRel_Pos_Max;
		}
#endif//PslRel_UseVirtualReel
		//次回の状態を、全相励磁中とする。
		pPslRelCh->iStat = PslRelCh_Stat_Stop;
		break;
	//-------------------------------------------------------------------------------------
	//全相励磁中
	case PslRelCh_Stat_Stop:
//{{2017/02/01修正:PslRelCh_Intr()にて全相励磁の出力パターンを間違えていたのを修正しました。
//		//今回の出力値を全相励磁とする。
//		fOut = -1;
//↓2017/02/01修正:PslRelCh_Intr()にて全相励磁の出力パターンを間違えていたのを修正しました。
		//今回の出力値を全相励磁とする。	//          X Y~X~Y
		fOut = (1<<0)|(1<<1)|(1<<2)|(1<<3);	//全相励磁 ●●●●
//}}2017/02/01修正:PslRelCh_Intr()にて全相励磁の出力パターンを間違えていたのを修正しました。
		//全総励磁カウンタを進めて、(全相励磁時間-1)を超えたら…
		if(++pPslRelCh->nStpCnt > PslRel_StpCnt_Max) {
			//全総励磁後に出力パターンインデクスに加算する値を加算する。
			pPslRelCh->iOutPat += PslRel_OutPat_Add;
			//次回の状態を、非励磁中とする。
			pPslRelCh->iStat = PslRelCh_Stat_Off;
		}
		break;
	}
	return fOut;
}
#ifdef  __RENESAS__
#pragma section
#endif//__RENESAS__
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
//構造体を初期化する。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
void PslRel_Init(ST_PslRel* pPslRel) {
	//構造体をクリアする。
	memset(pPslRel, 0, sizeof(ST_PslRel));
}
/*--------------------------------------------------------------------------*/
//割り込み処理を実行する。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
//	fSns		bit0=左リールのセンサの現在の状態
//			bit1=中リールのセンサの現在の状態
//			bit2=右リールのセンサの現在の状態
//[out]
//	戻り値		bit0～ 3=左リールの今回の出力値(X,Y,~X,~Y)
//			bit4～ 7=中リールの今回の出力値(X,Y,~X,~Y)
//			bit8～11=右リールの今回の出力値(X,Y,~X,~Y)
//[note]
//	- 呼び出し側にて、センサを入力ポートから読み出して、当関数に引き渡して下さい。
//	  呼び出し側にて、当関数の戻り値を、励磁パターンの出力ポートへ設定して下さい。
int PslRel_Intr(ST_PslRel* pPslRel, int fSns) {
	int fOut = 0;
	fOut |= (PslRelCh_Intr(&pPslRel->TBL_PslRelCh[0], ((fSns >> 0) & 1)) << 0);	//左リール
	fOut |= (PslRelCh_Intr(&pPslRel->TBL_PslRelCh[1], ((fSns >> 1) & 1)) << 4);	//中リール
	fOut |= (PslRelCh_Intr(&pPslRel->TBL_PslRelCh[2], ((fSns >> 2) & 1)) << 8);	//右リール
#ifndef PslRel_UseVirtualReel
	pPslRel->iIntr++;	//仮想リールでなければ、割り込みカウンタを進める。
#endif//PslRel_UseVirtualReel
	return fOut;
}
/*--------------------------------------------------------------------------*/
//割り込み処理が完了するタイミングに同期します。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
//[note]
//	- アプリケーションがこの関数を呼び出す目的は、ST_PslRelCh構造体の内容が割り込み処理によって変化するため、変化しない時間をなるべく長く確保するためです。
//	  アプリケーションが、ST_PslRelCh構造体の内容を参照して、停止位置を求め、PslRel_Brake()を呼び出す場合、一連の処理の直前で当関数を呼び出して下さい。
//	  その後、最長でもPslRel_IntrNs[ns]以内に、ST_PslRelCh構造体の内容の参照とPslRel_Brake()の呼び出しを完了して下さい。
//	  尚、リール制御のタイマ割り込みを、他の割り込みよりも高い優先度やマスク不可割り込みで駆動している場合は、他の割り込みを一時的にマスクする方がより安全です。
//	  <例>
//	  │ST_PslRelCh* pPslRelCh = PslRel_GetPslRelCh(pPslRel, iPslRelCh);
//	  │ENTER_IL(6); //例えば、リール制御のタイマ割り込みを優先度7で駆動している場合は、優先度6以下の割り込みをマスクする。
//	  │PslRel_Sync(pPslRel);							┐
//	  │int iStpPos = Slot_GetStpCtl1(pStpCtl, iHitCd, iPslRelCh, pPslRelCh->iPos);	├最長でもPslRel_IntrNs[ns]以内
//	  │PslRel_Brake(pPslRel, iPslRelCh, iStpPos);					┘
//	  │LEAVE_IL;
void PslRel_Sync(ST_PslRel* pPslRel) {
#ifndef PslRel_UseVirtualReel
	volatile uint8_t* pIntr = &pPslRel->iIntr;
	int iIntr = *pIntr;
	while(*pIntr == iIntr) { /** no job **/ };	//仮想リールでなければ、割り込みカウンタが変化するまで待つ。
#endif//PslRel_UseVirtualReel
}
/*--------------------------------------------------------------------------*/
//全てのリールを加速中へ移行する。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
void PslRel_Accel(ST_PslRel* pPslRel) {
	//出力パターンインデクスは維持する。
	int iOutPat0 = pPslRel->TBL_PslRelCh[0].iOutPat, iOutPat1 = pPslRel->TBL_PslRelCh[1].iOutPat, iOutPat2 = pPslRel->TBL_PslRelCh[2].iOutPat;
#ifdef  PslRel_UseVirtualReel
	//仮想リールならば、オフセットと絵柄位置も維持する。
	int iOfs0 = pPslRel->TBL_PslRelCh[0].iOfs, iOfs1 = pPslRel->TBL_PslRelCh[1].iOfs, iOfs2 = pPslRel->TBL_PslRelCh[2].iOfs;
	int iPos0 = pPslRel->TBL_PslRelCh[0].iPos, iPos1 = pPslRel->TBL_PslRelCh[1].iPos, iPos2 = pPslRel->TBL_PslRelCh[2].iPos;
#endif//PslRel_UseVirtualReel
	//構造体をクリアする。
	memset(pPslRel, 0, sizeof(ST_PslRel));
	//出力パターンインデクスは復元する。
	pPslRel->TBL_PslRelCh[0].iOutPat = iOutPat0, pPslRel->TBL_PslRelCh[1].iOutPat = iOutPat1, pPslRel->TBL_PslRelCh[2].iOutPat = iOutPat2;
#ifdef  PslRel_UseVirtualReel
	//仮想リールならば、オフセットと絵柄位置も復元する。
	pPslRel->TBL_PslRelCh[0].iOfs = iOfs0, pPslRel->TBL_PslRelCh[1].iOfs = iOfs1, pPslRel->TBL_PslRelCh[2].iOfs = iOfs2;
	pPslRel->TBL_PslRelCh[0].iPos = iPos0, pPslRel->TBL_PslRelCh[1].iPos = iPos1, pPslRel->TBL_PslRelCh[2].iPos = iPos2;
#endif//PslRel_UseVirtualReel
	//全てのリールを加速中へ移行する。
	pPslRel->TBL_PslRelCh[0].iStat = pPslRel->TBL_PslRelCh[1].iStat = pPslRel->TBL_PslRelCh[2].iStat = PslRelCh_Stat_Accel;
}
/*--------------------------------------------------------------------------*/
//指定したリールを停止中へ移行する。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
//	iPslRelCh	0=左リール,1=中リール,2=右リール
//[note]
//	- 等速中(センサ検出済み)以外のリールを、停止中へ移行する事は出来ません。
//	- リールの絵柄位置を参照して、停止位置を決定し、停止中へ移行する処理は、最長でもPslRel_IntrNs[ns]以内に完了する必要が有ります。
//	  詳細は、PslRel_Sync()関数のコメント内の、<例>を参照して下さい。
void PslRel_Brake(ST_PslRel* pPslRel, int iPslRelCh, int iStpPos) {
	ST_PslRelCh* pPslRelCh = PslRel_GetPslRelCh(pPslRel, iPslRelCh);
//※	ASSERT(pPslRelCh->iStat == PslRelCh_Stat_Cruise);	//等速中(センサ検出済み)以外から、停止中へ移行する事は出来ない。	⇒⇒⇒※要検討:厳密にはこの検査を行うべきなのですが、実用性を優先して行わない事にしました。なぜならばタイミングによっては、アプリケーションが「等速中(センサ検出済み)である事を確認」して「PslRel_Brake()を呼び出す」間に、リールが能動的に「加速中」又は「等速中(センサ未検出)」へ移行している可能性が有るからです。PslRel_Sync()を呼び出してなるべく左記が発生しないようにはするのですが、完全に防ぐ事は出来ないのでエラー停止するのは危険です。左記のようなレアケースにおいて、エラー停止するよりもリールがずれて止まる方がマシだと思うので、この検査は行わない事にしました。
	//次回の状態を、停止中とする。
	pPslRelCh->iStpPos = iStpPos;
	pPslRelCh->iStat = PslRelCh_Stat_Brake;
}
/*--------------------------------------------------------------------------*/
//ST_PslRelCh構造体のポインタを取得する。
//[in]
//	pPslRel		ST_PslRel構造体のポインタ
//	iPslRelCh	0=左リール,1=中リール,2=右リール
//[out]
//	戻り値		ST_PslRelCh構造体のポインタ
//[note]
//	- 当関数が返すST_PslRelCh構造体は、厳密にはvolatileと見なすべきで、本来は当関数の戻り値は'volatile ST_PslRelCh*'とするのが正しいです。
//	  しかし、呼び出し側にて、ST_PslRelCh構造体の内容が変化しないタイミングを狙い打って処理するので、volatileであってもなくても同じです。
//	  ST_PslRelCh構造体の内容が変化しない間ならば非volatileと見なせますし、非volatileの方が高速なコードが生成されるのでかえって安全です。
//	  従って、当関数が返すST_PslRelCh構造体は、非volatileとする事にしました。
ST_PslRelCh* PslRel_GetPslRelCh(ST_PslRel* pPslRel, int iPslRelCh) {
	ST_PslRelCh* pPslRelCh;
	ASSERT((unsigned)iPslRelCh < (unsigned)ARRAY_SIZE(pPslRel->TBL_PslRelCh));
	pPslRelCh = &pPslRel->TBL_PslRelCh[iPslRelCh];
	return pPslRelCh;
}
/*****************************************************************************
 *	使用例
 *****************************************************************************/
#if 0
//※TODO:これは開発中に適当に書いた使用例です。もっとちゃんとした使用例を書け。
int app_main(int argc, char* argv[]) {
	ST_PslRel stPslRel;
	PslRel_Init(&stPslRel);
	for(;;) {
		printf("%2d %2d %2d %2d %2d | %2d %2d %2d %2d %2d | %2d %2d %2d %2d %2d\n",
			stPslRel.TBL_PslRelCh[0].iOutPat&7, stPslRel.TBL_PslRelCh[0].iStat, stPslRel.TBL_PslRelCh[0].iOfs, stPslRel.TBL_PslRelCh[0].iPos, stPslRel.TBL_PslRelCh[0].iStpPos,
			stPslRel.TBL_PslRelCh[1].iOutPat&7, stPslRel.TBL_PslRelCh[1].iStat, stPslRel.TBL_PslRelCh[1].iOfs, stPslRel.TBL_PslRelCh[1].iPos, stPslRel.TBL_PslRelCh[1].iStpPos,
			stPslRel.TBL_PslRelCh[2].iOutPat&7, stPslRel.TBL_PslRelCh[2].iStat, stPslRel.TBL_PslRelCh[2].iOfs, stPslRel.TBL_PslRelCh[2].iPos, stPslRel.TBL_PslRelCh[2].iStpPos);

		PslRel_Intr(&stPslRel, 0);
		if(kbhit()) {
			int i = getch() - '1';
			if((unsigned)i < 3) {
				ST_PslRelCh* pPslRelCh = PslRel_GetPslRelCh(&stPslRel, i, 1);
				if(pPslRelCh->iStat == PslRelCh_Stat_Cruise) {
					int iPos = pPslRelCh->iPos;
					if(--iPos < 0) { iPos = PslRel_Pos_Max; }
					PslRel_Brake(&stPslRel, i, iPos);
				}
			} else {
				PslRel_Accel(&stPslRel);
			}
		}
	}
	return 0;
}
#endif
