/*****************************************************************************
 * Bison/Flexプログラム部分をテストするための、Windowsアプリケーションの雛型 *
 * □ビルド・実行方法:                                                       *
 * ┃"C:\Program Files (x86)\Microsoft Visual Studio\VC98\Bin\VCVARS32.BAT"  *
 * ┃make -f winapp.mk run                                                   *
 *****************************************************************************/
#include "winapp.h"

/*---------------------------------------------------------------------------*/
#define Label_Max     50000		/* ラベル登録最大数 */	//調整可
#define CmdByte_Max 1000000		/* 命令バイト最大数 */	//調整可
uint8_t TBL_CmdByte[CmdByte_Max];	/* 命令バイト配列 */
int nCmdByte;				/* 命令バイト配列の格納数 */

/*---------------------------------------------------------------------------*/
/* ローカル関数宣言 */
__declspec(noreturn) static void usage();
static uint8_t* add_cmd_byte(int n);
static void output_cmd_byte();

/*---------------------------------------------------------------------------*/
int main(int argc, char* argv[]) {
	int opt;
#if     (YYDEBUG!=0)
//	yydebug = 1; /* nonzero means print parse trace */
#endif/*(YYDEBUG!=0)*/
	/* コマンドラインオプションを処理する。 */
	opterr = 0; /* オプションエラーは当プログラム内で処理するので、getopt()内でのエラー表示は不要。 */
	while((opt = getopt(argc, argv, "h")) != -1) {
		switch(opt) {
		default:
		case 'h': usage();
		}
	}
	/* ラベルを登録するためのハッシュテーブルを作成する。 */
	hcreate(Label_Max);
	/* 判り易さのために、命令位置0は使用しないことにした。命令位置0には、ダミーのend命令を置くことにした。 */
	inst_end();
	/* ファイルが指定されていなければ、標準入力から読み込む。 */
	if(optind == argc) {
		yylex_init(stdin);
		if((yyparse() != 0) || (yynerrs != 0)) { die("エラーが発生しました。\n"); }
	/* ファイルが指定されていたら、各ファイルを読み込む。 */
	} else {
		int i;
		for(i = optind; i < argc; i++) {
			FILE* fp = fopen(argv[i], "r");
			if(!fp) { die("%s が開けません。\n", argv[i]); }
			yylex_init(fp);
			if((yyparse() != 0) || (yynerrs != 0)) { die("%s: エラーが発生しました。\n", argv[i]); }
			fclose(fp);
		}
	}
	/* 命令バイトを出力する。 */
	output_cmd_byte();
	return EXIT_SUCCESS;
}

/*---------------------------------------------------------------------------*/
/* 使い方を表示して終了する。 */
__declspec(noreturn) static void usage() {
	fprintf(stderr, "dSndSeqC - サウンドシーケンサコンパイラ (%s)\n", VERSION);
	fprintf(stderr, "Copyright (C) 2014 Naoyuki Sawa\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "USAGE:\n");
	fprintf(stderr, "  dSndSeqC [filename ...] \n");
	fprintf(stderr, "  ・filenameを指定しなければ、標準入力から読み込みます。\n");
	fprintf(stderr, "  ・filenameは、ワイルドカードによる複数指定も可能です。\n");
	fprintf(stderr, "\n");
	fprintf(stderr, "OPTIONS:\n");
	fprintf(stderr, "  -h 使い方を表示します。\n");
	exit(EXIT_FAILURE);
}

/*---------------------------------------------------------------------------*/
static uint8_t* add_cmd_byte(int n) {
	uint8_t* p;
	if((unsigned)(nCmdByte + n) > CmdByte_Max) {
		yyerror("命令バイト数が制限を超えました。(%d)\n", CmdByte_Max);
		exit(EXIT_FAILURE);
	}
	p = &TBL_CmdByte[nCmdByte];
	nCmdByte += n;
	return p;
}
void inst_def(const char* lab) {
	ENTRY item;
	item.key = (char*)lab;
	if(hsearch(item, FIND)) {
		yyerror("defのラベル定義が重複しています。(%s)", lab);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	item.data = (void*)nCmdByte;	/* ダミー */
	if(!hsearch(item, ENTER)) {
		yyerror("ラベル数が制限を超えました。(%d)", Label_Max);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	printf("#define %s %d\n", lab, nCmdByte);
}
void inst_end() {
	uint8_t* p;
	p = add_cmd_byte(1);
	p[0] = 0x00;	//End
}
void inst_wait(int ms) {
	uint8_t* p;
	       if((ms >= 0x00001) && (ms <= (0x00001 + 0x000000FF))) {
		   ms -= 0x00001;
		p = add_cmd_byte(2);
		p[0] = 0x01;	//Wait1
		p[1] = ms;
	} else if((ms >= 0x00101) && (ms <= (0x00101 + 0x0000FFFF))) {
		   ms -= 0x00101;
		p = add_cmd_byte(3);
		p[0] = 0x02;	//Wait2
		p[1] = ms;
		p[2] = ms >>  8;
	} else if((ms >= 0x10101) && (ms <= (0x10101 + 0x00FFFFFF))) {
		   ms -= 0x10101;
		p = add_cmd_byte(4);
		p[0] = 0x03;	//Wait3
		p[1] = ms;
		p[2] = ms >>  8;
		p[3] = ms >> 16;
	} else {
		p = add_cmd_byte(5);
		p[0] = 0x04;	//Wait4
		p[1] = ms;
		p[2] = ms >>  8;
		p[3] = ms >> 16;
		p[4] = ms >> 24;
	}
}
void inst_stop(int trk) {
	uint8_t* p;
	if((unsigned)trk > SndTrk_Max) {
		yyerror("stopの引数trkが範囲外です。(%d)", trk);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	p = add_cmd_byte(2);
	p[0] = 0x10;	//Stop
	p[1] = trk;
}
void inst_play_m(int trk, int phr, int vol, int pan) {
	uint8_t* p;
	if((unsigned)trk > SndTrk_Max) {
		yyerror("playの引数trkが範囲外です。(%d)", trk);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)phr > SndPhr_Max) {
		yyerror("playの引数phrが範囲外です。(%d)", phr);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)vol > SndVol_Max) {
		yyerror("playの引数volが範囲外です。(%d)", vol);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)pan > SndPan_Max) {
		yyerror("playの引数panが範囲外です。(%d)", pan);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	p = add_cmd_byte(6);
	p[0] = 0x11;	//PlayM
	p[1] = trk;
	p[2] = phr;
	p[3] = phr >> 8;
	p[4] = vol;
	p[5] = pan;
}
void inst_play_s(int trk, int phr, int vol) {
	uint8_t* p;
	if((unsigned)trk > SndTrk_Max) {
		yyerror("playの引数trkが範囲外です。(%d)", trk);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)phr > SndPhr_Max) {
		yyerror("playの引数phrが範囲外です。(%d)", phr);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)vol > SndVol_Max) {
		yyerror("playの引数volが範囲外です。(%d)", vol);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	p = add_cmd_byte(5);
	p[0] = 0x12;	//PlayS
	p[1] = trk;
	p[2] = phr;
	p[3] = phr >> 8;
	p[4] = vol;
}
void inst_vol(int trk, int vol) {
	uint8_t* p;
	if((unsigned)trk > SndTrk_Max) {
		yyerror("volの引数trkが範囲外です。(%d)", trk);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)vol > SndVol_Max) {
		yyerror("volの引数volが範囲外です。(%d)", vol);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	p = add_cmd_byte(3);
	p[0] = 0x20;	//Vol
	p[1] = trk;
	p[2] = vol;
}
void inst_pan(int trk, int pan) {
	uint8_t* p;
	if((unsigned)trk > SndTrk_Max) {
		yyerror("panの引数trkが範囲外です。(%d)", trk);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	if((unsigned)pan > SndPan_Max) {
		yyerror("panの引数panが範囲外です。(%d)", pan);
		yynerrs++;	/* 呼び出し元(main)に知らせるために */
		/* 全てのセマンティクスエラーを報告するために継続する */
	}
	p = add_cmd_byte(3);
	p[0] = 0x21;	//Pan
	p[1] = trk;
	p[2] = pan;
}

/*---------------------------------------------------------------------------*/
static void output_cmd_byte() {
	int iCmdByte;
	printf("const unsigned char TBL_SndSeq[%d]={", nCmdByte);
	for(iCmdByte = 0; iCmdByte < nCmdByte; iCmdByte++) {
		if(iCmdByte) { printf(","); }
		if(!(iCmdByte & 31)) { printf("\n"); }
		printf("0x%02X", TBL_CmdByte[iCmdByte]);
	}
	printf("};\n");
}
