/*	
 *	clipfp16.c
 *
 *	半精度浮動小数点数
 *
 *	CLiP - Common Library for P/ECE
 *	Copyright (C) 2001-2016 Naoyuki Sawa
 *
 *	* Mon Jul 27 18:33:57 JST 2009 Naoyuki Sawa
 *	- 1st リリース。
 *	* Wed Nov 18 21:37:53 JST 2015 Naoyuki Sawa
 *	- 'extern "C" {'〜'}'で囲みました。.cppを含むWin32プロジェクトからもインクルード出来るようにするためです。
 *	* Sun Mar 20 21:22:35 JST 2016 Naoyuki Sawa
 *	- 当モジュールの名前を、'16bit浮動小数点形式'⇒'半精度浮動小数点数'に変更しました。
 *	  下記の資料によると、'半精度浮動小数点数'と呼ぶのが正式なようです。
 *	  □参照資料
 *	  │http://ja.wikipedia.org/wiki/半精度浮動小数点数
 *	- グローバル関数を全て変更しました。
 *	  具体的には、既存のfp16_read(),fp16_write()を廃止し、新規にtruncsfhf2(),truncdfhf2(),extendhfsf2(),extendhfdf2()を作成しました。
 *	  上記の変更をおこなった理由は、以下の通りです。
 *	  既存のfp16_read(),fp16_write()は、参照渡しでfp16⇔float変換を行う関数でした。
 *	  これまではfp16_read(),fp16_write()は用意していただけであまり利用していなかったのですが、頻繁に使うようになると参照渡しよりも値渡しの方が使い易い事が判りました。
 *	  そこで、参照渡しの関数を廃止し、値渡しに変更する事にしました。
 *	  また、fp16⇔float変換だけでなく、fp16⇔double変換も行いたいので、fp16⇔double変換を行う関数も追加する事にしました。
 *	- これまでは、fp16型を構造体として定義していたのですが、uint16_tへのtypedefに変更しました。
 *	  変更前のfp16型を構造体は、FP16_HALFに名前を変えて残してありますが、当モジュールの内部処理用であり、アプリケーションが直接FP16_HALF型を使用する事は推奨しません。
 *	  上記の変更をおこなった理由は、以下の通りです。
 *	  これまでfp16型を構造体として定義していた理由は、fp16の値を間違って普通の式に含めた時にエラーを検出出来るようにするためでした。
 *	  それは良いのですが、構造体を関数に値渡ししたり戻り値として受け取ると、P/ECEのコンパイラは効率の悪いコードを生成します。
 *	  それを避けるために、これまでは参照渡しで変換していました。
 *	  しかし、前述の理由で、今回、グローバル関数を全て値渡し・値返しに変更しました。
 *	  fp16型を構造体が構造体のままでは、効率の悪いコードが生成されてしまいます。
 *	  そこで、fp16型をuint16_tへのtypedefに変更する事にしました。
 *	- 今回の変更によるデメリットとして、fp16の値を間違って普通の式に含めた時にエラーが検出出来ず、見つけ辛いバグになる恐れが有ります。
 *	  このデメリットによる問題を避けるために、アプリケーションはなるべくデータのロード/セーブ時だけにfp16への変換を使用して下さい。
 *	  データのロード/セーブ時にすぐfloat,又は,doubleへ変換し、計算は全てflot,又は,doubleを使うようにして下さい。
 *	  要するに、fp16を使用する箇所を、データのロード/セーブの箇所だけに限定するのが安全です。
 *	- 関数名truncsfhf2(),truncdfhf2(),extendhfsf2(),extendhfdf2()は、GCCの組み込み関数名から取りました。
 *	  P/ECEのコンパイラではfp16はサポートされていませんが、最近のGCCではfp16がサポートされているアーキテクチャも有るそうです。
 *	  'truncdfhf2'等で検索してみると、GCCやLLVMにそれらの関数が実装されているらしき情報がヒットします。(詳細は未確認です。)
 *	- 以上、今回の変更は当モジュール全体の大幅な変更であり、変更前のコードはコメントアウトして残していません。
 *	  変更前のコードを参照する必要がある場合は、前回までのアーカイブを参照して下さい。
 *	* Mon Mar 21 21:34:20 JST 2016 Naoyuki Sawa
 *	- truncsfhf2(),truncdfhf2(),extendhfsf2(),extendhfdf2()をアセンブラ化しました。
 *	- truncdfhf2(),extendhfdf2()は、C言語版はtruncsfhf2(),extendhfsf2()を経由する単純な実装としています。
 *	  C言語版を最適化しなかった理由は、doubleにはFLT_INTに相当するビットフィールドを定義していないためと、
 *	  C言語版が使用される環境(Win32等)では、最適化しなくても性能上もコードサイズ上(下記参照)も問題無いと思ったからです。
 *	- truncdfhf2(),extendhfdf2()は、アセンブラ版ではtruncsfhf2(),extendhfsf2()を経由せずに、直接fp16⇔double変換を行うように最適化しました。
 *	  アセンブラ版は最適化した理由は、もしtruncsfhf2(),extendhfsf2()を経由すると、それ以外にfloat演算を使っていなくてもframflt1.sがリンクされてしまい、コードサイズが無駄に増える事と、
 *	  アセンブラ版を使用する環境(P/ECE)では、少しでも高速化するために直接fp16⇔double変換を行う方が良いと思ったからです。
 */
#include "clip.h"
/****************************************************************************
 *	グローバル関数
 ****************************************************************************/
#ifndef PIECE
fp16 truncsfhf2(float _x) {
	FLT_INT x;
	FP16_HALF y;
	int e;
	x.f = _x;
	e = x.i.e - (127 - 15);
	if(e <= 0) { return 0; }
	if(e > 31) { DIE(); }	//overflow
	y.h.s = x.i.s;
	y.h.e = e;
	y.h.m = x.i.m >> (23 - 10);
	return y.f;
}
#else //PIECE
/*static*/ fp16 truncsfhf2(float _x);
asm("
		.code
		.align		1
		.global		truncsfhf2
truncsfhf2:
		;//						;//%r12 := seeeeeee emmmmmmm mmmmmmmm mmmmmmmm
		rl		%r12, 6				;//%r12 := eeemmmmm mmmmmmmm mmmmmmmm mmseeeee
		xand		%r10, %r12, 0x00000020		;//%r10 := 00000000 00000000 00000000 00s00000
		rl		%r12, 3				;//%r12 := mmmmmmmm mmmmmmmm mmmmmmms eeeeeeee
		ld.ub		%r11, %r12			;//%r11 := 00000000 00000000 00000000 eeeeeeee
		xor		%r12, %r11			;//%r12 := mmmmmmmm mmmmmmmm mmmmmmms 00000000
		xsub		%r11, %r11, 112			;//%r11 := 00000000 00000000 00000000 000eeeee (e -= (127-15))
		jrle		truncsfhf2_RET0			;//if(e <= 0) { return 0 }
		cmp		%r11, 31			;//if(e > 31) { overflow }
		jrgt		truncsfhf2_DIE			;//
		or		%r10, %r11			;//%r10 := 00000000 00000000 00000000 00seeeee
		or		%r10, %r12			;//%r10 := mmmmmmmm mmmmmmmm mmmmmmms 00seeeee
		xrl		%r10, 10			;//%r10 := mmmmmmmm mmmmms00 seeeeemm mmmmmmmm
		ret.d						;//return y.f
		ld.uh		%r10, %r10			;//%r10 := 00000000 00000000 seeeeemm mmmmmmmm				*delay*
truncsfhf2_RET0:
		ret.d						;//return 0
		ld.w		%r10, 0				;//%r10 := 00000000 00000000 00000000 00000000				*delay*
");
static void __attribute__((noreturn,unused)) truncsfhf2_DIE() { DIE(); }	//overflow
#endif//PIECE
/*--------------------------------------------------------------------------*/
#ifndef PIECE
fp16 truncdfhf2(double x) {
	return truncsfhf2(x);
}
#else //PIECE
/*static*/ fp16 truncdfhf2(double _x);
asm("
		.code
		.align		1
		.global		truncdfhf2
truncdfhf2:
		;//						;//%r13 := seeeeeee eeeemmmm mmmmmmmm mmmmmmmm
		rl		%r13, 6				;//%r13 := eeeeeemm mmmmmmmm mmmmmmmm mmseeeee
		xand		%r10, %r13, 0x00000020		;//%r10 := 00000000 00000000 00000000 00s00000
		rl		%r13, 6				;//%r13 := mmmmmmmm mmmmmmmm mmmmseee eeeeeeee
		xand		%r11, %r13, 0x000007FF		;//%r11 := 00000000 00000000 00000eee eeeeeeee
		xor		%r13, %r11			;//%r13 := mmmmmmmm mmmmmmmm mmmms000 00000000
		xsub		%r11, %r11, 1008		;//%r11 := 00000000 00000000 00000000 000eeeee (e -= (1023-15))
		jrle		truncdfhf2_RET0			;//if(e <= 0) { return 0 }
		cmp		%r11, 31			;//if(e > 31) { overflow }
		jrgt		truncdfhf2_DIE			;//
		or		%r10, %r11			;//%r10 := 00000000 00000000 00000000 00seeeee
		or		%r10, %r13			;//%r10 := mmmmmmmm mmmmmmmm mmmms000 00seeeee
		xrl		%r10, 10			;//%r10 := mmmmmmmm mms00000 seeeeemm mmmmmmmm
		ret.d						;//return y.f
		ld.uh		%r10, %r10			;//%r10 := 00000000 00000000 seeeeemm mmmmmmmm				*delay*
truncdfhf2_RET0:
		ret.d						;//return 0
		ld.w		%r10, 0				;//%r10 := 00000000 00000000 00000000 00000000				*delay*
");
static void __attribute__((noreturn,unused)) truncdfhf2_DIE() { DIE(); }	//overflow
#endif//PIECE
/*--------------------------------------------------------------------------*/
#ifndef PIECE
float extendhfsf2(fp16 _x) {
	FP16_HALF x;
	FLT_INT y;
	x.f = _x;
	if(!x.h.e) { return 0.0f; }
	y.i.s = x.h.s;
	y.i.e = x.h.e + (127 - 15);
	y.i.m = x.h.m << (23 - 10);
	return y.f;
}
#else //PIECE
/*static*/ float extendhfsf2(fp16 _x);
asm("
		.code
		.align		1
		.global		extendhfsf2
extendhfsf2:
		;//						;//%r12 := 00000000 00000000 seeeeemm mmmmmmmm
		ld.h		%r12, %r12			;//%r12 := ssssssss ssssssss seeeeemm mmmmmmmm
		xand		%r10, %r12, 0x00007C00		;//%r10 := 00000000 00000000 0eeeee00 00000000
		jreq		extendhfsf2_RET			;//if(!e) { return 0.0f }
		xand		%r12, %r12, 0xFFFC03FF		;//%r12 := ssssssss ssssss00 000000mm mmmmmmmm
		xadd		%r10, %r10, 0x0001C000		;//%r10 := 00000000 000000ee eeeeee00 00000000 (e += (127-15)<<10)
		or		%r10, %r12			;//%r10 := ssssssss ssssssee eeeeeemm mmmmmmmm
		sla		%r10, 8				;//%r10 := ssssssee eeeeeemm mmmmmmmm 00000000
extendhfsf2_RET:
		ret.d						;//return y.f
		sla		%r10, 5				;//%r10 := seeeeeee emmmmmmm mmm00000 00000000				*delay*
");
#endif//PIECE
/*--------------------------------------------------------------------------*/
#ifndef PIECE
double extendhfdf2(fp16 x) {
	return extendhfsf2(x);
}
#else //PIECE
/*static*/ double extendhfdf2(fp16 x);
asm("
		.code
		.align		1
		.global		extendhfdf2
extendhfdf2:
		;//						;//%r12 := 00000000 00000000 seeeeemm mmmmmmmm
		ld.h		%r12, %r12			;//%r12 := ssssssss ssssssss seeeeemm mmmmmmmm
		xand		%r11, %r12, 0x00007C00		;//%r11 := 00000000 00000000 0eeeee00 00000000
		jreq		extendhfdf2_RET			;//if(!e) { return 0.0 }
		xand		%r12, %r12, 0xFFE003FF		;//%r12 := ssssssss sss00000 000000mm mmmmmmmm
		xadd		%r11, %r11, 0x000FC000		;//%r11 := 00000000 000eeeee eeeeee00 00000000 (e += (1023-15)<<10)
		or		%r11, %r12			;//%r11 := ssssssss ssseeeee eeeeeemm mmmmmmmm
		xsla		%r11, 10			;//%r11 := seeeeeee emmmmmmm mmmmmm00 00000000
extendhfdf2_RET:
		ret.d						;//return y.f
		ld.w		%r10, 0				;//%r10 := 00000000 00000000 00000000 00000000				*delay*
");
#endif//PIECE
