/*	
 *	clipex10.c
 *
 *	16ビットで大きな値を表現するための、指数表現
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2018 Naoyuki Sawa
 *
 *	* Mon Apr 09 23:59:59 JST 2018 Naoyuki Sawa
 *	- 1st リリース。
 *	- Exp10_Decode()とExp10_Encode()を、cliplot.cからclipex10.cへ移動しました。
 *	  抽選処理以外でも使いたい事が有り、cliplot.oが無駄にリンクされてしまう事を避けるためです。
 */
#include "clip.h"
/****************************************************************************
 *	16ビットで大きな値を表現するための、指数表現
 ****************************************************************************/
#ifndef PIECE
int Exp10_Decode(int _x) {
	unsigned x = _x, e;
	if(x > UINT16_MAX) { DIE(); }	//呼び出し側のバグ
	//指数を求める。
	e = x % 6;			//0〜5
	//仮数を求める。
	x = x / 6;			//0〜10922
	//(仮数×10^指数)を求める。
	while(e--) { x *= 10; }		//0〜1092100000(=0x41181FA0)
	return x;
}
int Exp10_Encode(int _x) {
	unsigned x = _x, e = 0;
	//指数を求める。
	while(x && !(x % 10) && (e < 5)) {
		x /= 10;
		e++;
	}
	//(仮数×6＋指数)を求める。
	if(x > UINT16_MAX) { return -1; }	//エンコード値が16ビットを超えるならば、エンコード出来ない事を示す-1を返す。	「(x*6)+e」がオーバーフローする可能性が有るので、「(x*6)+e」の前でも調べる必要が有る事に注意せよ。
	x = (x * 6) + e;
	if(x > UINT16_MAX) { return -1; }	//エンコード値が16ビットを超えるならば、エンコード出来ない事を示す-1を返す。
	return x;
}
#else //PIECE
int Exp10_Decode(int _x);
int Exp10_Encode(int _x);
asm("
		.code
		.align		1
		.global		Exp10_Decode
		.global		Exp10_Encode
Exp10_Decode:
		xcmp		%r12, 65535			;//if(x > UINT16_MAX) { DIE() }
		jrugt		Exp10_Decode_DIE		;//
		xcall.d		__umodsi3			;//%r10 := e = x % 6					除算ルーチンは%r9,%r10,%alr,%ahr,%psr以外を破壊しません。/clip/ksvec200.s,及び,/clip/keep/08_高速除算ルーチン転送先変更前保存.20150103/clipidiv.sを参照して下さい。
		ld.w		%r13, 6				;//%r13 :=         6			*delay*
		xcall.d		__udivsi3			;//%r10 := x = x / 6					除算ルーチンは%r9,%r10,%alr,%ahr,%psr以外を破壊しません。/clip/ksvec200.s,及び,/clip/keep/08_高速除算ルーチン転送先変更前保存.20150103/clipidiv.sを参照して下さい。
		ld.w		%r4, %r10			;//%r4  := %r10				*delay*
		ld.w		%r5, 10				;//%r5  := 10
Exp10_Decode_L10:						;//for(;;) {
		sub		%r4, 1				;//  %r11 := e--
		jrult		Exp10_Decode_L20		;//  if(e == -1) { break }
		mltu.w		%r10, %r5			;//  %alr := x *= 10
		jp.d		Exp10_Decode_L10		;//}
		ld.w		%r10, %alr			;//  %r10 := x				*delay*
Exp10_Decode_L20:						;//
		ret						;//return    x
		;//---------------------------------------------;//
Exp10_Encode:
		ld.w		%r4, 0				;//%r4  := e = 0
		ld.w		%r13, 10			;//%r13 := 10
Exp10_Encode_L10:						;//for(;;) {
		cmp		%r12, 0				;//  if(!x) { break }
		jreq		Exp10_Encode_L20		;//  
		xcall		__umodsi3			;//  %r10 := x % 10					除算ルーチンは%r9,%r10,%alr,%ahr,%psr以外を破壊しません。/clip/ksvec200.s,及び,/clip/keep/08_高速除算ルーチン転送先変更前保存.20150103/clipidiv.sを参照して下さい。
		cmp		%r10, 0				;//  if(x % 10) { break }
		jrne		Exp10_Encode_L20		;//  
		cmp		%r4, 4				;//  if(e > 4) break }
		jrugt		Exp10_Encode_L20		;//  
		xcall.d		__udivsi3			;//  %r10 := x /= 10					除算ルーチンは%r9,%r10,%alr,%ahr,%psr以外を破壊しません。/clip/ksvec200.s,及び,/clip/keep/08_高速除算ルーチン転送先変更前保存.20150103/clipidiv.sを参照して下さい。
		add		%r4, 1				;//  %r4  := e++			*delay*
		jp.d		Exp10_Encode_L10		;//}
		ld.w		%r12, %r10			;//  %r10 := x				*delay*
Exp10_Encode_L20:						;//
		ld.w		%r10, -1			;//%r10 := -1
		ld.uh		%r5, %r10			;//%r5  := UINT16_MAX
		cmp		%r12, %r5			;//if(x > UINT16_MAX) { return -1 }
		jrugt		Exp10_Encode_RET		;//
		ld.w		%r6, 6				;//%r6  :=      6
		mltu.h		%r12, %r6			;//%alr :=  x * 6					ここへ来た時は、%r12,%r6共に(0〜65535)の範囲内である事が保証出来るので、mltu.hで足ります。mltu.wでも構いませんが、mltu.hの方が高速で、結果は同じになります。
		ld.w		%r12, %alr			;//%r12 :=  x * 6
		add		%r12, %r4			;//%r12 := (x * 6) + e
		cmp		%r12, %r5			;//if(x > UINT16_MAX) { return -1 }
		jrugt		Exp10_Encode_RET		;//
		ld.w		%r10, %r12			;//%r10 := x
Exp10_Encode_RET:						;//
		ret						;//return  x,又は,-1
");
static void __attribute__((noreturn,unused))/*asmブロックから参照*/ Exp10_Decode_DIE() { DIE(); }
#endif//PIECE
/*- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
#if 0
//□テストプログラム
void Test() {
	int a, b, c, d, e;
	for(a = 0; a <= UINT16_MAX; a++) {
		b = Exp10_Decode(a);
		c = Exp10_Encode(b);
		d = Exp10_Decode(c);
		e = Exp10_Encode(d);
		printf("%5d %d\n", a, b);
	//	if(a != c) { DIE(); }	//─この検査は行えません。ある整数値を表す指数表現は複数有るので、a:指数表現⇒b:整数値⇒c:指数表現で、cがaと同じになるとは限らないからです。
		if(b != d) { DIE(); }	//┬ただし、Exp10_Decode()とExp10_Encode()を使って変換した場合は一意なので、bとdの比較,及び,cとeの比較は可能です。
		if(c != e) { DIE(); }	//┘
	}
	puts("ok");
}
#endif
