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

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

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "dbTerm.h"
#include "ctrlUART4.h"
#include "MKYDevice.h"
#include "MKY43.h"
#include "Utility.h"

/* -----------------------------------------------------------------------------
 * #defineマクロ定義
 */
#define BUFFER_MAX			128
#define MAX_FORMAT_BYTES	8

/* -----------------------------------------------------------------------------
 * typedef定義
 */
typedef struct
{
	char Name[8];		/**< レジスタ名 */
	short Offset;		/**< レジスタの先頭を指すオフセット */
	short Length;		/**< レジスタのWord数 */
	short ReadOnly;	/**< 読取専用レジスタ属性 */
}RegCommandInfo_st;

/* -----------------------------------------------------------------------------
 * 変数定義
 */
static RegCommandInfo_st RegCommandTable[] =
{
	{"RFR",    OFS_regRFR,    4, 1 },
	{"LFR",    OFS_regLFR,    4, 1 },
	{"MFR",    OFS_regMFR,    4, 1 },
	{"DRFR",   OFS_regDRFR,   4, 1 },
	{"LGR",    OFS_regLGR,    4, 0 },
	{"MGR",    OFS_regMGR,    4, 0 },
	{"DRCR",   OFS_regDRCR,   4, 0 },
	{"RHCR0",  OFS_regRHCR0,  1, 0 },
	{"RHCR1",  OFS_regRHCR1,  1, 0 },
	{"WHCR0",  OFS_regWHCR0,  1, 0 },
	{"WHCR1",  OFS_regWHCR1,  1, 0 },
	{"MSLR",   OFS_regMSLR,   1, 0 },
	{"MSRR",   OFS_regMSRR,   1, 1 },
	{"MESR",   OFS_regMESR,   1, 0 },
	{"MSCR",   OFS_regMSCR,   1, 0 },
	{"MR0CR",  OFS_regMR0CR,  1, 0 },
	{"MR1CR",  OFS_regMR1CR,  1, 0 },
	{"CCTR",   OFS_regCCTR,   1, 0 },
	{"UTCR",   OFS_regUTCR,   1, 0 },
	{"QCR",    OFS_regQCR,    1, 0 },
	{"NFSR",   OFS_regNFSR,   1, 0 },
	{"FSR",    OFS_regFSR,    1, 1 },
	{"BCR",    OFS_regBCR,    1, 0 },
	{"INT0CR", OFS_regINT0CR, 1, 0 },
	{"INT1CR", OFS_regINT1CR, 1, 0 },
	{"IT0CR",  OFS_regIT0CR,  1, 0 },
	{"IT1CR",  OFS_regIT1CR,  1, 0 },
	{"INT0SR", OFS_regINT0SR, 1, 0 },
	{"INT1SR", OFS_regINT1SR, 1, 0 },
	{"SSR",    OFS_regSSR,    1, 0 },
	{"SCR",    OFS_regSCR,    1, 0 },
	{"CCR",    OFS_regCCR,    4, 1 },
	{"RHPB0",  OFS_regRHPB0,  4, 1 },
	{"RHPB1",  OFS_regRHPB1,  4, 1 },
	{"WHPB0",  OFS_regWHPB0,  4, 0 },
	{"WHPB1",  OFS_regWHPB1,  4, 0 },
};

static int REG_COMMAND_TABLE_COUNT = sizeof(RegCommandTable) / sizeof(RegCommandInfo_st);

/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */
short dbTerm_help(int argc, char *argv[]);
short dbTerm_gm(int argc, char *argv[]);
short dbTerm_reg(int argc, char *argv[]);
short dbTerm_hi(int argc, char *argv[]);
short dbTerm_ho(int argc, char *argv[]);
static void dbTermShowReg(char* regNames[], int count);
static void dbTermShowCCR(short offset);
void dbTermShowRegRO(short offset, const char* name, short format, short length);

/* -----------------------------------------------------------------------------
 * コマンド処理テーブル
 */
const MONITOR_TABLE CommonCmdTbl[] =
{
	MONITOR_COMMAND("HELP" ,		dbTerm_help   ),	// ＨＥＬＰコマンド
	MONITOR_COMMAND("?"    ,		dbTerm_help   ),	// ＨＥＬＰコマンド
	MONITOR_COMMAND("MD"   ,		dbTerm_md     ),	// メモリダンプ
	MONITOR_COMMAND("MW"   ,		dbTerm_mw     ),	// メモリ書込み
	MONITOR_COMMAND("MM"   ,		dbTerm_mm     ),	// メモリ読み出し
	MONITOR_COMMAND("GM"   ,		dbTerm_gm     ),	// GMアクセス
	MONITOR_COMMAND("REG"  ,		dbTerm_reg    ),	// レジスタアクセス
	MONITOR_COMMAND("DIN"  ,		dbTerm_din    ),	// メモリ読み出し(ダイレクト)
	MONITOR_COMMAND("DOUT" ,		dbTerm_dout   ),	// メモリ書込み(ダイレクト)
//	MONITOR_COMMAND("HI"   ,		dbTerm_hi     ),	// メモリ読み出し(ハザードプロテクト)
//	MONITOR_COMMAND("HO"   ,		dbTerm_ho     ),	// メモリ書込み(ハザードプロテクト)
	// 終端マーカ
	{ NULL, 0,	NULL						}
};


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [HELP] コマンド：ヘルプ表示
 * 
 * @note： ヘルプメッセージの表示
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 * 
 */
short dbTerm_help(int argc, char *argv[])
{
	print_char('\n');
	print_string("Usage :\n");
	print_string("help or ? : show this massages\n");
	print_string("md   [Format] [address] <[byte length]> : memory dump\n");
	print_string("mm   [Format] [address]                 : memory mode\n");
	print_string("mw   [Format] [address]                 : memory mode write\n");
	print_string("gm  <[Format]>                          : GM area dump\n");
	print_string("reg <[Name]>                            : register modify\n");
	print_string("din  [Format] [address]                 : direct read\n");
	print_string("dout [Format] [address] [data]          : direct write\n");
	print_char('\n');
	print_string("     [Format] : \n");
	print_string("            -b = byte      : 1byte\n");
	print_string("            -w = word      : 2byte\n");
	print_string("            -l = long      : 4byte\n");
	print_string("            -d = long long : 8byte\n");
	print_char('\n');

	showDbTermPrompt();

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [GM] コマンド：GM領域のダンプ
 * 
 * @note： GM領域のメモリダンプ
 *           argc == 1 : GM 
 *           argc == 2 : GM [Format] 
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_gm(int argc, char *argv[])
{
	short format;	//書式。
	short ret;

	ret = 0;
	if (argc == 1) {
		format = 1;
	} else if (argc == 2) {
		format = getFormatOption(argv[1]);
	} else {
		format = 0;
		print_string("???\n");
		ret = -1;
	}
	if (format < 0) {
		print_string("unrecognized <Format> strings.\n");
		ret = -1;
	}

	if (ret == 0) {
		//応答を送信する。
		md_dump(MKY_OFS_GM, MKY_LEN_GM, format);
	}

	showDbTermPrompt();

	return(ret);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [REG] コマンド：レジスタモディファイ
 * 
 * @note： レジスタアクセス
 *           argc == 1 : REG 
 *           argc == 2 : REG [RegName]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_reg(int argc, char *argv[])
{
	short format;	//書式。
	const RegCommandInfo_st* info;
	int i;
	char* regNames[REG_COMMAND_TABLE_COUNT];	// レジスタ名一覧

	if (argc == 1) {
		// レジスタ一覧表示
		for (i = 0; i < REG_COMMAND_TABLE_COUNT; i++)
		{
			regNames[i] = RegCommandTable[i].Name;
		}
		dbTermShowReg(regNames, REG_COMMAND_TABLE_COUNT);

		showDbTermPrompt();

		return(0);
	} else if (argc == 2) {
		format = 2;
	} else {
		print_string("???\n");
		return(-1);
	}

	for (i = 0; i < REG_COMMAND_TABLE_COUNT; i++)
	{
		info = &RegCommandTable[i];
		if( strcmp(info->Name, argv[1]) == 0 )
		{
			format = 2 * info->Length;
			if( strcmp(info->Name, "CCR") == 0 )
			{
				// CCRのみ特殊表示
				dbTermShowCCR(info->Offset);

				showDbTermPrompt();

			}
			else if(info->ReadOnly)
			{
				dbTermShowRegRO(info->Offset, info->Name, format, 1);

				showDbTermPrompt();

			}
			else
			{
				// 対話コマンド開始
				subDbTermStart(SubProcessSpecifiedCountReadWrite, info->Offset, format, info->Name, 1);
			}
		}
	}

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [HI] コマンド：メモリ書込み(ハザードプロテクト)
 * 
 * @note： GM領域のメモリ読出し
 *           argc == 2 : HI [address]
 *           argc == 3 : HI [Format] [address]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_hi(int argc, char *argv[])
{
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short length;	//バイト単位長さ。
	unsigned char rdDt[MAX_FORMAT_BYTES];
	char s[BUFFER_MAX];

	if (argc == 2) {
		format = 8;		// default = "-D"
		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);
	}
	if ( offset >= OFS_GM_MAX ) {
		print_string("offset address is not GM area.\n");
		showDbTermPrompt();
		return(-1);
	}

	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： [HO] コマンド：メモリ書き込み書込み(ハザードプロテクト)
 * 
 * @note： GM領域のメモリ書込み
 *           argc == 3 : HO [address] [data]
 *           argc == 4 : HO [Format] [address] [data]
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_ho(int argc, char *argv[])
{
	int i;
	short format;	//書式。
	short offset;	//メモリアドレス オフセット。
	short memadrs;	//メモリアドレス。
	short index;	//メモリアドレス。
	short length;	//バイト単位長さ。
	unsigned long long wrDt = 0UL;
	unsigned char rdDtPre[MAX_FORMAT_BYTES];
	unsigned char ｗｒDtMod[MAX_FORMAT_BYTES];
	unsigned char rdDtAft[MAX_FORMAT_BYTES];
	unsigned char      *p8;
	unsigned short     *p16;
	unsigned long      *p32;
	unsigned long long *p64;
	char s[BUFFER_MAX];
	char* endptr;

	if (argc == 3) {
		format = 8;		// default = "-D"
		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);
	}
	if ( offset >= OFS_GM_MAX ) {
		print_string("offset address is not GM area.\n");
		showDbTermPrompt();
		return(-1);
	}

	// メモリブロック先頭アドレス
	index = (offset % 8);
	memadrs = offset - index;

	// 1 ブロック読出し
	length = MKY_ReadBlock(memadrs, rdDtPre, 8);
	if (length != format) {
		print_string("cannot read data before write.\n");
		showDbTermPrompt();
		return(-1);
	}

	// 書き込みデータ差し替え
	for (i=0; i<MAX_FORMAT_BYTES; i++) {
		ｗｒDtMod[i] = rdDtPre[i];
	}

	switch(format)
	{
	case 1:
		p8 = &ｗｒDtMod[index];
		*p8 = (uint8_t)wrDt;
		break;
	case 2:
		// エンディアンによる表示変更
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapWord((uint16_t*)&wrDt);
		}
		p16 = (uint16_t*)&ｗｒDtMod[index];
		*p16 = (uint16_t)wrDt;
		break;
	case 4:
		// エンディアンによる表示変更
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLong((uint32_t*)&wrDt);
		}
		p32 = (uint32_t*)&ｗｒDtMod[index];
		*p32 = (uint32_t)wrDt;
		break;
	case 8:
		// エンディアンによる表示変更
		if(GetAccessEndian() == AccessEndianBig)
		{
			SwapLongLong((uint64_t*)&wrDt);
		}
		p64 = (uint64_t*)&ｗｒDtMod[index];
		*p64 = (uint64_t)wrDt;
		break;
	}

	// 1 ブロック書き込み
	length = MKY_WriteBlock(memadrs, &ｗｒDtMod, 8);
	if (length != format) {
		print_string("cannot write data.\n");
		showDbTermPrompt();
		return(-1);
	}

	// 1 ブロック読出し
	length = MKY_ReadBlock(memadrs, rdDtAft, 8);
	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, 8);
	print_char('\n');
	showDbTermPrompt();

	print_string(" --> ");

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

	return(0);
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： レジスタの一覧表示
 * 
 * @note： レジスタ名を表示する
 * 
 * @param  char* regNames[] : レジスタ名
 * @param  int count  : 個数
 * @retval none
 * 
 */
static void dbTermShowReg(char* regNames[], int count)
{
	const int NewLineInterval = 7;
	char s[BUFFER_MAX];
	int i;

	for (i = 0; i < count; i++)
	{
		sprintf(s, "%-7s", regNames[i]);
		print_string(s);
		if(((i+1) % NewLineInterval) == 0)
		{
			print_char('\n');
		}
	}
	print_char('\n');
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： CCRレジスタの表示
 * 
 * @note： レジスタ名を表示する
 * 
 * @param  short offset : レジスタアドレス
 * @retval none
 * 
 */
static void dbTermShowCCR(short offset)
{
	int i;
	unsigned char data[MAX_FORMAT_BYTES];
	char s[BUFFER_MAX];

	// 操作対象表示
	print_string("[CCR]");

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

	// 内容表示
	MKY_ReadBlock(offset, data, 8);
	for (i=0; i<8; i++)
	{
		sprintf(s, " %02X", data[i]);
		print_string(s);
	}

	// アスキー表示
	print_string(" :: ");
	dump_ascii_line((uint8_t*)data, 8);
	print_char('\n');
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： CCRレジスタの表示
 * 
 * @note： レジスタ名を表示する
 * 
 * @param  short offset : レジスタアドレス
 * @param  const char* name : レジスタ名
 * @param  short format : 書式
 * @param  short length : 表示個数
 * @retval none
 * 
 */
void dbTermShowRegRO(short offset, const char* name, short format, short length)
{
	int i;

	for (i = 0; i < length; i++)
	{
		dBTermShowReg(offset, name, format, false);
		print_char('\n');
		offset += format;
	}
}
