/*
 *	clipsnrs.h
 *
 *	シナリオシーケンサ
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2017 Naoyuki Sawa
 *
 *	* Sat Oct 14 22:22:29 JST 2017 Naoyuki Sawa
 *	- 1st リリース。
 *	  今回はまだ、dSnrSeqC.exe,及び,dSnrSeqD.exeを作成するため定数を定義しただけで、当モジュールの関数は未実装です。
 *	* Sun Oct 22 23:53:05 JST 2017 Naoyuki Sawa
 *	- 当モジュールの関数を実装しました。
 *	  基本的には、動作実績のある既存のclipdirs.cをベースにして作成したので、ロジックの大幅な間違いは無いと思います。
 *	  ただしclipdirs.cと比べて、命令の違い以外にも、以下の点を変更しました。
 *	- 演出番号バッファ(ActBuf)を廃止しました。
 *	  clipdirs.cでは、コールバック関数の中からシーケンサに対する処理が行われた場合に、処理をシリアライズするために、演出番号バッファを用いていました。
 *	  しかし、実際にいくつかのアプリケーションで使用したところ、コールバック関数の中からシーケンサに対する処理を行う必要は、ほとんど有りませんでした。
 *	  逆に、'act'に対するコールバックは演出番号バッファによって遅延するが、'if'に対するコールバックは演出番号バッファを介さないので遅延しないという、却って判り辛い問題が生じていました。
 *	  そこで今回、clipsnrs.cにおいては、演出番号バッファを廃止して、全てのコールバックは単純に即実行するという仕様にしました。
 *	  変更による利点としては、単純で判り易くなりましたし、メモリ消費量も低減しました。(clipdirs.cでは、演出番号バッファをスタック上に取っていたので、スタック溢れの危険が有った。)
 *	  変更による欠点としては、コールバック関数の中からシーケンサに対する処理が呼び出せなくなりました(もし呼び出した場合の動作は未定義です)が、前述の通り、実際のアプリケーションではまず不要だと思います。	⇒{{2017/10/24コメント追記:もし呼び出した場合はエラー停止するようにしました。}}
 *	- clipdirs.cでは、停止時にも実行する命令を'act flush'という名前にしていましたが、clipsnrs.cでは'act ensure'という名前に変更しました。
 *	  'flush'では、目的と名前が合っていなくて、直観的に判り辛いと思ったからです。
 *	  'ensure'という名前は、Ruby言語の例外処理のensure句に倣いました。
 *	  この変更は名前だけの変更であり、機能的にはclipdirs.cの'act flush'とclipsnrs.cの'act ensure'に違いは有りません。
 *	* Tue Oct 24 23:47:20 JST 2017 Naoyuki Sawa
 *	- ST_SnrSeqCh構造体に再入不可フラグ(bNonReentrant)を追加しました。
 *	  コールバック関数の中から、同一チャネルに対するシーケンサの処理を呼び出した場合、エラー停止します。
 *	  コールバック関数の中から、別のチャネルに対するシーケンサの処理を呼び出す事は可能ですが、その先で元のチャネルに対するシーケンサの処理を呼び出した場合も、エラー停止します。
 *	- 上記の検査を追加した理由は、以下の通りです。
 *	  2017/10/22のコメントに書いた通り、実際のアプリケーションで、コールバック関数の中からシーケンサに対する処理を呼び出す事はまず無いのですが、万一おこなった場合に発見し辛いバグとなってしまうため、明示的に検査してエラー停止する事にしました。
 *	  また、偶然、ST_SnrSeqCh構造体にパディング領域が1バイト余っていたので、今回、ST_SnrSeqCh構造体のサイズを増やさずに、再入不可フラグ(bNonReentrant)を追加する事が出来ました。
 *	* Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa
 *	- ロックバッファのロジックにバグが見つかり、修正しました。
 *	  clipdirs.cモジュール由来のバグであり、clipdirs.cモジュールの昨日を使用していた既存のアプリケーションにも、このバグが内包されていた事になります。
 *	  具体的には、以下の通りです。
 *	- ロックバッファが空の時に、ロック時間を減らす処理が行われていませんでした。
 *	  正しくは、ロックバッファが空の時も、ロック時間を減らす処理は行うべきです。
 *	  このバグが顕在化する状況としては、以下の通りです。
 *	  │Test
 *	  │        Trig1   .Trig1  5
 *	  │        Trig2   .Trig2
 *	  │end
 *	  │.Trig1
 *	  │        Puts    "Trig1"
 *	  │end
 *	  │.Trig2
 *	  │        Puts    "Trig2"
 *	  │end
 *	  T=0でTrig1を送信し、T=3でTrig2を送信したとします。
 *	  正しくは、T=5でTrig2が開始すべきですが、上記のバグのために、T=8でTrig2が開始していました。
 *	  Trig2がロックバッファに入るまで、ロック時間が減らず、5のままになってしまっていたからです。
 *	  今回、上記のバグを修正し、正しく、T=5でTrig2が開始するようになりました。
 *	- 今回、上記の修正に加えて、以下の仕様変更も行いました。
 *	- これまでは、_SnrSeqCh_Start()にて、シナリオシーケンスの開始と同時に、シナリオシーケンスを1フレーム実行する仕様としていました。					//┬{{2017/11/21コメント追記:やっぱり、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行したいケースも有るので、'on exec',及び,'on unlock exec'命令を追加して、アプリケーションが明示的に指定出来るようにしました。}}
 *	  しかし、実際に使って来た所、意図せず1フレーム進み過ぎて、却って直観に反して判り辛いと思い、この仕様は廃止する事にしました。						//│
 *	  この仕様を廃止しても、開始即停止でも実行したいが有る場合は、明示的に'act ensure'命令を使えば実現出来ます。								//│
 *	  アプリケーションの観点からは、SnrSeq_SendTrig()の時点では1フレーム目が実行されず、SnrSeqCh_Exec()の時点で1フレーム目が実行されるようになった事に注意して下さい。	//┘
 *	- これまでは、SnrSeqCh_Unlock()にて、ロックバッファが空になった時点で実行されている、最後のシーケンスのロック時間はクリアしませんでした。
 *	  そうしていた理由は、既にロックバッファに複数のシーケンスが入っている状態でアンロックした場合に、最後のシーケンスのロック時間を残すのが自然と思っていたからです。
 *	  しかし、実際に使って来た所、SnrSeqCh_Unlock()を読んだ直後にSnrSeq_SendTrig()を行った場合にそのトリガがロックされる可能性が有るため、却って判り辛いと感じました。
 *	  そこで今回、SnrSeqCh_Unlock()は、最後のシーケンスのロック時間もクリアするように変更しました。
 *	  SnrSeqCh_Unlock()を読んだ直後は、ロック時間がクリアされ,且つ,ロックバッファが空になっている事が保証されるようになりました。
 *	- SnrSeqCh_GetStat()にて、ロック時間が残っているか,又は,ロックバッファが空でなければ、このチャネルは動作中であると見なように変更しました。
 *	  これまでは、上記の判定を行っていなかったため、ロック中の状態であってもSnrSeqCh_GetStat()が0を返す可能性が有りました。
 *	  時間経過によって自動的に再びSnrSeqCh_GetStat()が1に戻る可能性があり、これまでの動作は適切ではありませんでした。
 *	  今回の変更により、SnrSeqCh_GetStat()は、ロック中の状態も動作中と見なして、1を返すようになりました。
 *	- 今回の変更点は、以上です。
 *	  変更箇所が多かったので、変更前のコードは、コメントアウトして残していません。
 *	  変更前のコードを参照する必要が有る場合は、前回のアーカイブを参照して下さい。
 *	  また、今回修正した点は、既存のclipdirs.cモジュール由来のバグだった点も多く、本来はclipdirs.cモジュールにもバックポートすべきなのですが、それは行わない事にしました。
 *	  今回の修正による挙動の変更が大きく、既存のclipdirs.cモジュールの(意図しない)動作に依存して動作していたアプリケーションが動作しなくなる恐れが有るからです。
 *	  今後ゆくゆくはclipdirs.cモジュールを廃止し、clipsnrs.cモジュールへ移行して行く予定なので、clipdirs.cモジュールは修正せずにそのままにしておく事にしました。
 *	* Sun Oct 29 23:57:57 JST 2017 Naoyuki Sawa
 *	- 'wait for'命令と'wait for not'命令を追加しました。
 *	  既存の命令でも'while cond; wait; end',及び,'until cond; wait; end'と同じですが、使用頻度が高いと思うので、利便性のために一命令で実行出来るようにしました。
 *	* Sat Nov 04 23:56:59 JST 2017 Naoyuki Sawa
 *	- ラベルテーブルを検索するユーティリティ関数を追加しました。
 *	  詳細は、/clip/tool/dSnrSeqC/winapp.h の同日のコメントを参照して下さい。
 *	* Sat Nov 11 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'escape'命令を追加しました。
 *	  停止時の処理の中でescape命令を見つけたら、exit命令と同じと見なします。
 *	  動作中の処理の中でescape命令を見つけたら、NOPと見なします。
 *	- escape命令を追加した理由は、以下の通りです。
 *	  当モジュールの仕様は、前身であるclipdirs.cモジュールの、さらに前身のclipprds.cモジュールに由来しています。
 *	  元々、clipprds.cモジュールは、パチスロゲームのトリガ単位の演出を再生し易いように作成したものでした。
 *	  パチスロゲームの演出の特性上、プレイヤーが演出を最後まで見ずに早打ちしたとしても、演出を最後まで見て停止した場合と同じ状態になって停止するのが望ましいと考えました。
 *	  そうしないと、演出の後半で表示(or消去)して次のトリガへ引き継がれるレイヤが表示(or消去)されなかったりして、次のトリガの処理で考慮する点が増えてしまい煩雑だからです。
 *	  上記の煩雑さを避けるために、各演出自身が(たとえ途中で停止されても)常に同じ状態で終了出来る事が目標でした。
 *	  その目標は達成されたのですが、その後、clipdirs.c,clipsnrs.cへと機能を発展させて行く中で、元々想定していた用途以外でも使用したいと思うようになりました。
 *	  そうなると、シーケンスが停止時にも常に最後まで走査されるという仕様が、支障となるケースも生じました。
 *	  シーケンス自身でループを回して処理を実行し、停止時には即終了したい事も有るからです。(演出に特化せず、汎用性を考慮すると、むしろこちらの方が一般的です。)
 *	  対策としては、停止時にシーケンスの残りを走査しないように仕様変更する事も可能ではあるのですが、元々の目的は捨てたくないので、それは行わない事にしました。
 *	  その代わりに、escape命令を追加して、停止時の走査終了可能ポイントを明示的に示す事にしました。
 *	  一般的に、処理の原子性を保障したいケースでは、「位置A〜位置Bの途中で停止要求が発行されたら、位置Bまでは処理を行う」という要求になる事が多いと思います。
 *	  その場合、位置Aの直前と位置Bの直後にescape命令を置く事で実現出来ます。
 *	  │MySeq1
 *	  │    wait    100
 *	  │    escape
 *	  │    Play    1,Bgm1		#//位置A
 *	  │    wait    100
 *	  │    Stop    1       ensure	#//位置B	停止時にも実行したい処理には'act ensure'命令(この例では簡略記法)を使う必要が有る事に注意して下さい。この点についてはこれまでと変わっていません。
 *	  │    escape
 *	  │    wait    100
 *	  │end
 *	  この例では、escape命令を使わずに最後まで走査してもさほど違いませんが、もっと長いシーケンスや、goto命令で無限ループが組まれているようなシーケンスの場合は、escape命令が有効に使えると思います。
 *	  尚、escape命令の仕組みはスタックを考慮しないので、call命令等の呼び出し先のシーケンスボディ内でescape命令を見つけた場合に、呼び出し元へ戻って走査するのでは無く、即終了する事に注意して下さい。
 *	  使用方法のガイドラインとしては、escape命令を使用する箇所は、シナリオテーブルから直接呼び出されたシナリオシーケンス内だけに限定しておく方が、混乱しなくて良いと思います。(必須ではありません。)
 *	* Tue Nov 21 23:59:59 JST 2017 Naoyuki Sawa
 *	- 'on unlock','on exec','on unlock exec'命令を追加しました。
 *	- 'on unlock'命令を追加した理由は、以下の通りです。
 *	  │・シナリオシーケンスを開始せずに、アンロックのみを行いたいケースが、時々有りました。
 *	  │　たとえば、前ゲームの3on演出がロックを掛けて、3off演出がロックされていても、次betトリガで強制的にアンロックして、3off演出を即再生したい場合等です。
 *	  │　しかし、これまでは、シナリオシーケンスを開始せずにアンロックのみを行いたい場合は、SnrSeqCh_SendTrig()は使用出来ず、明示的にSnrSeqCh_Unlock()を呼び出す必要が有りました。
 *	  │　なぜなら、既存の'on unlock goto'と'on unlock exec'命令は、シナリオシーケンスを開始せずにアンロックのみをおこなう機能は無く、トリガの処理として記述出来なかったからです。
 *	  │　そのため、トリガを契機として上記の処理を行いたい場合でも、SnrSeqCh_SendTrig()は使用出来ず、SnrSeqCh_SendTrig()/SnrSeqCh_Unlock()を使い分ける必要が有り、アプリケーションが処理内容を意識しなければいけない問題が有りました。
 *	  │・そこで今回、新たに'on unlock'命令を追加して、シナリオシーケンスを開始せずにアンロックのみを行う処理を、トリガの処理として記述出来るようにしました。
 *	  │　アプリケーションは、トリガの処理内容を意識せずに、常にSnrSeqCh_SendTrig()を使用出来るようになりました。
 *	  │　尚、SnrSeqCh_SendTrig()を使って'on unlock'命令が使用されているトリガを起動する事と、明示的にSnrSeqCh_Unlock()を呼び出す事の違いは、呼び出し方の違いだけで結果は同じです。
 *	  │　明示的にSnrSeqCh_Unlock()を呼び出した場合と同じく、ロックされているシナリオシーケンスが有ればそれが開始され、ロックされているシナリオシーケンスが無ければ何も行いません。
 *	  │・尚、これまでと同様に、アプリケーションが明示的にSnrSeqCh_Unlock()を呼び出してアンロックする事も可能ですので、用途によって使い分けて下さい。
 *	  │　ガイドラインとしては、アンロックする目的がシナリオの一部と見なせるならばSnrSeqCh_SendTrig()を使い、アンロックする目的がシナリオと無関係でアプリケーションの処理と見なせるならばSnrSeqCh_Unlock()を使うのが適切だと思います。
 *	- 'on exec','on unlock exec'命令を追加した理由は、以下の通りです。
 *	  │・clipdirs.cでは、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行していましたが、当モジュール(clipsnrs.c)では、その仕様は廃止していました。(clipsnrs.cの'Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa'のコメント参照)
 *	  │　ほとんどのケースでは仕様を廃止して良かったのですが、やっぱり、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行したいケースも有る事に気付きました。
 *	  │　例えば、setトリガやclrトリガから起動されるシナリオシーケンスに1フレームで完了する処理を記述しておき、setトリガやclrトリガを契機としてそれらの処理を即実行したいケース等です。(左記はあくまでも例であり、必ずしもsetトリガやclrトリガに限りません。)
 *	  │　上記のようなケースにおいては、clipdirs.cの頃のようなシナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行する仕様の方が望ましいのですが、'Sun Oct 29 23:41:29 JST 2017 Naoyuki Sawa'以降、その仕様は廃止していました。
 *	  │　別の方法として、'act ensure'命令を使えば、直後にSnrSeqCh_Exec()が呼ばれた時に処理が実行される事は保証出来ますが、プログラムの他の部分の処理とのタイミングの兼ね合いのために、トリガを起動した瞬間に処理が実行されて欲しいケースはやはり有り得ます。
 *	  │　結局の所、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行するのが適切か、実行しないのが適切かは、処理内容に依存するため、モジュールの機能としては両方に対応しておき、アプリケーションが明示的に指定出来る必要が有ると思いました。
 *	  │・そこで今回、新たに'on exec','on unlock exec'命令を追加しました。
 *	  │　既存の'on goto','on unlock goto'命令との違いは、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行する事のみです。
 *	  │　尚、シナリオシーケンスの開始と同時にシナリオシーケンスを1フレーム実行するのはSnrSeqCh_SendTrig()から直接起動された場合だけであり、ロックされた場合は'on goto'命令と同じ扱いとなります。
 *	  │　具体的には、以下の通りです。
 *	  │　│'on goto'		ロック時間が残っていなければ、シナリンシーケンスを開始し、1フレーム目を即実行する事はおこなわない。	ロック時間が残っていたら、ロックバッファに入る。ロックが(自然に,又は,明示的に)解除された時に、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on unlock goto'	強制的にアンロックして、確実にシナリンシーケンスを開始し、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on exec'		ロック時間が残っていなければ、シナリンシーケンスを開始し、1フレーム目を即実行する。			ロック時間が残っていたら、ロックバッファに入る。ロックが(自然に,又は,明示的に)解除された時に、1フレーム目を即実行する事はおこなわない。
 *	  │　│'on unlock exec'	強制的にアンロックして、確実にシナリンシーケンスを開始し、1フレーム目を即実行する。
 *	  │　尚、対象性のために'on exec'命令も用意しておきましたが、上記の'on exec'命令の(ロックされるかロックされないかによって挙動が変わるという)動作が望ましいケースはあまり無いと思うので、実際に使用するのは'on goto','on unlock goto','on unlock exec'の三種類だけだと思います。
 */
#ifndef __CLIP_SNRS_H__
#define __CLIP_SNRS_H__
#ifdef  __cplusplus
extern "C" {
#endif//__cplusplus
//*****************************************************************************
//	定数
//*****************************************************************************
//シナリオテーブルのコード
#define SnrTbl_end		  0	//00000000
#define SnrTbl_on_unlock	  1	//00000001 tttttttt
#define SnrTbl_on_goto		  2	//00000010 tttttttt ssssssss ssssssss ssssssss llllllll llllllll		t=トリガ番号,s=シナリオシーケンスボディへの相対距離,l=ロック時間(0=ロック無し)
#define SnrTbl_on_unlock_goto	  3	//00000011 tttttttt ssssssss ssssssss ssssssss llllllll llllllll		t=トリガ番号,s=シナリオシーケンスボディへの相対距離,l=ロック時間(0=ロック無し)
#define SnrTbl_on_exec		  4	//00000100 tttttttt ssssssss ssssssss ssssssss llllllll llllllll		t=トリガ番号,s=シナリオシーケンスボディへの相対距離,l=ロック時間(0=ロック無し)
#define SnrTbl_on_unlock_exec	  5	//00000101 tttttttt ssssssss ssssssss ssssssss llllllll llllllll		t=トリガ番号,s=シナリオシーケンスボディへの相対距離,l=ロック時間(0=ロック無し)
//-----------------------------------------------------------------------------
//シナリオシーケンスのコード
#define SnrSeq_end		  0	//00000000
#define SnrSeq_exit		  1	//00000001
#define SnrSeq_escape		  2	//00000010
#define SnrSeq_wait		  3	//00000011 wwwwwwww wwwwwwww							w=待ち時間(0=エラー)
#define SnrSeq_wait_for		  4	//00000100 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa					f=関数番号,a=引数リストへの相対距離
#define SnrSeq_wait_for_not	  5	//00000101 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa					f=関数番号,a=引数リストへの相対距離
#define SnrSeq_fork		  6	//00000110 ssssssss ssssssss ssssssss						s=シナリオシーケンスボディへの相対距離
#define SnrSeq_hide		  7	//00000111
#define SnrSeq_join		  8	//00001000
#define SnrSeq_goto		  9	//00001001 ssssssss ssssssss ssssssss						s=シナリオシーケンスボディへの相対距離
#define SnrSeq_call		 10	//00001010 ssssssss ssssssss ssssssss						s=シナリオシーケンスボディへの相対距離
#define SnrSeq_else		 11	//00001011 ssssssss ssssssss ssssssss						s=シナリオシーケンスボディへの相対距離						if文とcase文から共通で使用される。
#define SnrSeq_if		 12	//00001100 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離	┬(if|if_not)(elif|elif_not)*else?
#define SnrSeq_if_not		 13	//00001101 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離	│
#define SnrSeq_elif		 14	//00001110 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離	│
#define SnrSeq_elif_not		 15	//00001111 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離	┘
#define SnrSeq_case		 16	//00010000 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa					f=関数番号,a=引数リストへの相対距離						┬(case)(of)+else?
#define SnrSeq_of		 17	//00010001 vvvvvvvv ssssssss ssssssss ssssssss					v=値,s=シナリオシーケンスボディへの相対距離					┘
#define SnrSeq_repeat		 18	//00010010 rrrrrrrr rrrrrrrr ssssssss ssssssss ssssssss				r=繰り返し回数(0=エラー,65535=無限),s=シナリオシーケンスボディへの相対距離
#define SnrSeq_break		 19	//00010011 nnnnnnnn								n=ネストしたrepeatを抜ける数(0=エラー)
#define SnrSeq_while		 20	//00010100 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離
#define SnrSeq_while_not	 21	//00010101 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa ssssssss ssssssss ssssssss	f=関数番号,a=引数リストへの相対距離,s=シナリオシーケンスボディへの相対距離
#define SnrSeq_act		 22	//00010110 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa					f=関数番号,a=引数リストへの相対距離
#define SnrSeq_act_ensure	 23	//00010111 ffffffff aaaaaaaa aaaaaaaa aaaaaaaa					f=関数番号,a=引数リストへの相対距離
//-----------------------------------------------------------------------------
//トリガ番号
#define SnrTrg_set		  0	//┐0="set"と255="clr"は当モジュールの動作仕様に依存するため固定です。
#define SnrTrg_clr		255	//┘1〜254はアプリケーション定義で、dSnrSeqC.exeによって定義されます。
//*****************************************************************************
//	構造体
//*****************************************************************************
#define SnrSeqCtx_StackSize	  4	//スタッサイズ				1以上で調整可		調整可ではあるがアプリケーション毎に調整する事は想定していない。変更する場合はライブラリをリビルドせよ。
//シナリオシーケンサコンテキスト
typedef struct _ST_SnrSeqCtx {
	const uint8_t*		pSnrSeq;					//+ 0, 4   シナリオシーケンスの次に実行する命令位置	NULL=空き
	uint16_t		WaitCnt;					//+ 4, 2   残り待ち時間																(pSnrSeq==NULL)ならば、(WaitCnt==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_SnrSeq_AllocSnrSeqCtx(),及び,_SnrSeq_FreeSnrSeqCtx()のコメントを参照して下さい。
	uint8_t			iCtxNext;					//+ 6, 1   コンテキストチェイン				0〜254=このチャネルのコンテキストチェインの次のコンテキストのインデクス,255=終端	(pSnrSeq==NULL)ならば、(iCtxNext==255)とします。	処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_SnrSeq_AllocSnrSeqCtx(),及び,_SnrSeq_FreeSnrSeqCtx()のコメントを参照して下さい。
	uint8_t			bHide;						//+ 7, 1   隠れコンテキストフラグ			0=通常のコンテキスト,1=隠れコンテキスト							(pSnrSeq==NULL)ならば、(bHide==0)とします。		処理上必須ではありませんが、メモリ破壊やバグ検出のためです。詳細は、_SnrSeq_AllocSnrSeqCtx(),及び,_SnrSeq_FreeSnrSeqCtx()のコメントを参照して下さい。
	//--- スタック ---
	uint16_t		TBL_StackRep[SnrSeqCtx_StackSize];		//+ 8, 2*4 0=Callスタック,1〜65534=Repeatスタックの残り繰返し回数,65535=Repeatスタック(無限)
	const uint8_t*		TBL_StackRet[SnrSeqCtx_StackSize];		//+16, 4*4 戻り先の命令位置				NULL要素で終端		'while'命令によるCallスタックの場合は、呼び出し元の'while'命令自身を指す。それ以外の命令によるCallスタック,及び,Repeatスタックの場合は、呼び出し元の命令の次の命令を指す。
} ST_SnrSeqCtx;									//=32
//-----------------------------------------------------------------------------
#define SnrSeqCh_LockBufSize	  2	//ロックバッファサイズ			1以上で調整可		調整可ではあるがアプリケーション毎に調整する事は想定していない。変更する場合はライブラリをリビルドせよ。
//シナリオシーケンサチャネル
typedef struct _ST_SnrSeqCh {
	const uint8_t*		pSnrTbl;					//+ 0, 4   現在設定されているシナリオテーブル		NULL=無し
	uint8_t			iCtxHead;					//+ 4, 1   コンテキストチェイン				0〜254=このチャネルのコンテキストチェインの最初のコンテキストのインデクス,255=終端
	uint8_t			bNonReentrant;					//+ 5, 1   再入不可フラグ				0=コールバック関数の呼び出し中でない,1=コールバック関数の呼び出し中である
	//--- ロック ---
	uint16_t		LockCnt;					//+ 6, 2   残りロック時間
	const uint8_t*		TBL_LockBuf[SnrSeqCh_LockBufSize];		//+ 8, 4*2 ロック終了を待っているシナリオシーケンス	NULL要素で終端		'on goto'命令,又は,'on unlock goto'命令,又は,'on exec'命令,又は,'on unlock exec'命令の、3バイト目を指す。
} ST_SnrSeqCh;									//=16
//-----------------------------------------------------------------------------
//シナリオシーケンサ
typedef struct _ST_SnrSeq {
	int (*pFunc)(struct _ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, struct blob_reader* pArgs);	//+ 0, 4 コールバック関数	NULL不可
	uint8_t			nCh;						//+ 4, 1   シナリオシーケンサチャネル数			0〜255
	uint8_t			nCtx;						//+ 5, 1   コンテキストバッファサイズ			0〜255
	//									//+ 6, 2   (パディング)
    //	ST_SnrSeqCh		TBL_SnrSeqCh[nCh];				//+ 8,16*nCh
    //	ST_SnrSeqCtx		TBL_SnrSeqCtx[nCtx];				//+ 8+16*nCh,32*nCtx
} ST_SnrSeq;									//= 8+16*nCh+32*nCtx
//*****************************************************************************
//	グローバル関数
//*****************************************************************************
ST_SnrSeq* SnrSeq_New(int (*pFunc)(struct _ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, struct blob_reader* pArgs), int nCh, int nCtx);
void SnrSeq_Init(ST_SnrSeq* pSnrSeq, int (*pFunc)(struct _ST_SnrSeq* pSnrSeq, int iCh, int iSnrFnc, struct blob_reader* pArgs), int nCh, int nCtx);
void SnrSeq_Free(ST_SnrSeq* pSnrSeq);
void SnrSeq_Exec(ST_SnrSeq* pSnrSeq);
void SnrSeqCh_Exec(ST_SnrSeq* pSnrSeq, int iCh);
void SnrSeq_ClrTbl(ST_SnrSeq* pSnrSeq);
void SnrSeqCh_SetTbl(ST_SnrSeq* pSnrSeq, int iCh, const uint8_t* pSnrTbl/*NULL=クリア*/);
void SnrSeq_SendTrig(ST_SnrSeq* pSnrSeq, int iSnrTrg);
void SnrSeqCh_SendTrig(ST_SnrSeq* pSnrSeq, int iCh, int iSnrTrg);
void SnrSeq_Unlock(ST_SnrSeq* pSnrSeq);
void SnrSeqCh_Unlock(ST_SnrSeq* pSnrSeq, int iCh);
void SnrSeq_Stop(ST_SnrSeq* pSnrSeq);
void SnrSeqCh_Stop(ST_SnrSeq* pSnrSeq, int iCh);
int SnrSeq_GetStat(ST_SnrSeq* pSnrSeq);
int SnrSeqCh_GetStat(ST_SnrSeq* pSnrSeq, int iCh);
//*****************************************************************************
//	dSnrSeqC.exeが出力したバイナリ形式を扱うためのユーティリティ関数
//*****************************************************************************
#define USE_SNRSEQ_BIN	//ユーティリティ関数が不要ならばこのシンボル定義をコメントアウトしてください。
#ifdef  USE_SNRSEQ_BIN
void SnrSeqCh_SetTblNo(ST_SnrSeq* pSnrSeq, int iCh, const void* pSnrSeqBin, int iSnrTblNo);
//{{2017/11/04追加:ラベルテーブルを検索するユーティリティ関数を追加しました。
const char* SnrSeq_GetLabelTable(const void* pSnrSeqBin);
const char* SnrSeq_GetTableName(const void* pSnrSeqBin, int iSnrTblNo);
const char* SnrSeq_GetTrigName(const void* pSnrSeqBin, int iSnrTrg);
const char* SnrSeq_GetFuncName(const void* pSnrSeqBin, int iSnrFnc);
int SnrSeq_GetTableNo(const void* pSnrSeqBin, const char* pTableName);
int SnrSeq_GetTrigNo(const void* pSnrSeqBin, const char* pTrigName);
int SnrSeq_GetFuncNo(const void* pSnrSeqBin, const char* pFuncName);
const char* SnrSeq_TryGetTableName(const void* pSnrSeqBin, int iSnrTblNo);
const char* SnrSeq_TryGetTrigName(const void* pSnrSeqBin, int iSnrTrg);
const char* SnrSeq_TryGetFuncName(const void* pSnrSeqBin, int iSnrFnc);
int SnrSeq_TryGetTableNo(const void* pSnrSeqBin, const char* pTableName);
int SnrSeq_TryGetTrigNo(const void* pSnrSeqBin, const char* pTrigName);
int SnrSeq_TryGetFuncNo(const void* pSnrSeqBin, const char* pFuncName);
//}}2017/11/04追加:ラベルテーブルを検索するユーティリティ関数を追加しました。
#endif//USE_SNRSEQ_BIN
#ifdef  __cplusplus
}//extern "C"
#endif//__cplusplus
#endif//__CLIP_SNRS_H__
