162306a36Sopenharmony_ci|
262306a36Sopenharmony_ci|	stanh.sa 3.1 12/10/90
362306a36Sopenharmony_ci|
462306a36Sopenharmony_ci|	The entry point sTanh computes the hyperbolic tangent of
562306a36Sopenharmony_ci|	an input argument; sTanhd does the same except for denormalized
662306a36Sopenharmony_ci|	input.
762306a36Sopenharmony_ci|
862306a36Sopenharmony_ci|	Input: Double-extended number X in location pointed to
962306a36Sopenharmony_ci|		by address register a0.
1062306a36Sopenharmony_ci|
1162306a36Sopenharmony_ci|	Output: The value tanh(X) returned in floating-point register Fp0.
1262306a36Sopenharmony_ci|
1362306a36Sopenharmony_ci|	Accuracy and Monotonicity: The returned result is within 3 ulps in
1462306a36Sopenharmony_ci|		64 significant bit, i.e. within 0.5001 ulp to 53 bits if the
1562306a36Sopenharmony_ci|		result is subsequently rounded to double precision. The
1662306a36Sopenharmony_ci|		result is provably monotonic in double precision.
1762306a36Sopenharmony_ci|
1862306a36Sopenharmony_ci|	Speed: The program stanh takes approximately 270 cycles.
1962306a36Sopenharmony_ci|
2062306a36Sopenharmony_ci|	Algorithm:
2162306a36Sopenharmony_ci|
2262306a36Sopenharmony_ci|	TANH
2362306a36Sopenharmony_ci|	1. If |X| >= (5/2) log2 or |X| <= 2**(-40), go to 3.
2462306a36Sopenharmony_ci|
2562306a36Sopenharmony_ci|	2. (2**(-40) < |X| < (5/2) log2) Calculate tanh(X) by
2662306a36Sopenharmony_ci|		sgn := sign(X), y := 2|X|, z := expm1(Y), and
2762306a36Sopenharmony_ci|		tanh(X) = sgn*( z/(2+z) ).
2862306a36Sopenharmony_ci|		Exit.
2962306a36Sopenharmony_ci|
3062306a36Sopenharmony_ci|	3. (|X| <= 2**(-40) or |X| >= (5/2) log2). If |X| < 1,
3162306a36Sopenharmony_ci|		go to 7.
3262306a36Sopenharmony_ci|
3362306a36Sopenharmony_ci|	4. (|X| >= (5/2) log2) If |X| >= 50 log2, go to 6.
3462306a36Sopenharmony_ci|
3562306a36Sopenharmony_ci|	5. ((5/2) log2 <= |X| < 50 log2) Calculate tanh(X) by
3662306a36Sopenharmony_ci|		sgn := sign(X), y := 2|X|, z := exp(Y),
3762306a36Sopenharmony_ci|		tanh(X) = sgn - [ sgn*2/(1+z) ].
3862306a36Sopenharmony_ci|		Exit.
3962306a36Sopenharmony_ci|
4062306a36Sopenharmony_ci|	6. (|X| >= 50 log2) Tanh(X) = +-1 (round to nearest). Thus, we
4162306a36Sopenharmony_ci|		calculate Tanh(X) by
4262306a36Sopenharmony_ci|		sgn := sign(X), Tiny := 2**(-126),
4362306a36Sopenharmony_ci|		tanh(X) := sgn - sgn*Tiny.
4462306a36Sopenharmony_ci|		Exit.
4562306a36Sopenharmony_ci|
4662306a36Sopenharmony_ci|	7. (|X| < 2**(-40)). Tanh(X) = X.	Exit.
4762306a36Sopenharmony_ci|
4862306a36Sopenharmony_ci
4962306a36Sopenharmony_ci|		Copyright (C) Motorola, Inc. 1990
5062306a36Sopenharmony_ci|			All Rights Reserved
5162306a36Sopenharmony_ci|
5262306a36Sopenharmony_ci|       For details on the license for this file, please see the
5362306a36Sopenharmony_ci|       file, README, in this same directory.
5462306a36Sopenharmony_ci
5562306a36Sopenharmony_ci|STANH	idnt	2,1 | Motorola 040 Floating Point Software Package
5662306a36Sopenharmony_ci
5762306a36Sopenharmony_ci	|section	8
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_ci#include "fpsp.h"
6062306a36Sopenharmony_ci
6162306a36Sopenharmony_ci	.set	X,FP_SCR5
6262306a36Sopenharmony_ci	.set	XDCARE,X+2
6362306a36Sopenharmony_ci	.set	XFRAC,X+4
6462306a36Sopenharmony_ci
6562306a36Sopenharmony_ci	.set	SGN,L_SCR3
6662306a36Sopenharmony_ci
6762306a36Sopenharmony_ci	.set	V,FP_SCR6
6862306a36Sopenharmony_ci
6962306a36Sopenharmony_ciBOUNDS1:	.long 0x3FD78000,0x3FFFDDCE | ... 2^(-40), (5/2)LOG2
7062306a36Sopenharmony_ci
7162306a36Sopenharmony_ci	|xref	t_frcinx
7262306a36Sopenharmony_ci	|xref	t_extdnrm
7362306a36Sopenharmony_ci	|xref	setox
7462306a36Sopenharmony_ci	|xref	setoxm1
7562306a36Sopenharmony_ci
7662306a36Sopenharmony_ci	.global	stanhd
7762306a36Sopenharmony_cistanhd:
7862306a36Sopenharmony_ci|--TANH(X) = X FOR DENORMALIZED X
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci	bra		t_extdnrm
8162306a36Sopenharmony_ci
8262306a36Sopenharmony_ci	.global	stanh
8362306a36Sopenharmony_cistanh:
8462306a36Sopenharmony_ci	fmovex		(%a0),%fp0	| ...LOAD INPUT
8562306a36Sopenharmony_ci
8662306a36Sopenharmony_ci	fmovex		%fp0,X(%a6)
8762306a36Sopenharmony_ci	movel		(%a0),%d0
8862306a36Sopenharmony_ci	movew		4(%a0),%d0
8962306a36Sopenharmony_ci	movel		%d0,X(%a6)
9062306a36Sopenharmony_ci	andl		#0x7FFFFFFF,%d0
9162306a36Sopenharmony_ci	cmp2l		BOUNDS1(%pc),%d0	| ...2**(-40) < |X| < (5/2)LOG2 ?
9262306a36Sopenharmony_ci	bcss		TANHBORS
9362306a36Sopenharmony_ci
9462306a36Sopenharmony_ci|--THIS IS THE USUAL CASE
9562306a36Sopenharmony_ci|--Y = 2|X|, Z = EXPM1(Y), TANH(X) = SIGN(X) * Z / (Z+2).
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	movel		X(%a6),%d0
9862306a36Sopenharmony_ci	movel		%d0,SGN(%a6)
9962306a36Sopenharmony_ci	andl		#0x7FFF0000,%d0
10062306a36Sopenharmony_ci	addl		#0x00010000,%d0	| ...EXPONENT OF 2|X|
10162306a36Sopenharmony_ci	movel		%d0,X(%a6)
10262306a36Sopenharmony_ci	andl		#0x80000000,SGN(%a6)
10362306a36Sopenharmony_ci	fmovex		X(%a6),%fp0		| ...FP0 IS Y = 2|X|
10462306a36Sopenharmony_ci
10562306a36Sopenharmony_ci	movel		%d1,-(%a7)
10662306a36Sopenharmony_ci	clrl		%d1
10762306a36Sopenharmony_ci	fmovemx	%fp0-%fp0,(%a0)
10862306a36Sopenharmony_ci	bsr		setoxm1		| ...FP0 IS Z = EXPM1(Y)
10962306a36Sopenharmony_ci	movel		(%a7)+,%d1
11062306a36Sopenharmony_ci
11162306a36Sopenharmony_ci	fmovex		%fp0,%fp1
11262306a36Sopenharmony_ci	fadds		#0x40000000,%fp1	| ...Z+2
11362306a36Sopenharmony_ci	movel		SGN(%a6),%d0
11462306a36Sopenharmony_ci	fmovex		%fp1,V(%a6)
11562306a36Sopenharmony_ci	eorl		%d0,V(%a6)
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	fmovel		%d1,%FPCR		|restore users exceptions
11862306a36Sopenharmony_ci	fdivx		V(%a6),%fp0
11962306a36Sopenharmony_ci	bra		t_frcinx
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ciTANHBORS:
12262306a36Sopenharmony_ci	cmpl		#0x3FFF8000,%d0
12362306a36Sopenharmony_ci	blt		TANHSM
12462306a36Sopenharmony_ci
12562306a36Sopenharmony_ci	cmpl		#0x40048AA1,%d0
12662306a36Sopenharmony_ci	bgt		TANHHUGE
12762306a36Sopenharmony_ci
12862306a36Sopenharmony_ci|-- (5/2) LOG2 < |X| < 50 LOG2,
12962306a36Sopenharmony_ci|--TANH(X) = 1 - (2/[EXP(2X)+1]). LET Y = 2|X|, SGN = SIGN(X),
13062306a36Sopenharmony_ci|--TANH(X) = SGN -	SGN*2/[EXP(Y)+1].
13162306a36Sopenharmony_ci
13262306a36Sopenharmony_ci	movel		X(%a6),%d0
13362306a36Sopenharmony_ci	movel		%d0,SGN(%a6)
13462306a36Sopenharmony_ci	andl		#0x7FFF0000,%d0
13562306a36Sopenharmony_ci	addl		#0x00010000,%d0	| ...EXPO OF 2|X|
13662306a36Sopenharmony_ci	movel		%d0,X(%a6)		| ...Y = 2|X|
13762306a36Sopenharmony_ci	andl		#0x80000000,SGN(%a6)
13862306a36Sopenharmony_ci	movel		SGN(%a6),%d0
13962306a36Sopenharmony_ci	fmovex		X(%a6),%fp0		| ...Y = 2|X|
14062306a36Sopenharmony_ci
14162306a36Sopenharmony_ci	movel		%d1,-(%a7)
14262306a36Sopenharmony_ci	clrl		%d1
14362306a36Sopenharmony_ci	fmovemx	%fp0-%fp0,(%a0)
14462306a36Sopenharmony_ci	bsr		setox		| ...FP0 IS EXP(Y)
14562306a36Sopenharmony_ci	movel		(%a7)+,%d1
14662306a36Sopenharmony_ci	movel		SGN(%a6),%d0
14762306a36Sopenharmony_ci	fadds		#0x3F800000,%fp0	| ...EXP(Y)+1
14862306a36Sopenharmony_ci
14962306a36Sopenharmony_ci	eorl		#0xC0000000,%d0	| ...-SIGN(X)*2
15062306a36Sopenharmony_ci	fmoves		%d0,%fp1		| ...-SIGN(X)*2 IN SGL FMT
15162306a36Sopenharmony_ci	fdivx		%fp0,%fp1		| ...-SIGN(X)2 / [EXP(Y)+1 ]
15262306a36Sopenharmony_ci
15362306a36Sopenharmony_ci	movel		SGN(%a6),%d0
15462306a36Sopenharmony_ci	orl		#0x3F800000,%d0	| ...SGN
15562306a36Sopenharmony_ci	fmoves		%d0,%fp0		| ...SGN IN SGL FMT
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_ci	fmovel		%d1,%FPCR		|restore users exceptions
15862306a36Sopenharmony_ci	faddx		%fp1,%fp0
15962306a36Sopenharmony_ci
16062306a36Sopenharmony_ci	bra		t_frcinx
16162306a36Sopenharmony_ci
16262306a36Sopenharmony_ciTANHSM:
16362306a36Sopenharmony_ci	movew		#0x0000,XDCARE(%a6)
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci	fmovel		%d1,%FPCR		|restore users exceptions
16662306a36Sopenharmony_ci	fmovex		X(%a6),%fp0		|last inst - possible exception set
16762306a36Sopenharmony_ci
16862306a36Sopenharmony_ci	bra		t_frcinx
16962306a36Sopenharmony_ci
17062306a36Sopenharmony_ciTANHHUGE:
17162306a36Sopenharmony_ci|---RETURN SGN(X) - SGN(X)EPS
17262306a36Sopenharmony_ci	movel		X(%a6),%d0
17362306a36Sopenharmony_ci	andl		#0x80000000,%d0
17462306a36Sopenharmony_ci	orl		#0x3F800000,%d0
17562306a36Sopenharmony_ci	fmoves		%d0,%fp0
17662306a36Sopenharmony_ci	andl		#0x80000000,%d0
17762306a36Sopenharmony_ci	eorl		#0x80800000,%d0	| ...-SIGN(X)*EPS
17862306a36Sopenharmony_ci
17962306a36Sopenharmony_ci	fmovel		%d1,%FPCR		|restore users exceptions
18062306a36Sopenharmony_ci	fadds		%d0,%fp0
18162306a36Sopenharmony_ci
18262306a36Sopenharmony_ci	bra		t_frcinx
18362306a36Sopenharmony_ci
18462306a36Sopenharmony_ci	|end
185