/**
 * @file   dbTerm.c
 * @author StepTechnica
 * @date   May 16, 2023
 * 
 * @brief Evaluation Board 用ソースファイル「ターミナルコンソール」
 * 
 * @copyright (C) 2022- StepTechnica Co.,Ltd.
 * 
 * このソフトウェアは、コピー利用、配布、変更の追加、変更を加えたもの再配布、商用利用、有料販売など、どなたも自由にお使いいただくことができます。
 * このソフトウェアには保証はついていません。
 * このソフトウェアを利用したことで問題が起きた際に、ソフトウェアの製作者は一切の責任を負いません。
 * 
 */

/* -----------------------------------------------------------------------------
 * #include
 */

#include <string.h>
#include <ctype.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbTerm.h"
#include "ctrlUART4.h"
#include "MKYDevice.h"
#include "Utility.h"


/* -----------------------------------------------------------------------------
 * #defineマクロ定義
 */
#ifndef NULL
#define NULL	0
#endif

#define MKY_MEMORY_BASE 	0x60000000	//メモリ開始アドレス
#define MAX_ACCESS_SIZE		(0x800)		//コマンドからアクセスできるメモリのサイズ
#define BUFFER_MAX			128
#define LINE_BYTES			16
#define MAX_FORMAT_BYTES	8


#define HEADER_BYTE1	"   ADRESS :  0  1  2  3  4  5  6  7  8  9  A  B  C  D  E  F :: <  A  S  C  I  I >\n"
#define HEADER_BYTE2	" ======== : == == == == == == == == == == == == == == == == :: ==================\n"

#define HEADER_WORD1	"   ADRESS :    0    2    4    6    8    A    C    E\n"
#define HEADER_WORD2	" ======== : ==== ==== ==== ==== ==== ==== ==== ====\n"

#define HEADER_LONG1	"   ADRESS :        0        4        8        C\n"
#define HEADER_LONG2	" ======== : ======== ======== ======== ========\n"

#define HEADER_DOUBLE1	"   ADRESS :                0                8\n"
#define HEADER_DOUBLE2	" ======== : ================ ================\n"


/* -----------------------------------------------------------------------------
 * 変数定義
 */

short        	gRxStringsPos;
short        	gRxStringsLen;
short        	gRxInSeqMode ;
unsigned char   gRxStrings[MAX_CMDT_LENGTH];
SubProcessState_st subState = {SubProcessNone, 1, 0, 0, 0};

/* -----------------------------------------------------------------------------
 * 外部変数定義
 */
extern char strPROMPT[16];		// 標準プロンプト文字列 : MKYxx.c

/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */
short dbTerm_initialize(void);
short dbTerm_main(void);
static short chkRxdata(unsigned char *last);
static short purseCmd(void);
short dbTerm_md(int argc, char *argv[]);
short dbTerm_mw(int argc, char *argv[]);
short dbTerm_mm(int argc, char *argv[]);
short dbTerm_din(int argc, char *argv[]);
short dbTerm_dout(int argc, char *argv[]);
short getFormatOption(char* format);
static short getFormatMask(short format);
void md_dump(short offset, short length, short format);
static void dump_byte(short offset, short length, short format);
static void dump_word(short offset, short length, short format);
static void dump_long(short offset, short length, short format);
static void dump_double(short offset, short length, short format);
void dump_ascii_line(unsigned char* line, short length);
void subDbTermStart(SubProcessKind kind, short offset, short format, const char* name, int count);
static void subDbTermContinueAccessOpDesc(void);
static void subDbTermProcess(int argc, char *argv[]);
static void subDbTermNextAddress(void);
static void subDbTermWrite(unsigned long long value);
void dBTermShowReg(short offset, const char* name, short format, short showAscii);
void showDbTermPrompt(void);
static void showSubDbTermPrompt(void);
void showMemValue(unsigned char data[], short format);


/* -----------------------------------------------------------------------------
 * 外部関数宣言
 */
extern const MONITOR_TABLE CommonCmdTbl[];
extern void dbTermShowRegRO(short offset, const char* name, short format, short length);


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： ターミナル初期化
 * 
 * @note： 受信パラメータの初期化とUARTポート初期化
 * 
 * @param  none
 * @retval short err : 0 = 正常終了
 * 
 */
short dbTerm_initialize(void)
{
	int         i;
	short       err;
	SCI_CONFIG  term;

	for (i=0; i<MAX_CMDT_LENGTH; i++) {
		gRxStrings[i] = 0x00;
	}
	gRxStringsPos = 0;
	gRxStringsLen = 0;
	gRxInSeqMode  = 0;

	// initialize UART
	term.Baud   = Baud115200;	// 115200bps
	term.DtLen  = DATALen8; 	// Data = 8bit
	term.StLen  = STOPLEN1; 	// Stop = 1bit
	term.Parity = PARITYNONE;	// Npn Parity
	term.LFCode = LFC_CRLF;   	// <CR><LF>
	gLFcode = CHR_LF;

	err = init_UART(&term);
	if (err == 0) {
		print_char('\n');
		print_string("**********************************************\n");
		print_string("*  MKY Device Evaluation Board Terminal      *\n");
		print_string("*      May. 25, 2023                         *\n");
		print_string("*  copyright (C) 2022- StepTechnica Co.,Ltd. *\n");
		print_string("**********************************************\n");
		showDbTermPrompt();
	}

	return(err);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： ターミナルメインルーチン
 * 
 * @note： mainルーチンから呼ばれるエントリ
 *         文字入力を受け改行コードを認識すると内容を解釈して処理する
 * 
 * @param  none
 * @retval short err : 0 = 正常終了
 * 
 */
short dbTerm_main(void)
{
	short	err = 0;
	unsigned char	lastDt = 0x00;

	err = chkRxdata(&lastDt);
	if (err != NoData) {
		if (lastDt == gLFcode) {
			err = purseCmd();
		}
	}

	return(err);
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 受信データ確認
 * 
 * @note： mainルーチンから呼ばれるエントリ
 *         文字入力を受け改行コードを認識すると内容を解釈して処理する
 * 
 * @param  unsigned char *last : 最終受信文字を返す
 * @retval NoData = 正常終了
 *         CHR_LF = 改行コード受信
 *         CHR_CR = 改行コード受信
 *         reBuf[rp_rxBuf] = 受信エラー
 * 
 */
static short chkRxdata(unsigned char *last)
{
	unsigned char	rd;
	short        	err;
	unsigned short	rp;

	*last = 0x00;
	// check pointer
	rp  = (rp_rxBuf + 1);
	rp &= MSK_POINTER;
	if (rp == wp_rxBuf ) return(NoData);

	do {
		// load receive data
		rp_rxBuf++;
		rp_rxBuf &= MSK_POINTER;
		rd  = rxBuf[rp_rxBuf];
		err = reBuf[rp_rxBuf];
		if (err != 0) return(err);

		switch(rd) {
		case CHR_BS: 	// BackSpace
			if (gRxStringsPos > 0) {
				print_char(CHR_BS);

				gRxStringsPos--;
				gRxStringsLen--;
				gRxStrings[gRxStringsPos] = CHR_NUL;
			}
			break;
		case CHR_LF: 	// LineFeed
			print_char(rd);		//	EchoBack
			if (rd == gLFcode) {
				*last = rd;
				gRxStrings[gRxStringsLen] = CHR_NUL;
				return(rd);
			}
			break;
		case CHR_CR: 	// CarriageReturn
			print_char(rd);		//	EchoBack
			if (rd == gLFcode) {
				*last = rd;
				gRxStrings[gRxStringsLen] = CHR_NUL;
				return(rd);
			}
			break;
		case CHR_ESC: 	// Escape
			print_char(rd);		//	EchoBack
			if (gRxInSeqMode == 0) {
				gRxInSeqMode = 1 ;
			} else {
				gRxInSeqMode = 0 ;
			}
			break;
		default :
			print_char(rd);		//	EchoBack
			switch (gRxInSeqMode) {
			case 1 :
				if (rd == '[') {
					gRxInSeqMode = 2 ;
				} else {
					gRxInSeqMode = 0 ;
				}
				break;
			case 2:
				if (rd == 'C') {
					// RIGHT
					if (gRxStringsPos == gRxStringsLen) {
						// end
					} else {
						gRxStringsPos++;
					}
				} else if (rd == 'D') {
					// LEFT
					if (gRxStringsPos == 0) {
						// end
					} else {
						gRxStringsPos--;
					}
				}
				gRxInSeqMode = 0 ;
				break;
			default :
				gRxStrings[gRxStringsPos] = rd;
				if (gRxStringsPos == gRxStringsLen) {
					gRxStringsPos++;
					gRxStringsLen++;
					gRxStrings[gRxStringsPos] = CHR_NUL;
				} else {
					gRxStringsPos++;
				}
				break;
			}
			break;
		}

		rp  = (rp_rxBuf + 1);
		rp &= MSK_POINTER;
	} while ( rp != wp_rxBuf );

	return(NoData);

}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 受信データ解釈
 * 
 * @note： 受信した文字列からコマンド処理を呼び出す
 * 
 * @param  none
 * @retval 0 = 正常終了
 *         < 0 = 解釈できない場合
 * 
 */
static short purseCmd(void)
{
	int     i;
	int     argc ;
	char    *argv[10] ;							// モニタコマンド引数列
	short    (*pfunction)(int argc, char *argv[]) ;

	gRxStringsPos = 0;
	gRxStringsLen = 0;
	gRxInSeqMode = 0;

	memset(argv, 0, sizeof(argv)) ;
	argc = divideToken(gRxStrings, argv) ;
	if(subState.Kind != SubProcessNone)
	{
		subDbTermProcess(argc, argv);
		return(0);
	}
	else
	{
		if (argc != 0) {
			for ( i = 0; CommonCmdTbl[i].command != 0x0000; i++ ) {
				if (memcmp(CommonCmdTbl[i].command, argv[0], CommonCmdTbl[i].cmdLen) == 0) {
					pfunction = CommonCmdTbl[i].myfunction;
					if (pfunction != NULL) {
						pfunction(argc, argv) ;
						return(0);
					}
				}
			}
			print_string("<");
			print_string(argv[0]);
			print_string("> ");
			print_string("Command not support ! \n");
			showDbTermPrompt();
			return(-1);
		}
	}
	if (strlen(argv[0]) == 0) {
		showDbTermPrompt();
		return(-2);
	}

	showDbTermPrompt();
	return(-1);
}



/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [MD] コマンド：メモリダンプ
 * 
 * @note： 指定された領域のメモリダンプ
 *           argc == 2 : MD [address]
 *           argc == 3 : MD [Format] [address]
 *           argc == 3 : MD [address] [length]
 *           argc == 4 : MD [Format] [address] [length]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_md(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short length;	//バイト単位長さ。
	short mask;

	if (argc == 2) {
		format = 1;		// default = "-B"
		offset = getHexStrings(argv[1], format);
		length = 0x100;		// default = 256byte
	} else if (argc == 3) {
		if (argv[1][0] == '-') {
			format = getFormatOption(argv[1]);
			if (format < 0) {
				print_string("unrecognized <Format> strings.\n");
				showDbTermPrompt();
				return(-1);
			}
			offset = getHexStrings(argv[2], format);
			length = 0x100;		// default = 256byte
		} else {
			format = 1;		// default = "-B"
			offset = getHexStrings(argv[1], format);
			length = getHexStrings(argv[2], format);
		}
	} else if (argc == 4) {
		format = getFormatOption(argv[1]);
		if (format < 0) {
			print_string("unrecognized <Format> strings.\n");
			showDbTermPrompt();
			return(-1);
		}
		offset = getHexStrings(argv[2], format);
		length = getHexStrings(argv[3], format);
	} else {
		print_string("???\n");
		showDbTermPrompt();
		return(-1);
	}

	if (offset < 0) {
		print_string("unrecognized <address> strings.\n");
		showDbTermPrompt();
		return(-1);
	}
	if (length < 0) {
		print_string("unrecognized <length> strings.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アクセスできる範囲に制限する
	mask = getFormatMask(format);
	if (mask < 0) {
		showDbTermPrompt();
		return(-1);
	}
	offset &= ~mask;
	//length &= ~mask;
	if (MAX_ACCESS_SIZE <= offset) {
		offset = MAX_ACCESS_SIZE;
	}
	if (MAX_ACCESS_SIZE <= (offset + length)) {
		length = MAX_ACCESS_SIZE - offset;
	}
	//応答を送信する。
	md_dump(offset, length, format);

	showDbTermPrompt();

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [MW] コマンド：書き込み
 * 
 * @note： 指定されたメモリへの書き込み
 *           argc == 2 : MW [address]
 *           argc == 3 : MW [Format] [address]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mw(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short mask;

	if (argc == 2) {
		format = 2;		// default = "-W"
		offset = getHexStrings(argv[1], format);
	} else if (argc == 3) {
		format = getFormatOption(argv[1]);
		if (format < 0) {
			print_string("unrecognized <Format> strings.\n");
			showDbTermPrompt();
			return(-1);
		}
		offset = getHexStrings(argv[2], format);
	} else {
		print_string("???\n");
		showDbTermPrompt();
		return(-1);
	}

	if (offset < 0) {
		print_string("unrecognized <address> strings.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アクセスできる範囲に制限する
	mask = getFormatMask(format);
	if (mask < 0) {
		return(-1);
	}
	offset &= ~mask;
	//length &= ~mask;
	if ((MAX_ACCESS_SIZE - format) <= offset) {
		offset = MAX_ACCESS_SIZE - format;
	}

	// 対話コマンド開始
	subDbTermStart(SubProcessContinueWrite, offset, format, 0, 0);

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [MM] コマンド：モディファイ
 * 
 * @note： 指定されたメモリのモディファイ
 *           argc == 2 : MM [address]
 *           argc == 3 : MM [Format] [address]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mm(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short mask;

	if (argc == 2) {
		format = 2;		// default = "-W"
		offset = getHexStrings(argv[1], format);
	} else if (argc == 3) {
		format = getFormatOption(argv[1]);
		if (format < 0) {
			print_string("unrecognized <Format> strings.\n");
			showDbTermPrompt();
			return(-1);
		}
		offset = getHexStrings(argv[2], format);
	} else {
		print_string("???\n");
		showDbTermPrompt();
		return(-1);
	}

	if (offset < 0) {
		print_string("unrecognized <address> strings.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アクセスできる範囲に制限する
	mask = getFormatMask(format);
	if (mask < 0) {
		showDbTermPrompt();
		return(-1);
	}
	offset &= ~mask;
	//length &= ~mask;
	if ((MAX_ACCESS_SIZE - format) <= offset) {
		offset = MAX_ACCESS_SIZE - format;
	}

	// 対話コマンド開始
	subDbTermStart(SubProcessContinueReadWrite, offset, format, 0, 0);

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [DI] コマンド：メモリ書込み(ダイレクト)
 * 
 * @note： メモリ読出し
 *           argc == 3 : DI [Format] [address]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_din(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short length;
	short mask;
	unsigned char rdDt[MAX_FORMAT_BYTES];
	char s[BUFFER_MAX];

	if (argc == 2) {
		format = 2;		// default = "-W"
		offset = getHexStrings(argv[1], format);
	} else if (argc == 3) {
		format = getFormatOption(argv[1]);
		if (format < 0) {
			print_string("unrecognized <Format> strings.\n");
			showDbTermPrompt();
			return(-1);
		}
		offset = getHexStrings(argv[2], format);
	} else {
		print_string("???\n");
		showDbTermPrompt();
		return(-1);
	}

	if (offset < 0) {
		print_string("unrecognized <address> strings.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アクセスできる範囲に制限する
	if (offset % format) {
		print_string("offset address is not match for format.\n");
		showDbTermPrompt();
		return(-1);
	}

	mask = getFormatMask(format);
	if (mask < 0) {
		showDbTermPrompt();
		return(-1);
	}
	offset &= ~mask;
	//length &= ~mask;
	if ((MAX_ACCESS_SIZE - format) <= offset) {
		offset = MAX_ACCESS_SIZE - format;
	}

	length = MKY_ReadBlock(offset, rdDt, format);
	if (length == format) {
		//アドレス表示。
		sprintf(s, "[DI] %08X : ", MKY_MEMORY_BASE + offset);
		print_string(s);

		// 内容表示
		showMemValue(rdDt, format);
		print_char('\n');
		showDbTermPrompt();
	} else {
		print_string("cannot read data.\n");
		showDbTermPrompt();
		return(-1);
	}

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [DO] コマンド：メモリ書き込み書込み(ダイレクト)
 * 
 * @note： メモリ書込み
 *           argc == 4 : DO [Format] [address] [data]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_dout(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short length;	//バイト単位長さ。
	short mask;
	unsigned long long wrDt = 0UL;
	unsigned char rdDtPre[MAX_FORMAT_BYTES];
	unsigned char rdDtAft[MAX_FORMAT_BYTES];
	char s[BUFFER_MAX];
	char* endptr;

	if (argc == 3) {
		format = 2;		// default = "-W"
		offset = getHexStrings(argv[1], format);
		wrDt = strtoull(argv[2], &endptr, 16);
	} else if (argc == 4) {
		format = getFormatOption(argv[1]);
		if (format < 0) {
			print_string("unrecognized <Format> strings.\n");
			showDbTermPrompt();
			return(-1);
		}
		offset = getHexStrings(argv[2], format);
		wrDt = strtoull(argv[3], &endptr, 16);
	} else {
		print_string("???\n");
		showDbTermPrompt();
		return(-1);
	}

	if (offset < 0) {
		print_string("unrecognized <address> strings.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アクセスできる範囲に制限する
	if (offset % format) {
		print_string("offset address is not match for format.\n");
		showDbTermPrompt();
		return(-1);
	}

	mask = getFormatMask(format);
	if (mask < 0) {
		showDbTermPrompt();
		return(-1);
	}
	offset &= ~mask;
	//length &= ~mask;
	if ((MAX_ACCESS_SIZE - format) <= offset) {
		offset = MAX_ACCESS_SIZE - format;
	}

#ifdef SWAP_BIGENDIAN
	if(GetAccessEndian() == AccessEndianBig)
	{
		SwapLongLong(&wrDt);
		switch(format)
		{
		case 1:
			wrDt = wrDt >> 56;
			break;
		case 2:
			wrDt = wrDt >> 48;
			break;
		case 4:
			wrDt = wrDt >> 32;
			break;
		}
	}
#endif

	length = MKY_ReadBlock(offset, rdDtPre, format);
	if (length != format) {
		print_string("cannot read data before write.\n");
		showDbTermPrompt();
		return(-1);
	}

	length = MKY_WriteBlock(offset, &wrDt, format);
	if (length != format) {
		print_string("cannot write data.\n");
		showDbTermPrompt();
		return(-1);
	}

	length = MKY_ReadBlock(offset, rdDtAft, format);
	if (length != format) {
		print_string("cannot read data after write.\n");
		showDbTermPrompt();
		return(-1);
	}

	//アドレス表示。
	sprintf(s, "[D0] %08X : ", MKY_MEMORY_BASE + offset);
	print_string(s);

	// 内容表示：書き換え前
	showMemValue(rdDtPre, format);
	print_char('\n');
	showDbTermPrompt();

	print_string(" --> ");

	// 内容表示：書き換え後
	showMemValue(rdDtAft, format);
	print_char('\n');
	showDbTermPrompt();

	return(0);
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 書式オプションの解釈
 * 
 * @note： 文字列から指定された書式を取得する
 * 
 * @param  char* format  : 対象の文字列
 * @retval n   = 書式(バイト数)
 *         < 0 = 解釈エラー
 * 
 */
short getFormatOption(char* format)
{
	if ( strlen(format) != 2 )	return(-1);

	if ( format[0] == '-' ) {
		if (format[1] == 'B')	    return(1);
		else if (format[1] == 'W')	return(2);
		else if (format[1] == 'L')	return(4);
		else if (format[1] == 'D')	return(8);
		else                     	return(-2);
	}
	return(-1);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： マスクするバイト数の取得
 * 
 * @note： 書式からマスクする(読み飛ばすアドレス)バイト数を取得する
 * 
 * @param  short format  : 書式
 * @retval n   = マスク数(バイト数)
 *         < 0 = 解釈エラー
 * 
 */
static short getFormatMask(short format)
{
	if (format == 1)	    return(0);
	else if (format == 2)	return(1);
	else if (format == 4)	return(3);
	else if (format == 8)	return(7);
	else                    return(-1);

	return(-1);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリダンプ
 *
 * @note： 書式に従い、関数を振り分ける
 *
 * @param  short offset  : 先頭アドレス
 * @param  short length  : データ長
 * @param  short format  : 書式
 * @retval none
 *
 */
void md_dump(short offset, short length, short format)
{
	switch(format)
	{
	case 1:
		dump_byte(offset, length, format);
		break;
	case 2:
		dump_word(offset, length, format);
		break;
	case 4:
		dump_long(offset, length, format);
		break;
	case 8:
		dump_double(offset, length, format);
		break;
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリダンプ[Byte]
 * 
 * @note： バイト(1Byte)単位でメモリをダンプする
 * 
 * @param  short offset  : 先頭アドレス
 * @param  short length  : データ長
 * @param  short format  : 書式
 * @retval none
 * 
 */
static void dump_byte(short offset, short length, short format)
{
	unsigned char line[LINE_BYTES];
	unsigned char *p;
	char s[BUFFER_MAX];
	int i, j;

	print_string(HEADER_BYTE1);
	print_string(HEADER_BYTE2);

	while( length > 0 )
	{
		MKY_ReadBlock(offset, line, LINE_BYTES);

		//アドレス表示。
		sprintf(s, " %08X :", MKY_MEMORY_BASE + offset);
		print_string(s);

		//Hex表示。
		p = (unsigned char *)line;
		j = length;
		for( i=0; i<LINE_BYTES; i+=1, j-=1 )
		{
			if( j > 0 )
			{
				sprintf(s, " %02X", *p++);
				print_string(s);
			}
			else
			{
				print_string("   ");
			}
		}

		print_string(" ::  ");

		//ASCII表示。
		dump_ascii_line(line, length);

		print_char('\n');

		offset += LINE_BYTES;
		length -= ( length < LINE_BYTES )? length : LINE_BYTES;
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリダンプ[Word]
 * 
 * @note： ワード(2Byte)単位でメモリをダンプする
 * 
 * @param  short offset  : 先頭アドレス
 * @param  short length  : データ長
 * @param  short format  : 書式
 * @retval none
 * 
 */
static void dump_word(short offset, short length, short format)
{
	unsigned char line[LINE_BYTES];
	unsigned short *p;
	char s[BUFFER_MAX];
	int i, j;

	print_string(HEADER_WORD1);
	print_string(HEADER_WORD2);

	while( length > 0 )
	{
		MKY_ReadBlock(offset, line, LINE_BYTES);

		//アドレス表示。
		sprintf(s, " %08X :", MKY_MEMORY_BASE + offset);
		print_string(s);

		//ヘキサ表示。
		p = (unsigned short *)line;
		j = length;
		for( i=0; i<LINE_BYTES; i+=2, j-=2 )
		{
			if( j > 0 )
			{
				// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
				if(GetAccessEndian() == AccessEndianBig)
				{
					SwapWord(p);
				}
#endif
				sprintf(s, " %04X", *p++);
				print_string(s);
			}
			else
			{
				print_string("     ");
			}
		}

		print_char('\n');

		offset += LINE_BYTES;
		length -= ( length < LINE_BYTES )? length : LINE_BYTES;
	}

}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリダンプ[Long]
 * 
 * @note： ロング(4Byte)単位でメモリをダンプする
 * 
 * @param  short offset  : 先頭アドレス
 * @param  short length  : データ長
 * @param  short format  : 書式
 * @retval none
 * 
 */
static void dump_long(short offset, short length, short format)
{
	print_string(HEADER_LONG1);
	print_string(HEADER_LONG2);

	unsigned char line[LINE_BYTES];
	unsigned long *p;
	char s[BUFFER_MAX];
	int i, j;

	while( length > 0 )
	{
		MKY_ReadBlock(offset, line, LINE_BYTES);

		//アドレス表示。
		sprintf(s, " %08X :", MKY_MEMORY_BASE + offset);
		print_string(s);

		//ヘキサ表示。
		p = (unsigned long *)line;
		j = length;
		for( i=0; i<LINE_BYTES; i+=4, j-=4 )
		{
			if( j > 0 )
			{
				// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
				if(GetAccessEndian() == AccessEndianBig)
				{
					SwapLong(p);
				}
#endif
				sprintf(s, " %08lX", *p++);
				print_string(s);
			}
			else
			{
				print_string("         ");
			}
		}

		print_char('\n');

		offset += LINE_BYTES;
		length -= ( length < LINE_BYTES )? length : LINE_BYTES;
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリダンプ[LongLong]
 * 
 * @note： ダブルロング(8Byte)単位でメモリをダンプする
 * 
 * @param  short offset  : 先頭アドレス
 * @param  short length  : データ長
 * @param  short format  : 書式
 * @retval none
 * 
 */
static void dump_double(short offset, short length, short format)
{
	unsigned char line[LINE_BYTES];
	unsigned long long *p;
	char s[BUFFER_MAX];
	int i, j;

	print_string(HEADER_DOUBLE1);
	print_string(HEADER_DOUBLE2);

	while( length > 0 )
	{
		MKY_ReadBlock(offset, line, LINE_BYTES);

		//アドレス表示。
		sprintf(s, " %08X :", MKY_MEMORY_BASE + offset);
		print_string(s);

		//ヘキサ表示。
		p = (unsigned long long *)line;
		j = length;
		for( i=0; i<LINE_BYTES; i+=8, j-=8 )
		{
			if( j > 0 )
			{
				// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
				if(GetAccessEndian() == AccessEndianBig)
				{
					SwapLongLong(p);
				}
#endif
				s[0] = ' ';
				Uint64ToHexStr(&s[1], *p++);
				print_string(s);
			}
			else
			{
				print_string("                 ");
			}
		}

		print_char('\n');

		offset += LINE_BYTES;
		length -= ( length < LINE_BYTES )? length : LINE_BYTES;
	}

}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 文字列のASCII表示
 * 
 * @note： 文字列をASCIIで表示する
 *         ASCII でない場合は '.' とする
 * 
 * @param  unsigned char* line  : 対象文字列先頭アドレス
 * @param  short length  : データ長
 * @retval none
 * 
 */
void dump_ascii_line(unsigned char* line, short length)
{
	char s[BUFFER_MAX];
	char* p = &s[0];
	int i;

	for( i=0; i<LINE_BYTES && i<length ; ++i, ++line, ++p )
	{
		if( isgraph(*line) )
		{
			*p = *line;
		}
		else
		{
			*p = '.';
		}
	}
	*p = '\0';
	print_string(s);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： サブコマンド開始
 * 
 * @note： MW/MM/REG のサブコマンド
 * 
 * @param  SubProcessKind kind  : モード
 * @param  short offset  : メモリアドレス
 * @param  short format  : 書式
 * @param  const char* name  : レジスタ名
 * @param  int count  : 回数
 * @retval none
 * 
 */
void subDbTermStart(SubProcessKind kind, short offset, short format, const char* name, int count)
{
	subState.Kind = kind;
	subState.TargetOffset = offset;
	subState.Format = format;
	subState.TargetName = name;
	subState.Count = count;

	// 操作説明表示
	switch(subState.Kind)
	{
    case SubProcessNone:
    case SubProcessSpecifiedCountReadWrite:
		break;
    case SubProcessContinueReadWrite:
    case SubProcessContinueWrite:
    	subDbTermContinueAccessOpDesc();
		break;
	}
	showSubDbTermPrompt();
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： サブコマンドの説明表示
 * 
 * @note： サブコマンド中に使えるコマンドを表示
 * 
 * @param  none
 * @retval none
 * 
 */
static void subDbTermContinueAccessOpDesc(void)
{
	print_string(" HexStrings = write value\n"); 
	print_string(" '.' = process end\n");        
	print_string(" '+' = next address\n");       
	print_string(" '-' = previous address\n\n"); 
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： サブコマンド受信処理
 * 
 * @note： サブコマンド実行中の受信データ解釈
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval none
 * 
 */
static void subDbTermProcess(int argc, char *argv[])
{
	unsigned long long value = 0UL;
	char* endptr;

	switch(subState.Kind)
	{
    case SubProcessNone:
		break;
    case SubProcessContinueReadWrite:
    case SubProcessContinueWrite:
		// 書込データまたは操作の取得
		if( argc == 0 || argv[0][0] == '+')
		{
			subDbTermNextAddress();
		}
		else if(argv[0][0] == '.')
		{
			// コマンド内ステート終了処理
			subState.Kind = SubProcessNone;

			showDbTermPrompt();
		}
		else if(argv[0][0] == '-')
		{
			// 前のアドレスへ
			if(subState.Format <= subState.TargetOffset)
			{
				subState.TargetOffset -= subState.Format;
			}
		}
		else
		{
			// write value
			value = strtoull(argv[0], &endptr, 16);

			if( *endptr == '\0' )
			{
				// 書込
				subDbTermWrite(value);
				subDbTermNextAddress();
			}
			else
			{
				// 変換できない文字なら現状維持
			}
		}
		break;
    case SubProcessSpecifiedCountReadWrite:
		if( argc == 0 )
		{
			subState.Kind = SubProcessNone;

			showDbTermPrompt();
			break;
		}

		// write value
		value = strtoull(argv[0], &endptr, 16);
		if( *endptr == '\0' )
		{
			// 書込
			subDbTermWrite(value);
			subDbTermNextAddress();
			if(subState.Count <= 0)
			{
				subState.Kind = SubProcessNone;

				showDbTermPrompt();
			}
		}
		else
		{
			// 変換できない文字なら現状維持
		}
		break;
	}
	showSubDbTermPrompt();
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 次のアドレス設定
 * 
 * @note： サブコマンド実行中のアドレス増加
 * 
 * @param  none
 * @retval none
 * 
 */
static void subDbTermNextAddress(void)
{
	// 次のアドレスへ
	if(subState.TargetOffset + subState.Format < MAX_ACCESS_SIZE)
	{
		subState.TargetOffset += subState.Format;
		if(0 < subState.Count)
		{
			subState.Count--;
		}
	}
	else
	{
		// アクセス範囲を超える場合は現状維持
	}
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： データ書き込み
 * 
 * @note： サブコマンド実行中のデータ書き込み
 * 
 * @param  unsigned long long value : 書き込みデータ
 * @retval none
 * 
 */
static void subDbTermWrite(unsigned long long value)
{
#ifdef SWAP_BIGENDIAN
	if(GetAccessEndian() == AccessEndianBig)
	{
		SwapLongLong(&value);
		switch(subState.Format)
		{
		case 1:
			value = value >> 56;
			break;
		case 2:
			value = value >> 48;
			break;
		case 4:
			value = value >> 32;
			break;
		}
	}
#endif
	//<yu>MKY_WriteBlock_Bus16(subState.TargetOffset, &value, subState.Format);
	MKY_WriteBlock(subState.TargetOffset, &value, subState.Format);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリ値表示
 * 
 * @note： サブコマンド実行中のデータ表示
 * 
 * @param  short offset : アドレス
 * @param  const char* name : レジスタ名
 * @param  short format : 書式
 * @param  short showAscii : ASCII表示指示
 * @retval none
 * 
 */
void dBTermShowReg(short offset, const char* name, short format, short showAscii)
{
	unsigned char data[MAX_FORMAT_BYTES];
	char s[BUFFER_MAX];

	// 操作対象表示
	if(name != 0)
	{
		sprintf(s, "[%s]", name);
		print_string(s);
	}

	//アドレス表示。
	sprintf(s, "%08X :", MKY_MEMORY_BASE + offset);
	print_string(s);

	// 内容表示
	switch(format)
	{
	case 1:
		MKY_ReadBlock(offset, data, 1);
		sprintf(s, " %02X", data[0]);
		print_string(s);
		break;
	case 2:
		MKY_ReadBlock(offset, data, 2);

		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapWord((uint16_t*)data);
		}
#endif
		sprintf(s, " %04X", *((uint16_t*)data));
		print_string(s);
		break;
	case 4:
		MKY_ReadBlock(offset, data, 4);

		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLong((uint32_t*)data);
		}
#endif
		sprintf(s, " %08lX", *((uint32_t*)data));
		print_string(s);
		break;
	case 8:
		MKY_ReadBlock(offset, data, 8);

		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLongLong((uint64_t*)data);
		}
#endif
		//Uint64ToHexStr(s, *((uint64_t*)data));
		s[0] = ' ';
		Uint64ToHexStr(&s[1], *((uint64_t*)data));
		print_string(s);
		break;
	}

	// アスキー表示
	if(showAscii)
	{
		print_string(" :: ");
		dump_ascii_line((uint8_t*)data, format);
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： プロンプト表示
 * 
 * @note： 標準プロンプトの表示
 * 
 * @param  none
 * @retval none
 * 
 */
void showDbTermPrompt(void)
{
	print_string(strPROMPT);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： プロンプト表示
 * 
 * @note： サブコマンド中のプロンプトの表示
 * 
 * @param  none
 * @retval none
 * 
 */
static void showSubDbTermPrompt(void)
{
	char s[BUFFER_MAX];

	switch(subState.Kind)
	{
    case SubProcessNone:
		break;
    case SubProcessContinueReadWrite:
    case SubProcessSpecifiedCountReadWrite:
		// FmtSend_ReadWritePrompt(subState.TargetOffset, subState.TargetName, subState.Format);
		dbTermShowRegRO(subState.TargetOffset, subState.TargetName, subState.Format, 1);
		print_string(" --> ");
		break;
    case SubProcessContinueWrite:
		// FmtSend_WritePrompt(subState.TargetOffset, subState.TargetName, subState.Format);
		// 操作対象表示
		if(strlen(subState.TargetName) != 0)
		{
			sprintf(s, "[%s]", subState.TargetName);
			print_string(s);
		}

		//アドレス表示。
		sprintf(s, "%08x :", MKY_MEMORY_BASE + subState.TargetOffset);
		print_string(s);

		// プロンプト表示
		print_string(" --> ");
		
		break;
	}
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリ値の表示
 * 
 * @note： サブコマンド中のプロンプトの表示
 * 
 * @param  unsigned char data[] : 表示データ
 * @param  short format : 書式
 * @retval none
 * 
 */
void showMemValue(unsigned char data[], short format)
{
	char s[BUFFER_MAX];

	// 内容表示
	switch(format)
	{
	case 1:
		sprintf(s, " %02X", data[0]);
		print_string(s);
		break;
	case 2:
		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapWord((uint16_t*)data);
		}
#endif
		sprintf(s, " %04X", *((uint16_t*)data));
		print_string(s);
		break;
	case 4:
		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLong((uint32_t*)data);
		}
#endif
		sprintf(s, " %08lX", *((uint32_t*)data));
		print_string(s);
		break;
	case 8:
		// エンディアンによる表示変更
#ifdef SWAP_BIGENDIAN
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLongLong((uint64_t*)data);
		}
#endif
		s[0] = ' ';
		Uint64ToHexStr(&s[1], *((uint64_t*)data));
		print_string(s);
		break;
	}
}

