/*	
 *	clipreg.c
 *
 *	レジストリテーブル
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2015 Naoyuki Sawa
 *
 *	* Tue Mar 24 02:10:47 JST 2015 Naoyuki Sawa
 *	- 1st リリース。
 *	* Tue Mar 24 21:45:37 JST 2015 Naoyuki Sawa
 *	- REG_open_key(),及び,REG_get_value()に、(keyBody=NULL)を指定しても安全にしました。
 *	  REG_open_key_v()とREG_get_value_v()の処理の大部分を共通化してコード削減しました。
 *	* Thu Mar 26 21:28:16 JST 2015 Naoyuki Sawa
 *	- dRegTblC.exeの出力データ構造を変更した事に伴い、当モジュールも大幅に変更しました。
 *	  前回まではキー配列をリニアサーチしていましたが、今後はバイナリサーチになりました。
 *	  詳細は、「clip/tool/dRegTblC/winapp.h」の同日のコメントを参照してください。
 *	* Sat Apr 11 13:20:40 JST 2015 Naoyuki Sawa
 *	 - 文字列のデータタイプを追加し、大幅な変更を行いました。
 *	   変更前のコードを参照するには、前回のアーカイブを参照してください。
 */
#include "clip.h"
/****************************************************************************
 *	基本的な関数
 ****************************************************************************/
#ifndef PIECE
static int REG_name_compar(const void* _x, const void* _y) {
	const unsigned char* x = _x;
	const unsigned char* y = _y;
	int retVal = *x++ - *y++;		//name(符号無し16ビット)の上位8ビットを比較する。┬オーバーフローの心配は無いので減算結果を比較結果と見なして良い。
	if(!retVal) { retVal = *x - *y; }	//上位16ビットが同じならば下位8ビットを比較する。┘
	return retVal;
}
//(keyBody=NULL)を指定しても安全です。結果は常にNULLになります。
//keyNameに範囲外の値(0~0xFFFF以外)を指定しても安全です。結果は常にNULLになります。
const unsigned char* REG_open_key(const unsigned char* keyBody, int keyName) {
	unsigned char keyName2[2];
	int nKey, keyBodyDist;
	if(!keyBody  ) { return NULL; }		//(keyBody=NULL)が指定されたらNULLを返す。
	keyName2[1]    = keyName;		//┐
	keyName      >>= 8;			//├符号無し16ビット,ビッグエンディアン
	keyName2[0]    = keyName;		//┘
	keyName      >>= 8;			//┐
	if(keyName   ) { return NULL; }		//┴以下の処理はkeyNameの上位16bitを無視するので、下位16bitが偶然一致しないよう範囲外の値を除外しておく。
	//キーの配列内を検索する。
	nKey           = *keyBody++;		//┐
	nKey         <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nKey          |= *keyBody++;		//┘
	keyBody = bsearch(
		keyName2,			//keyNameの下位16bitのみを参照する。
		keyBody,
		nKey,
		(2/*keyName*/+3/*keyBodyDist*/),
		REG_name_compar);
	if(!keyBody) { return NULL; }		//キーが見つからなければNULLを返す。
	keyBody += 2/*keyName*/;
	keyBodyDist    = (char)*keyBody++;	//┐
	keyBodyDist  <<= 8;			//│
	keyBodyDist   |=       *keyBody++;	//├符号有り24ビット,ビッグエンディアン
	keyBodyDist  <<= 8;			//│
	keyBodyDist   |=       *keyBody++;	//┘
	keyBody += keyBodyDist;			//キーボディのアドレスを求める。
	return keyBody;				//キーボディのアドレスを返す。
}
//(keyBody=NULL)を指定しても安全です。結果は常に(-1)になります。
//valueNameに範囲外の値(0~0xFFFF以外)を指定しても安全です。結果は常に(-1)になります。
int REG_get_value(const unsigned char* keyBody, int valueName) {
	unsigned char valueName2[2];
	int nKey, nValue, valueData;
	if(!keyBody  ) { return -1; }		//(keyBody=NULL)が指定されたら(-1)を返す。
	valueName2[1]  = valueName;		//┐
	valueName    >>= 8;			//├符号無し16ビット,ビッグエンディアン
	valueName2[0]  = valueName;		//┘
	valueName    >>= 8;			//┐
	if(valueName ) { return -1; }		//┴以下の処理はvalueNameの上位16bitを無視するので、下位16bitが偶然一致しないよう範囲外の値を除外しておく。
	//キーの配列をスキップする。
	nKey           = *keyBody++;		//┐
	nKey         <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nKey          |= *keyBody++;		//┘
	keyBody       += nKey   * (2/*keyName*/+3/*keyBodyDist*/);
	//値の配列内を検索する。
	nValue         = *keyBody++;		//┐
	nValue       <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nValue        |= *keyBody++;		//┘
	keyBody = bsearch(
		valueName2,			//valueNameの下位16bitのみを参照する。
		keyBody,
		nValue,
		(2/*valueName*/+2/*valueData*/),
		REG_name_compar);
	if(!keyBody  ) { return -1; }		//値が見つからなければ(-1)を返す。
	keyBody += 2/*valueName*/;
	valueData      = *keyBody++;		//┐
	valueData    <<= 8;			//├符号無し16ビット,ビッグエンディアン
	valueData     |= *keyBody;		//┘
	return valueData;			//値のデータを返す。
}
//(keyBody=NULL)を指定しても安全です。結果は常にNULLになります。
//stringNameに範囲外の値(0~0xFFFF以外)を指定しても安全です。結果は常にNULLになります。
const char* REG_get_string(const unsigned char* keyBody, int stringName) {
	unsigned char stringName2[2];
	int nKey, nValue, nString, stringDist;
	if(!keyBody  ) { return NULL; }		//(keyBody=NULL)が指定されたらNULLを返す。
	stringName2[1] = stringName;		//┐
	stringName   >>= 8;			//├符号無し16ビット,ビッグエンディアン
	stringName2[0] = stringName;		//┘
	stringName   >>= 8;			//┐
	if(stringName) { return NULL; }		//┴以下の処理はstringNameの上位16bitを無視するので、下位16bitが偶然一致しないよう範囲外の値を除外しておく。
	//キーの配列をスキップする。
	nKey           = *keyBody++;		//┐
	nKey         <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nKey          |= *keyBody++;		//┘
	keyBody       += nKey   * (2/*keyName*/+3/*keyBodyDist*/);
	//値の配列をスキップする。
	nValue         = *keyBody++;		//┐
	nValue       <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nValue        |= *keyBody++;		//┘
	keyBody       += nValue * (2/*valueName*/+2/*valueData*/);
	//文字列の配列内を検索する。
	nString        = *keyBody++;		//┐
	nString      <<= 8;			//├符号無し16ビット,ビッグエンディアン
	nString       |= *keyBody++;		//┘
	keyBody = bsearch(
		stringName2,			//stringNameの下位16bitのみを参照する。
		keyBody,
		nString,
		(2/*stringName*/+3/*stringDist*/),
		REG_name_compar);
	if(!keyBody  ) { return NULL; }		//文字列が見つからなければNULLを返す。
	keyBody += 2/*stringName*/;
	stringDist     = (char)*keyBody++;	//┐
	stringDist   <<= 8;			//│
	stringDist    |=       *keyBody++;	//├符号有り24ビット,ビッグエンディアン
	stringDist   <<= 8;			//│
	stringDist    |=       *keyBody++;	//┘
	keyBody += stringDist;			//文字列データのアドレスを求める。
	return (const char*)keyBody;		//文字列データのアドレスを返す。
}
#else //PIECE
const unsigned char* REG_open_key(const unsigned char* keyBody, int keyName);
int REG_get_value(const unsigned char* keyBody, int valueName);
const char* REG_get_string(const unsigned char* keyBody, int stringName);
asm("
		.code
		.align		1
		.global		REG_open_key
		.global		REG_get_value
		.global		REG_get_string
REG_open_key:
		;//%r12 := keyBody
		;//%r13 := keyName
		jp.d		REG_open_get				;//
		ld.w		%r14, 0					;//%r14    := flag = 0												*delay*
REG_get_string:
		;//%r12 := keyBody
		;//%r13 := stringName
		ld.w		%r14, 1					;//%r14    := flag = 1
REG_open_get:
		xsub		%sp, %sp, 8				;//
		cmp		%r12, 0					;//if(!keyBody) { return NULL }
		jreq.d		REG_open_get_RET			;//
		ld.w		%r10, 0					;//%r10    :=            NULL											*delay*
		xld.b		[%sp+5], %r13				;//[%sp+5] := Name2[1] = Name
		sra		%r13, 8					;//%r13    :=            Name >>= 8
		xld.b		[%sp+4], %r13				;//[%sp+4] := Name2[0] = Name
		sra		%r13, 8					;//%r13    :=            Name >>= 8
		jrne		REG_open_get_RET			;//if(Name    ) { return NULL }
		cmp		%r14, 0					;//if(flag) {
		jreq.d		REG_open_get_L10			;//  
		ld.w		%r13, %r12				;//%r13    := keyBody												*delay*
		ld.ub		%r10, [%r12]+				;//  %r10    :=                      nKeyHi                = *keyBody++
		ld.ub		%r13, [%r12]+				;//  %r13    :=                                   nKeyLo   = *keyBody++
		sla		%r10, 8					;//  %r10    :=                      nKeyHi  <<8
		or		%r13, %r10				;//  %r13    :=            nKey   = (nKeyHi  <<8)|nKeyLo
		add		%r12, %r13				;//  %r12    := keyBody +  nKey   * 1
		sla		%r13, 2					;//  %r13    :=            nKey   * 4
		add		%r12, %r13				;//  %r12    := keyBody += nKey   * (2/*keyName*/+3/*keyBodyDist*/)
		ld.ub		%r10, [%r12]+				;//  %r10    :=                      nValueHi              = *keyBody++
		ld.ub		%r13, [%r12]+				;//  %r13    :=                                   nValueLo = *keyBody++
		sla		%r10, 8					;//  %r10    :=                      nValueHi<<8
		or		%r13, %r10				;//  %r13    :=            nValue = (nValueHi<<8)|nValueLo
		sla		%r13, 2					;//  %r13    :=            nValue * (2/*valueName*/+2/*valueData*/)
		add		%r13, %r12				;//  %r13    := keyBody += nValue * (2/*valueName*/+2/*valueData*/)
REG_open_get_L10:							;//}
		ld.w		%r12, %sp				;//%r12    := &[%sp+0]
		xld.w		%r10, REG_name_compar			;//%r10    := REG_name_compar
		ld.w		[%r12]+, %r10				;//[%sp+0] := REG_name_compar, %r12 := Name2
		ld.ub		%r10, [%r13]+				;//%r10    :=       nHi         = *keyBody++
		ld.ub		%r14, [%r13]+				;//%r14    :=               nLo = *keyBody++
		sla		%r10, 8					;//%r10    :=       nHi<<8
		or		%r14, %r10				;//%r14    := n  = (nHi<<8)|nLo
		xcall.d		bsearch					;//%r10    := keyBody = bsearch(Name2, keyBody, n, (2/*Name*/+3/*Dist*/), REG_name_compar)
		ld.w		%r15, 5					;//%r15    :=                                      (2/*Name*/+3/*Dist*/)					*delay*
		cmp		%r10, 0					;//if(!keyBody) { return NULL }
		jreq		REG_open_get_RET			;//
		add		%r10, 2					;//%r10    :=                                                             keyBody += 2/*Name*/
		ld.b		%r11, [%r10]+				;//%r11    :=                      DistHi                        = (char)*keyBody++
		ld.ub		%r12, [%r10]+				;//%r12    :=                                 DistMi             =       *keyBody++
		ld.ub		%r13, [%r10]+				;//%r13    :=                                             DistLo =       *keyBody++
		sla		%r11, 8					;//%r11    :=                      DistHi<<8
		or		%r11, %r12				;//%r11    :=                     (DistHi<<8)|DistMi
		sla		%r11, 8					;//%r11    :=                    ((DistHi<<8)|DistMi)<<8
		or		%r11, %r13				;//%r11    :=            Dist = (((DistHi<<8)|DistMi)<<8)|DistLo
		add		%r10, %r11				;//%r10    := keyBody += Dist
REG_open_get_RET:							;//
		xadd		%sp, %sp, 8				;//
		ret							;//return     keyBody
		;//-----------------------------------------------------;//
REG_get_value:
		;//%r12 := keyBody
		;//%r13 := valueName
		xsub		%sp, %sp, 8				;//
		cmp		%r12, 0					;//if(!keyBody ) { return -1 }
		jreq		REG_get_value_RET_M1			;//
		xld.b		[%sp+5], %r13				;//[%sp+5] := valueName2[1] = valueName
		sra		%r13, 8					;//%r13    :=                 valueName >>= 8
		xld.b		[%sp+4], %r13				;//[%sp+4] := valueName2[0] = valueName
		sra		%r13, 8					;//%r13    :=                 valueName >>= 8
		jrne		REG_get_value_RET_M1			;//if(valueName) { return -1 }
		ld.ub		%r10, [%r12]+				;//%r10    :=                    nKeyHi            = *keyBody++
		ld.ub		%r13, [%r12]+				;//%r13    :=                               nKeyLo = *keyBody++
		sla		%r10, 8					;//%r10    :=                    nKeyHi<<8
		or		%r13, %r10				;//%r13    :=            nKey = (nKeyHi<<8)|nKeyLo
		add		%r12, %r13				;//%r12    := keyBody +  nKey * 1
		sla		%r13, 2					;//%r13    :=            nKey * 4
		add		%r13, %r12				;//%r13    := keyBody += nKey * (2/*keyName*/+3/*keyBodyDist*/)
		ld.w		%r12, %sp				;//%r12    := &[%sp+0]
		xld.w		%r10, REG_name_compar			;//%r10    := REG_name_compar
		ld.w		[%r12]+, %r10				;//[%sp+0] := REG_name_compar, %r12 := valueName2
		ld.ub		%r10, [%r13]+				;//%r10    :=            nValueHi              = *keyBody++
		ld.ub		%r14, [%r13]+				;//%r14    :=                         nValueLo = *keyBody++
		sla		%r10, 8					;//%r10    :=            nValueHi<<8
		or		%r14, %r10				;//%r14    := nValue  = (nValueHi<<8)|nValueLo
		xcall.d		bsearch					;//%r10    := keyBody = bsearch(valueName2, keyBody, nValue, (2/*valueName*/+2/*valueData*/), REG_name_compar)
		ld.w		%r15, 4					;//%r15    :=                                                (2/*valueName*/+2/*valueData*/)			*delay*
		cmp		%r10, 0					;//if(!keyBody ) { return -1 }
		jreq		REG_get_value_RET_M1			;//
		add		%r10, 2					;//%r10    :=                                             keyBody += 2/*valueName*/
		ld.ub		%r11, [%r10]+				;//%r11    :=              valueDataHi                 = *keyBody++
		ld.ub		%r10, [%r10]				;//%r10    :=                              valueDataLo = *keyBody
		sla		%r11, 8					;//%r11    :=              valueDataHi<<8									*anti-interlock*
		or		%r10, %r11				;//%r10    := valueData = (valueDataHi<<8)|valueDataLo
REG_get_value_RET:							;//
		xadd		%sp, %sp, 8				;//
		ret							;//return     valueData
REG_get_value_RET_M1:							;//
		jp.d		REG_get_value_RET			;//
		ld.w		%r10, -1				;//%r10    := valueData = -1											*delay*
		;//-----------------------------------------------------;//
REG_name_compar:
		;//%r12 := x
		;//%r13 := y
		ld.ub		%r10, [%r12]+				;//%r10 := xVal = *x++
		ld.ub		%r11, [%r13]+				;//%r11 := yVal = *y++
		cmp		%r10, %r11				;//if(xVal == yVal) {
		jrne		3					;//  
		 ld.ub		%r10, [%r12]				;//  %r10 := xVal = *x
		 ld.ub		%r11, [%r13]				;//  %r11 := yVal = *y }
		ret.d							;//return  xVal - yVal												*anti-interlock*
		sub		%r10, %r11				;//%r10 := xVal - yVal												*delay*
");
#endif//PIECE
/****************************************************************************
 *	ユーティリティ関数
 ****************************************************************************/
#ifndef PIECE
static int REG_open_get_v(const unsigned char* keyBody, const int* names, int (*fn)(const unsigned char*, int)) {
	int name = *names++;
	if(name != -1) {
		goto L_START;
		do {
			keyBody = REG_open_key(keyBody, name);	//(keyBody=NULL)が指定されたり、REG_open_key()がNULLを返して(keyBody=NULL)になっても、継続して安全です。
			name = *names++;
L_START:		;
		} while(*names != -1);
	}
	return (*fn)(keyBody, name);	//(keyBody=NULL)や(name=-1)の場合でも、この処理を実行して安全です。
}
/*--------------------------------------------------------------------------*/
const unsigned char* REG_open_key_l(const unsigned char* keyBody, ...) {
	va_list ap;
	va_start(ap, keyBody);
	keyBody = REG_open_key_a(keyBody, ap);
	va_end(ap);
	return keyBody;
}
const unsigned char* REG_open_key_a(const unsigned char* keyBody, va_list ap) {
	int names[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((names[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(names)) { DIE(); }	//ここで停止した場合は、names[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	return REG_open_key_v(keyBody, names);
}
const unsigned char* REG_open_key_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]keyName,]-1*/) {
	return (const unsigned char*)REG_open_get_v(keyBody, names, (int (*)(const unsigned char*, int))REG_open_key);
}
/*--------------------------------------------------------------------------*/
int REG_get_value_l(const unsigned char* keyBody, ...) {
	int valueData;
	va_list ap;
	va_start(ap, keyBody);
	valueData = REG_get_value_a(keyBody, ap);
	va_end(ap);
	return valueData;
}
int REG_get_value_a(const unsigned char* keyBody, va_list ap) {
	int names[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((names[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(names)) { DIE(); }	//ここで停止した場合は、names[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	return REG_get_value_v(keyBody, names);
}
int REG_get_value_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]valueName,]-1*/) {
	return REG_open_get_v(keyBody, names, REG_get_value);
}
/*--------------------------------------------------------------------------*/
const char* REG_get_string_l(const unsigned char* keyBody, ...) {
	const char* stringData;
	va_list ap;
	va_start(ap, keyBody);
	stringData = REG_get_string_a(keyBody, ap);
	va_end(ap);
	return stringData;
}
const char* REG_get_string_a(const unsigned char* keyBody, va_list ap) {
	int names[(7+1)/*調整可*/], i = 0;	//実用上、キーと値の階層が7以下(終端(-1)を含めて8以下)と考えて充分だろう。不足した場合は増やせ。
	for(;;) {
		if((names[i] = va_arg(ap, int)) == -1) { break; }
		if(++i >= ARRAY_SIZE(names)) { DIE(); }	//ここで停止した場合は、names[]不足か,又は,呼出し側が終端を忘れている。おそらく後者だろう。
	}
	return REG_get_string_v(keyBody, names);
}
const char* REG_get_string_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]stringName,]-1*/) {
	return (const char*)REG_open_get_v(keyBody, names, (int (*)(const unsigned char*, int))REG_get_string);
}
#else //PIECE
const unsigned char* REG_open_key_l(const unsigned char* keyBody, ...);
const unsigned char* REG_open_key_a(const unsigned char* keyBody, va_list ap);
const unsigned char* REG_open_key_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]keyName,]-1*/);
/*--------------------------------------------------------------------------*/
int REG_get_value_l(const unsigned char* keyBody, ...);
int REG_get_value_a(const unsigned char* keyBody, va_list ap);
int REG_get_value_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]valueName,]-1*/);
/*--------------------------------------------------------------------------*/
const char* REG_get_string_l(const unsigned char* keyBody, ...);
const char* REG_get_string_a(const unsigned char* keyBody, va_list ap);
const char* REG_get_string_v(const unsigned char* keyBody, const int* names/*[[[[keyName,]keyName],…,]stringName,]-1*/);
asm("
		.code
		.align		1
		.global		REG_open_key_l
		.global		REG_open_key_a
		.global		REG_open_key_v
		.global		REG_get_value_l
		.global		REG_get_value_a
		.global		REG_get_value_v
		.global		REG_get_string_l
		.global		REG_get_string_a
		.global		REG_get_string_v
REG_open_key_l:
		;//[%sp+0] := retp
		;//[%sp+4] := keyBody
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]				;//%r12 := keyBody
		xadd		%r13, %sp, 8				;//%r13 := ap = &...
REG_open_key_a:
		;//%r12 := keyBody
		;//%r13 := ap
REG_open_key_v:
		;//%r12 := keyBody
		;//%r13 := names
		xld.w		%r14, REG_open_key			;//%r14 := fn = REG_open_key
		jp		REG_open_get_v				;//
		;//-----------------------------------------------------;//
REG_get_value_l:
		;//[%sp+0] := retp
		;//[%sp+4] := keyBody
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]				;//%r12 := keyBody
		xadd		%r13, %sp, 8				;//%r13 := ap = &...
REG_get_value_a:
		;//%r12 := keyBody
		;//%r13 := ap
REG_get_value_v:
		;//%r12 := keyBody
		;//%r13 := names
		xld.w		%r14, REG_get_value			;//%r14 := fn = REG_get_value
		jp		REG_open_get_v				;//
		;//-----------------------------------------------------;//
REG_get_string_l:
		;//[%sp+0] := retp
		;//[%sp+4] := keyBody
		;//[%sp+8] := ...
		xld.w		%r12, [%sp+4]				;//%r12 := keyBody
		xadd		%r13, %sp, 8				;//%r13 := ap = &...
REG_get_string_a:
		;//%r12 := keyBody
		;//%r13 := ap
REG_get_string_v:
		;//%r12 := keyBody
		;//%r13 := names
		xld.w		%r14, REG_get_string			;//%r14 := fn = REG_get_string
		;//-----------------------------------------------------;//
REG_open_get_v:
		;//%r12 := keyBody
		;//%r13 := names
		;//%r14 := fn
		pushn		%r1					;//
		ld.w		%r0, %r13				;//%r0  :=         names
		ld.w		%r1, %r14				;//%r1  :=         fn
		ld.w		%r13, [%r0]+				;//%r13 := name = *names++
		cmp		%r13, -1				;//if(name != -1)
		jreq		REG_open_get_v_NAMES_EMPTY		;//  
		jp		REG_open_get_v_START			;//  goto L_START
REG_open_get_v_LOOP:							;//  do {
		xcall		REG_open_key				;//    %r10 := keyBody = REG_open_key(keyBody, name)
		ld.w		%r12, %r10				;//    %r12 := keyBody
		ld.w		%r13, [%r0]+				;//    %r13 := name = *names++
REG_open_get_v_START:							;//    
		ld.w		%r14, [%r0]				;//    %r14 := *names
		cmp		%r14, -1				;//    														!INTERLOCK!
		jrne		REG_open_get_v_LOOP			;//  } while(*names != -1)
REG_open_get_v_NAMES_EMPTY:						;//}
		ld.w		%r14, %r1				;//%r14 :=  fn
		popn		%r1					;//
		jp		%r14					;//return (*fn)(keyBody, name)
");
#endif//PIECE
