/**
 * @file   dbTerm36.c
 * @author StepTechnica
 * @date   May 16, 2023
 * 
 * @brief Evaluation Board 用ソースファイル「ターミナルコンソール」: MKY36用
 * 
 * @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 "MKY36.h"
#include "MKYDevice.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[] =
{
	{"SCR",   OFS_regSCR,   1, 0 },
	{"SSR",   OFS_regSSR,   1, 1 },
	{"HPR",   OFS_regHPR,   1, 0 },
	{"INT0R", OFS_regINT0R, 1, 0 },
	{"INT1R", OFS_regINT1R, 1, 0 },
	{"C1CR",  OFS_regC1CR,  1, 0 },
	{"C2CR",  OFS_regC2CR,  1, 0 },
	{"BCR",   OFS_regBCR,   1, 0 },
	{"CCR",   OFS_regCCR,   3, 1 }
};

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_mdctrl(int argc, char *argv[]);
short dbTerm_mddrc(int argc, char *argv[]);
short dbTerm_mddo(int argc, char *argv[]);
short dbTerm_mddi(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("CTRL" ,		dbTerm_mdctrl ),	// コントロール領域のダンプ
	MONITOR_COMMAND("DRC"  ,		dbTerm_mddrc  ),	// DRC領域のダンプ
	MONITOR_COMMAND("DO"   ,		dbTerm_mddo   ),	// Do領域のダンプ
	MONITOR_COMMAND("DI"   ,		dbTerm_mddi   ),	// Di領域のダンプ
	// 終端マーカ
	{ 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("reg <[Name]>                            : register modify\n");
	print_string("din  [Format] [address]                 : direct read\n");
	print_string("dout [Format] [address] [data]          : direct write\n");
	print_string("ctrl [Format]                           : control area dump\n");
	print_string("drc  [Format]                           : DRC area dump\n");
	print_string("do   [Format]                           : Do area dump\n");
	print_string("di   [Format]                           : 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");
	print_char('\n');

	showDbTermPrompt();

	return(0);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @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： [CTRL] コマンド：コントロールM領域のダンプ
 * 
 * @note： コントロール領域のメモリダンプ
 *           argc == 1 : CTRL 
 *           argc == 2 : [Format] 
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mdctrl(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(TOP_CtrlArea, SZ_AREA, format);
	}

	showDbTermPrompt();

	return(ret);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [DRC] コマンド：DRC領域のダンプ
 * 
 * @note： DRC領域のメモリダンプ
 *           argc == 1 : DRC 
 *           argc == 2 : [Format] 
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mddrc(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(TOP_DRCArea, SZ_AREA, format);
	}

	showDbTermPrompt();

	return(ret);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [DO] コマンド：Do領域のダンプ
 * 
 * @note： Do領域のメモリダンプ
 *           argc == 1 : DO 
 *           argc == 2 : [Format] 
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mddo(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(TOP_DoArea, SZ_AREA, format);
	}

	showDbTermPrompt();

	return(ret);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [DI] コマンド：Di領域のダンプ
 * 
 * @note： Di領域のメモリダンプ
 *           argc == 1 : GM 
 *           argc == 2 : GM [Format] 
 * 
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 * 
 */
short dbTerm_mddi(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(TOP_DiArea, SZ_AREA, format);
	}

	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)
{
	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;
	}
}
