// * Mon Dec 01 13:44:05 JST 2014 Naoyuki Sawa
// - 1st リリース。
//   「/clip/tool/dRudSeqS」を元にして作成しました。
// * Fri Dec 12 21:12:36 JST 2014 Naoyuki Sawa
// - 条件分岐'if on','if off'を追加したことに伴い、シーケンサの命令セット,命令コード,命令コード定義用マクロを変更しました。
#include <clip.h>
/*---------------------------------------------------------------------------*/
#define VERSION "20141212"	/* バージョン定義 */
/*---------------------------------------------------------------------------*/
int iDataLen;
uint16_t TBL_Data[1000000/*調整可*/];
/*---------------------------------------------------------------------------*/
void LoadXml(const char* pFileName);
void loadData(TiXmlElement* pRoot);
void simMoveDirect(uint32_t iInst);
/*---------------------------------------------------------------------------*/
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dRudSeqD - パルスモーターの台形制御シーケンサ逆コンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2014 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dRudSeqD filename.xml\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -h     使い方を表示します。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "EXAMPLE:\n");
	fprintf(stderr, "  dRudSeqD sample.xml\n");
	fprintf(stderr, "  sample.xmlを読込み、逆コンパイルを実行し、標準出力へ出力します。\n");
	fprintf(stderr, "  入力ファイル形式は、dRudSeqSと同じです。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "eXtension:\n");
	fprintf(stderr, "  プロジェクト固有の拡張として、\n");
	fprintf(stderr, "  dRudSeqD -xデータ実行インデクス filename.xml\n");
	fprintf(stderr, "  直接制御のシミュレーションを実行します。\n");
	exit(EXIT_FAILURE);
}
/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt, optX = -1;
	/* コマンドラインオプションを処理する。 */
	opterr = 0; /* オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。 */
	while((opt = getopt(argc, argv, "hx:")) != -1) {
		switch(opt) {
		default:
		case 'h': usage();
		case 'x': optX = atoi(optarg); break;
		}
	}
	if(optind != (argc - 1)) { usage(); }
	/* XMLファイルを読み込む。 */
	LoadXml(argv[optind]);
	/* '-x'オプションが指定されていなければ… */
	if(optX == -1) {
		/* 逆コンパイルを実行する。 */
		RudSeq_Decompile(TBL_Data, iDataLen);
	/* '-x'オプションが指定されていたら… */
	} else {
		if((unsigned)optX >= (unsigned)iDataLen) { die("-xオプションの引数が不正です。"); }
		/* 直接制御のシミュレーションを実行する。 */
		simMoveDirect(optX);
	}
	return EXIT_SUCCESS;
}
/*---------------------------------------------------------------------------*/
void LoadXml(const char* pFileName) {
	TiXmlDocument* pDoc = TiXmlDocument_new(NULL);
	TiXmlDocument_LoadFile(pDoc, pFileName);
	{
		TiXmlElement* pRoot = TiXmlNode_FirstChildElement(TiXmlDocument_super(pDoc), "Root");
		if(!pRoot) { DIE(); }
		loadData(pRoot);
	}
}
/*---------------------------------------------------------------------------*/
void loadData(TiXmlElement* pRoot) {
	TiXmlElement* pData = TiXmlNode_FirstChildElement(TiXmlElement_super(pRoot), "Data");
	if(!pData) { DIE(); }
	{
		TiXmlNode* pNode = TiXmlNode_FirstChild(TiXmlElement_super(pData), NULL);
		if(!pNode) { DIE(); }
		{
			TiXmlText* pText = TiXmlNode_ToText(pNode);
			if(!pText) { DIE(); }
			{
				char* p = strtok(strdup(TiXmlNode_Value(TiXmlText_super(pText))), ", ");
				while(p) {
					if(iDataLen >= ARRAY_SIZE(TBL_Data)) { DIE(); }
					TBL_Data[iDataLen++] = strtol(p, &p, 0);
					if(*p) { DIE(); }
					p = strtok(NULL, ", ");
				}
			}
		}
	}
}
/*===========================================================================*
 *	直接制御のシミュレーションを実行する。
 *===========================================================================*/
//「clip/clipruds.c」のRudSeqCh_Exec()をシミュレーション用に改造したものです。
//「clip/clipruds.c」のRudSeqCh_Exec()を変更する際は当関数も変更してください。
/*---------------------------------------------------------------------------*/
static uint16_t Speed;		/* 分周比(0=最速) */
static uint8_t StackLevel;	/* スタックレベル(0〜RudSeqCh_StackSize) */
typedef struct _ST_Stack {
	uint32_t iInst;		/* 戻り先の命令ポインタ */
	 int16_t Repeat;	/* 0=Callスタック,0以外=Repeatスタックの残り繰り返し回数(負=無限) */
} ST_Stack;
static ST_Stack TBL_Stack[RudSeqCh_StackSize];
/*==========================================================================*/
//void simMoveDirect(uint32_t iInst) {
//	int Hi, Lo;
//	for(;;) {
//		/* 命令コードを取得する。 */
//		Hi = TBL_Data[iInst];
//		Lo = (Hi & 0xFFF);	/* 命令コードの[11: 0] */
//		Hi >>= 12;		/* 命令コードの[15:12] */
//		/* 命令コードの[15:12]によって… */
//		switch(Hi) {
//		case 0:
//			/* 命令コードの[11: 0]によって… */
//			switch(Lo) {
//			case 0: /* end */
//				/* スタックが有れば… */
//				if(StackLevel) {
//					/* スタックを減らす。 */
//					StackLevel--;
//					/* Repeatスタックならば… */
//					if(TBL_Stack[StackLevel].Repeat) {
//						/* 無限,又は,残り繰り返し回数を減らして0でなければ… */
//						if((TBL_Stack[StackLevel].Repeat < 0) ||
//						 (--TBL_Stack[StackLevel].Repeat)) {
//							/* 命令ポインタを設定する。 */
//							iInst = TBL_Stack[StackLevel].iInst;
//							/* スタックを増やす。 */
//							StackLevel++;
//						}
//					/* Callスタックならば… */
//					} else {
//						/* 命令ポインタを設定する。 */
//						iInst = TBL_Stack[StackLevel].iInst;
//					}
//				/* スタックが無ければ… */
//				} else {
//					/* 再生終了する。 */
//					return; /* ここまで */
//				}
//				break;
//			case 1: /* wait stop */
//				printf("%s\n", "wait stop");
//				break;
//			case 2: /* move init */
//			case 3: /* move stop */
//				die("%04X 当拡張機能は直接制御以外のｼﾐｭﾚｰｼｮﾝを想定していません｡\n", iInst);
//			default: /* user u */
//				/** no job **/							//ｱﾌﾟﾘｹｰｼｮﾝ定義の処理は､当拡張機能のｼﾐｭﾚｰｼｮﾝ結果に影響しないものと仮定して､｢user u｣を出力しないことにした｡
//				break;
//			}
//			break;
//		case 1: /* wait rel */
//			/* 直前の命令完了からの相対時間が、指定された相対時間に到達していなければ… */
//			printf("%s %u\n", "wait rel", Lo);
//			break;
//		case 2: /* goto */
//			/* 命令ポインタを設定する。 */
//			if(!Lo) { DIE(); } /* データバグ */					//goto命令自身へのgotoは明らかにデータバグである。
//			iInst += (((int16_t)(Lo<<4))>>4) - 1/*共通処理の「+1」を相殺*/;
//			break;
//		case 3: /* call */
//			if((unsigned)StackLevel >= RudSeqCh_StackSize) { DIE(); }		/* RudSeqCh_StackSizeを調整せよ */
//			/* 戻り先の命令ポインタを格納する。 */
//			TBL_Stack[StackLevel].iInst = iInst;					//「〜=iInst+1/*次の命令*/;」ではないことに注意せよ。「+1」は戻り時のendの実行後の共通処理で行われるから。
//			/* Callスタックであることを示す。 */
//			TBL_Stack[StackLevel].Repeat = 0;
//			/* スタックを増やす。 */
//			StackLevel++;
//			/* 命令ポインタを設定する。 */
//			if(!Lo) { DIE(); } /* データバグ */					//call命令自身へのcallは明らかにデータバグである。
//			iInst += (((int16_t)(Lo<<4))>>4) - 1/*共通処理の「+1」を相殺*/;
//			break;
//		case 4:
//			Hi = Lo;
//			Lo = (Hi & 0x3FF);	/* 命令コードの[ 9: 0] */
//			Hi >>= 10;		/* 命令コードの[11:10] */
//			/* 命令コードの[11:10]によって… */
//			switch(Hi) {
//			case 0: /* repeat */
//				if((unsigned)StackLevel >= RudSeqCh_StackSize) { DIE(); }	/* RudSeqCh_StackSizeを調整せよ */
//				/* 戻り先の命令ポインタを格納する。 */
//				TBL_Stack[StackLevel].iInst = iInst;				//「〜=iInst+1/*次の命令*/;」ではないことに注意せよ。「+1」は戻り時のendの実行後の共通処理で行われるから。
//				/* Repeatスタックの残り繰り返し回数を格納する。 */
//				TBL_Stack[StackLevel].Repeat = Lo ? Lo : -1/*無限*/;
///*{{ｼﾐｭﾚｰｼｮﾝ特有}}*/		if(TBL_Stack[StackLevel].Repeat == -1) { TBL_Stack[StackLevel].Repeat = 10; }	//ｼﾐｭﾚｰｼｮﾝが終わらなくなることを防ぐため､無限ﾙｰﾌﾟは10回に置換えてｼﾐｭﾚｰｼｮﾝを行うことにした｡
//				/* スタックを増やす。 */
//				StackLevel++;
//				break;
//			case 1: /* speed */
//				/* 分周比を設定する。 */
//				Speed = Lo;
//				printf("%s %u\n", "speed", Lo);
//				break;
//			case 2: /* move rel */
//			default: /* move abs */
//				die("%04X 当拡張機能は直接制御以外のｼﾐｭﾚｰｼｮﾝを想定していません｡\n", iInst);
//				break;
//			}
//			break;
//		case 5: /* move direct if on */
//		case 6: /* move direct if off */
//		case 7: /* move direct always */
//			/* 台形制御チャネルの動作を実行する。 */
//			if(Speed && Lo) {
//				static const char* TBL_CondIf[]  ={""," if on"     ," if off"    ," always"    };
//				static const char* TBL_CondWhen[]={""," until rise"," until fall"," until edge"};
//				printf("%s %d%s%s\n", "move direct", ((int16_t)(Lo<<6))>>6, TBL_CondIf[(Lo>>12)&3], TBL_CondWhen[(Lo>>10)&3]);
//			} else {
//				printf("%s\n", "move stop direct");
//			}
//			break;
//		default: /* wait abs */
//			/* データ再生開始からの絶対時間が、指定された絶対時間に到達していなければ… */
//			printf("%s %u\n", "wait abs", ((Hi&7)<<12)|Lo);
//			break;
//		}
//		/* 命令ポインタを進める。 */
//		iInst++;									//end,goto,callの直後も命令ポインタが進むことに注意せよ。
//	}
//}
/*==========================================================================*/
//↓2014/12/12変更:条件分岐'if on','if off'を追加したことに伴い、シーケンサの命令セット,命令コード,命令コード定義用マクロを変更しました。
/*==========================================================================*/
void simMoveDirect(uint32_t iInst) {
	int Hi, Lo;
	for(;;) {
		/* 命令コードを取得する。 */
		Hi = TBL_Data[iInst];
		Lo = (Hi & 0xFFF);	/* 命令コードの[11: 0] */
		Hi >>= 12;		/* 命令コードの[15:12] */
		/* 命令コードの[15:12]によって… */
		switch(Hi) {
		case 0:
			/* 命令コードの[11: 0]によって… */
			switch(Lo) {
			case 0x0FFA: /* if on */
				/* センサ安定状態がOn でなければ次の命令を飛ばす。 */
				printf("%s\n", "if on");					//※TODO:実際には､ﾚﾎﾟｰﾄ形式としてはこれでは問題が有る｡ｺﾏﾝﾄﾞﾗｲﾝｵﾌﾟｼｮﾝでｾﾝｻ安定状態を指定し､それに沿って次の命令を飛ばすかどうかを決定し､制御せよ｡'if on','if off'を出力する必要は無いはずだ｡尚､左記の案は､ﾚﾎﾟｰﾄ形式の用途では､ｼｰｹﾝｽの最初でしか条件分岐しないという前提である｡
				break;
			case 0x0FFB: /* if off */
				/* センサ安定状態がOffでなければ次の命令を飛ばす。 */
				printf("%s\n", "if off");					//※TODO:実際には､ﾚﾎﾟｰﾄ形式としてはこれでは問題が有る｡ｺﾏﾝﾄﾞﾗｲﾝｵﾌﾟｼｮﾝでｾﾝｻ安定状態を指定し､それに沿って次の命令を飛ばすかどうかを決定し､制御せよ｡'if on','if off'を出力する必要は無いはずだ｡尚､左記の案は､ﾚﾎﾟｰﾄ形式の用途では､ｼｰｹﾝｽの最初でしか条件分岐しないという前提である｡
				break;
			case 0x0FFC: /* wait stop */
				printf("%s\n", "wait stop");
				break;
			case 0x0FFD: /* move stop */
			case 0x0FFE: /* move init */
				die("%04X 当拡張機能は直接制御以外のｼﾐｭﾚｰｼｮﾝを想定していません｡\n", iInst);
			case 0x0FFF: /* end */
				/* スタックが有れば… */
				if(StackLevel) {
					/* スタックを減らす。 */
					StackLevel--;
					/* Repeatスタックならば… */
					if(TBL_Stack[StackLevel].Repeat) {
						/* 無限,又は,残り繰り返し回数を減らして0でなければ… */
						if((TBL_Stack[StackLevel].Repeat < 0) ||
						 (--TBL_Stack[StackLevel].Repeat)) {
							/* 命令ポインタを設定する。 */
							iInst = TBL_Stack[StackLevel].iInst;
							/* スタックを増やす。 */
							StackLevel++;
						}
					/* Callスタックならば… */
					} else {
						/* 命令ポインタを設定する。 */
						iInst = TBL_Stack[StackLevel].iInst;
					}
				/* スタックが無ければ… */
				} else {
					/* 再生終了する。 */
					return; /* ここまで */
				}
				break;
			default: /* user u */
				/** no job **/							//ｱﾌﾟﾘｹｰｼｮﾝ定義の処理は､当拡張機能のｼﾐｭﾚｰｼｮﾝ結果に影響しないものと仮定して､｢user u｣を出力しないことにした｡
				break;
			}
			break;
		case 1: /* wait rel */
			/* 直前の命令完了からの相対時間が、指定された相対時間に到達していなければ… */
			printf("%s %u\n", "wait rel", Lo);
			break;
		case 2: /* goto */
			/* 命令ポインタを設定する。 */
			if(!Lo) { DIE(); } /* データバグ */					//goto命令自身へのgotoは明らかにデータバグである。
			iInst += (((int16_t)(Lo<<4))>>4) - 1/*共通処理の「+1」を相殺*/;
			break;
		case 3: /* call */
			if((unsigned)StackLevel >= RudSeqCh_StackSize) { DIE(); }		/* RudSeqCh_StackSizeを調整せよ */
			/* 戻り先の命令ポインタを格納する。 */
			TBL_Stack[StackLevel].iInst = iInst;					//「〜=iInst+1/*次の命令*/;」ではないことに注意せよ。「+1」は戻り時のendの実行後の共通処理で行われるから。
			/* Callスタックであることを示す。 */
			TBL_Stack[StackLevel].Repeat = 0;
			/* スタックを増やす。 */
			StackLevel++;
			/* 命令ポインタを設定する。 */
			if(!Lo) { DIE(); } /* データバグ */					//call命令自身へのcallは明らかにデータバグである。
			iInst += (((int16_t)(Lo<<4))>>4) - 1/*共通処理の「+1」を相殺*/;
			break;
		case 4:
			Hi = Lo;
			Lo = (Hi & 0x3FF);	/* 命令コードの[ 9: 0] */
			Hi >>= 10;		/* 命令コードの[11:10] */
			/* 命令コードの[11:10]によって… */
			switch(Hi) {
			case 0: /* repeat */
				if((unsigned)StackLevel >= RudSeqCh_StackSize) { DIE(); }	/* RudSeqCh_StackSizeを調整せよ */
				/* 戻り先の命令ポインタを格納する。 */
				TBL_Stack[StackLevel].iInst = iInst;				//「〜=iInst+1/*次の命令*/;」ではないことに注意せよ。「+1」は戻り時のendの実行後の共通処理で行われるから。
				/* Repeatスタックの残り繰り返し回数を格納する。 */
				TBL_Stack[StackLevel].Repeat = Lo ? Lo : -1/*無限*/;
/*{{ｼﾐｭﾚｰｼｮﾝ特有}}*/		if(TBL_Stack[StackLevel].Repeat == -1) { TBL_Stack[StackLevel].Repeat = 10; }	//ｼﾐｭﾚｰｼｮﾝが終わらなくなることを防ぐため､無限ﾙｰﾌﾟは10回に置換えてｼﾐｭﾚｰｼｮﾝを行うことにした｡
				/* スタックを増やす。 */
				StackLevel++;
				break;
			case 1: /* speed */
				/* 分周比を設定する。 */
				Speed = Lo;
				printf("%s %u\n", "speed", Lo);
				break;
			case 2: /* move rel */
			default: /* move abs */
				die("%04X 当拡張機能は直接制御以外のｼﾐｭﾚｰｼｮﾝを想定していません｡\n", iInst);
				break;
			}
			break;
		case 5: /* move direct if on */
		case 6: /* move direct if off */
		case 7: /* move direct always */
			/* 台形制御チャネルの動作を実行する。 */
			if(Speed && Lo) {
				static const char* TBL_CondIf[]  ={""," if on"     ," if off"    ," always"    };
				static const char* TBL_CondWhen[]={""," until rise"," until fall"," until edge"};
				printf("%s %d%s%s\n", "move direct", ((int16_t)(Lo<<6))>>6, TBL_CondIf[(Lo>>12)&3], TBL_CondWhen[(Lo>>10)&3]);
			} else {
				printf("%s\n", "move stop direct");
			}
			break;
		default: /* wait abs */
			/* データ再生開始からの絶対時間が、指定された絶対時間に到達していなければ… */
			printf("%s %u\n", "wait abs", ((Hi&7)<<12)|Lo);
			break;
		}
		/* 命令ポインタを進める。 */
		iInst++;									//end,goto,callの直後も命令ポインタが進むことに注意せよ。
	}
}
/*==========================================================================*/
