/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"
//-----------------------------------------------------------------------------
//警告定義
enum {
	WARN_CAST,			//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
};
static const char* const warn_tokens[]={
	"cast",				//暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。このオプションを指定しなければ、警告表示もエラー終了もしません。
NULL};//終端
int warn;				//各警告(WARN_*)について(warn&(1<<WARN_*))がセットされていたらその警告がOnである事を示します。
/*---------------------------------------------------------------------------*/
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
//言語選択
static char* opt_x = "c";		//"c"=C言語形式,"cs"=C#形式
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
/*---------------------------------------------------------------------------*/
//命令
#define Inst_Max	4194304		//命令登録最大数		//調整可
typedef struct _ST_Inst {
	uint32_t		line;		//行番号				//1パス目で格納する。エラーメッセージのためだけに使用する。
	uint8_t			len;		//命令コード長				//1パス目で格納する。1～7。
	uint8_t			op[7];		//命令コード				//1パス目で格納する。相対位置フィールドは2パス目で設定する。
	union {
	  char*			labName;	//相対位置ラベル名			//相対位置フィールドが有る命令の場合のみ1パス目で格納する。
	  struct _ST_Inst*	labInst;	//相対位置ラベルが指す命令へのポインタ	//2パス目でlabName⇒labInstに変換する。
	} lab;
	char*			trigName;	//トリガ名				//トリガ名が有る命令の場合のみ1パス目で格納する。
    //{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	char*			actName;	//演出名				//演出名が有る命令の場合のみ1パス目で格納する。
    //}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
    //{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	char*			actStr;		//演出文字列				//演出名が有る命令の場合のみ1パス目で格納する。
    //}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	struct _ST_Inst*	redir;		//リダイレクト先の命令へのポインタ	//2パス目で最適化を行い命令を削除した場合に、リダイレクト先の命令へのポインタを格納する。
	uint32_t		addr;		//アドレス				//2パス目で計算する。
} ST_Inst;
static ST_Inst TBL_Inst[Inst_Max + 1];		//命令配列				//'+1'は最後のアドレスを格納するため。
static int nInst;				//命令配列の格納数
//ラベル名
#define Label_Max	1048576		//ラベル登録最大数		//調整可
static char* TBL_Label[Label_Max];		//ラベル配列
static int nLabel;				//ラベル配列の格納数
static int nAtmarkLocalLabel;			//'@'ローカルラベルの出現数		//'def @@'が出現したら、'@%d'という形式の内部ローカルラベルに変換して、ハッシュテーブルに登録する。その後、+1する。要するに、次に出現する'@'ローカルラベルの番号を表していると見なせるので、'goto @F'や'call @F'からは、nAtmarkLocalLabelで参照できる。'goto @B'や'call @B'からは、(nAtmarkLocalLabel-1)で参照できる。
static char* sDotLocalLabelPrefix = "";		//'.'ローカルラベルのプレフィクス	//'def .～'が出現したら、'%s.～'という形式の内部ローカルラベルに変換して、ハッシュテーブルに登録する。グローバルラベルの定義が出現したら、sDotLocalLabelPrefixを更新する。結果として、グローバルラベル定義～グローバルラベル定義の間でのローカルラベルとなる。
//トリガ名
#define Trig_Max	256		//トリガ名登録最大数	"set","clr"も含む	!!!変更不可!!!
static char* TBL_Trig[Trig_Max] = { "set", "clr" };	//トリガ名配列
static int nTrig = 2;					//トリガ名配列の格納数
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//演出名
#define ActName_Max	65536		//演出名登録最大数		//調整可
static char* TBL_ActName[ActName_Max];		//演出名配列
static int nActName;				//演出名配列の格納数
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//演出文字列
#define ActStr_Max	65536		//演出文字列登録最大数		//調整可
static char* TBL_ActStr[ActStr_Max];		//演出文字列配列
static int nActStr;				//演出文字列配列の格納数
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//ラベル名のソート方法
int (*fnLabCmp)(const void*, const void*) = strcmp;	//'n',又は,'nl'オプションが指定されたらstrnatcmpに変更する。
//トリガ名のソート方法
int (*fnTrgCmp)(const void*, const void*) = strcmp;	//'n',又は,'nt'オプションが指定されたらstrnatcmpに変更する。
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//演出名のソート方法
int (*fnActCmp)(const void*, const void*) = strcmp;	//'n',又は,'na'オプションが指定されたらstrnatcmpに変更する。
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//{{進捗表示用
static int opt_q;
static int newMsgLen, oldMsgLen = 0;
//}}進捗表示用
/*---------------------------------------------------------------------------*/
//エラーログ用
static char err_path[_MAX_PATH];	//'-e'オプションが指定されたら、エラーログファイル名を格納する。
static FILE* fp_err;			//最初のエラーログを記録する時に、エラーログファイルを作成する。
//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
typedef struct _ST_Location {
	const char*	path;					//push_location()が呼び出された直前のファイル名
	int		line;					//push_location()が呼び出された直前の行番号
} ST_Location;
static ST_Location TBL_Location[16/*調整可*/];			//ロケーションスタック
static nLocation;						//ロケーションスタックの格納数
static const char* filename = "-";				//現在のファイル名(popen時はコマンド名)。現在の行番号はyylinenoを参照して下さい。
static void init_location(const char* path) {
	nLocation = 0;						//ロケーションスタックを空にする。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。	yylex_init()でも初期化しているのでこの行は無くても構わないのだが、判り易さのためにここでも初期化する事にした。
}
void push_location(const char* path) {
	if(nLocation >= ARRAY_SIZE(TBL_Location)) { DIE(); }	//ロケーションスタックがフルでない事を確認する。
	TBL_Location[nLocation].path = filename;		//現在のファイル名をロケーションスタックに退避する。
	TBL_Location[nLocation].line = yylineno;		//現在の行番号をロケーションスタックに退避する。
	nLocation++;						//ロケーションスタックの格納数を増やす。
	filename = strdup(path);				//現在のファイル名をコピーして保持する。
	yylineno = 1;						//現在の行番号を初期化する。
}
void pop_location() {
	if(!nLocation) { DIE(); }				//ロケーションスタックが空でない事を確認する。
	nLocation--;						//ロケーションスタックの格納数を減らす。
	filename = TBL_Location[nLocation].path;		//現在のファイル名をロケーションスタックから復元する。
	yylineno = TBL_Location[nLocation].line;		//現在の行番号をロケーションスタックから復元する。
}
//}}2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
void yyerror(const char* fmt, ...) {	//←{{2015/03/06:'-e'オプション対応のためにwinapp.cから移動しました。}}
	//'-e'オプションが指定されていて、最初のエラーログを記録する時ならば…
	if(*err_path && !fp_err) {
		//エラーログファイルを作成する。
		fp_err = fopen(err_path, "w");
		if(!fp_err) { error(EXIT_FAILURE, errno, "%s が作成できません。", err_path); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//エラーログファイルの出力ストリームは、バッファリング無しとする。
		// - die()で終了した時(abort()時)に、バッファ分がフラッシュされないようなので、こうしておく必要が有る。
		//   別の案として、この下の処理で毎回fflush()する方法も有るが、バッファリング無しとする方が簡単だろう。
		setbuf(fp_err, NULL);
	}
	//エラーメッセージ(,及び,エラーログ)を出力する。
//{{2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
//	             fprintf(stderr, "### error at line %d : ", yylineno);	//エラーメッセージ
//	if(fp_err) { fprintf(fp_err, "### error at line %d : ", yylineno); }	//エラーログ
//↓2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	             fprintf(stderr, "%s(%d) : ", filename, yylineno);		//エラーメッセージ
	if(fp_err) { fprintf(fp_err, "%s(%d) : ", filename, yylineno); }	//エラーログ
//}}2016/09/24変更:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。
	{
		va_list ap;
		va_start(ap, fmt);
		             vfprintf(stderr, fmt, ap);		//エラーメッセージ
		if(fp_err) { vfprintf(fp_err, fmt, ap); }	//エラーログ
		va_end(ap);
	}
	             fputc('\n', stderr);	//エラーメッセージ
	if(fp_err) { fputc('\n', fp_err); }	//エラーログ
}
/*---------------------------------------------------------------------------*/
/* ローカル関数宣言 */
__declspec(noreturn) static void usage();
static int subr(int argc, char* argv[]);
static ST_Inst* add_inst();
static ST_Inst* getRedir(ST_Inst* pInst);
static void pass2(int bOptimize);
//{{2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//static void output(const char* sBinOut);
//↓2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
static void output(const char* sBinOut, const char* sRegOut);
//}}2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
static void optimize();
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int exitCode;
	SEH_init();
	exitCode = subr(argc, argv);
	return exitCode;
}
/*---------------------------------------------------------------------------*/
static int subr(int argc, char* argv[]) {
	int opt, bOptimize = 1;	//'-d'オプションが指定されなければ最適化を行う。
	const char* sBinOut = NULL;	//'-o'オプションが指定されたらバイナリ出力ファイル名を設定する。
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	const char* sRegOut = NULL;	//'-r'オプションが指定されたらレジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
#if     (YYDEBUG!=0)
//	yydebug = 1; //nonzero means print parse trace
#endif/*(YYDEBUG!=0)*/
	//コマンドラインオプションを処理する。
	opterr = 0; //オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。
	while((opt = getopt(argc, argv, "do:r::en::w::x:qh")) != -1) {
		switch(opt) {
		case 'd':
			bOptimize = 0;		//最適化を行わない。
			break;
		case 'o':
			sBinOut = optarg;	//バイナリ出力ファイル名を設定する。
			break;
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
		case 'r':
			sRegOut = optarg ? optarg : "-";	//レジストリスクリプトのファイル名,又は,"-"(標準出力)を設定する。
			break;
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
		case 'e':
			{
				//エラーログファイル名を作成する。
				// - カレントフォルダに'実行ファイル名.err'という名前で生成する事にした。この動作は、'lk33.exe'の仕様を参考にした。
				char fname[_MAX_FNAME];
				_splitpath(argv[0], NULL, NULL, fname, NULL);
				_makepath(err_path, NULL, NULL, fname, ".err");
				//'-e'オプションが指定されたら、最初に確実に、既存のエラーログファイルを削除しておく。
				//前回に生成したエラーログが残っていて、今回生成したエラーログと混同する危険を避けるためである。
				//尚、'-e'オプションが指定されなかった場合は、既存のエラーログファイルを削除する処理は行わない。
				remove(err_path);
			}
			break;
		case 'n':
			if(optarg) {
				     if(!strcmp(optarg, "l")) { fnLabCmp = strnatcmp; }	//ラベル名のソートにstrnatcmp()を使用する。
				else if(!strcmp(optarg, "t")) { fnTrgCmp = strnatcmp; }	//トリガ名のソートにstrnatcmp()を使用する。
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
				else if(!strcmp(optarg, "a")) { fnActCmp = strnatcmp; }	//演出名のソートにstrnatcmp()を使用する。
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
				else { usage(); }
			} else {
//{{2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//				fnLabCmp = fnTrgCmp = strnatcmp;	//ラベル名とトリガ名のソートにstrnatcmp()を使用する。
//↓2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
				fnLabCmp = fnTrgCmp = fnActCmp = strnatcmp;	//ラベル名とトリガ名と演出名のソートにstrnatcmp()を使用する。
//}}2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
			}
			break;
		case 'w':
			if(optarg) {
				char *optionp = optarg, *valuep;
				while(*optionp) {
					switch(getsubopt(&optionp, (char* const*)warn_tokens, &valuep)) {
					case WARN_CAST:
						if(valuep) { usage(); }
						warn |= (1<<WARN_CAST);
						break;
					default:
						usage();
					}
				}
			} else {
				warn = -1;
			}
			break;
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
		case 'x':
			opt_x = optarg;		//言語選択を設定する。
			if(strcmp(opt_x, "c") && strcmp(opt_x, "cs")) { usage(); }
			break;
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
		case 'q':
			opt_q = 1;
			break;
		case 'h':
		default:
			usage();
		}
	}
	//ラベルを登録するためのハッシュテーブルを作成する。
	hcreate(Label_Max);
	//判り易さのために、命令位置0は使用しないことにした。命令位置0には、ダミーのend命令を置くことにした。
	inst_end();
	//--- 1パス目の処理 ---
	//ファイルが指定されていなければ、標準入力から読み込む。
	if(optind == argc) {
		init_location("-");	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
		yylex_init(stdin);
	    //{{2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
	    //	if((yyparse() != 0) || (yynerrs != 0)) { die("エラーが発生しました。\n"); }
	    //↓2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
		if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	    //}}2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
	//ファイルが指定されていたら、各ファイルを読み込む。
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { error(EXIT_FAILURE, errno, "%s が開けません。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
			init_location(argv[i]);	//{{2016/09/24追加:エラーメッセージに、現在の行番号だけでなく、現在のファイル名も表示するようにしました。}}
			yylex_init(fp);
		    //{{2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
		    //	if((yyparse() != 0) || (yynerrs != 0)) { die("%s: エラーが発生しました。\n", argv[i]); }
		    //↓2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
//{{2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
//			if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
//↓2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
			{
				int save_yynerrs = yynerrs;	//yyparse()がyynerrsをクリアするので、その前に現在のyynerrsを保存しておく。
				if((yyparse() != 0) /*|| (yynerrs != 0)*/) { error(EXIT_FAILURE, 0, "%s: エラーが発生しました。", argv[i]); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
				yynerrs += save_yynerrs;	//今回のyyparse()が0から数えたであろうyynerrsに対して、先ほど保存しておいたyynerrsを足し込む。
			}
//}}2017/03/16変更:複数の入力ファイルを指定した時に、最後以外のファイルでエラーが発生した場合、当ツールの終了ステータスが0になっていたバグを修正しました。
		    //}}2015/02/23修正:致命的なエラーが発生した場合(yyparse()!=0)のみ中断する。セマンティクスエラー(yynerrs!=0)だけならば全てのセマンティクスエラーを報告するために継続する。
			fclose(fp);
		}
	}
	//安全のために、最後にendが無かった場合は、自動的にendを補う事にした。
	if(TBL_Inst[nInst - 1].op[0]) {	//命令位置0にダミーのend命令を置いたので、ここで(nInst>=1)は保証できる。
		inst_end();
	}
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rラベル %d個", nLabel);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
	}
    //}}進捗表示用
	//1パス目の処理でエラーが発生しても、2パス目の処理を行って、全てのセマンティクスエラーを報告する。
	//--- 2パス目の処理 ---
	//2パス目の処理を行う。
	pass2(bOptimize);
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//1パス目,又は,2パス目の処理でエラーが発生していたら、出力処理を行わない。	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	//--- 出力処理 ---
	//出力処理を行う。
//{{2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//	output(sBinOut);
//↓2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	output(sBinOut, sRegOut);
//}}2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	if(yynerrs != 0) { error(EXIT_FAILURE, 0, "エラーが発生しました。"); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
//使い方を表示して終了する。
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dDirSeqC - 演出シーケンサコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2015-2017 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dDirSeqC [option ...] [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -d             最適化を行いません。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、最適化を行います。\n");
	fprintf(stderr, "  -oファイル名   演出制御のバイナリデータを、バイナリ形式でファイルへ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、Cソース形式で標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションの指定に関らず、ラベルは常に標準出力へ出力します。\n");
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	fprintf(stderr, "  -r[ファイル名] 演出文字列を定義するためのレジストリスクリプトを、ファイルへ出力します。\n");
	fprintf(stderr, "                 ファイル名を指定しなければ、標準出力へ出力します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、レジストリスクリプトを出力しません。\n");
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	fprintf(stderr, "  -e             エラーログファイルを生成します。\n");
	fprintf(stderr, "                 このオプションを指定しても、エラーが発生しなければ生成されません。\n");
//{{2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//	fprintf(stderr, "  -n             ラベル名とトリガ名のソートに、strnatcmp()を使用します。\n");
//↓2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	fprintf(stderr, "  -n             ラベル名とトリガ名と演出名のソートに、strnatcmp()を使用します。\n");
//}}2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	fprintf(stderr, "  -nl            ラベル名のソートに、strnatcmp()を使用します。\n");
	fprintf(stderr, "  -nt            トリガ名のソートに、strnatcmp()を使用します。\n");
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	fprintf(stderr, "  -na            演出名のソートに、strnatcmp()を使用します。\n");
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	fprintf(stderr, "                 これらのオプションを指定しなければ、strcmp()を使用します。\n");
	fprintf(stderr, "  -w             全ての警告を有効にします。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、個別に設定した警告のみが有効になります。\n");
	fprintf(stderr, "  -wcast         暗黙の型変換で誤差が生じたら、警告を表示し最終的にエラー終了します。\n");
	fprintf(stderr, "                 このオプションを指定しなければ、警告表示もエラー終了もしません。\n");
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	fprintf(stderr, "  -xc            出力ソースをC言語形式とします。(既定値)\n");
	fprintf(stderr, "  -xcs           出力ソースをC#形式とします。\n");
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	fprintf(stderr, "  -q             進捗表示を抑制します。\n");
	fprintf(stderr, "  -h             使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  ①dDirSeqC.exe DirSeq.txt > DirSeq.c\n");
	fprintf(stderr, "  　DirSeq.txtをコンパイルして、DirSeq.cへ出力します。\n");
	fprintf(stderr, "  ②dDirSeqC.exe -oDirSeq.bin DirSeq.txt > DirSeq.h\n");
	fprintf(stderr, "  　DirSeq.txtをコンパイルして、ラベルをDirSeq.hへ出力します。\n");
	fprintf(stderr, "  　演出制御のバイナリデータを、DirSeq.binへ出力します。\n");
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	fprintf(stderr, "  ③dDirSeqC.exe -oDirSeq.bin -rRegDirSeq.txt DirSeq.txt > DirSeq.h\n");
	fprintf(stderr, "  　DirSeq.txtをコンパイルして、ラベルをDirSeq.hへ出力します。\n");
	fprintf(stderr, "  　演出制御のバイナリデータを、DirSeq.binへ出力します。\n");
	fprintf(stderr, "  　演出文字列のレジストリスクリプトを、RegDirSeq.txtへ出力します。\n");
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
//1パス目の処理
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
int cast_dbl_to_int(double d) {
	int i = d;
	if(i != d) {
		if(warn & (1<<WARN_CAST)) {
			yyerror("切り捨て誤差が発生しました: %f → %d", d, i);
			yynerrs++;	//呼び出し元(main)に知らせるために。
			//全てのセマンティクスエラーを報告するために継続する。
		}
	}
	return i;
}
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -*/
static ST_Inst* add_inst() {
	ST_Inst* pInst;
	if((unsigned)nInst >= Inst_Max) {
		yyerror("命令数が制限を超えました。(%d)", Inst_Max);
		DIE();	//継続しても無駄なので終了する。
	}
	pInst = &TBL_Inst[nInst++];
	pInst->line = yylineno;		//行番号
	return pInst;
}
void inst_def(char* lab) {
	ENTRY item;
    //{{ローカルラベルの処理
	if(lab[0] == '@') {
		if(lab[1] == '@') {	//'@@'
			lab = strdup_printf("@%d", nAtmarkLocalLabel++);	//内部ローカルラベルに変換して、ハッシュテーブルに登録する。その後、+1する。
		} else {		//'@B','@F'
			yyerror("defのラベル定義が不正です。(%s)", lab);
			yynerrs++;	//呼び出し元(main)に知らせるために
			//全てのセマンティクスエラーを報告するために継続する
		}
	} else if(lab[0] == '.') {
		lab = strconcat(sDotLocalLabelPrefix, lab, NULL);		//内部ローカルラベルに変換して、ハッシュテーブルに登録する。
	} else {
		sDotLocalLabelPrefix = lab;					//グローバルラベルの定義が出現したら、sDotLocalLabelPrefixを更新する。
	}
    //}}ローカルラベルの処理
	item.key = lab;
	if(hsearch(item, FIND)) {
		yyerror("defのラベル定義が重複しています。(%s)", lab);
		yynerrs++;	//呼び出し元(main)に知らせるために
		//全てのセマンティクスエラーを報告するために継続する
	}
	item.data = &TBL_Inst[nInst];
	if(!hsearch(item, ENTER)) {
		yyerror("ラベル数が制限を超えました。(%s)", lab);
		DIE();	//継続しても無駄なので終了する。
	}
	if((lab[0] == '@') || strchr(lab, '.')) { return; }			//ローカルラベルは出力しない。
	if(nLabel >= Label_Max) {
		yyerror("ラベル数が制限を超えました。(%s)", lab);
		DIE();	//継続しても無駄なので終了する。
	}
	TBL_Label[nLabel++] = lab;	//hsearch()はイテレーションの機能が無いので、後でイテレートするためにはキー配列を別に作成しておく必要が有る。
    //{{進捗表示用
	if(!opt_q) {
		newMsgLen = fprintf(stderr, "\rラベル %d個目 %s", nLabel, lab);
		if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
	}
    //}}進捗表示用
}
void inst_end() {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = 0x00;//end
}
void inst_repeat(int num) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = 0x01;//repeat
	pInst->op[pInst->len++] = num;
	if(!num || ((unsigned)num >= (unsigned)(1<<8))) {
		yyerror("repeatの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
}
void inst_wait(int num) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = 0x02;//wait
	pInst->op[pInst->len++] = (uint8_t)(num     );
	pInst->op[pInst->len++] = (uint8_t)(num >> 8);
	if(!num || ((unsigned)num >= (unsigned)(1<<16))) {
		yyerror("waitの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
}
void inst_call(char* lab) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = 0x03;//call
	pInst->op[pInst->len++] = 0;//┐
	pInst->op[pInst->len++] = 0;//├2パス目で設定する。
	pInst->op[pInst->len++] = 0;//┘
    //{{ローカルラベルの処理
	if(lab[0] == '@') {
		if(lab[1] == 'B') {		//'@B'
			lab = strdup_printf("@%d", nAtmarkLocalLabel - 1);	//'goto @B'や'call @B'からは、(nAtmarkLocalLabel-1)で参照できる。もし'def @@'が出現する前に@Bを参照した場合は-1になってしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です。
		} else if(lab[1] == 'F') {	//'@F'
			lab = strdup_printf("@%d", nAtmarkLocalLabel);		//'goto @F'や'call @F'からは、nAtmarkLocalLabelで参照できる。もしこの後に'def @@'が出現しなければ存在しない内部ローカルラベルを参照してしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です
		} else {			//'@@'
			yyerror("callのラベル定義が不正です。(%s)", lab);
			yynerrs++;	/* 呼び出し元(main)に知らせるために */
			/* 全てのセマンティクスエラーを報告するために継続する */
		}
	} else if(lab[0] == '.') {
		lab = strconcat(sDotLocalLabelPrefix, lab, NULL);		//内部ローカルラベルに変換する。
	}
    //}}ローカルラベルの処理
	pInst->lab.labName = lab;
}
void inst_act(int flush, int num) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = !flush ? 0x04/*act*/ : 0x05/*act flush*/; 
	pInst->op[pInst->len++] = (uint8_t)(num      );
	pInst->op[pInst->len++] = (uint8_t)(num >>  8);
	pInst->op[pInst->len++] = (uint8_t)(num >> 16);
	if((unsigned)num >= (unsigned)(1<<24)) {
		yyerror("actの引数が範囲外です。(%d)", num);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
}
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
void inst_act_lab(int flush, char* actName) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = !flush ? 0x04/*act*/ : 0x05/*act flush*/; 
	pInst->op[pInst->len++] = 0;//┐
	pInst->op[pInst->len++] = 0;//├2パス目で設定する。
	pInst->op[pInst->len++] = 0;//┘
    //{{演出名の処理
	if((actName[0] == '@') || (actName[0] == '.')) {	//演出名にローカルラベルの書式を使ってはいけない。
		yyerror("actの演出名が不正です。(%s)", actName);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->actName = actName;
	{
		int iActName;
		for(iActName = 0; iActName < nActName; iActName++) {
			if(!strcmp(TBL_ActName[iActName], actName)) { break; }
		}
		if(iActName == nActName) {	//まだ登録されていなければ…
			if(nActName >= ActName_Max) {
				yyerror("演出名の数が制限を超えました。(%s)", actName);
				DIE();	//継続しても無駄なので終了する。
			}
			TBL_ActName[nActName++] = actName;
		}
	}
    //}}演出名の処理
}
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
void inst_act_str(int flush, char* actStr) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = !flush ? 0x04/*act*/ : 0x05/*act flush*/; 
	pInst->op[pInst->len++] = 0;//┐
	pInst->op[pInst->len++] = 0;//├2パス目で設定する。
	pInst->op[pInst->len++] = 0;//┘
    //{{演出文字列の処理
	pInst->actStr = actStr;
	{
		int iActStr;
		for(iActStr = 0; iActStr < nActStr; iActStr++) {
			if(!strcmp(TBL_ActStr[iActStr], actStr)) { break; }
		}
		if(iActStr == nActStr) {	//まだ登録されていなければ…
			if(nActStr >= ActStr_Max) {
				yyerror("演出文字列の数が制限を超えました。(%s)", actStr);
				DIE();	//継続しても無駄なので終了する。
			}
			TBL_ActStr[nActStr++] = actStr;
		}
	}
    //}}演出文字列の処理
}
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
void inst_on(int unlock, char* trig, char* lab, int lock) {
	ST_Inst* pInst = add_inst();
	pInst->op[pInst->len++] = !unlock ? 0x06/*on*/ : 0x07/*on unlock*/; 
	pInst->op[pInst->len++] = 0;//─trig:2パス目で設定する。
	pInst->op[pInst->len++] = 0;//┐
	pInst->op[pInst->len++] = 0;//├dist:2パス目で設定する。
	pInst->op[pInst->len++] = 0;//┘
	pInst->op[pInst->len++] = (uint8_t)(lock     );
	pInst->op[pInst->len++] = (uint8_t)(lock >> 8);
    //{{トリガ名の処理
	if((trig[0] == '@') || (trig[0] == '.')) {	//トリガ名にローカルラベルの書式を使ってはいけない。
		yyerror("onのトリガ名が不正です。(%s)", trig);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	pInst->trigName = trig;
	{
		int iTrig;
		for(iTrig = 0; iTrig < nTrig; iTrig++) {
			if(!strcmp(TBL_Trig[iTrig], trig)) { break; }
		}
		if(iTrig == nTrig) {	//まだ登録されていなければ…
			if(nTrig >= Trig_Max) {
				yyerror("トリガ名の数が制限を超えました。(%s)", trig);
				DIE();	//継続しても無駄なので終了する。
			}
			TBL_Trig[nTrig++] = trig;
		}
	}
    //}}トリガ名の処理
    //{{ローカルラベルの処理
	if(lab[0] == '@') {
		if(lab[1] == 'B') {		//'@B'
			lab = strdup_printf("@%d", nAtmarkLocalLabel - 1);	//'goto @B'や'call @B'からは、(nAtmarkLocalLabel-1)で参照できる。もし'def @@'が出現する前に@Bを参照した場合は-1になってしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です。
		} else if(lab[1] == 'F') {	//'@F'
			lab = strdup_printf("@%d", nAtmarkLocalLabel);		//'goto @F'や'call @F'からは、nAtmarkLocalLabelで参照できる。もしこの後に'def @@'が出現しなければ存在しない内部ローカルラベルを参照してしまうが、2パス目に未定義ラベルとしてエラー検出できるはずであり、安全です
		} else {			//'@@'
			yyerror("gotoのラベル定義が不正です。(%s)", lab);
			yynerrs++;	/* 呼び出し元(main)に知らせるために */
			/* 全てのセマンティクスエラーを報告するために継続する */
		}
	} else if(lab[0] == '.') {
		lab = strconcat(sDotLocalLabelPrefix, lab, NULL);		//内部ローカルラベルに変換する。
	}
    //}}ローカルラベルの処理
	pInst->lab.labName = lab;
	if((unsigned)lock >= (unsigned)(1<<16)) {
		yyerror("lockの引数が範囲外です。(%d)", lock);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
}
/*---------------------------------------------------------------------------*/
//NULLが指定された場合、NULLを返す。
//NULL以外が指定された場合、リダイレクト先の命令へのポインタをたどって、末端のポインタを返す。
static ST_Inst* getRedir(ST_Inst* pInst) {
	if(pInst) {
		while(pInst->redir) {
			pInst = pInst->redir;
		}
	}
	return pInst;
}
/*---------------------------------------------------------------------------*/
//2パス目の処理
static int Trig_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	if((!strcmp(x, "set")) && (strcmp(y, "set"))) { return -1; }	//x="set",y="set"以外 ⇒ x<y	┐
	if((strcmp(x, "set")) && (!strcmp(y, "set"))) { return  1; }	//x="set"以外,y="set" ⇒ x>y	├"set"をTBL_Trig[0],"clr"をTBL_Trig[nTrig-1]にソートするための特例処理。
	if((!strcmp(x, "clr")) && (strcmp(y, "clr"))) { return  1; }	//x="clr",y="clr"以外 ⇒ x>y	│
	if((strcmp(x, "clr")) && (!strcmp(y, "clr"))) { return -1; }	//x="clr"以外,y="clr" ⇒ x<y	┘
	return (*fnTrgCmp)(x, y);
}
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
static int ActName_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	return (*fnActCmp)(x, y);
}
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
static void pass2(int bOptimize) {
	int iInst, addr, dist;
	ST_Inst *pInst, *labInst;
	ENTRY item, *pEntry;
    //{{トリガ名の処理
	//トリガ名を名前の昇順にソートする。
	qsort(TBL_Trig, nTrig, sizeof(char*), Trig_compar);
	//全ての命令のトリガ名を、トリガ番号に変換する。
	for(iInst = 0; iInst < nInst; iInst++) {
		pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		//yyerrorのために
		//トリガ名が有れば…
		if(pInst->trigName) {
			int iTrig;
			for(iTrig = 0; iTrig < nTrig; iTrig++) {
				if(!strcmp(TBL_Trig[iTrig], pInst->trigName)) { break; }
			}
			if(iTrig == nTrig) { DIE(); }	//バグ
			if(iTrig == (nTrig - 1)) { iTrig = 255; }	//"clr"を255番にする特例処理。尚、"set"は0番になっているはずなので特例処理は不要です。
			switch(pInst->op[0]) {
			case 0x06://on
			case 0x07://on unlock
				pInst->op[1] = iTrig;
				break;
			default:DIE();
			}
		}
	}
    //}}トリガ名の処理
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
    //{{演出名の処理
	//演出名を名前の昇順にソートする。
	qsort(TBL_ActName, nActName, sizeof(char*), ActName_compar);
	//全ての命令の演出名を、演出番号に変換する。
	for(iInst = 0; iInst < nInst; iInst++) {
		pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		//yyerrorのために
		//演出名が有れば…
		if(pInst->actName) {
			int iActName;
			for(iActName = 0; iActName < nActName; iActName++) {
				if(!strcmp(TBL_ActName[iActName], pInst->actName)) { break; }
			}
			if(iActName == nActName) { DIE(); }	//バグ
			iActName++;	//演出名に対する番号は1ベース
			switch(pInst->op[0]) {
			case 0x04:/*act*/
			case 0x05:/*act flush*/
				pInst->op[1] = (uint8_t)(iActName      );
				pInst->op[2] = (uint8_t)(iActName >>  8);
				pInst->op[3] = (uint8_t)(iActName >> 16);
				break;
			default:DIE();
			}
		}
	}
    //}}演出名の処理
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
    //{{演出文字列の処理
	//演出文字列を文字列の昇順にソートする。
	qsort(TBL_ActStr, nActStr, sizeof(char*), ActName_compar);	//演出文字列のソート方法は、演出名のソート方法と同じにする事にしました。実際の所、演出文字列の番号順に依存するようなアプリケーションを作る事は無いと思うので、ソートする必要も無いと思うのですが、一応、演出名のソート方法と同じ方法でソートしておく事にしました。
	//全ての命令の演出文字列を、演出番号に変換する。
	for(iInst = 0; iInst < nInst; iInst++) {
		pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		//yyerrorのために
		//演出文字列が有れば…
		if(pInst->actStr) {
			int iActStr;
			for(iActStr = 0; iActStr < nActStr; iActStr++) {
				if(!strcmp(TBL_ActStr[iActStr], pInst->actStr)) { break; }
			}
			if(iActStr == nActStr) { DIE(); }	//バグ
			iActStr += (nActName + 1);	//演出文字列に対する番号は、演出名に対する番号の続きから割り当てる。演出名に対する番号は1～nActNameなので、演出文字列に対する番号は(nActName+1)～(nActName+nActName)となる。
			switch(pInst->op[0]) {
			case 0x04:/*act*/
			case 0x05:/*act flush*/
				pInst->op[1] = (uint8_t)(iActStr      );
				pInst->op[2] = (uint8_t)(iActStr >>  8);
				pInst->op[3] = (uint8_t)(iActStr >> 16);
				break;
			default:DIE();
			}
		}
	}
    //}}演出文字列の処理
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	//全ての命令の相対位置ラベル名を、相対位置ラベルが指す命令へのポインタに変換する。
	for(iInst = 0; iInst < nInst; iInst++) {
		pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		//yyerrorのために
		//相対位置ラベル名が有れば…
		if(pInst->lab.labName) {
			//ラベルが定義されていたら…
			item.key = pInst->lab.labName;
			if((pEntry = hsearch(item, FIND))) {
				//相対位置ラベルが指す命令へのポインタを格納する。
				pInst->lab.labInst = pEntry->data;
			//ラベルが定義されていなければ…
			} else {
				yyerror("ラベルが未定義です。(%s)", pInst->lab);
				yynerrs++;	//呼び出し元(main)に知らせるために
				//全てのセマンティクスエラーを報告するために継続する
				pInst->lab.labInst = &TBL_Inst[0];	//この後の処理が継続できるように、ダミーとして命令位置0を指しておく。
			}
		}
	}
	//最適化を行う。
	if(bOptimize) { optimize(); }
	//アドレスを計算する。
	addr = 0;
	for(iInst = 0; iInst < nInst + 1; iInst++) {	//'+1'は最後のアドレスを格納するため。
		pInst = &TBL_Inst[iInst];
		pInst->addr = addr;
		addr += pInst->len;	//最適化によって削除された命令ならば(pInst->len==0)になっているのでaddrは進まない。
	}
	//相対位置フィールドの設定を行う。
	for(iInst = 0; iInst < nInst; iInst++) {
		pInst = &TBL_Inst[iInst];
		yylineno = pInst->line;		//yyerrorのために
		//最適化によって削除された命令でなければ…
		if(pInst->len) {
			//相対位置ラベルが指す命令へのポインタが有れば…
			if(pInst->lab.labInst) {
				labInst = getRedir(pInst->lab.labInst);
				dist = labInst->addr - (pInst + 1)->addr;	//'+1'は次の命令の位置を基準とするため。
				switch(pInst->op[0]) {
				case 0x03://call
					pInst->op[1] = (uint8_t)(dist      );
					pInst->op[2] = (uint8_t)(dist >>  8);
					pInst->op[3] = (uint8_t)(dist >> 16);
					break;
				case 0x06://on
				case 0x07://on unlock
					pInst->op[2] = (uint8_t)(dist      );
					pInst->op[3] = (uint8_t)(dist >>  8);
					pInst->op[4] = (uint8_t)(dist >> 16);
					break;
				default:DIE();
				}
				if(((int)dist <= (int)(-1<<(24-1))) ||
				   ((int)dist >= (int)( 1<<(24-1)))) {
					yyerror("相対位置が範囲外です。(%s)", dist);
					yynerrs++;	//呼び出し元(main)に知らせるために
					//全てのセマンティクスエラーを報告するために継続する
				}
			}
		}
	}
}
/*---------------------------------------------------------------------------*/
//出力処理
static int Label_compar(const void* _x, const void* _y) {
	char* x = *(char**)_x;
	char* y = *(char**)_y;
	return (*fnLabCmp)(x, y);
}
//{{2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//static void output(const char* sBinOut) {
//↓2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
static void output(const char* sBinOut, const char* sRegOut) {
//}}2017/03/02変更:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
//{{2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
//	int iInst, iLabel, iTrig, i;
//↓2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	int iInst, iLabel, iTrig, iActName, i;
//}}2016/09/10変更:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	ST_Inst *pInst, *labInst;
	ENTRY item, *pEntry;
//{{2016/09/27追加:演出シーケンスのラベルを、標準出力のラベル定義,及び,アドレステーブルに出力しないようにしました。
	//TBL_Label[0～(nLabel-1)]から、演出シーケンスのラベルを削除する。
	{
		int oLabel = 0;	//この変数に、演出シーケンスのラベルではない(=演出テーブルのラベルである)と判断したラベルの数を数える。
		//全てのラベルについて…
		for(iLabel = 0; iLabel < nLabel; iLabel++) {
			char* label = TBL_Label[iLabel];
			//このラベルが、演出テーブルのラベルであるかを判断する。
			// - このラベルが指すアドレスに有る命令が、'on',又は'on unlock'であるラベルが、演出テーブルのラベルであると見なす。
			//   それ以外の命令を指すラベルは、明らかに、演出シーケンスのラベルである。
			// - 尚、厳密には、'end'を指すラベルは、演出テーブルのラベル,又は,演出シーケンスのラベルの、どちらの可能性も有るのだが、演出シーケンスのラベルと見なす事にした。
			//   演出テーブルのラベルが'end'を指す可能性としては、空の演出テーブルが定義された場合のみであり、そのような演出テーブルは定義しない方が望ましいので、万一、そのような演出テーブルが定義された場合にもシンボルを出力せずに、使用側のプログラムでシンボル未定義エラーとして検出する方が安全だと思うからです。
			//   演出シーケンスのラベルが'end'を指す可能性としては、演出テーブルのトリガが(例えばunlockの効果のみを期待して)何もしない空の演出シーケンスへgotoした場合に、その演出シーケンスのラベルは'end'を指すラベルとなるので、実際に有り得ます。
			item.key = label;
			if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
			labInst = getRedir(pEntry->data);
			//演出テーブルのラベルならば…
			if((labInst->op[0] == 0x06) ||	//on
			   (labInst->op[0] == 0x07)) {	//on unlock
				//TBL_Label[]に、演出テーブルのラベルだけを、前詰めで格納する。
				TBL_Label[oLabel++] = label;
			}
		}
		//TBL_Label[0～(nLabel-1)]を、演出テーブルのラベルだけの配列とする。
		nLabel = oLabel;
	}
//}}2016/09/27追加:演出シーケンスのラベルを、標準出力のラベル定義,及び,アドレステーブルに出力しないようにしました。
	//ラベル定義を名前の昇順にソートする。
	qsort(TBL_Label, nLabel, sizeof(char*), Label_compar);
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	//C#形式
	if(!strcmp(opt_x, "cs")) {
		puts("static partial class Const {");
	}
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	//--- ラベルを標準出力に出力する ---
//{{2017/03/01変更:'-xc','-xcs'オプションを追加しました。
//	for(iLabel = 0; iLabel < nLabel; iLabel++) {				//名前の昇順	ラベル=0(ダミー)は定義しない。
//		char* label = TBL_Label[iLabel];
//		printf("#define DirTblNo_%s %d\n", label, iLabel + 1);		//ラベルは1ベース
//	}
//	printf("#define DirTblNo_Max %d\n", nLabel);				//最大値
//↓2017/03/01変更:'-xc','-xcs'オプションを追加しました。
	//C言語形式
	if(!strcmp(opt_x, "c")) {
		for(iLabel = 0; iLabel < nLabel; iLabel++) {				//名前の昇順	ラベル=0(ダミー)は定義しない。
			char* label = TBL_Label[iLabel];
			printf("#define DirTblNo_%s %d\n", label, iLabel + 1);		//ラベルは1ベース
		}
		printf("#define DirTblNo_Max %d\n", nLabel);				//最大値
	//C#形式
	} else if(!strcmp(opt_x, "cs")) {
		for(iLabel = 0; iLabel < nLabel; iLabel++) {					//名前の昇順	ラベル=0(ダミー)は定義しない。
			char* label = TBL_Label[iLabel];
			printf("public const int DirTblNo_%s = %d;\n", label, iLabel + 1);	//ラベルは1ベース
		}
		printf("public const int DirTblNo_Max = %d;\n", nLabel);			//最大値
	} else {
		DIE();	//バグ
	}
//}}2017/03/01変更:'-xc','-xcs'オプションを追加しました。
    //{{トリガ名の処理
	//--- トリガ名を標準出力に出力する ---
//{{2017/03/01変更:'-xc','-xcs'オプションを追加しました。
//	for(iTrig = 1; iTrig < (nTrig - 1); iTrig++) {				//名前の昇順	"set"(clipdirs.cの仕様で0番に固定),"clr"(clipdirs.cの仕様で255番に固定)は出力しない。
//		char* trig = TBL_Trig[iTrig];
//		printf("#define DirTrg_%s %d\n", trig, iTrig);
//	}
//	if(nTrig > 2) { printf("#define DirTrg_Max %d\n", nTrig - 2); }		//"set"と"clr"以外のトリガ名が定義されている場合のみ最大値を出力する。
//↓2017/03/01変更:'-xc','-xcs'オプションを追加しました。
	//C言語形式
	if(!strcmp(opt_x, "c")) {
		for(iTrig = 1; iTrig < (nTrig - 1); iTrig++) {				//名前の昇順	"set"(clipdirs.cの仕様で0番に固定),"clr"(clipdirs.cの仕様で255番に固定)は出力しない。
			char* trig = TBL_Trig[iTrig];
			printf("#define DirTrg_%s %d\n", trig, iTrig);
		}
		if(nTrig > 2) { printf("#define DirTrg_Max %d\n", nTrig - 2); }		//"set"と"clr"以外のトリガ名が定義されている場合のみ最大値を出力する。
	//C#形式
	} else if(!strcmp(opt_x, "cs")) {
		for(iTrig = 1; iTrig < (nTrig - 1); iTrig++) {					//名前の昇順	"set"(clipdirs.cの仕様で0番に固定),"clr"(clipdirs.cの仕様で255番に固定)は出力しない。
			char* trig = TBL_Trig[iTrig];
			printf("public const int DirTrg_%s = %d;\n", trig, iTrig);
		}
		if(nTrig > 2) { printf("public const int DirTrg_Max = %d;\n", nTrig - 2); }	//"set"と"clr"以外のトリガ名が定義されている場合のみ最大値を出力する。
	} else {
		DIE();	//バグ
	}
//}}2017/03/01変更:'-xc','-xcs'オプションを追加しました。
    //}}トリガ名の処理
//{{2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
    //{{演出名の処理
	//--- 演出名を標準出力に出力する ---
//{{2017/03/01変更:'-xc','-xcs'オプションを追加しました。
//	for(iActName = 0; iActName < nActName; iActName++) {			//名前の昇順	演出名=0(ダミー)は定義しない。
//		char* actName = TBL_ActName[iActName];
//		printf("#define DirAct_%s %d\n", actName, iActName + 1);	//演出名に対する番号は1ベース
//	}
//	if(nActName) { printf("#define DirAct_Max %d\n", nActName); }		//演出名指定による'act'が使用された場合のみ、演出名に対する番号の最大値を出力する。
//↓2017/03/01変更:'-xc','-xcs'オプションを追加しました。
	//C言語形式
	if(!strcmp(opt_x, "c")) {
		for(iActName = 0; iActName < nActName; iActName++) {			//名前の昇順	演出名=0(ダミー)は定義しない。
			char* actName = TBL_ActName[iActName];
			printf("#define DirAct_%s %d\n", actName, iActName + 1);	//演出名に対する番号は1ベース
		}
		if(nActName) { printf("#define DirAct_Max %d\n", nActName); }		//演出名指定による'act'が使用された場合のみ、演出名に対する番号の最大値を出力する。
	//C#形式
	} else if(!strcmp(opt_x, "cs")) {
		for(iActName = 0; iActName < nActName; iActName++) {				//名前の昇順	演出名=0(ダミー)は定義しない。
			char* actName = TBL_ActName[iActName];
			printf("public const int DirAct_%s = %d;\n", actName, iActName + 1);	//演出名に対する番号は1ベース
		}
		if(nActName) { printf("public const int DirAct_Max = %d;\n", nActName); }	//演出名指定による'act'が使用された場合のみ、演出名に対する番号の最大値を出力する。
	} else {
		DIE();	//バグ
	}
//}}2017/03/01変更:'-xc','-xcs'オプションを追加しました。
    //}}演出名の処理
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	//C#形式
	if(!strcmp(opt_x, "cs")) {
		puts("}");
	}
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
//}}2016/09/10追加:actの引数として、演出番号の直接指定だけでなく、演出名も指定出来るようにしました。演出名に対する番号は、当ツールが自動的に割り振ります。
	//--- データを出力する ---
	//バイナリ形式の場合…
	if(sBinOut) {
		FILE* fp = fopen(sBinOut, "wb");
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成できません。", sBinOut); }	//{{2016/09/21変更:die(～)で終了していた箇所を、error(EXIT_FAILURE,～)で終了するように変更しました。}}	//{{2016/09/28変更:error()の文字列の最後に余計な改行が付いていたのを削除しました。}}
		//アドレステーブルを出力する。
		for(iLabel = -1; iLabel < nLabel; iLabel++) {
			int addr;
			if(iLabel < 0) {
    //{{2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
    //				addr = 0;					//ラベル番号=0(ダミー)
    //↓2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
				addr = nLabel + 1;				//TBL_DirTblNo[0]=(DirTblNo_Max+1)	ラベルは1ベースなのでnLabelではなく(nLabel+1)とする事に注意せよ。
    //}}2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
			} else {
				char* label = TBL_Label[iLabel];
				item.key = label;
				if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
				labInst = getRedir(pEntry->data);
				addr = labInst->addr;
			}
			fwrite(&addr, sizeof(int), 1, fp);
		}
		//命令コードの出力を行う。
		for(iInst = 0; iInst < nInst; iInst++) {
			pInst = &TBL_Inst[iInst];
			for(i = 0; i < pInst->len; i++) {	//最適化によって削除された命令ならば、(pInst->len=0)になっているので、このループ内の処理を行わない。
				int data = pInst->op[i];
				fwrite(&data, sizeof(unsigned char), 1, fp);
			}
		}
		fclose(fp);
	//テキスト形式の場合…
	} else {
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
		//C#形式
		if(!strcmp(opt_x, "cs")) {
			puts("static partial class Const {");
		}
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
		//アドレステーブルを出力する。
//{{2017/03/01変更:'-xc','-xcs'オプションを追加しました。
//		printf("const int TBL_DirTblNo[%d]={", nLabel + 1);
//↓2017/03/01変更:'-xc','-xcs'オプションを追加しました。
		//C言語形式
		if(!strcmp(opt_x, "c")) {
			printf("const int TBL_DirTblNo[%d]={", nLabel + 1);
		//C#形式
		} else if(!strcmp(opt_x, "cs")) {
			printf("public static readonly int[/*%d*/] TBL_DirTblNo={", nLabel + 1);
		} else {
			DIE();	//バグ
		}
//}}2017/03/01変更:'-xc','-xcs'オプションを追加しました。
		for(iLabel = -1; iLabel < nLabel; iLabel++) {
			int addr;
			if(iLabel < 0) {
    //{{2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
    //				addr = 0;					//ラベル番号=0(ダミー)
    //↓2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
				addr = nLabel + 1;				//TBL_DirTblNo[0]=(DirTblNo_Max+1)	ラベルは1ベースなのでnLabelではなく(nLabel+1)とする事に注意せよ。
    //}}2015/03/02変更:TBL_DirTblNo[0]に(DirTblNo_Max+1)を格納するようにしました。
			} else {
				char* label = TBL_Label[iLabel];
				item.key = label;
				if(!(pEntry = hsearch(item, FIND))) { DIE(); }	//バグ
				labInst = getRedir(pEntry->data);
				addr = labInst->addr;
			}
			if((iLabel & 15) == 15)    { putchar('\n'); }
			printf("0x%08X", addr);
			if(iLabel != (nLabel - 1)) { putchar(',' ); }
		}
		puts("};");
		//命令コードの出力を行う。
//{{2017/03/01変更:'-xc','-xcs'オプションを追加しました。
//		printf("const unsigned char TBL_DirSeq[%d]={", TBL_Inst[nInst].addr);
//↓2017/03/01変更:'-xc','-xcs'オプションを追加しました。
		//C言語形式
		if(!strcmp(opt_x, "c")) {
			printf("const unsigned char TBL_DirSeq[%d]={", TBL_Inst[nInst].addr);
		//C#形式
		} else if(!strcmp(opt_x, "cs")) {
			printf("public static readonly byte[/*%d*/] TBL_DirSeq={", TBL_Inst[nInst].addr);
		} else {
			DIE();	//バグ
		}
//}}2017/03/01変更:'-xc','-xcs'オプションを追加しました。
		for(iInst = 0; iInst < nInst; iInst++) {
			pInst = &TBL_Inst[iInst];
			for(i = 0; i < pInst->len; i++) {	//最適化によって削除された命令ならば、(pInst->len=0)になっているので、このループ内の処理を行わない。
				int data = pInst->op[i];
				if(   pInst->addr + i       ) { putchar(',' ); }
				if(!((pInst->addr + i) & 31)) { putchar('\n'); }
				printf("0x%02X", data);
			}
		}
		puts("};");
//{{2017/03/01追加:'-xc','-xcs'オプションを追加しました。
		//C#形式
		if(!strcmp(opt_x, "cs")) {
			puts("}");
		}
//}}2017/03/01追加:'-xc','-xcs'オプションを追加しました。
	}
//{{2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
	//レジストリスクリプトのファイル名,又は,"-"(標準出力)が指定されていたら…
	if(sRegOut) {
		FILE* fp = strcmp(sRegOut, "-") ? fopen(sRegOut, "w") : stdout;
		if(!fp) { error(EXIT_FAILURE, errno, "%s が作成出来ません。", sRegOut); }
		{
			int iActStr;
			fprintf(fp, "DirAct\n");
			//各演出文字列について…
			for(iActStr = 0; iActStr < nActStr; iActStr++) {
				char* actStr = TBL_ActStr[iActStr];
				fprintf(fp, "\t%d\t= \"%s\"\n",
					(nActName + 1) + iActStr,	//演出文字列に対する番号は(nActName+1)～(nActName+nActName)です。詳細は、pass2()内の処理を参照して下さい。
					TBL_ActStr[iActStr]);
			}
			fprintf(fp, "end\n");
		}
		if(fp != stdout) { fclose(fp); }
	//レジストリスクリプトのファイル名,又は,"-"(標準出力)が指定されていなければ…
	} else {
		//演出文字列が使用されていたら…
		if(nActStr) {
			fprintf(stderr, "演出文字列が使用されていますが、-rオプションが指定されなかったので、演出文字列の出力を行いませんでした。\n");
		}
	}
//}}2017/03/02追加:actの引数として、演出文字列も指定出来るようにしました。演出番号は当ツールが自動的に割り振り、演出番号と演出文字列の対応はレジストリスクリプトとして出力します。
}
/*===========================================================================*
 * 最適化
 *===========================================================================*/
#define Block_Max	(Inst_Max/4)	//ブロック最大数		//調整可
typedef struct _ST_Block {
	int	bDel;		//このブロックを削除したら1にする。
	int	iInst;		//命令配列の何要素目からこのブロックが開始するか
	int	nInst;		//このブロックの命令数
} ST_Block;
ST_Block TBL_Block[Block_Max];	//ブロック配列
int nBlock;			//ブロック配列の格納数
/*---------------------------------------------------------------------------*/
static void optimize() {
	//--- ブロック分割 ---
	{
		//ブロックとは、「先頭,又は,endの次」～(repeat～endのネスト)～「end」の区間である。
		//要するに、一つの演出テーブル,又は,一つの演出シーケンスの事である。
		int iInst, nRepeat = 0;
		for(iInst = 0; iInst < nInst; iInst++) {
			ST_Inst* pInst = &TBL_Inst[iInst];
			switch(pInst->op[0]) {
			case 0x00://end
				//Repeat中ならば…
				if(nRepeat) {
					//Repeatの深さを減らす。
					nRepeat--;
				//Repeat中でなければ…
				} else {
					//現在のブロックを終了する。
					TBL_Block[nBlock].nInst = (iInst + 1) - TBL_Block[nBlock].iInst;	//現在のブロックの命令数を決定する。
					if(++nBlock >= Block_Max) { DIE(); }	//Block_Maxを増やせ
					TBL_Block[nBlock].iInst = (iInst + 1);					//次のブロックの開始インデクスを格納しておく。(最後はダミー)
				}
				break;
			case 0x01://repeat
				//Repeatの深さを増やす。
				nRepeat++;
				break;
			}
		}
		//ブロックを終了した直後以外で命令配列の末尾に到達したら…
		// - 元のスクリプトが間違っていた場合(たとえばendが不足していたりrepeat～endの対応が間違っている場合)、正常にブロック分割できず、最適化を行えない。
		//   正常にブロック分割できないと判断した場合は、最適化を行わないだけでなく、エラーとしてコンパイルを中断する事にした。
		//   必ずしもエラーとしなくても、コンパイルを行ってコードを生成する事は可能だが、実行時に終端を突き抜けて異常動作する可能性が高く、コンパイルエラーで止めておく方が安全だと思ったからです。
		// - どうしてもそのようなスクリプトをコンパイルしたい場合は、'-d'オプションを指定して最適化をオフにしてください。
		if(TBL_Block[nBlock].iInst != nInst) {
			yyerror("ブロックが閉じていません。endが不足しているかrepeat～endの対応が間違っている可能性が有ります。");
			yynerrs++;	//呼び出し元(main)に知らせるために
			return;		//ここまで
		}
	}
	//--- 最適化 ---
	{
		int nOpt, iTry = 0;
		do {
			int iBlock1, iBlock2, i;
			nOpt = 0;	//最適化を行った箇所の数をリセットする。
			iTry++;		//最適化の試行回数を進める。
			for(iBlock1 = 0; iBlock1 < nBlock - 1; iBlock1++) {						//比較元の各ブロックについて…
				ST_Block* pBlock1 = &TBL_Block[iBlock1];
				if(pBlock1->bDel) { continue; }								//比較元のブロックが削除済みならば、この比較元をスキップする。
			    //{{進捗表示用
				if(!opt_q) {
					newMsgLen = fprintf(stderr, "\r最適化 %d回目 %d箇所 %d/%d", iTry, nOpt, iBlock1, nBlock - 1);
					if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } oldMsgLen = newMsgLen;
				}
			    //}}進捗表示用
				for(iBlock2 = iBlock1 + 1; iBlock2 < nBlock; iBlock2++) {				//比較先の各ブロックについて…
					ST_Block* pBlock2 = &TBL_Block[iBlock2];
					if(pBlock2->bDel) { continue; }							//比較先のブロックが削除済みならば、この比較元をスキップする。
					//比較元のブロックと比較先のブロックを比較する処理。
					if(pBlock1->nInst != pBlock2->nInst) { goto L_NEXT; }				//命令数が違うならば、不一致。
					for(i = 0; i < pBlock1->nInst; i++) {						//各命令について…
						ST_Inst* pInst1 = &TBL_Inst[pBlock1->iInst + i];
						ST_Inst* pInst2 = &TBL_Inst[pBlock2->iInst + i];
						if(pInst1->len != pInst2->len) { goto L_NEXT; }				//命令長が違うならば、不一致。
						if(memcmp(pInst1->op, pInst2->op, pInst1->len)) { goto L_NEXT; }	//バイト値が違うならば、不一致。	相対位置フィールドはまだ0のままで比較する。
						if(getRedir(pInst1->lab.labInst) != getRedir(pInst2->lab.labInst)) { goto L_NEXT; }	//相対位置ラベルが指す命令へのポインタが違うならば、不一致。		getRedir()にはNULLを渡しても安全。両方ともNULLの場合も一致と見なす。
					}
					//ブロックが一致したので、比較先のブロックを、比較元のブロックにリダイレクトする。
					for(i = 0; i < pBlock1->nInst; i++) {					//各命令について…
						ST_Inst* pInst1 = &TBL_Inst[pBlock1->iInst + i];
						ST_Inst* pInst2 = &TBL_Inst[pBlock2->iInst + i];
						pInst2->redir = pInst1;	//比較先の命令を比較元の命令へリダイレクトする。
						pInst2->len = 0;	//比較先の命令を削除する。
					}
					pBlock2->bDel = 1;		//比較先のブロックを削除する。
					nOpt++;				//最適化を行った箇所の数を増やす。
				L_NEXT:	;
				}
			}
		    //{{進捗表示用
			if(!opt_q) {
				newMsgLen = fprintf(stderr, "\r最適化 %d回目 %d箇所 %d/%d", iTry, nOpt, iBlock1, nBlock - 1);
				if(oldMsgLen > newMsgLen) { fprintf(stderr, "%*s", oldMsgLen - newMsgLen, ""); } putc('\n', stderr); oldMsgLen = 0;
			}
		    //}}進捗表示用
		} while(nOpt);		//一箇所でも最適化を行ったら、それによって別の最適化が可能になった可能性が有るので、繰り替えす。
	}
}
