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

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

#include <stdbool.h>
#include <stddef.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include "DebugMonitor\dbTerm.h"
#include "IO\ctrlUART4.h"
#include "MKY39\MKY39.h"
#include "HLSDevice\MKYDevice.h"
#include "IO\Utility.h"

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

/* -----------------------------------------------------------------------------
 * typedef定義
 */
typedef struct
{
	char  Name[8];   	/**< レジスタ名 */
	short OffsetT1;  	/**< MKY39-1 のレジスタの先頭を指すオフセット */
	short OffsetT2C1;	/**< MKY39-2 のセンター１側のレジスタの先頭を指すオフセット */
	short OffsetT2C2;	/**< MKY39-2 のレジスタの先頭を指すオフセット */
	short Length;    	/**< レジスタのWord数 */
	short ReadOnly;  	/**< 読取専用レジスタ属性 1=RO, 0=RW */
	short Individual;	/**< センターごとに個別のレジスタ>*/
}RegCommandInfo_st;

/* -----------------------------------------------------------------------------
 * 変数定義
 */
static RegCommandInfo_st RegCommandTable[] =
{
		{"LSR"  , OFS_regT1LSR  , OFS_regT2C1LSR  , OFS_regT2C2LSR  , 4, 1, 1},
		{"LSR3" , OFS_regT1LSR3 , OFS_regT2C1LSR3 , OFS_regT2C2LSR3 , 4, 1, 1},
		{"C1SR" , OFS_regT1C1SR , OFS_regT2C1C1SR , OFS_regT2C2C1SR , 1, 1, 1},
		{"C2SR" , OFS_regT1C2SR , OFS_regT2C1C2SR , OFS_regT2C2C2SR , 1, 1, 1},
		{"SSR"  , OFS_regT1SSR  , OFS_regT2C1SSR  , OFS_regT2C2SSR  , 1, 1, 1},
		{"INTCR", OFS_regT1INTCR, OFS_regT2C1INTCR, OFS_regT2C2INTCR, 1, 0, 1},
		{"INTSR", OFS_regT1INTSR, OFS_regT2C1INTSR, OFS_regT2C2INTSR, 1, 1, 1},
		{"INTRR", OFS_regT1INTRR, OFS_regT2C1INTRR, OFS_regT2C2INTRR, 1, 0, 1},
		{"SCR"  , OFS_regT1SCR  , OFS_regT2C1SCR  , OFS_regT2C2SCR  , 1, 0, 1},
		{"CNRR" , OFS_regT1CnRR , OFS_regT2C1CnRR , OFS_regT2C2CnRR , 1, 0, 1},
		{"BCR"  , OFS_regT1BCR  , OFS_regT2C1BCR  , OFS_regT2C2BCR  , 1, 0, 1},
		{"CCR"  , OFS_regT1CCR  , OFS_regT2CCR    , OFS_regT2CCR    , 5, 1, 0},
		{"GPDO" , OFS_regT1GPDO , OFS_regT2GPDO   , OFS_regT2GPDO   , 1, 0, 0},
		{"GPDI" , OFS_regT1GPDI , OFS_regT2GPDI   , OFS_regT2GPDI   , 2, 1, 0},
};

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

/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */
short dbTerm_help(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[]);
short dbTerm_mddo(int argc, char *argv[]);
short dbTerm_mddi(int argc, char *argv[]);
short dbTerm_mdcmdsr(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("REG"  ,		dbTerm_reg    ),	// レジスタアクセス
	MONITOR_COMMAND("DIN"  ,		dbTerm_din    ),	// メモリ読み出し(ダイレクト)
	MONITOR_COMMAND("DOUT" ,		dbTerm_dout   ),	// メモリ書込み(ダイレクト)
	MONITOR_COMMAND("DO"   ,		dbTerm_mddo   ),	// Do領域のダンプ
	MONITOR_COMMAND("DI"   ,		dbTerm_mddi   ),	// Di領域のダンプ
	MONITOR_COMMAND("CMDSR",		dbTerm_mdcmdsr),	// CMDSR領域のダンプ
	// 終端マーカ
	{ 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");
	if (gDeviceType == MKY39_1)
	{
		print_string("reg <[Name]>                            : register modify\n");
	}
	else if (gDeviceType == MKY39_2)
	{
		print_string("reg <[Name] <[Num]>>                    : register modify\n");
	}
		print_string("din  [Format] [address]                 : direct read\n");
		print_string("dout [Format] [address] [data]          : direct write\n");
	if (gDeviceType == MKY39_1)
	{
		print_string("cmdsr <[Format]>                        : CMDSR area dump\n");
		print_string("do <[Format]>                           : Do area dump\n");
		print_string("di <[Format]>                           : Di area dump\n");
	}
	else if (gDeviceType == MKY39_2)
	{
		print_string("cmdsr <[Format]> <[Num]>                : CMDSR area dump\n");
		print_string("do <[Format]> <[Num]>                   : Do area dump\n");
		print_string("di <[Format]> <[Num]>                   : Di area dump\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");
	if (gDeviceType == MKY39_2)
	{
		print_string("     [Num] : \n");
		print_string("         1 = First Center\n");
		print_string("         2 = Second Center\n");
	}
	print_char('\n');

	showDbTermPrompt();

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [REG] コマンド：レジスタモディファイ
 * 
 * @note： レジスタアクセス
 *           argc == 1 : REG 
 *           argc == 2 : REG [RegName]
 *           argc == 3 : Center Number(only MKY39-2)
 * 
 * @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];	// レジスタ名一覧
	char  regName[8];
	char  addName[4];
	short numCenter;

	numCenter = 1;

	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) {
		if (strlen(argv[1]) > 8) {
			print_string("The register name is too long.\n");
			return(-1);
		} else {
			strcpy(regName, argv[1]);
		}
		format = 2;
		numCenter = 1;

	} else if (argc == 3) {
		if (gDeviceType == MKY39_2) {
			if (strlen(argv[1]) > 8) {
				print_string("The register name is too long.\n");
				return(-1);
			} else {
				strcpy(regName, argv[1]);
			}
			format = 2;
			numCenter = atoi(argv[2]);
			if ((numCenter != 1) && (numCenter != 2)) {
				print_string("The Center value is not correct.\n");
				return(-1);
			}
		} else {
			print_string("Too mach arguments.\n");
			return(-1);
		}
	} else {
		print_string("Too mach arguments.\n");
		return(-1);
	}

	for (i = 0; i < REG_COMMAND_TABLE_COUNT; i++)
	{
		info = &RegCommandTable[i];
		if( strcmp(info->Name, regName) == 0 ) {
			format = 2 * info->Length;
			if( strcmp(info->Name, "CCR") == 0 ) {
				// CCRのみ特殊表示
				if (gDeviceType == MKY39_1)
				{
					dbTermShowCCR(info->OffsetT1);
				// } else if (gDeviceType == MKY39_2) {
				} else {
					dbTermShowCCR(info->OffsetT2C1);
				}
				showDbTermPrompt();

			} else if(info->ReadOnly) {
				if (gDeviceType == MKY39_1) {
					dbTermShowRegRO(info->OffsetT1, info->Name, format, 1);
				// } else if (gDeviceType == MKY39_2) {
				} else {
					if (info->Individual)
					{
						sprintf(addName, "-%d", numCenter);
						strcat(regName, addName);
					}
					if (numCenter == 1) {
						dbTermShowRegRO(info->OffsetT2C1, regName, format, 1);
					// } else if (numCenter == 2) {
					} else {
						dbTermShowRegRO(info->OffsetT2C2, regName, format, 1);
					}
				}
				showDbTermPrompt();

			}
			else
			{
				// 対話コマンド開始
				if (gDeviceType == MKY39_1) {
					subDbTermStart(SubProcessSpecifiedCountReadWrite, info->OffsetT1, format, info->Name, 1);
				// } else if (gDeviceType == MKY39_2) {
				} else {
					if (info->Individual)
					{
						sprintf(addName, "-%d", numCenter);
						strcat(regName, addName);
					}
					if (numCenter == 1) {
						subDbTermStart(SubProcessSpecifiedCountReadWrite, info->OffsetT2C1, format, regName, 1);
					// } else if (numCenter == 2) {
					} else {
						subDbTermStart(SubProcessSpecifiedCountReadWrite, info->OffsetT2C2, format, regName, 1);
					}
				}
			}
			return(0);
		}
	}

	print_string("The register name is not correct.\n");
	return(-1);
}


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

	format = 0;
	ret = 0;
	numCenter = 1;
	dSize = SZ_AREA;

	if (argc == 1) {
		if (gDeviceType == MKY39_1) {
			format = 2;	// WORD
		} else if (gDeviceType == MKY39_2) {
			format = 2;	// WORD
			numCenter = 1;
			dSize = 2 * SZ_AREA;	// Both
		} else {
			print_string("???\n");
			ret = -1;
		}
	} else if (argc == 2) {
		if (gDeviceType == MKY39_1) {
			format = getFormatOption(argv[1]);
		} else if (gDeviceType == MKY39_2) {
			if (argv[1][0] == '-') {
				format = getFormatOption(argv[1]);
				numCenter = 1;
				dSize = 2 * SZ_AREA;	// Both
			} else {
				format = 2;	// WORD
				numCenter = atoi(argv[1]);
			}
		} else {
			print_string("???\n");
			return(-1);
		}
	} else if (argc == 3) {
		if (gDeviceType == MKY39_1) {
			print_string("Too mach arguments.\n");
		} else if (gDeviceType == MKY39_2) {
			format = getFormatOption(argv[1]);
			numCenter = atoi(argv[2]);
		} else {
			print_string("???\n");
			return(-1);
		}
	} else {
		print_string("Too mach arguments.\n");
		ret = -1;
	}

	if (format < 0) {
		print_string("unrecognized <Format> strings.\n");
		ret = -1;
	}
	if ((numCenter != 1) && (numCenter != 2)) {
		print_string("The Center value is not correct.\n");
		return(-1);
	}

	if (ret == 0) {
		//応答を送信する。
		if (gDeviceType == MKY39_1) {
			md_dump(TOP_T1DoArea, SZ_AREA, format);
		// } else if (gDeviceType == MKY39_2) {
		} else {
			if (numCenter == 1) {
				md_dump(TOP_T2C1DoArea, dSize, format);
			} else if (numCenter == 2) {
				md_dump(TOP_T2C2DoArea, dSize, format);
			} else {
				print_string("The Center value is not correct.\n");
				return(-1);
			}
		}
	}

	showDbTermPrompt();

	return(ret);
}

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

	format = 0;
	ret = 0;
	numCenter = 1;
	dSize = SZ_AREA;

	if (argc == 1) {
		if (gDeviceType == MKY39_1) {
			format = 2;	// WORD
		} else if (gDeviceType == MKY39_2) {
			format = 2;	// WORD
			numCenter = 1;
			dSize = 2 * SZ_AREA;	// Both
		} else {
			print_string("???\n");
			ret = -1;
		}
	} else if (argc == 2) {
		if (gDeviceType == MKY39_1) {
			format = getFormatOption(argv[1]);
		} else if (gDeviceType == MKY39_2) {
			if (argv[1][0] == '-') {
				format = getFormatOption(argv[1]);
				numCenter = 1;
				dSize = 2 * SZ_AREA;	// Both
			} else {
				format = 2;	// WORD
				numCenter = atoi(argv[1]);
			}
		} else {
			print_string("???\n");
			return(-1);
		}
	} else if (argc == 3) {
		if (gDeviceType == MKY39_1) {
			print_string("Too mach arguments.\n");
		} else if (gDeviceType == MKY39_2) {
			format = getFormatOption(argv[1]);
			numCenter = atoi(argv[2]);
		} else {
			print_string("???\n");
			return(-1);
		}
	} else {
		print_string("Too mach arguments.\n");
		ret = -1;
	}

	if (format < 0) {
		print_string("unrecognized <Format> strings.\n");
		ret = -1;
	}
	if ((numCenter != 1) && (numCenter != 2)) {
		print_string("The Center value is not correct.\n");
		return(-1);
	}

	if (ret == 0) {
		//応答を送信する。
		if (gDeviceType == MKY39_1) {
			md_dump(TOP_T1DiArea, SZ_AREA, format);
		// } else if (gDeviceType == MKY39_2) {
		} else {
			if (numCenter == 1) {
				md_dump(TOP_T2C1DiArea, dSize, format);
			} else if (numCenter == 2) {
				md_dump(TOP_T2C2DiArea, dSize, format);
			} else {
				print_string("The Center value is not correct.\n");
				return(-1);
			}
		}
		// ##### MKY39-2
	}

	showDbTermPrompt();

	return(ret);
}

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

	format = 0;
	ret = 0;
	numCenter = 1;
	dSize = SZ_AREA;

	if (argc == 1) {
		if (gDeviceType == MKY39_1) {
			format = 2;	// WORD
		} else if (gDeviceType == MKY39_2) {
			format = 2;	// WORD
			numCenter = 1;
			dSize = 2 * SZ_AREA;	// Both
		} else {
			print_string("???\n");
			ret = -1;
		}
	} else if (argc == 2) {
		if (gDeviceType == MKY39_1) {
			format = getFormatOption(argv[1]);
		} else if (gDeviceType == MKY39_2) {
			if (argv[1][0] == '-') {
				format = getFormatOption(argv[1]);
				numCenter = 1;
				dSize = 2 * SZ_AREA;	// Both
			} else {
				format = 2;	// WORD
				numCenter = atoi(argv[1]);
			}
		} else {
			print_string("???\n");
			return(-1);
		}
	} else if (argc == 3) {
		if (gDeviceType == MKY39_1) {
			print_string("Too mach arguments.\n");
		} else if (gDeviceType == MKY39_2) {
			format = getFormatOption(argv[1]);
			numCenter = atoi(argv[2]);
		} else {
			print_string("???\n");
			return(-1);
		}
	} else {
		print_string("Too mach arguments.\n");
		ret = -1;
	}

	if (format < 0) {
		print_string("unrecognized <Format> strings.\n");
		ret = -1;
	}
	if ((numCenter != 1) && (numCenter != 2)) {
		print_string("The Center value is not correct.\n");
		return(-1);
	}

	if (ret == 0) {
		//応答を送信する。
		if (gDeviceType == MKY39_1) {
			md_dump(TOP_T1CMDSRArea, SZ_AREA, format);
		// } else if (gDeviceType == MKY39_2) {
		} else {
			if (numCenter == 1) {
				md_dump(TOP_T2C1CMDSRArea, dSize, format);
			} else if (numCenter == 2) {
				md_dump(TOP_T2C2CMDSRArea, dSize, format);
			} else {
				print_string("The Center value is not correct.\n");
				return(-1);
			}
		}
		// ##### MKY39-2
	}

	showDbTermPrompt();

	return(ret);
}



/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @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)
{
	char	s[BUFFER_MAX];
	char	strCCR[16];

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

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

	// 内容表示
	MKY_ReadBlock(offset, strCCR, 16);
	strCCR[15] = 0;
	print_string(strCCR);

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

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： レジスタ名の表示
 * 
 * @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;
	}
}
