/**
 * @file   dbTerm43.c
 * @author StepTechnica
 * @date   Sep. 05, 2025
 * 
 * @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 "main.h"

#include "MKYDevice.h"
#include "dbTerm.h"
#include "ctrlUART4.h"
#include "IB49config.h"
#include "Utility.h"
#include "MKY49.h"

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

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

/* -----------------------------------------------------------------------------
 * 変数定義
 */
static RegCommandInfo_st RegCommandTable[] =
{
	{"RFR",    MKY49_RFR,     4, 1 },
	{"LFR",    MKY49_LFR,     4, 1 },
	{"MFR",    MKY49_MFR,     4, 1 },
	{"CCTR",   MKY49_CCTR,    1, 1 },
	{"FSR",    MKY49_FSR,     1, 1 },
	{"SSR",    MKY49_SSR0,    2, 1 },
	{"MGM",    MKY49_MGM,   252, 1 },

	{"CCR",    MKY49_CCR,     4, 1 },
	{"BCR",    MKY49_BCR0,    2, 0 },
	{"SCR",    MKY49_SCR,     1, 0 },
	{"MSCR",   MKY49_MSCR,    1, 0 },
	{"MESR",   MKY49_MESR,    1, 1 },
	{"MSRR",   MKY49_MSRR,    1, 1 },
	{"MSLR",   MKY49_MSLR,    1, 0 },
	{"MR0CR",  MKY49_MR0CR,   1, 0 },
	{"MR0SR",  MKY49_MR0SR,   1, 0 },
	{"MR1CR",  MKY49_MR1CR,   1, 0 },
	{"MR1SR",  MKY49_MR1SR,   1, 0 },

	{"NFSR",   MKY49_NFSR,    1, 0 },
	{"ITCR",   MKY49_ITCR,    1, 0 },
	{"INTCR",  MKY49_INTCR,   1, 0 },
	{"INTSR",  MKY49_INTSR,   1, 1 },
	{"INTRR",  MKY49_INTRR,   1, 0 },
	{"SSR1CR", MKY49_SSR1CR,  1, 0 },
	{"CCTCR",  MKY49_CCTCR,   1, 0 },
	{"LGR",    MKY49_LGR,     4, 0 },
	{"MGR",    MKY49_MGR,     4, 0 },
	{"QCR",    MKY49_QCR,     1, 0 },
	{"DO03",   MKY49_DOUT,    1, 0 },
	{"DIO03",  MKY49_DIN,     2, 1 },
};
unsigned char rxd[1024 * 100] = {};
unsigned int rxd_rp = 0;
unsigned int rxd_wp = 0;

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

extern TIM_HandleTypeDef htim4;
extern void tmod_spiHost(void);
extern void tmod_spiTarget(void);

/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */
short dbTerm_help(int argc, char *argv[]);
short dbTerm_gm(int argc, char *argv[]);
short dbTerm_mgm(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);

static int16_t hex2bin(char *buf, uint32_t *bin);


/* -----------------------------------------------------------------------------
 * コマンド処理テーブル
 */
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("MGM"  ,		dbTerm_mgm    ),	// 通信ステータス + GMアクセス
	MONITOR_COMMAND("REG"  ,		dbTerm_reg    ),	// レジスタアクセス
	MONITOR_COMMAND("DIN"  ,		dbTerm_din    ),	// メモリ読み出し(ダイレクト)
	MONITOR_COMMAND("DOUT" ,		dbTerm_dout   ),	// メモリ書込み(ダイレクト)

	// 終端マーカ
	{ 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 + Status(32 Byte) dump\n");
	print_string("mgm <[Format]>                          : Status(32 Byte) + MGM 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(MKY49_GM_BASE_ADDR, MKY49_GM_SIZE + MKY49_LENSTATUS, format);
	}

	showDbTermPrompt();

	return(ret);
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： [GM] コマンド：通信ステータス+MGM領域のダンプ
 *
 * @note： 通信ステータス+MGM領域のメモリダンプ
 *           argc == 1 : MGM
 *           argc == 2 : MGM [Format]
 *
 * @param  int argc : パラメータ数
 * @param  char *argv[]  : パラメータ文字列
 * @retval 0   = 正常終了
 *         < 0 = 解釈エラー
 *
 */
short dbTerm_mgm(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) {
		short regBCR1 = MKY_Read16(MKY49_BCR1);
		// short msa = (regBCR1 >> 0) & 0x003F;
		short msz = (regBCR1 >> 8) & 0x003F;

		//応答を送信する。
		md_dump(MKY49_OFSSTATUS, MKY49_LENSTATUS + (msz * 8), format);
		md_dump(MKY49_LGR, 16, format);	//LGR + MGR
	}

	showDbTermPrompt();

	return(ret);
}



#include <stdarg.h>
#include <stdio.h>

short println(const char *format, ...) {
    short   ret;
    va_list args;
    char    buf[128];

    va_start(args, format);
    ret = vsprintf(buf, format, args);
    va_end(args);
    print_string(buf);
    print_string("\n");

    return ret;
}

short print(const char *format, ...) {
    short   ret;
    va_list args;
    char    buf[128];

    va_start(args, format);
    ret = vsprintf(buf, format, args);
    va_end(args);
    print_string(buf);

    return ret;
}


/* for str2hex */
#define ERR_STR2HEX_ILLLEN -1
#define ERR_STR2HEX_ILLCHR -2

/** Error Code in manual_test */
typedef enum {
    /* R command */
    ERR_R_ILLARGC = 1000, /**< argc error */
    ERR_R_ILLADR1,        /**< address Over-length */
    ERR_R_ILLADR2,        /**< address Holding illegal character */
    ERR_R_ILLADR3,        /**< address byte order error */
    ERR_R_ILLSIZE1,       /**< size Over-length */
    ERR_R_ILLSIZE2,       /**< size Holding illegal character */
    ERR_R_ILLSIZE3,       /**< size Over-Size */

    /* W command */
    ERR_W_ILLARGC = 2000, /**< argc error */
    ERR_W_ILLADR1,        /**< address Over-length */
    ERR_W_ILLADR2,        /**< address Holding illegal character */
    ERR_W_ILLADR3,        /**< address byte order error */
    ERR_W_ILLDATA1,       /**< 32bit data Over-length */
    ERR_W_ILLDATA2,       /**< 32bit data Holding illegal character */
    ERR_W_ILLDATA3,       /**< 32bit data byte order error */
    ERR_W_ILLSIZE1,       /**< size Over-length */
    ERR_W_ILLSIZE2,       /**< size Holding illegal character */
    ERR_W_ILLSIZE3,       /**< size Over-Size */

    /* manual test */
    ERR_MT_ILLARGC1 = 3000, /**< argc error1 */
    ERR_MT_ILLARGC2,        /**< argc error2 */
    ERR_MT_ILLARGV1,        /**< address Invalid Test Kind */
    ERR_MT_ILLARGV2,        /**< address Invalid Test No. */
} mt_errcode_et;


/**
 * Convert hex string to binary
 *
 * @param buf hex string
 * @param bin destination
 *
 * @return 0:OK / -1:Over-Length / -2:Holding illegal character.
 */
inline int16_t hex2bin(char *buf, uint32_t *bin) {
    int16_t ret = 0;
    int16_t i = 0;
    int16_t len = 0;
    *bin = 0;

    /* check length */
    len = (int16_t)strlen(buf);
    if (8 < len) {
        ret = ERR_STR2HEX_ILLLEN;
        goto exit;
    }

    /* check character */
    for (i = 0; 0 != buf[i]; i++) {
        if (((0x30 <= buf[i]) && (buf[i] <= 0x39)) || // '0'-'9'
            ((0x41 <= buf[i]) && (buf[i] <= 0x46)) || // 'A'-'F'
            ((0x61 <= buf[i]) && (buf[i] <= 0x66))    // 'a'-'f'
        ) {
            // OK
        } else {
            // NG
            ret = ERR_STR2HEX_ILLCHR;
            goto exit;
        }
    }

    /* convert */
    while (*buf != 0) {
        if ((*buf >= '0') && (*buf <= '9')) {
            *bin = (*bin * 0x10) + (*buf - (char)'0');
        } else if ((*buf >= 'a') && (*buf <= 'z')) {
            *bin = (*bin * 0x10) + (*buf - 'a' + 0x0a);
        } else if ((*buf >= 'A') && (*buf <= 'Z')) {
            *bin = (*bin * 0x10) + (*buf - 'A' + 0x0a);
        }

        buf++;
    }
exit:
    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： レジスタの一覧表示
 * 
 * @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;
	}
}


