/*
 *	SimpleGDBServer.c
 *
 *	Interface 2010年1月号増刊 すぐに使える! 液晶搭載マイコン・モジュール
 *	S1C17702基板 デバッグモニタ 擬似コード
 *
 *	* Sat Dec 19 20:50:05 JST 2009 Naoyuki Sawa
 *	- 1st リリース。
 */

/****************************************************************************
 *	定数
 ****************************************************************************/

/* 0x008000番地〜 .db_vectorセクション
 * イニシャルリセット直後のベクタテーブルです。
 * init_gdbsrv()による初期化処理で、ユーザー定義のベクタテーブル(0x00C000)に切り替えられます。
 */
const long vector[32] = {
	boot, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
	NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
};

/* 0x008080番地〜 .exp_to_gdbセクション
 * gdbから、実機上の変数を参照したり、関数を実行したりするための、アドレステーブルです。
 * inf_sb_init_minimoni〜inf_sb_load_imgは、セルフバックアップ用の変数や関数のようです。
 * ※2009/12/19現在、セルフバックアップ関連の部分は、未調査です。
 */
const long vector[8] = {
	fls_work,		/* 0x008080 */
	sb_work,		/* 0x008084 */
	&ext_brk_flag,		/* 0x008088 */
	inf_sb_init_minimoni,	/* 0x00808C */
	inf_sb_trans_fls,	/* 0x008090 */
	inf_sb_clear_from,	/* 0x008094 */
	inf_sb_trans_img,	/* 0x008098 */
	inf_sb_load_img,	/* 0x00809C */
};

/****************************************************************************
 *	変数
 ****************************************************************************/

/* フラッシュ書き込みコマンド('L')を実行する前に、書き込むデータを転送しておくRAMです。
 * gdbがフラッシュ書き込みを行う手順は、おそらく、以下のとおりだと思います。
 * 1. メモリ読み出しコマンド('m')を使って、0x008088番地を読み出し、ext_brk_flagのアドレスを取得する。
 * 2. メモリ読み出しコマンド('m')を使って、ext_brk_flagの値を読み出す。
 *    ext_brk_flagの値が0以外ならば、外部ブレークが発生しているので、フラッシュ書き込みは行えない。
 * 3. メモリ読み出しコマンド('m')を使って、0x008080番地を読み出し、fls_workのアドレスを取得する。
 * 4. メモリ書き込みコマンド('M')を使って、fls_workに、書き込むデータを転送する。
 *    書き込むデータのサイズは、4キロバイト(=1セクタ)固定である。
 *    ただし、メモリ書き込みコマンド('M')は、一度に、199バイトまでしか転送できない。
 *    従って、199バイト以下のサイズに分割して、合計4キロバイトを転送する必要がある。
 * 5. フラッシュ書き込みコマンド('L')を実行して、fls_workに転送したデータを、フラッシュに書き込む。
 */
unsigned char fls_work[4096];

/* セルフバックアップ時に、何かの目的で使われる変数?
 * ※2009/12/19現在、セルフバックアップ関連の部分は、未調査です。
 */
unsigned char sb_work[/*サイズ不明*/];

/* ユーザーコンテキストの、ブレーク時のレジスタが保存されているメモリを指すポインタです。
 * init_gdbsrv()の処理で、値0x002FC4が設定され、変更されることはありません。
 * 定数でも良かったと思うのですが、たまたま、グローバル変数として実装したのだと思います。
 */
long* registers;

/* デバッグコンテキストの%spを保存する変数です。
 * デバッグコンテキストからユーザーコンテキストに切り替えるときに、%spを退避します。
 * また、デバッグブレークが発生したときに読み出し、%spを復元します。
 */
long* debug_sp;

/* 外部ブレーク発生の有無を示す変数です。
 * 初期化直後は0ですが、一度でも外部ブレークが発生したら1になり、以後0に戻りません。
 * この変数の用途は、フラッシュ書き込みの可否を判断するためのようです。
 * フラッシュ書き込みは、リセット直後の安定した状態で行わなければ、危険です。
 * ext_brk_flagが0でなければ、フラッシュ書き込みの処理を実行しないようになっているようです。
 * なお、ext_brk_flagの値は、gdbからも参照できます。(0x008088番地から、アドレスを取得できる)
 * gdbが、フラッシュ書き込みコマンド('L')を実行する前に、ext_brk_flagを確認しているかどうかは、不明です。
 * もし、gdbが確認していなかったとしても、実機のデバッグモニタが確認しているので、安全です。
 */
int ext_brk_flag;

/****************************************************************************
 *	boot
 ****************************************************************************/

/* イニシャルリセット後、この関数からプログラムが開始します。 */
void boot() {
	for(;;) {
		/* スタックを0x000FC0番地に設定します。
		 * 0x000FC0番地という値には、特に意味は無いようです。
		 * - S1C17702は、オンチップデバッガの予約領域を避けて、0x002FC0番地にスタックを設定するのが一般的です。
		 *   ただし、本基板では、デバッグモニタが0x002C00以降を使用するので、0x002C00にスタックを設定するのが自然です。
		 *   0x000FC0番地というのは、内蔵RAMが4キロバイトの機種(S1C17701等)の、一般的なスタック設定値です。
		 *   たぶん、S1C17701のプログラムからそのままコピーしたため、こうなっているのではないかと思います。
		 * - ここで設定したスタックは、init_gdbsrv()の間でしか、使用しません。
		 *   boot_user()を実行すると、ユーザープログラムがスタックを(おそらく0x002C00番地に)再設定するはずです。
		 *   あるいは、brk命令を実行すると、デバッグコンテキストのスタックフレームは、0x002C00〜0x002FC0領域の末尾に、別途確保されています。
		 *   したがって、init_gdbsrv()を実行している間、0x000FC0番地あたりのメモリを破壊しなければ、問題ありません。
		 *   実は、0x000FC0番地は、fls_work[4096](0x0004A8〜0x0014A7番地)と被っているのですが、init_gdbsrv()の中で使わないので、大丈夫です。
		 */
		asm("Xld.a %sp, 0x000FC0");

		/* デバッグモニタを初期化します。 */
		if(init_gdbsrv()) {
			/* フリーランモードなら、ユーザープログラムを実行します。 */
			boot_user();
		} else {
			/* フリーランモードでなければ、デバッグコンテキストへ切り替え、gdbからのコマンドを待ちます。 */
			asm("brk");
		}
	}
}

/****************************************************************************
 *	boot_user
 ****************************************************************************/

/* ユーザープログラムの、リセット処理ルーチンから、実行を開始します。 */
void boot_user() {
	/* ユーザ定義ベクタの0番(リセット)へジャンプします。 */
	long* v = MISC_TTBRL_TTBR | (MISC_TTBRH_TTBR << 16);	/* init_gdbsrv()で切り替えているので、実際には、必ず0x00C000のはずです。 */
	void (*fn)() = (void (*)())v[0];
	fn();
}

/****************************************************************************
 *	init_gdbsrv
 ****************************************************************************/

/* フリーランモードかどうかを調べ、フリーランモードならば1を返します。
 * フラーランモードでなければ、デバッグモニタを初期化し、0を返します。
 * - フリーランモードだった場合は、デバッグモニタを初期化しないことに注意してください。
 *   ユーザープログラムが、デバッガ接続モードから実行('c')コマンドで実行された場合と、フリーランモードで実行された場合とで、周辺回路の初期化状態が異なります。
 *   ユーザープログラムは、フリーランモードで実行された場合を想定して、プリスケーラやアクセスサイクルの初期化を、確実に行う必要があります。
 */
int init_gdbsrv() {
	/* ベクタテーブルを、ユーザー定義のベクタテーブルに切り替えます。 */
	MISC_PROT_PROT = 0x96;		/* MISC register write protect = Writing 0x96 removes the write protection of the MISC registers */
	MISC_TTBRL_TTBR = 0xC000;	/* Vector table base address = 0x00C000 */
	MISC_TTBRH_TTBR = 0x00;
	MISC_PROT_PROT = 0x00;		/* MISC register write protect = Writing another value set the write protection */

	/* ユーザー定義のベクタテーブルの31番に、0xEC17????(マジックコード)が書かれていたら… */
	if(*(unsigned short*)0xC07E == 0xEC17) {
		/* JP2を調べるために、P30ポートを入力モードに設定します。 */
		P3_PMUX_P30MUX = 0;		/* P30 port function select = 0 P30 */
		P3_IO_P3IO &= ~(1 << 0);	/* P30 port output enable = 0 Disable */
		P3_PU_P3PU |=  (1 << 0);	/* P30 port pull-up enable = 1 Enable */
		P3_SM_P3SM |=  (1 << 0);	/* P30 port Schmitt trigger input enable = 1 Enable (Schmitt) */

		/* JP2がショートされていたら、フリーランモードと見なします。(P30 = 0 = JP2ショート, 1 = JP2オープン) */
		if(!(P3_IN_P3IN & (1 << 0))) {	/* P30 port input data */
			return 1;
		}

	/* ユーザー定義のベクタテーブルの31番に、0xEC17????(マジックコード)が書かれていなければ… */
	} else {
		/* SW2を調べるために、P06ポートを入力モードに設定します。 */
		P0_IO_P0IO &= ~(1 << 6);	/* P06 port output enable = 0 Disable */
		P0_PU_P0PU |=  (1 << 6);	/* P06 port pull-up enable = 1 Enable */
		P0_SM_P0SM |=  (1 << 6);	/* P06 port Schmitt trigger input enable = 1 Enable (Schmitt) */

		/* SW2が押されていたら、フリーランモードと見なします。(P06 = 0 = SW2 ON, 1 = SW2 OFF) */
		if(!(P0_IN_P0IN & (1 << 6))) {	/* P06 port input data */
			return 1;
		}
	}

	/* フラッシュROMリードサイクルを1サイクルに設定します。
	 * 本誌p.68によると、これは必須の要件とのことですが、なぜ必須なのかわかりませんでした。
	 * 試しに別のサイクル数に設定してみたところ、問題無く動作しているように見えました。
	 */
	MISC_FL_FLCYC = 4;	/* FLASHC read access cycle = 0x4 1 cycle */

	/* デバッグブレークが発生したときの分岐先アドレスを、0x0番地に切り替えます。 */
	MISC_PROT_PROT = 0x96;	/* MISC register write protect = Writing 0x96 removes the write protection of the MISC registers */
	MISC_IRAMSZ_DBADR = 1;	/* Debug base address select = 1 0x0 */
	MISC_PROT_PROT = 0x00;	/* MISC register write protect = Writing another value set the write protection */

	/* デバッグモニタのワークエリアを初期化します。 */
	init_work_gdbsrv();
	/* registers変数のコメントを参照してください。 */
	registers = (long*)0x002FC4;
	/* デバッグコンテキストの初期スタックフレームを作成します。 */
	init_frame_gdbsrv();

	/* デバッグモード中も、プリスケーラが動作し続けるように設定します。
	 * デバッグモニタがUARTを使ってgdbと通信するために、必要な設定です。
	 * そして、プリスケーラを開始します。
	 */
	PSC_CTL_PRUND = 1;	/* Prescaler run/stop in debug mode = 1 Run */
	PSC_CTL_PRUN = 1;	/* Prescaler run/stop control = 1 Run */

	/* 内部周辺回路に、8MHzのシステムクロックを供給します。 */
	CLG_PCLK_PCKEN = 3;	/* PCLK enable = 0x3 Enable */
	CLG_CCLK_CCLKGR = 0;	/* CCLK clock gear ratio select = 0x0 1/1 */

	/* OSC3クロックの発振を開始します。 */
	OSC_CTL_OSC3WT = 0;	/* OSC3 wait cycle select = 0x0 1024 cycles */
	OSC_CTL_OSC3EN = 1;	/* OSC3 enable = 1 Enable */

	/* システムクロックに、OSC3を選択します。 */
	OSC_SRC_HSCLKSEL = 1;	/* High-speed clock select = 1 OSC3 */
	OSC_SRC_CLKSRC = 0;	/* System clock source select = 0 HSCLK */

	/* gdbと通信するために、UARTを初期化し、開始します。 */
	init_usb_uart();
	start_usb_uart();

	return 0;
}

/****************************************************************************
 *	init_work_gdbsrv
 ****************************************************************************/

/* デバッグブレークが発生したときに実行するフックです。
 * init_work_gdbsrv()の処理で、0x0番地にコピーされます。
 */
handler_dbg:
		ext		link_gdbsrv(19:7)
		jpa		link_gdbsrv( 6:0)		;// Sjpa link_gdbsrv
end_handler_dbg:

/*--------------------------------------------------------------------------*/

void init_work_gdbsrv() {
	/* デバッグモニタのbss領域を0クリアします。
	 * 厳密には「memcpy(__START_db_bss, __END_db_bss - __START_db_bss);」なのですけれど、これでもokです。
	 * デバッグモニタのdata領域もクリアしてしまっていますが、このあとすぐに転送するので、問題ありません。
	 */
	memset(0x002C00, 0, 0x002FC0 - 0x002C00);

	/* デバッグモニタのdata領域を転送します。 */
	memcpy(__START_db_data, __START_db_data_lma, __END_db_data_lma - __START_db_data_lma);

	/* デバッグブレークが発生したときに実行するフックを、0x0番地にコピーします。 */
	memcpy(__START_handler_dbg, handler_dbg, end_handler_dbg - handler_dbg);
}

/****************************************************************************
 *	init_frame_gdbsrv
 ****************************************************************************/

//	■デバッグコンテキストのスタック領域と、初期スタックフレーム構成について
//
//	0x002E7C〜	デバッグコンテキストのスタック領域(?)
//
//	↑デバッグコンテキストのスタック領域は、下限が0x002FBFが末尾で、上限は不明です。
//	↑推測すると、デバッグコンテキストの.db_dataセクションの末尾が0x002E7C番地なので、たぶん、0x002E7C以降がスタック領域だと思います。
//	{{ここから、デバッグコンテキストの初期スタックフレーム構造です。
//	0x002F9C	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r7復元値です。
//	0x002FA0	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r6復元値です。
//	0x002FA4	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r5復元値です。
//	0x002FA8	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r4復元値です。
//	0x002FAC	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r3復元値です。
//	0x002FB0	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r2復元値です。
//	0x002FB4	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r1復元値です。
//	0x002FB8	init_work_gdbsrv()の処理で、0にクリアされていますが、値は何でも構いません。最初のデバッグブレークが発生したときの%r0復元値です。
//	0x002FBC	init_frame_gdbsrv()の処理で、gdbsrv_mainが格納されています。boot()関数のbrkで最初のデバッグブレークが発生したとき、link_gdbsrv()の処理によって、gdbsrv_main()から処理が開始します。
//	}}ここまで、デバッグコンテキストの初期スタックフレーム構造です。
//	↑ここまで、デバッグコンテキストのスタック領域です。
//
//	↓ここから、S1C17コアのデバッグ機能、および、デバッグモニタがレジスタを格納するために使用します。
//	↓0x002FC0以降は、オンチップデバッガの予約領域なのですが、本基板はオンチップデバッガを使用しないため、デバッグモニタが独自の用途に使用しています。
//	0x002FC0	S1C17コアのデバッグ機能によって、デバッグブレーク時に{%pc,%psr}が格納されるメモリです。
//	{{'g'コマンドによるレジスタ読み出し、および、'G'コマンドによるレジスタ書き込みは、この領域を参照、および、変更します。
//	0x002FC4	S1C17コアのデバッグ機能によって、デバッグブレーク時に%r0が格納されるメモリです。
//	0x002FC8	link_gdbsrv()の処理によって、デバッグブレーク時の%r1が格納されるメモリです。
//	0x002FCC	link_gdbsrv()の処理によって、デバッグブレーク時の%r2が格納されるメモリです。
//	0x002FD0	link_gdbsrv()の処理によって、デバッグブレーク時の%r3が格納されるメモリです。
//	0x002FD4	link_gdbsrv()の処理によって、デバッグブレーク時の%r4が格納されるメモリです。
//	0x002FD8	link_gdbsrv()の処理によって、デバッグブレーク時の%r5が格納されるメモリです。
//	0x002FDC	link_gdbsrv()の処理によって、デバッグブレーク時の%r6が格納されるメモリです。
//	0x002FE0	link_gdbsrv()の処理によって、デバッグブレーク時の%r7が格納されるメモリです。
//	0x002FE4	link_gdbsrv()の処理によって、デバッグブレーク時の%spが格納されるメモリです。
//	0x002FE8	link_gdbsrv()の処理によって、デバッグブレーク時の%pcが格納されるメモリです。0x002FC0と同じ値を格納しておき、resume_user_program()の処理で0x002FC0と比較することにより、'G'コマンドで%pcが変更されたかどうかを検出できます。
//	0x002FEC	link_gdbsrv()の処理によって、デバッグブレーク時の%psrが格納されるメモリです。'G'コマンドによる%psrの変更は、こちらを変更します。resume_user_program()の処理では、こちらの%psrを使って、ユーザーコンテキストへ切り替えます。
//	}}'g'コマンドによるレジスタ読み出し、および、'G'コマンドによるレジスタ書き込みは、この領域を参照、および、変更します。
//
//	0x002FF0〜	0x002FF0〜0x002FFFは未使用です。オンチップデバッガの予約領域の一部ですが、本基板はオンチップデバッガを使用していません。

/* デバッグコンテキストの初期スタックフレームを作成します。 */
void init_frame_gdbsrv() {
	/* デバッグコンテキストの初期スタックフレームを作成します。 */
	debug_sp = 0x002F9C;
	[0x002FBC] = gdbsrv_main;

	/* ext_brk_flag変数のコメントを参照してください。
	 * 実際には、init_work_gdbsrv()の処理でデバッグモニタのワークエリアを0クリア済みなので、既に0であるはずです。
	 * この処理は、無駄処理です。
	 */
	ext_brk_flag = 0;
}

/****************************************************************************
 *	init_usb_uart
 ****************************************************************************/

/* UART Ch.0を初期化します。 */
void init_usb_uart() {
	/* 8ビットタイマCh.0を初期化します。
	 * 8ビットタイマCh.0は、UART Ch.0のクロックを生成するために使用します。
	 */
	init_timer8();

	/* 入出力ポートP23、P24、P25の端子機能を、UARTに切り替えます。 */
	P2_PMUX_P25MUX = 1;	/* P25 port function select = 1 SCLK0 */
	P2_PMUX_P24MUX = 1;	/* P24 port function select = 1 SOUT0 */
	P2_PMUX_P23MUX = 1;	/* P23 port function select = 1 SIN0 */

	/* UART割り込みは使用しません。 */
	UART_CTL0_REIEN = 0;	/* Receive error int. enable = 0 Disable */
	UART_CTL0_RIEN = 0;	/* Receive buffer full int. enable = 0 Disable */
	UART_CTL0_TIEN = 0;	/* Transmit buffer empty int. enable = 0 Disable */
	UART_CTL0_RXEN = 0;	/* UART enable = 0 Disable */

	/* 転送データ形式を設定します。 */
	UART_MOD0_CHLN = 1;	/* Character length = 1 8 bits */
	UART_MOD0_PREN = 0;	/* Parity enable = 0 No parity */
	UART_MOD0_STPB = 0;	/* Stop bit select = 0 1 bit */
	UART_MOD0_SSCK = 0;	/* Input clock select = 0 Internal */

	/* 受信エラーをリセットします。 */
	UART_ST0_FER = 1;	/* Framing error flag = Reset by writing 1. */
	UART_ST0_PER = 1;	/* Parity error flag = Reset by writing 1. */
	UART_ST0_OER = 1;	/* Overrun error flag = Reset by writing 1. */

	/* 8ビットタイマCh.0を開始します。 */
	start_timer8();
}

/****************************************************************************
 *	start_usb_uart
 ****************************************************************************/

/* UART Ch.0を開始します。 */
void start_usb_uart() {
	/* UART Ch.0を開始します。 */
	UART_CTL0_RXEN = 1;	/* UART enable = 1 Enable */
}

/****************************************************************************
 *	init_timer8
 ****************************************************************************/

/* 8ビットタイマCh.0を初期化します。 */
void init_timer8() {
	/* 8ビットタイマCh.0を停止します。 */
	T8F_CTL0_PRUN = 0;	/* Timer run/stop control = 0 Stop */

	/* デバッグモード中も、プリスケーラが動作し続けるように設定します。
	 * init_gdbsrv()で、既にそのように設定されているはずで、この処理は冗長です。
	 */
	PSC_CTL_PRUND = 1;	/* Prescaler run/stop in debug mode = 1 Run */
	PSC_CTL_PRUN = 1;	/* Prescaler run/stop control = 1 Run */

	/* 8ビットタイマCh.0を115200bpsに設定します。(本誌p.92に解説有り) */
	T8F_CTL0_TFMD = 5;	/* Fine mode setup = 0x5 */
	T8F_CTL0_TRMD = 0;	/* Count mode select = 0 Repeat */
	T8F_CTL0_PRESER = 1;	/* Timer reset = 1 Reset */
	//
	T8F_TR0_TR = 3;		/* 8-bit timer reload data = 0x3 */
	T8F_CLK0_DF = 0;	/* 8-bit timer input clock select = 0x0 PCLK*1/1 */
}

/****************************************************************************
 *	start_timer8
 ****************************************************************************/

/* 8ビットタイマCh.0を開始します。 */
void start_timer8() {
	/* 8ビットタイマCh.0を開始します。 */
	T8F_CTL0_PRUN = 1;	/* Timer run/stop control = 1 Run */
}

/****************************************************************************
 *	gdbsrv_main
 ****************************************************************************/

/* デバッグモニタのメインループです。 */
void gdbsrv_main() {
	static char own_buf[400];		/* gdbから受信したコマンド文字列を格納したり、gdbへ送信する文字列を組み立てるために使用します。 */
	static unsigned char mem_buf[200];	/* コマンド文字列を解析してバイナリデータにしたり、gdbへ送信する文字列の元データを一時的に格納します。 */

	int signal = 2;	/* 最後に発生したデバッグブレークの要因を保持します。2 = SIGINT (外部ブレーク), 5 = SIGTRAP (brk命令、または、ハードウェアPCブレーク) */

	int ch;
	int len;
	long mem_addr;
	int sect_num;

	for(;;) {
		/* コマンドパケットを受信します。 */
		len = getpkt(own_buf)

		/* 正常なコマンドパケットを受信したら…
		 * 実際には、getpkt()は正常なコマンドパケットを受信するまで処理を返しません。
		 * この判断は、コマンド文字列が空のコマンドパケット($#00)を除外するだけです。
		 */
		if(len > 0) {
			/* コマンド文字列の一文字目が、コマンド種別を表しています。 */
			ch = own_buf[0]
			switch(ch) {

			/* レジスタ読み出し */
			case 'g':
				/* デバッグブレーク時のレジスタ値を、文字列にして返送します。
				 * %r0,%r1,%r2,%r3,%r4,%r5,%r6,%r7,%sp,%pc,%psrの、11本のレジスタを、それぞれ16進数8桁で表します。
				 * 実際には、%psr以外は24ビットレジスタ、%psrは8ビットレジスタなのですが、32ビット表現に統一しています。
				 * レジスタ値は、インテル並びの4バイト値として格納されており、その並びのままバイト表現で文字列にします。
				 * たとえば、レジスタ値が0x12346789とすると、文字列表現は"89673412"となることに注意してください。
				 * なお、レジスタ間の区切り文字は有りません。
				 */
				convert_int_to_ascii(registers, own_buf, 44);
				putpkt(owm_buf);
				break;

			/* レジスタ書き込み */
			case 'G':
				/* コマンド種別の後の88文字を、レジスタ値の文字列表現と見なして、レジスタ値に設定します。
				 * 次に、'c'または's'コマンドでユーザーコンテキストに切り替わったとき、このレジスタ値が復元されます。
				 * レジスタ値の文字列表現は、'g'コマンドと同様です。
				 */
				convert_ascii_to_int(&owm_buf[1], registers, 44);
				write_ok(owm_buf);
				putpkt(owm_buf);
				break;

			/* メモリ読み出し */
			case 'm':
				/* コマンド文字列を解析し、読み出し開始アドレスと、バイト数を取得します。
				 * バイト数は最大199バイトとし、それ以上ならば"ENN"で応答します。(ENNって何でしょうか?)
				 */
				decode_m_packet(&owm_buf[1], &mem_addr, &len);
				if(len > 199) {
					write_enn(owm_buf);
					putpkt(owm_buf);
					break;
				}
				/* 0xFFFFC0以降には、オンチップデバッガ用レジスタ等があるため、アクセスさせないようになっているようです。
				 * 具体的には、SSR、SDR、IBAR4が有ります。
				 * このうち、IBAR4だけは、gdbからブレークポイントを書き込む必要があるため、特例としてアクセス許可します。
				 * ただし、この特例が必要なのは'M'(メモリ設定)だけであり、'm'(メモリ取得)では必要ありません。
				 * gdbは、毎回CPUからブレークポイントを読み出すのではなく、gdb自身でブレークポイントを記憶しているからです。
				 * 以下は、IBAR4の特例処理ですが、IBAR4のワード読み出しが要求されたらmem_bufの現在値を返すという、無意味な動作になっています。
				 * おそらく、'M'の処理をコピーした際の、ゴミが残っているのだと思います。
				 * 前述のとおり、gdbはこの動作に依存しないので、実害は無いはずです。
				 * ちなみに、SSR、SDRにアクセスされても特に問題は無いはずで、0xFFFFC0以降を除外する処理自体が不要と思うのですが...(?)
				 */
				if((mem_addr == 0xFFFFD0/*IBAR4*/) && (len == 4)) {	//無駄処理
					convert_int_to_ascii(mem_buf, own_buf, len);	//無駄処理
					putpkt(owm_buf);				//無駄処理
					break;						//無駄処理
				}							//無駄処理
				/* 要求されたメモリ領域が、完全に0xFFFFC0以降ならば、全データを0として返します。 */
				if(mem_addr >= 0xFFFFC0) {
					memset(mem_buf, 0, 200);
					convert_int_to_ascii(mem_buf, own_buf, len);
					putpkt(owm_buf);
					break;
				}
				/* 要求されたメモリ領域が、0xFFFFC0以降を含んでいたら、その範囲は0として返します。 */
				end_addr = mem_addr + len;
				if(end_addr >= 0xFFFFC0) {
					memset(mem_buf, 0, 200);
					memcpy_swhw(mem_buf, mem_addr, 0xFFFFC0 - mem_addr);
				} else {
					memcpy_swhw(mem_buf, mem_addr, len);	/* 0xFFFFC0以降の範囲を含まない、通常のダンプ要求は、ここで処理されます */
				}
				convert_int_to_ascii(mem_buf, own_buf, len);
				putpkt(owm_buf);
				break;

			/* メモリ書き込み */
			case 'M':
				/* コマンド文字列を解析し、書き込み開始アドレス、バイト数、および、データを取得します。
				 * バイト数は最大199バイトとし、それ以上ならば"ENN"で応答します。(ENNって何でしょうか?)
				 */
				decode_M_packet(&owm_buf[1], &mem_addr, &len, mem_buf);
				if(len > 199) {	/* もうmem_buf[]に格納した後で、既にバッファオーバーフローしており、ここでチェックしても遅いのですが... */
					write_enn(owm_buf);
					putpkt(owm_buf);
					break;
				}
				/* 0xFFFFC0以降でも、IBAR4だけは、特例で設定可能とします。
				 * gdbが、ハードウェアブレークポイントを設定するために、'M'の機能を使って、IBAR0〜IBAR4を設定するからです。
				 * なお、IBAR0〜IBAR3は0xFFFFC0以前に有ります。
				 */
				if((mem_addr == 0xFFFFD0/*IBAR4*/) && (len == 4)) {
					memcpy_swhw(mem_addr, mem_buf, len)
					putpkt(owm_buf);	/* バグ!? "OK"で応答すべきところ、受信文字列をそのまま返送している。gdbは応答を見ていない様子... */
					break;
				}
				/* 要求されたメモリ領域が、完全に0xFFFFC0以降ならば、何もせずに"OK"で応答します。 */
				if(mem_addr >= 0xFFFFC0) {
					write_ok(own_buf);
					putpkt(owm_buf);
					break;
				}
				/* 要求されたメモリ領域が、0xFFFFC0以降を含んでいたら、それ以前の範囲だけ設定します。 */
				end_addr = mem_addr + len;
				if(end_addr >= 0xFFFFC0) {
					memcpy_swhw(mem_addr, mem_buf, 0xFFFFC0 - mem_addr);
				} else {
					memcpy_swhw(mem_addr, mem_buf, len);	/* 0xFFFFC0以降の範囲を含まない、通常の設定要求は、ここで処理されます */
				}
				/* "OK"で応答します。 */
				write_ok(own_buf);
				putpkt(owm_buf);
				break;

			/* 実行 */
			case 'c':
			/* ステップ実行 */
			case 's':
				/* シングルステップ機能をOn、または、Offに設定します。 */
				if(ch == 'c') {
					DCR.SE = 0;	/* Single step enable = 0 Disable */
					/* ここでDCRを書き戻す際に、DCR.DRもそのまま書き戻されて、DCR.DR=1だったならば1書き込みになり、DCR.DRがリセットされます */
				} else {
					DCR.SE = 1;	/* Single step enable = 1 Enable */
					/* ここでDCRを書き戻す際に、DCR.DRもそのまま書き戻されて、DCR.DR=1だったならば1書き込みになり、DCR.DRがリセットされます */
				}
				/* ユーザーコンテキストへ切り替えて、ユーザープログラムを実行します。
				 * 次にデバッグブレークが発生するまで、resume_user_program()は処理を返しません。
				 */
				resume_user_program();
				/* 最後に発生したデバッグブレークの要因を更新します。 */
				if(DCR.DR) {	/* Debug request flag */
					signal = 2;	/* 2 SIGINT */
				} else {
					signal = 5;	/* 5 SIGTRAP */
				}
				/* FALLTHRU */

			/* 最後に発生したデバッグブレークの要因を取得 */
			case '!':
			case '?':
				/* 最後に発生したデバッグブレークの要因を、文字列にして返送します。
				 * 外部ブレークならば"S02"、それ以外ならば"S05"です。
				 */
				prepare_resume_reply(own_buf, 'S', signal);
				putpkt(owm_buf);
				break;

			/* フラッシュ書き込み */
			case 'L':
				/* コマンド文字列を解析し、書き込むセクタ番号を取得します。 */
				decode_L_packet(&owm_buf[1], &sect_num);
				/* 一度でも外部ブレークが発生していたら、フラッシュ書き換えをするのは安全ではないので、実行を拒否し、"ENN"で応答します。 */
				if(ext_brk_flag) {
					write_enn(owm_buf);
					putpkt(owm_buf);
					break;
				}
				/* フラッシュ書き込みを実行します。
				 * 書き込むデータはあらかじめ、'M'コマンドを使ってfls_work[]に転送しておく必要があります。
				 * fls_work[]のコメントを参照してください。
				 */
				command_L_exe(sect_num);
				/* "OK"で応答します。 */
				write_ok(own_buf);
				putpkt(owm_buf);
				break;

			/* 受信したコマンド文字列をそのままエコーバック */
			case 'd':
				/* owm_buf[]には、受信したコマンド文字列が入っています。
				 * 本コマンドは、受信したコマンド文字列を変更せず、そのままエコーバックします。
				 */
				putpkt(owm_buf);
				break;

			/* 不明なコマンド種別 */
			default:
				/* 空文字列で応答します。
				 * putpkt()の共通処理で、パケットヘッダとチェックサムとnulが付くので、実際には"$#00(nul)"が応答されます。
				 * また、putpkt()の共通処理のため、PC側は、この応答に対しても、'+'でACK応答しなければなりません。
				 */
				owm_buf[0] = '\0';
				putpkt(owm_buf);
				break;
			}
		}
	}
}

/****************************************************************************
 *	decode_m_packet
 ****************************************************************************/

/* メモリ読み出し('m')のコマンド文字列を解析します。
 * [in]
 *	from		コマンド文字列へのポインタ。
 *	mem_addr_ptr	アドレスを格納する変数へのポインタ。
 *	len_ptr		データ長を格納する変数へのポインタ。
 * [note]
 *	* コマンド文字列の書式は、以下のとおりです。
 *
 *		"[0-9a-f]*,[0-9a-f]{0,4}"
 *		 ~~~~~~~~~ ~~~~~~~~~~~~~
 *		 アドレス    データ長   
 */
void decode_m_packet(char* from, long* mem_addr_ptr, int* len_ptr) {
	int i = 0;
	int j;
	int c;

	/* アドレスとデータ長を、0に初期化します。 */
	*mem_addr_ptr = 0;
	*len_ptr = 0;

	/* アドレスを取得します。
	 * 桁数は無制限で、省略も可能です。省略した場合は、0と見なします。
	 * ','を見つけたら、アドレス部終了と見なします。
	 * ','が欠落していたら、バッファ終端を超えてどこまでも読み続けてしまうという、危険が有ります。
	 * 実際には、本デバッグモニタはgdb.exeとの連携用ですから、gdb.exeがそのようなコマンドを送信しない限りは安全です。
	 * しかしもし、シリアル通信ツールなどを使って直接コマンドを送信する場合は、要注意です。
	 */
	for(;;) {
		c = from[i++];
		if(c == ',') {	/* アドレス部終了 */
			break;
		}
		*mem_addr_ptr = (*mem_addr_ptr << 4) | fromhex(c);
	}

	/* データ長を取得します。
	 * 桁数は最大4桁で、省略も可能です。省略した場合は、0と見なします。
	 * '\0'を見つけたら、データ長部終了と見なします。
	 * データ長部の後ろからは何も読み出さないのだから、4桁に制限する必要は無いと思うのですが...特に問題はありません。
	 */
	for(j = 0; j < 4; j++) {
		c = from[i++];
		if(c == '\0') {
			break;
		}
		*len_ptr = (*len_ptr << 4) | fromhex(c);
	}
}

/****************************************************************************
 *	decode_M_packet
 ****************************************************************************/

/* メモリ書き込み('M')のコマンド文字列を解析します。
 * [in]
 *	from		コマンド文字列へのポインタ。
 *	mem_addr_ptr	アドレスを格納する変数へのポインタ。
 *	len_ptr		データ長を格納する変数へのポインタ。
 *	to		バイナリデータを格納する変数へのポインタ。
 * [note]
 *	* コマンド文字列の書式は、以下のとおりです。
 *
 *		"[0-9a-f]*,[0-9a-f]*:[0-9a-f]{2×データ長}"
 *		 ~~~~~~~~~ ~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~
 *		 アドレス  データ長         データ        
 *
 */
void decode_M_packet(char* from, long* mem_addr_ptr, int* len_ptr, char* to) {
	int i = 0;
	int c;

	/* アドレスとデータ長を、0に初期化します。 */
	*mem_addr_ptr = 0;
	*len_ptr = 0;

	/* アドレスを取得します。
	 * 桁数は無制限で、省略も可能です。省略した場合は、0と見なします。
	 * ','を見つけたら、アドレス部終了と見なします。
	 * ','が欠落していたら、バッファ終端を超えてどこまでも読み続けてしまうという、危険が有ります。
	 * 実際には、本デバッグモニタはgdb.exeとの連携用ですから、gdb.exeがそのようなコマンドを送信しない限りは安全です。
	 * しかしもし、シリアル通信ツールなどを使って直接コマンドを送信する場合は、要注意です。
	 */
	for(;;) {
		c = from[i++];
		if(c == ',') {	/* アドレス部終了 */
			break;
		}
		*mem_addr_ptr = (*mem_addr_ptr << 4) | fromhex(c);
	}

	/* データ長を取得します。
	 * 桁数は無制限で、省略も可能です。省略した場合は、0と見なします。
	 * ':'を見つけたら、データ長部終了と見なします。
	 * ':'が欠落していたら、バッファ終端を超えてどこまでも読み続けてしまうという、危険が有ります。
	 * 実際には、本デバッグモニタはgdb.exeとの連携用ですから、gdb.exeがそのようなコマンドを送信しない限りは安全です。
	 * しかしもし、シリアル通信ツールなどを使って直接コマンドを送信する場合は、要注意です。
	 */
	for(;;) {
		c = from[i++];
		if(c == ':') {
			break;
		}
		*len_ptr = (*len_ptr << 4) | fromhex(c);
	}

	/* データを取得します。
	 * 指定どおりの長さのデータ部が続いていることが前提。文字列終端をチェックしていません。危険!
	 * 指定した長さが、格納先バッファサイズ以内かどうか、チェックしていません。危険!
	 * gdb.exeが正しいコマンドを送信すること前提で、間違ったコマンドは想定していません。要注意。
	 */
	convert_ascii_to_int(&from[i], to, *len_ptr);
}

/****************************************************************************
 *	decode_L_packet
 ****************************************************************************/

/* フラッシュ書き込み('L')のコマンド文字列を解析します。
 * [in]
 *	from		コマンド文字列へのポインタ。
 *	sect_num_ptr	セクタ番号を格納する変数へのポインタ。
 * [note]
 *	* コマンド文字列の書式は、以下のとおりです。
 *
 *		"[0-9a-f]*"
 *		 ~~~~~~~~~
 *		 セクタ番号
 *
 */
void decode_L_packet(char* from, int* sect_num_ptr) {
	int i = 0;
	int c;

	/* セクタ番号を、0に初期化します。 */
	*sect_num_ptr = 0;

	/* セクタ番号を取得します。
	 * 桁数は無制限で、省略も可能です。省略した場合は、0と見なします。
	 * '\0'を見つけたら、セクタ番号部終了と見なします。
	 */
	for(;;) {
		c = from[i++];
		if(c == '\0') {
			break;
		}
		*sect_num_ptr = (*sect_num_ptr << 4) | fromhex(c);
	}
}

/****************************************************************************
 *	command_L_exe
 ****************************************************************************/

/* フラッシュ書き込みを実行します。
 * 書き込むデータはあらかじめ、'M'コマンドを使ってfls_work[]に転送しておく必要があります。
 * fls_work[]のコメントを参照してください。
 */
void command_L_exe(int sect_num) {
	int result = -1;

	/* セクタ番号と、フラッシュROMのアドレス対応は、以下のとおりです。
	 *
	 *			 フラッシュROM
	 *	0x008000	+-------------+
	 *			|セクタ番号= 0|
	 *	0x009000	+-------------+
	 *			|セクタ番号= 1|
	 *	0x009000	+-------------+
	 *			|セクタ番号= 2|
	 *	0x00A000	+-------------+
	 *			|      ・     |
	 *			|      ・     |
	 *			|      ・     |
	 *	0x026000	+-------------+
	 *			|セクタ番号=30|
	 *	0x027000	+-------------+
	 *			|セクタ番号=31|
	 *	0x028000	+-------------+
	 *
	 * セクタ番号は、上図のように4キロバイト単位なのですが、プロテクト単位は16キロバイト単位で、以下のようになっています。
	 *
	 *			  フラッシュROM
	 *	0x008000	+----------------+
	 *			|プロテクト単位=0|	ライトプロテクトされています。
	 *	0x00C000	+----------------+
	 *			|プロテクト単位=1|
	 *	0x010000	+----------------+
	 *			|       ・       |
	 *			|       ・       |
	 *			|       ・       |
	 *	0x024000	+----------------+
	 *			|プロテクト単位=7|	ライトプロテクトされています。
	 *	0x028000	+----------------+
	 *
	 * プロテクト単位=0の領域には、デバッグモニタが書き込まれているため、ライトプロテクトされています。
	 * プロテクト単位=7の領域には、ライトプロテクトビット自身が書き込まれているため、ライトプロテクトされています。
	 * 従って、書き換え可能な領域はプロテクト単位=1〜6の範囲で、セクタ番号4〜27の範囲に相当します。
	 * フラッシュROMの容量は128キロバイトですが、ユーザープログラムには96キロバイトしか使えません。
	 *
	 * デバッグモニタのプログラムは小さいので、プロテクト単位=0の領域は、かなり余っています。
	 * また、ライトプロテクトビットは数バイト程度しか無いのに、プロテクト単位=7の領域がまるまる利用不可になっています。
	 * かなり勿体無いです。
	 *
	 * そもそも、S1C17のフラッシュROMプロテクト機能は、誤って書き換えられることを防ぐのが主目的ではないように思います。
	 * ライトプロテクトは、データ書き込みやセクタ消去を防ぎますが、チップ消去は実行できてしまうからです。(試していませんが…)
	 * プロテクトの主目的は、ライトプロテクトとリードプロテクトを併用して、内蔵プログラムを読み出せなくするのが主目的です。
	 * 内蔵プログラムを読み出せなくするのが主目的ですから、チップ消去は防ぐ必要が無いわけです。(内蔵プログラムも消えるから)
	 *
	 * 誤って書き換えたり消したりすることを防ぐ目的ならば、フラッシュ書き換えルーチンによる判断だけで充分ではなかったかと思います。
	 * デバッグモニタが占有する領域は8キロバイト程度で、ユーザープログラムは120キロバイトぐらい使えたと思うのです。
	 * ちなみに、P/ECEのフラッシュROMにはプロテクト機能自体が無く、フラッシュ書き換えルーチンによる判断だけですが、
	 * 意図せず誤って緊急カーネル(本基板で言うところのデバッグモニタに相当)を壊してしまったというトラブルは、聞いたことが無いです。
	 */
	if((sect_num < 4) || (sect_num > 27)) {
		goto L_EXIT;
	}

	/* フラッシュ書き換えルーチンを、内蔵RAMに転送する。 */
	load_fls();

	/* フラッシュ領域のベースアドレス(定数)、消去開始セクタ番号、消去終了セクタ番号。
	 * flash_erase()へのセクタ番号指定は、1ベースで指定する。
	 */
	if(flash_erase(0x008000, sect_num + 1, sect_num + 1) != 0) {
		goto L_EXIT;
	}

	/* 書き込み先フラッシュ先頭アドレス、書き込むバイト数、書き込むデータのアドレス。
	 * 書き込むデータは、RAM領域にあるfls_work[4096]にあらかじめ転送しておく。
	 */
	if(flash_load(0x008000 + sect_num * 4096, 4096, fls_work) != 0) {
		goto L_EXIT;
	}

	result = 0;

L_EXIT:
	/* 内蔵RAMに転送した(かも知れない)フラッシュ書き換えルーチンを、確実に削除する。
	 * 必須ではないのだが、プログラムが暴走した拍子などに、偶然実行されてしまうのを避けるためだと思う。
	 * 万一、チップ消去ルーチンが起動されてしまったりすると、被害が大きいから。
	 */
	clear_fls();

	return result;
}

/****************************************************************************
 *	link_gdbsrv
 ****************************************************************************/

/* デバッグブレークが発生したときに、0x0番地のhandler_dbg経由で、ここへジャンプします。 */
void link_gdbsrv() {
	/* ユーザーコンテキストを退避します。 */
//	[0x002FC0] ← {%pc,%psr}	S1C17コアのデバッグ機能によって、デバッグブレーク発生時に自動的に格納されています
//	[0x002FC4] ← %r0		S1C17コアのデバッグ機能によって、デバッグブレーク発生時に自動的に格納されています
	[0x002FC8] ← %r1
	[0x002FCC] ← %r2
	[0x002FD0] ← %r3
	[0x002FD4] ← %r4
	[0x002FD8] ← %r5
	[0x002FDC] ← %r6
	[0x002FE0] ← %r7
	[0x002FE4] ← %sp
	{
		/* もしbrk命令でブレークした場合、保存された%pcは、brk命令の次を指しています。
		 * gdbがレジスタを取得した場合、brk命令の次の命令でブレークしたように見えてわかりづらいため、調整します。
		 * 保存された%pcが、brk命令を指すように調整し、調整したことを「brk_ins_skip=2」でマークしておきます。
		 * brk命令以外の要因でブレークした場合は、この調整は不要です。
		 */
		unsigned short* pc = [0x002FC0] & 0xFFFFFF;
		if(*(pc - 1) == 0x0160/*brk*/) {
			brk_ins_skip = 2;	/* brk命令でブレークした場合 */
			pc--;
		} else {
			brk_ins_skip = 0;	/* それ以外の要因でブレークした場合 */
		}
	}
	[0x002FE8] ← %pc		/* [0x002FC0]に保存されている%pcの値を、上述の処理で調整した%pcの値 */
	[0x002FEC] ← %psr		/* [0x002FC3]に保存されている%psrの値そのまま */
	[0x002FC0] ← {%pc,%psr}	/* 調整した%pcの値を、[0x002FC0]にも書き戻しておきます。[0x002FC0]と[0x002FE8]を比較して、gdbが%pcを変更したかどうか、判断するためです。 */

	/* 外部ブレーク要因ならば、ext_brk_flagを1にします。
	 * ext_brk_flag変数のコメントを参照してください。
	 */
	if(DCR.DR) {	/* Debug request flag */
		ext_brk_flag = 1;
	}

	/* デバッグコンテキストを復元します。 */
	%sp ← [debug_sp]
	%r7 ← [%sp]+
	%r6 ← [%sp]+
	%r5 ← [%sp]+
	%r4 ← [%sp]+
	%r3 ← [%sp]+
	%r2 ← [%sp]+
	%r1 ← [%sp]+
	%r0 ← [%sp]+
}

/****************************************************************************
 *	resume_user_program
 ****************************************************************************/

/* ユーザーコンテキストへ切り替えて、ユーザープログラムを実行します。
 * 次にデバッグブレークが発生するまで、resume_user_program()は処理を返しません。
 */
void resume_user_program() {
	/* デバッグコンテキストを退避します。 */
	-[%sp] ← %r0
	-[%sp] ← %r1
	-[%sp] ← %r2
	-[%sp] ← %r3
	-[%sp] ← %r4
	-[%sp] ← %r5
	-[%sp] ← %r6
	-[%sp] ← %r7
	[debug_sp] ← %sp

	/* ブレーク要因がbrk命令だった場合(brk_ins_skip=2)、gdbが%pcを変更していなければ、brk命令の次から再開する。
	 * ブレーク要因がbrk命令でないか(brk_ins_skip=0)、gdbが%pcを変更していたら、brk_ins_skipによる調整は行わない。
	 */
	if([0x002FE8].a == [0x002FC0].a) {
		[0x002FC0].a = [0x002FE8].l + brk_ins_skip;	// %pc
		[0x002FC3].b = [0x002FEC].l;			// %psr
	} else {
		[0x002FC0].a = [0x002FE8].l;			// %pc
		[0x002FC3].b = [0x002FEC].l;			// %psr
	}

	/* ユーザーコンテキストを復元します。 */
	%sp ← [0x002FE4]
	%r7 ← [0x002FE0]
	%r6 ← [0x002FDC]
	%r5 ← [0x002FD8]
	%r4 ← [0x002FD4]
	%r3 ← [0x002FD0]
	%r2 ← [0x002FCC]
	%r1 ← [0x002FC8]
	retd	// {%pc,%psr} ← [0x002FC0]
}

/****************************************************************************
 *	prepare_resume_reply
 ****************************************************************************/

/* デバッグブレークの要因を、文字列に変換します。
 * 実際には、statusは'S'固定、signoは2か5で、文字列は以下のどちらかになります。
 *
 *	"S02"	外部ブレークを意味します。
 *	"S05"	brk命令、または、ハードウェアPCブレークを意味します。
 */
void prepare_resume_reply(char* buf, int status, int signo) {
	*buf++ = status;
	*buf++ = tohex((signo >> 4) & 15);
	*buf++ = tohex( signo       & 15);
	*buf++ = '\0';
}

/****************************************************************************
 *	write_ok
 ****************************************************************************/

/* "OK"という文字列を作成します。 */
void write_ok(char* buf) {
	buf[0] = 'O';
	buf[1] = 'K';
	buf[2] = '\0';
}

/****************************************************************************
 *	write_enn
 ****************************************************************************/

/* "ENN"という文字列を作成します。 */
void write_enn(char* buf) {
	buf[0] = 'E';
	buf[1] = 'N';
	buf[2] = 'N';
	buf[3] = '\0';
}

/****************************************************************************
 *	convert_int_to_ascii
 ****************************************************************************/

/* バイナリデータを、16進数文字列に変換します。 */
void convert_int_to_ascii(char from[/*n*/], char to[/*2n*/], int n) {
	while(n--) {
		int c = *from++;
		*to++ = tohex((c >> 4) & 15);
		*to++ = tohex( c       & 15);
	}
	*to = '\0';
}

/****************************************************************************
 *	convert_ascii_to_int
 ****************************************************************************/

/* 16進数文字列を、バイナリデータに変換します。 */
void convert_ascii_to_int(char from[/*2n*/], char to[/*n*/], int n) {
	while(n--) {
		int c1 = fromhex(*from++);
		int c2 = fromhex(*from++);
		*to++ = (c1 << 4) | c2;
	}
}

/****************************************************************************
 *	tohex
 ****************************************************************************/

/* 0〜15の値を、16進数一文字('0'〜'9'、'a'〜'f')に変換します。 */
int tohex(int nib) {
	if(nib < 10) {
		return nib + '0';
	} else {
		return nib + 'a' - 10;
	}
}

/****************************************************************************
 *	fromhex
 ****************************************************************************/

/* 16進数一文字('0'〜'9'、'a'〜'f')を、0〜15の値に変換します。 */
int fromhex(int a) {
	if((a >= '0') && (a <= '9')) {
		return a - '0';
	}
	if((a >= 'a') && (a <= 'f')) {	/* 小文字のみ対応。大文字は不可です */
		return a - 'a' + 10;
	}
	return -1;
}

/****************************************************************************
 *	readchar
 ****************************************************************************/

/* UARTから、1文字受信します。
 * [out]
 *	戻り値		受信した文字。
 * [note]
 *	- readchar()は、1文字受信するまで、処理を返しません。
 *	- エラーを示す戻り値はありません。
 *	  0、および、負の戻り値は、'\0'、および、'\x80'〜'\0xFF'を受信したことを示します。
 *	- readchar()を呼び出している関数が、readchar()の戻り値の意味を誤っているようです。
 *	  readchar()を呼び出している関数は、readchar()の戻り値が0未満ならば、受信失敗と判断しているようです。
 *	  しかし上述のとおり、readchar()が受信失敗のステータスを返すことは、絶対にありません。
 *	  readchar()が受信した文字が、'\x80'〜'\0xFF'のときに、たまたも、戻り値が0未満となります。
 *	  readchar()を呼び出している関数は、それらを誤って、受信失敗と判断することになりそうです。
 *	  ただし実際には、'\x80'〜'\0xFF'の文字を送受信することは無いので、問題にはならないと思います。
 */
int readchar() {
	while(!UART_ST0_RDRY) {	/* Receive data ready flag */
		/** no job **/
	}
	return UART_RXD0_RXD;	/* Receive data in the receive data buffer */
}

/****************************************************************************
 *	getpkt
 ****************************************************************************/

/* コマンドパケットを受信し、コマンド文字列を格納します。
 * [note]
 *	* gdbが送信し、S1C17702基板が受信する、パケットの構造は、以下のとおりです。
 *
 *		"$.*#[0-9a-f][0-9a-f]"
 *		  ~~ ~~~~~~~~~~~~~~~~
 *		  ↑   チェックサム  
 *		  コマンド文字列
 */
int getpkt(char buf[/*400*/]) {
	/* 正常なコマンドパケットを受信しるまで、処理を返しません。 */
	for(;;) {
		char* bp = buf;
		int count = 0;
		int csum = 0;
		int c;
		int c1;
		int c2;

		/* '$'を受信するまで、空読みします。 */
		for(;;) {
			c = readchar();
			if(c < 0) {
				return -1;	/* '\x80'以上の文字を受信した場合に有り得ますが、通常はここで帰ることは考えなくて良いです。 */
			}
			if(c == '$') {
				break;
			}
		}

		/* '$'の後、'#'の前までが、コマンド文字列の本体です。
		 * バッファには、コマンド文字列の本体だけを格納します。
		 */
		for(;;) {
			c = readchar();
			if(c < 0) {
				return -1;	/* '\x80'以上の文字を受信した場合に有り得ますが、通常はここで帰ることは考えなくて良いです。 */
			}
			if(c == '#') {
				break;
			}
			*bp++ = c;	/* 文字を格納します。 */
			csum += c;	/* チェックサムを計算します。 */
			count++;
			if(count > 399) {	/* bug? 実際にこの条件で抜けると、このあと格納するnulがbuf[400]から溢れてしまう。「if(count >= 399)」が正解では? */
				break;
			}
		}
		*bp = '\0';	/* 終端 */

		/* '#'の後に、16進数二文字のチェックサムを受信します。
		 * チェックサムが一致したら、正常なコマンドパケットを受信したと見なして、処理を返します。
		 */
		c1 = fromhex(readchar());
		c2 = fromhex(readchar());
		if(((c1 << 4) | c2) == (unsigned char)csum) {
			return count;
		}

		/* チェックサムが一致しなかったら、'-'を応答します。 */
		write(0, "-", 1);
	}
}

/****************************************************************************
 *	writechar
 ****************************************************************************/

/* UARTへ、1文字送信します。 */
void writechar(int data) {
	while(!UART_ST0_TDBE) {	/* Transmit data buffer empty flag */
		/** no job **/
	}
	UART_TXD0_TXD = data;	/* Transmit data */
}

/****************************************************************************
 *	write
 ****************************************************************************/

/* UARTへ、文字列を送信します。 */
int write(int dummy, char* buf, int size) {
	while(size--) {
		writechar(*buf++);
	}
	return 0;
}

/****************************************************************************
 *	putpkt
 ****************************************************************************/

/* 応答文字列に、パケットヘッダとチェックサムを付加し、パケットとして送信します。
 * その後、gdbからのACK応答として、'+'が送られてくるまで待ちます。
 * [note]
 *	* S1C17702基板が送信し、gdbが受信する、パケットの構造は、以下のとおりです。
 *	  ほぼ、受信パケットと同じ構造ですが、最後にヌルキャラクタが付く点が異なります。
 *
 *		"$.*#[0-9a-f][0-9a-f]\0"
 *		  ~~ ~~~~~~~~~~~~~~~~
 *		  ↑   チェックサム  
 *		  応答文字列
 */
int putpkt(char* buf) {
	int cnt = strlen(buf);
	int csum = 0;
	int i;

	/* 応答文字列のチェックサムを計算します。 */
	for(i = 0; i < cnt; i++) {
		csum += buf[i];
	}

	for(;;) {
		/* パケットヘッダを送信します。 */
		writechar('$');

		/* 応答文字列を送信します。 */
		for(i = 0; i < cnt; i++) {
			writechar(buf[i]);
		}

		/* チェックサムを送信します。 */
		writechar('#');
		writechar(tohex((csum >> 4) & 15));
		writechar(tohex( csum       & 15));

		/* 最後に、ヌルキャラクタを送信します。
		 * シリアル通信の行区切りにnulを送るという、ちょっと珍しい仕様です。
		  */
		writechar('\0');

		/* gdbからのACK応答として、'+'が送られてくるまで待ちます。
		 * '+'以外の文字を受信したら、再度、同じパケットを送信する処理を繰り返します。
		 */
		c = readchar()
		if(c < 0) {
			return -1;	/* '\x80'以上の文字を受信した場合に有り得ますが、通常はここで帰ることは考えなくて良いです。 */
		}
		if(c == '+') {
			return 1;	/* '+'を受信したので、処理を返します。 */
		}
	}
}

/****************************************************************************
 *	memcpy_swhw
 ****************************************************************************/

/* memcpy()の高速版です。 */
void* memcpy_swhw(void* dest, const void* src, size_t count) {
	/* コピー先アドレス、コピー元アドレス、コピーサイズがすべて偶数ならば、2バイトづつコピーします。 */
	if((dest | src | count) & 1) {
		int* d = dest;
		int* s = src;
		while((count -= 2) >= 0) {
			*d++ = *s++;
		}
		return dest;
	/* コピー先アドレス、コピー元アドレス、コピーサイズのうち一つでも奇数ならば、1バイトづつコピーします。 */
	} else {
		return memcpy(dest, src, count);
	}
}

/****************************************************************************
 *	strlen
 ****************************************************************************/

/* 普通のstrlen()です。 */
size_t strlen(const char* string) {
	/* 説明省略 */
}

/****************************************************************************
 *	memcpy
 ****************************************************************************/

/* 普通のmemcpy()です。 */
void* memcpy(void* dest, const void* src, size_t count) {
	/* 説明省略 */
}

/****************************************************************************
 *	memset
 ****************************************************************************/

/* 普通のmemcpy()です。 */
void* memset(void* dest, int c, size_t count) {
	/* 説明省略 */
}

/****************************************************************************
 *	flash_erase
 ****************************************************************************/

/* フラッシュ書き換えルーチンを、内蔵RAMにコピーします。 */
void load_fls() {
	memcpy(__START_fls_text, __START_fls_text_lma, __END_fls_text_lma - __START_fls_text_lma);
}

/****************************************************************************
 *	clear_fls
 ****************************************************************************/

/* 内蔵RAMにコピーしたフラッシュ書き換えルーチンを、クリアします。 */
void clear_fls() {
	memset(__START_fls_text, 0, __END_fls_text - __START_fls_text);
}

/****************************************************************************
 *	flash_erase
 ****************************************************************************/

/* フラッシュROMを消去します。
 * [in]
 *	ulCtrlReg	Flashエリアのベースアドレス。
 *			S1C17702の場合は、0x008000番地に固定です。
 *	ulStart		消去するフラッシュROMの範囲の、開始セクタ番号。
 *			1ベースで、1〜32で指定します。
 *	ulEnd		消去するフラッシュROMの範囲の、終了セクタ番号。
 *			1ベースで、1〜32で指定します。
 * [out]
 *	戻り値		成功したら、0を返します。
 *			失敗したら、0以外の値を返します。
 * [note]
 *	* 本関数は、load_fls()関数を使って内蔵RAMにコピーしてから、実行します。
 *	* フラッシュROMのセクタ番号については、command_L_exe()関数内のコメントを参照してください。
 *	* 終了セクタ番号のセクタも、消去されることに中止してください。
 *	  たとえば、ulStart=10、ulEnd=12と指定すると、セクタ10、セクタ11、セクタ12が消去されます。
 */
int flash_erase(long ulCtrlReg, int ulStart, int ulEnd) {
	/* ※2009/12/19現在、まだ詳細に読んでいません。 */
}

/****************************************************************************
 *	flash_load
 ****************************************************************************/

/* フラッシュROMに書き込みます。
 * [in]
 *	ulProgAddr	書き込み先の、フラッシュROMのアドレス。
 *	ulSize		書き込むバイト数。
 *	pData		書き込むデータ。
 * [out]
 *	戻り値		成功したら、0を返します。
 *			失敗したら、0以外の値を返します。
 * [note]
 *	* 本関数は、load_fls()関数を使って内蔵RAMにコピーしてから、実行します。
 */
int flash_load(long ulProgAddr, long ulSize, unsigned char* pData) {
	/* ※2009/12/19現在、まだ詳細に読んでいません。 */
}


