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

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

#include "MKY43.h"
#include "IB43config.h"
#include <memory.h>
#include <stdint.h>
#include <stdbool.h>

/* -----------------------------------------------------------------------------
 * #defineマクロ定義
 */
#define	CNT_RESET	100	// Reset Cycle count

/* -----------------------------------------------------------------------------
 * 変数定義
 */
#ifdef DEBUG
char strPROMPT[16] = {"MKY43_D>"};
#else
char strPROMPT[16] = {"MKY43>"};
#endif


/* -----------------------------------------------------------------------------
 * 関数プロトタイプ宣言
 */

static inline unsigned get_mb(unsigned offset);
static inline unsigned get_hpofs(unsigned offset);
static inline unsigned get_hplen(unsigned hpofs, unsigned length);
static int read16_direct_8_little(unsigned offset);
static int read16_direct_8_big(unsigned offset);
static int read16_direct_16(unsigned offset);
static int read_block_direct_8_little(unsigned offset, void* block, unsigned length);
static int read_block_direct_8_big(unsigned offset, void* block, unsigned length);
static int read_block_direct_16(unsigned offset, void* block, unsigned length);
static void write16_direct_8_little(unsigned offset, unsigned value);
static void write16_direct_8_big(unsigned offset, unsigned value);
static inline void write16lowbyte(unsigned address, unsigned lowbyte);
static inline void write16highbyte(unsigned address, unsigned highbyte);
static void write16_direct_16(unsigned offset, unsigned value);
static int write_block_direct_8_little(unsigned offset, const void* block, unsigned length);
static int write_block_direct_8_big(unsigned offset, const void* block, unsigned length);
static int write_block_direct_16(unsigned offset, const void* block, unsigned length);
static int fill_memory_direct_8_little(unsigned offset, int value, unsigned length);
static int fill_memory_direct_8_big(unsigned offset, int value, unsigned length);
static int fill_memory_direct_16(unsigned offset, int value, unsigned length);
static inline int read16_hp(unsigned offset);
static inline int read_block_hp(unsigned offset, void* v_block, unsigned length);
static inline int write_block_hp(unsigned offset, const void* v_block, unsigned length);
static inline int fill_memory_hp(unsigned offset, int value, unsigned length);

int MKY_Initialize(void);
int MKY_Read16(unsigned short offset);
int MKY_ReadBlock(unsigned short offset, void* block, unsigned short length);
int MKY_Write16(unsigned short offset, unsigned short value);
int MKY_WriteBlock(unsigned short offset, const void* block, unsigned short length);
int MKY_FillMemory(unsigned short offset, int value, unsigned short length);


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： メモリブロック(MB)を得る。
 * 
 * @note： アドレスからメモリブロック番号を取得する
 *         １メモリブロック ＝ 8Byte
 * 
 * @param  unsigned offset : アドレス
 * @retval unsigned :    メモリブロック番号
 * 
 */
static inline unsigned get_mb(unsigned offset)
{
	return offset / 8;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： ハザードプロテクションバッファ(RHPB/WHPB)のオフセットを得る
 * 
 * @note： アドレスからオフセットを取得する
 *         １メモリブロック ＝ 8Byte
 * 
 * @param  unsigned offset : アドレス
 * @retval unsigned :    オフセット
 * 
 */
static inline unsigned get_hpofs(unsigned offset)
{
	return offset % 8;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： ハザードプロテクションバッファ(RHPB/WHPB)の残りサイズを得る
 * 
 * @note： オフセットから残サイズを取得する
 *         １メモリブロック ＝ 8Byte
 * 
 * @param  unsigned hpofs : オフセット
 * @param  unsigned length : レングス
 * @retval unsigned :    残サイズ
 * 
 */
static inline unsigned get_hplen(unsigned hpofs, unsigned length)
{
	unsigned len;
	len = 8 - hpofs;
	if( len > length )
	{
		len = length;
	}
	return len;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * direct: ハザードプロテククションを考慮せす直接読み書きする)
 */ 

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 16ビットの値を読み出す
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @retval int :    読み出し値
 * 
 */
static int (*read16_direct)(unsigned offset);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 8ビットバス・リトルエンディアン用
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @retval int :    読み出し値
 * 
 */
static int read16_direct_8_little(unsigned offset)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	int value;
	value = *(uint8_t*)address++;
	value += ( *(uint8_t*)address ) << 8;
	return value;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief： 8ビットバス・ビッグディアン用
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @retval int :    読み出し値
 * 
 */
static int read16_direct_8_big(unsigned offset)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	int value;
	value = *(uint8_t*)(address++ ^ 1);
	value += ( *(uint8_t*)(address ^ 1) ) << 8;
	return value;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバス用
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @retval int :    読み出し値
 * 
 */
static int read16_direct_16(unsigned offset)
{
	return *( (uint16_t*)( MKY_MEMORY_BASE + offset ) );
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで読み出す
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  void* block : 格納バッファ
 * @param  unsigned length : レングス
 * @retval int :    読み出し値
 * 
 */
static int (*read_block_direct)(unsigned offset, void* block, unsigned length);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・リトルエンディアン用ブロック読み出し
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  void* block : 格納バッファ
 * @param  unsigned length : レングス
 * @retval int :    読み出し値
 * 
 */
static int read_block_direct_8_little(unsigned offset, void* block, unsigned length)
{
	uint8_t* p_dest = (uint8_t*)block;
	const uint8_t* p_src = (const uint8_t*)(MKY_MEMORY_BASE + offset);
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*p_dest++ = *p_src++;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・ビッグエンディアン用ブロック読み出し
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  void* block : 格納バッファ
 * @param  unsigned length : レングス
 * @retval int :    読み出し値
 * 
 */
static int read_block_direct_8_big(unsigned offset, void* block, unsigned length)
{
	uint8_t* p_dest = (uint8_t*)block;
	unsigned address = MKY_MEMORY_BASE + offset;
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*p_dest++ = *(uint8_t*)(address++ ^ 1);
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバス用ブロック読み出し
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  void* block : 格納バッファ
 * @param  unsigned length : レングス
 * @retval int :    読み出し値
 * 
 */
static int read_block_direct_16(unsigned offset, void* block, unsigned length)
{
	uint8_t* p_dest = (uint8_t*)block;
	const uint8_t* p_src = (const uint8_t*)( MKY_MEMORY_BASE + offset );
	unsigned count = 0;
	if( ((unsigned)p_src % 2) != 0 && (length > 0) )
	{
		*p_dest++ = *p_src++;
		--length;
		++count;
	}
	while( length > 1 )
	{
		*(uint16_t*)p_dest = *(const uint16_t*)p_src;
		p_dest += 2;
		p_src += 2;
		length -= 2;
		count += 2;
	}
	if( length > 0 )
	{
		*p_dest = *p_src;
		++count;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットの値を書き込む
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static void (*write16_direct)(unsigned offset, unsigned value);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・リトルエンディアン用データ書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static void write16_direct_8_little(unsigned offset, unsigned value)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	*((uint8_t*)address++) = (uint8_t)value;
	*((uint8_t*)address) = (uint8_t)(value >> 8);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・ビッグエンディアン用データ書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static void write16_direct_8_big(unsigned offset, unsigned value)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	*(uint8_t*)(address++ ^ 1) = (uint8_t)value;
	*(uint8_t*)(address ^ 1) = (uint8_t)(value >> 8);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバスのLowバイトにデータ書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static inline void write16lowbyte(unsigned address, unsigned lowbyte)
{
	uint16_t value;
	value = *((uint16_t*)address);
	*(uint16_t*)address = (value & 0xff00) | (lowbyte & 0xff);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバスのHighバイトにデータ書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static inline void write16highbyte(unsigned address, unsigned highbyte)
{
	uint16_t value;
	value = *((uint16_t*)address);
	*(uint16_t*)address = (value & 0xff) | ((highbyte << 8) & 0xff00);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットのデータ書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  unsigned value : 書き込みデータ
 * @retval none
 * 
 */
static void write16_direct_16(unsigned offset, unsigned value)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	if( (address & 1) == 0 )
	{
		*((uint16_t*)address) = (uint16_t)value;
	}
	else
	{
		write16highbyte(address-1, value&0xff);
		write16lowbyte(address+1, value>>8);
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  const void* block : 書き込みデータ格納バッファ
 * @param  unsigned length : レングス
 * @retval int
 * 
 */
static int (*write_block_direct)(unsigned offset, const void* block, unsigned length);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・リトルエンディアン用ブロックで書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  const void* block : 書き込みデータ格納バッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int write_block_direct_8_little(unsigned offset, const void* block, unsigned length)
{
	uint8_t* p_dest = (uint8_t*)(MKY_MEMORY_BASE + offset);
	const uint8_t* p_src = (const uint8_t*)block;	
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*p_dest++ = *p_src++;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・ビッグエンディアン用ブロックで書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  const void* block : 書き込みデータ格納バッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int write_block_direct_8_big(unsigned offset, const void* block, unsigned length)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	const uint8_t* p_src = (const uint8_t*)block;	
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*(uint8_t*)(address++ ^ 1) = *p_src++;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバス用ブロックで書き込み
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  const void* block : 書き込みデータ格納バッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int write_block_direct_16(unsigned offset, const void* block, unsigned length)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	const uint8_t* p_src = (const uint8_t*)block;	
	unsigned count = 0;
	if( (address % 2) != 0 && (length > 0) )
	{
		write16highbyte(address-1, *p_src++);
		++address;
		--length;
		++count;
	}
	while( length > 1 )
	{
		*(uint16_t*)address = *(const uint16_t*)p_src;
		address += 2;
		p_src += 2;
		length -= 2;
		count += 2;
	}
	if( length > 0 )
	{
		write16lowbyte(address, *p_src);
		++count;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：MKYのメモリーに同じ値を格納する
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  const void* block : 書き込みデータ格納バッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int (*fill_memory_direct)(unsigned offset, int value, unsigned length);

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：8ビットバス・リトルエンディアン用同じ値を格納する
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  int value : 書き込みデータ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int fill_memory_direct_8_little(unsigned offset, int value, unsigned length)
{
	uint8_t* p_dest = (uint8_t*)(MKY_MEMORY_BASE + offset);
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*p_dest++ = value;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ビットバス・ビッグエンディアン用同じ値を格納する
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  int value : 書き込みデータ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int fill_memory_direct_8_big(unsigned offset, int value, unsigned length)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	unsigned count = 0;
	for( ; count < length; ++count )
	{
		*(uint8_t*)(address++ ^ 1) = value;
	}
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットバス用同じ値を格納する
 * 
 * @note： ハザードプロテククションを考慮せす
 * 
 * @param  unsigned offset : オフセット
 * @param  int value : 書き込みデータ
 * @param  unsigned length : レングス
 * @retval int : 書き込みデータ数
 * 
 */
static int fill_memory_direct_16(unsigned offset, int value, unsigned length)
{
	unsigned address = MKY_MEMORY_BASE + offset;
	unsigned count = 0;
	value &= 0xff;
	if( (address % 2) != 0 && (length > 0) )
	{
		write16highbyte(address-1, value);
		++address;
		--length;
		++count;
	}
	while( length > 1 )
	{
		*(uint16_t*)address = (value << 8) | value;
		address += 2;
		length -= 2;
		count += 2;
	}
	if( length > 0 )
	{
		write16lowbyte((unsigned)address, value);
		++count;
	}
	return count;
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * hp: ハザードプロテクションを考慮して読み書きする
 */ 


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットの値を読み出す
 * 
 * @note： ハザードプロテクションを考慮して読み出す
 * 
 * @param  unsigned offset : オフセット
 * @retval int : 読み出した値
 * 
 */
static inline int read16_hp(unsigned offset)
{
	unsigned mb;
	mb = get_mb(offset);
	if( mb == get_mb(offset+1) )
	{
		write16_direct(OFS_regRHCR0, mb);
		return read16_direct(OFS_regRHPB0 + get_hpofs(offset));
	}
	else
	{
		//2つのハザードプロテクションバッファをまたがる場合。
		//プロテクションはかからないので、直に読む。
		return read16_direct(offset);
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで読み出す
 * 
 * @note： ハザードプロテクションを考慮して読み出す
 * 
 * @param  unsigned offset : オフセット
 * @param  void* v_block : 格納するバッファ
 * @param  unsigned length : レングス
 * @retval int : 読み出したバイト数
 * 
 */
static inline int read_block_hp(unsigned offset, void* v_block, unsigned length)
{
	unsigned mb;
	unsigned ofs;
	unsigned len;
	unsigned char* block = v_block;
	unsigned count = 0;

	if( offset >= OFS_GM_MIN )
	{
		while( (length > 0) && (offset <= OFS_GM_MAX) )
		{
			//ハザードプロテクションバッファがある領域。
			mb = get_mb(offset);
			ofs = get_hpofs(offset);
			len = get_hplen(ofs, length);

			write16_direct(OFS_regRHCR0, mb);
			len = read_block_direct(OFS_regRHPB0 + ofs, block, len);

			offset += len;
			block += len;
			length -= len;
			count += len;
		}
	}

	//ハザードプロテクションバッファがある領域を越えた場合は、終了する。
	return count;
}


/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで書き込む
 * 
 * @note： ハザードプロテクションを考慮して書き込む
 * 
 * @param  unsigned offset : オフセット
 * @param  void* v_block : 書き込むデータバッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込んだバイト数
 * 
 */
static inline int write_block_hp(unsigned offset, const void* v_block, unsigned length)
{
	unsigned mb;
	unsigned ofs;
	unsigned len;
	const unsigned char* block = v_block;
	unsigned count = 0;

	if( offset >= OFS_GM_MIN )
	{
		while( (length > 0) && (offset <= OFS_GM_MAX) )
		{
			//ハザードプロテクションバッファがある領域。
			mb = get_mb(offset);
			ofs = get_hpofs(offset);
			len = get_hplen(ofs, length);

			if( len != GM_BLKSIZE )
			{
				//ひとつのブロックすべてでない場合は、直接書き込む。
				write_block_direct(offset, block, len);
			}
			else
			{
				write_block_direct(OFS_regWHPB0 + ofs, block, len);
				write16_direct(OFS_regWHCR0, mb);
			}

			offset += len;
			block += len;
			length -= len;
			count += len;
		}
	}

	//ハザードプロテクションバッファがある領域を越えた場合は、抜ける。
	return count;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：同じ値を書き込む
 * 
 * @note： ハザードプロテクションを考慮して書き込む
 * 
 * @param  unsigned offset : オフセット
 * @param  int value : 書き込むデータ
 * @param  unsigned length : レングス
 * @retval int : 書き込んだバイト数
 * 
 */
static inline int fill_memory_hp(unsigned offset, int value, unsigned length)
{
	unsigned mb;
	unsigned ofs;
	unsigned len;
	unsigned count = 0;

	if( offset >= OFS_GM_MIN )
	{
		while( (length > 0) && (offset <= OFS_GM_MAX) )
		{
			//ハザードプロテクションバッファがある領域。
			mb = get_mb(offset);
			ofs = get_hpofs(offset);
			len = get_hplen(ofs, length);

			if( len != GM_BLKSIZE )
			{
				//ひとつのブロックすべてでない場合は、直接書き込む。
				fill_memory_direct(offset, value, length);
			}
			else
			{
				fill_memory_direct(OFS_regWHPB0 + ofs, value, length);
				write16_direct(OFS_regWHCR0, mb);
			}

			offset += len;
			length -= len;
			count += len;
		}
	}

	//ハザードプロテクションバッファがある領域を越えた場合は、抜ける。
	return count;
}





/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：MKYデバイス初期化
 * 
 * @note： 基板の設定を読み込み、初期値を格納する
 * 
 * @param  none
 * @retval true : 正常終了
 * 
 */
int MKY_Initialize(void)
{
	int i;

	switch( GetMkyBus() )
	{
	case MKY_BUS_8:
		switch( GetMkyEndian() )
		{
		case MKY_ENDIAN_BIG:
			read16_direct = read16_direct_8_big;
			read_block_direct = read_block_direct_8_big;
			write16_direct = write16_direct_8_big;
			write_block_direct = write_block_direct_8_big;
			fill_memory_direct = fill_memory_direct_8_big;
			break;
		case MKY_ENDIAN_LITTLE:
		default:
			read16_direct = read16_direct_8_little;
			read_block_direct = read_block_direct_8_little;
			write16_direct = write16_direct_8_little;
			write_block_direct = write_block_direct_8_little;
			fill_memory_direct = fill_memory_direct_8_little;
			break;
		}
		break;
	case MKY_BUS_16:
	default:
		read16_direct = read16_direct_16;
		read_block_direct = read_block_direct_16;
		write16_direct = write16_direct_16;
		write_block_direct = write_block_direct_16;
		fill_memory_direct = fill_memory_direct_16;
		break;
	}

	drvMKYRESET(false);
	for(i=0; i<CNT_RESET; i++) {
	}
	drvMKYRESET(true);

	return true;

}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットの値を読み出す
 * 
 * @note： 
 * 
 * @param  unsigned offset : オフセットアドレス
 * @retval int : 読み出し値
 * 
 */
int MKY_Read16(unsigned short offset)
{
	if( (offset + 2) > MKY_MEMORY_SIZE )
	{
		return 0;
	}
	if( offset >= OFS_GM_MIN && offset <= OFS_GM_MAX )
	{
		//ハザードプロテクションバッファがある領域。
		return read16_hp(offset);
	}
	else
	{
		//ハザードプロテクションバッファがない領域。
		return read16_direct(offset);
	}
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで読み出し
 * 
 * @note： 
 * 
 * @param  unsigned offset : オフセットアドレス
 * @param  void* v_block   : 格納するバッファ
 * @param  unsigned length : レングス
 * @retval int : 読み出したデータ数
 * 
 */
int MKY_ReadBlock(unsigned short offset, void* v_block, unsigned short length)
{
	unsigned char* block = v_block;
	unsigned count = 0;
	if( offset >= OFS_GM_MIN && offset <= OFS_GM_MAX )
	{
		//ハザードプロテクションバッファがある領域。
		count = read_block_hp(offset, block, length);
		offset += count;
		block += count;
		length -= count;
	}
	//ハザードプロテクションバッファがない領域。
	return count + read_block_direct(offset, block, length);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：16ビットの値を書き込む
 * 
 * @note： 
 * 
 * @param  unsigned offset : オフセットアドレス
 * @param  unsigned value  : 書き込む値
 * @retval false = 領域外
 *         true  = 書き込み実行
 * 
 */
int MKY_Write16(unsigned short offset, unsigned short value)
{
	if( (offset + 2) > MKY_MEMORY_SIZE )
	{
		return false;
	}

	//2バイトの書き込みではハザードプロテクション機能が使えないので、直接書き込む。
	write16_direct(offset, value);
	return true;
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：ブロックで書き込む
 * 
 * @note： 
 * 
 * @param  unsigned offset : オフセットアドレス
 * @param  void* v_block   : 書き込むデータのバッファ
 * @param  unsigned length : レングス
 * @retval int : 書き込んだデータ数
 * 
 */
int MKY_WriteBlock(unsigned short offset, const void* v_block, unsigned short length)
{
	const unsigned char* block = v_block;
	unsigned count = 0;
	if( offset >= OFS_GM_MIN && offset <= OFS_GM_MAX )
	{
		//ハザードプロテクションバッファがある領域。
		count = write_block_hp(offset, block, length);
		offset += count;
		block += count;
		length -= count;
	}
	//ハザードプロテクションバッファがない領域。
	return count + write_block_direct(offset, block, length);
}

/**
 *~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
 * @brief：同じ値を書き込む
 * 
 * @note： 
 * 
 * @param  unsigned offset : オフセットアドレス
 * @param  int value       : 書き込むデータ
 * @param  unsigned length : レングス
 * @retval int : 書き込んだデータ数
 * 
 */
int MKY_FillMemory(unsigned short offset, int value, unsigned short length)
{
	unsigned count = 0;
	if( offset >= OFS_GM_MIN && offset <= OFS_GM_MAX )
	{
		//ハザードプロテクションバッファがある領域。
		count = fill_memory_hp(offset, value, length);
		offset += count;
		length -= count;
	}
	//ハザードプロテクションバッファがない領域。
	return count + fill_memory_direct(offset, value, length);
}

