18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
28c2ecf20Sopenharmony_ci/*---------------------------------------------------------------------------+
38c2ecf20Sopenharmony_ci |  poly_l2.c                                                                |
48c2ecf20Sopenharmony_ci |                                                                           |
58c2ecf20Sopenharmony_ci | Compute the base 2 log of a FPU_REG, using a polynomial approximation.    |
68c2ecf20Sopenharmony_ci |                                                                           |
78c2ecf20Sopenharmony_ci | Copyright (C) 1992,1993,1994,1997                                         |
88c2ecf20Sopenharmony_ci |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
98c2ecf20Sopenharmony_ci |                  E-mail   billm@suburbia.net                              |
108c2ecf20Sopenharmony_ci |                                                                           |
118c2ecf20Sopenharmony_ci |                                                                           |
128c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
138c2ecf20Sopenharmony_ci
148c2ecf20Sopenharmony_ci#include "exception.h"
158c2ecf20Sopenharmony_ci#include "reg_constant.h"
168c2ecf20Sopenharmony_ci#include "fpu_emu.h"
178c2ecf20Sopenharmony_ci#include "fpu_system.h"
188c2ecf20Sopenharmony_ci#include "control_w.h"
198c2ecf20Sopenharmony_ci#include "poly.h"
208c2ecf20Sopenharmony_ci
218c2ecf20Sopenharmony_cistatic void log2_kernel(FPU_REG const *arg, u_char argsign,
228c2ecf20Sopenharmony_ci			Xsig * accum_result, long int *expon);
238c2ecf20Sopenharmony_ci
248c2ecf20Sopenharmony_ci/*--- poly_l2() -------------------------------------------------------------+
258c2ecf20Sopenharmony_ci |   Base 2 logarithm by a polynomial approximation.                         |
268c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
278c2ecf20Sopenharmony_civoid poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign)
288c2ecf20Sopenharmony_ci{
298c2ecf20Sopenharmony_ci	long int exponent, expon, expon_expon;
308c2ecf20Sopenharmony_ci	Xsig accumulator, expon_accum, yaccum;
318c2ecf20Sopenharmony_ci	u_char sign, argsign;
328c2ecf20Sopenharmony_ci	FPU_REG x;
338c2ecf20Sopenharmony_ci	int tag;
348c2ecf20Sopenharmony_ci
358c2ecf20Sopenharmony_ci	exponent = exponent16(st0_ptr);
368c2ecf20Sopenharmony_ci
378c2ecf20Sopenharmony_ci	/* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */
388c2ecf20Sopenharmony_ci	if (st0_ptr->sigh > (unsigned)0xb504f334) {
398c2ecf20Sopenharmony_ci		/* Treat as  sqrt(2)/2 < st0_ptr < 1 */
408c2ecf20Sopenharmony_ci		significand(&x) = -significand(st0_ptr);
418c2ecf20Sopenharmony_ci		setexponent16(&x, -1);
428c2ecf20Sopenharmony_ci		exponent++;
438c2ecf20Sopenharmony_ci		argsign = SIGN_NEG;
448c2ecf20Sopenharmony_ci	} else {
458c2ecf20Sopenharmony_ci		/* Treat as  1 <= st0_ptr < sqrt(2) */
468c2ecf20Sopenharmony_ci		x.sigh = st0_ptr->sigh - 0x80000000;
478c2ecf20Sopenharmony_ci		x.sigl = st0_ptr->sigl;
488c2ecf20Sopenharmony_ci		setexponent16(&x, 0);
498c2ecf20Sopenharmony_ci		argsign = SIGN_POS;
508c2ecf20Sopenharmony_ci	}
518c2ecf20Sopenharmony_ci	tag = FPU_normalize_nuo(&x);
528c2ecf20Sopenharmony_ci
538c2ecf20Sopenharmony_ci	if (tag == TAG_Zero) {
548c2ecf20Sopenharmony_ci		expon = 0;
558c2ecf20Sopenharmony_ci		accumulator.msw = accumulator.midw = accumulator.lsw = 0;
568c2ecf20Sopenharmony_ci	} else {
578c2ecf20Sopenharmony_ci		log2_kernel(&x, argsign, &accumulator, &expon);
588c2ecf20Sopenharmony_ci	}
598c2ecf20Sopenharmony_ci
608c2ecf20Sopenharmony_ci	if (exponent < 0) {
618c2ecf20Sopenharmony_ci		sign = SIGN_NEG;
628c2ecf20Sopenharmony_ci		exponent = -exponent;
638c2ecf20Sopenharmony_ci	} else
648c2ecf20Sopenharmony_ci		sign = SIGN_POS;
658c2ecf20Sopenharmony_ci	expon_accum.msw = exponent;
668c2ecf20Sopenharmony_ci	expon_accum.midw = expon_accum.lsw = 0;
678c2ecf20Sopenharmony_ci	if (exponent) {
688c2ecf20Sopenharmony_ci		expon_expon = 31 + norm_Xsig(&expon_accum);
698c2ecf20Sopenharmony_ci		shr_Xsig(&accumulator, expon_expon - expon);
708c2ecf20Sopenharmony_ci
718c2ecf20Sopenharmony_ci		if (sign ^ argsign)
728c2ecf20Sopenharmony_ci			negate_Xsig(&accumulator);
738c2ecf20Sopenharmony_ci		add_Xsig_Xsig(&accumulator, &expon_accum);
748c2ecf20Sopenharmony_ci	} else {
758c2ecf20Sopenharmony_ci		expon_expon = expon;
768c2ecf20Sopenharmony_ci		sign = argsign;
778c2ecf20Sopenharmony_ci	}
788c2ecf20Sopenharmony_ci
798c2ecf20Sopenharmony_ci	yaccum.lsw = 0;
808c2ecf20Sopenharmony_ci	XSIG_LL(yaccum) = significand(st1_ptr);
818c2ecf20Sopenharmony_ci	mul_Xsig_Xsig(&accumulator, &yaccum);
828c2ecf20Sopenharmony_ci
838c2ecf20Sopenharmony_ci	expon_expon += round_Xsig(&accumulator);
848c2ecf20Sopenharmony_ci
858c2ecf20Sopenharmony_ci	if (accumulator.msw == 0) {
868c2ecf20Sopenharmony_ci		FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
878c2ecf20Sopenharmony_ci		return;
888c2ecf20Sopenharmony_ci	}
898c2ecf20Sopenharmony_ci
908c2ecf20Sopenharmony_ci	significand(st1_ptr) = XSIG_LL(accumulator);
918c2ecf20Sopenharmony_ci	setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1);
928c2ecf20Sopenharmony_ci
938c2ecf20Sopenharmony_ci	tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign);
948c2ecf20Sopenharmony_ci	FPU_settagi(1, tag);
958c2ecf20Sopenharmony_ci
968c2ecf20Sopenharmony_ci	set_precision_flag_up();	/* 80486 appears to always do this */
978c2ecf20Sopenharmony_ci
988c2ecf20Sopenharmony_ci	return;
998c2ecf20Sopenharmony_ci
1008c2ecf20Sopenharmony_ci}
1018c2ecf20Sopenharmony_ci
1028c2ecf20Sopenharmony_ci/*--- poly_l2p1() -----------------------------------------------------------+
1038c2ecf20Sopenharmony_ci |   Base 2 logarithm by a polynomial approximation.                         |
1048c2ecf20Sopenharmony_ci |   log2(x+1)                                                               |
1058c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
1068c2ecf20Sopenharmony_ciint poly_l2p1(u_char sign0, u_char sign1,
1078c2ecf20Sopenharmony_ci	      FPU_REG * st0_ptr, FPU_REG * st1_ptr, FPU_REG * dest)
1088c2ecf20Sopenharmony_ci{
1098c2ecf20Sopenharmony_ci	u_char tag;
1108c2ecf20Sopenharmony_ci	long int exponent;
1118c2ecf20Sopenharmony_ci	Xsig accumulator, yaccum;
1128c2ecf20Sopenharmony_ci
1138c2ecf20Sopenharmony_ci	if (exponent16(st0_ptr) < 0) {
1148c2ecf20Sopenharmony_ci		log2_kernel(st0_ptr, sign0, &accumulator, &exponent);
1158c2ecf20Sopenharmony_ci
1168c2ecf20Sopenharmony_ci		yaccum.lsw = 0;
1178c2ecf20Sopenharmony_ci		XSIG_LL(yaccum) = significand(st1_ptr);
1188c2ecf20Sopenharmony_ci		mul_Xsig_Xsig(&accumulator, &yaccum);
1198c2ecf20Sopenharmony_ci
1208c2ecf20Sopenharmony_ci		exponent += round_Xsig(&accumulator);
1218c2ecf20Sopenharmony_ci
1228c2ecf20Sopenharmony_ci		exponent += exponent16(st1_ptr) + 1;
1238c2ecf20Sopenharmony_ci		if (exponent < EXP_WAY_UNDER)
1248c2ecf20Sopenharmony_ci			exponent = EXP_WAY_UNDER;
1258c2ecf20Sopenharmony_ci
1268c2ecf20Sopenharmony_ci		significand(dest) = XSIG_LL(accumulator);
1278c2ecf20Sopenharmony_ci		setexponent16(dest, exponent);
1288c2ecf20Sopenharmony_ci
1298c2ecf20Sopenharmony_ci		tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1);
1308c2ecf20Sopenharmony_ci		FPU_settagi(1, tag);
1318c2ecf20Sopenharmony_ci
1328c2ecf20Sopenharmony_ci		if (tag == TAG_Valid)
1338c2ecf20Sopenharmony_ci			set_precision_flag_up();	/* 80486 appears to always do this */
1348c2ecf20Sopenharmony_ci	} else {
1358c2ecf20Sopenharmony_ci		/* The magnitude of st0_ptr is far too large. */
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci		if (sign0 != SIGN_POS) {
1388c2ecf20Sopenharmony_ci			/* Trying to get the log of a negative number. */
1398c2ecf20Sopenharmony_ci#ifdef PECULIAR_486		/* Stupid 80486 doesn't worry about log(negative). */
1408c2ecf20Sopenharmony_ci			changesign(st1_ptr);
1418c2ecf20Sopenharmony_ci#else
1428c2ecf20Sopenharmony_ci			if (arith_invalid(1) < 0)
1438c2ecf20Sopenharmony_ci				return 1;
1448c2ecf20Sopenharmony_ci#endif /* PECULIAR_486 */
1458c2ecf20Sopenharmony_ci		}
1468c2ecf20Sopenharmony_ci
1478c2ecf20Sopenharmony_ci		/* 80486 appears to do this */
1488c2ecf20Sopenharmony_ci		if (sign0 == SIGN_NEG)
1498c2ecf20Sopenharmony_ci			set_precision_flag_down();
1508c2ecf20Sopenharmony_ci		else
1518c2ecf20Sopenharmony_ci			set_precision_flag_up();
1528c2ecf20Sopenharmony_ci	}
1538c2ecf20Sopenharmony_ci
1548c2ecf20Sopenharmony_ci	if (exponent(dest) <= EXP_UNDER)
1558c2ecf20Sopenharmony_ci		EXCEPTION(EX_Underflow);
1568c2ecf20Sopenharmony_ci
1578c2ecf20Sopenharmony_ci	return 0;
1588c2ecf20Sopenharmony_ci
1598c2ecf20Sopenharmony_ci}
1608c2ecf20Sopenharmony_ci
1618c2ecf20Sopenharmony_ci#undef HIPOWER
1628c2ecf20Sopenharmony_ci#define	HIPOWER	10
1638c2ecf20Sopenharmony_cistatic const unsigned long long logterms[HIPOWER] = {
1648c2ecf20Sopenharmony_ci	0x2a8eca5705fc2ef0LL,
1658c2ecf20Sopenharmony_ci	0xf6384ee1d01febceLL,
1668c2ecf20Sopenharmony_ci	0x093bb62877cdf642LL,
1678c2ecf20Sopenharmony_ci	0x006985d8a9ec439bLL,
1688c2ecf20Sopenharmony_ci	0x0005212c4f55a9c8LL,
1698c2ecf20Sopenharmony_ci	0x00004326a16927f0LL,
1708c2ecf20Sopenharmony_ci	0x0000038d1d80a0e7LL,
1718c2ecf20Sopenharmony_ci	0x0000003141cc80c6LL,
1728c2ecf20Sopenharmony_ci	0x00000002b1668c9fLL,
1738c2ecf20Sopenharmony_ci	0x000000002c7a46aaLL
1748c2ecf20Sopenharmony_ci};
1758c2ecf20Sopenharmony_ci
1768c2ecf20Sopenharmony_cistatic const unsigned long leadterm = 0xb8000000;
1778c2ecf20Sopenharmony_ci
1788c2ecf20Sopenharmony_ci/*--- log2_kernel() ---------------------------------------------------------+
1798c2ecf20Sopenharmony_ci |   Base 2 logarithm by a polynomial approximation.                         |
1808c2ecf20Sopenharmony_ci |   log2(x+1)                                                               |
1818c2ecf20Sopenharmony_ci +---------------------------------------------------------------------------*/
1828c2ecf20Sopenharmony_cistatic void log2_kernel(FPU_REG const *arg, u_char argsign, Xsig *accum_result,
1838c2ecf20Sopenharmony_ci			long int *expon)
1848c2ecf20Sopenharmony_ci{
1858c2ecf20Sopenharmony_ci	long int exponent, adj;
1868c2ecf20Sopenharmony_ci	unsigned long long Xsq;
1878c2ecf20Sopenharmony_ci	Xsig accumulator, Numer, Denom, argSignif, arg_signif;
1888c2ecf20Sopenharmony_ci
1898c2ecf20Sopenharmony_ci	exponent = exponent16(arg);
1908c2ecf20Sopenharmony_ci	Numer.lsw = Denom.lsw = 0;
1918c2ecf20Sopenharmony_ci	XSIG_LL(Numer) = XSIG_LL(Denom) = significand(arg);
1928c2ecf20Sopenharmony_ci	if (argsign == SIGN_POS) {
1938c2ecf20Sopenharmony_ci		shr_Xsig(&Denom, 2 - (1 + exponent));
1948c2ecf20Sopenharmony_ci		Denom.msw |= 0x80000000;
1958c2ecf20Sopenharmony_ci		div_Xsig(&Numer, &Denom, &argSignif);
1968c2ecf20Sopenharmony_ci	} else {
1978c2ecf20Sopenharmony_ci		shr_Xsig(&Denom, 1 - (1 + exponent));
1988c2ecf20Sopenharmony_ci		negate_Xsig(&Denom);
1998c2ecf20Sopenharmony_ci		if (Denom.msw & 0x80000000) {
2008c2ecf20Sopenharmony_ci			div_Xsig(&Numer, &Denom, &argSignif);
2018c2ecf20Sopenharmony_ci			exponent++;
2028c2ecf20Sopenharmony_ci		} else {
2038c2ecf20Sopenharmony_ci			/* Denom must be 1.0 */
2048c2ecf20Sopenharmony_ci			argSignif.lsw = Numer.lsw;
2058c2ecf20Sopenharmony_ci			argSignif.midw = Numer.midw;
2068c2ecf20Sopenharmony_ci			argSignif.msw = Numer.msw;
2078c2ecf20Sopenharmony_ci		}
2088c2ecf20Sopenharmony_ci	}
2098c2ecf20Sopenharmony_ci
2108c2ecf20Sopenharmony_ci#ifndef PECULIAR_486
2118c2ecf20Sopenharmony_ci	/* Should check here that  |local_arg|  is within the valid range */
2128c2ecf20Sopenharmony_ci	if (exponent >= -2) {
2138c2ecf20Sopenharmony_ci		if ((exponent > -2) || (argSignif.msw > (unsigned)0xafb0ccc0)) {
2148c2ecf20Sopenharmony_ci			/* The argument is too large */
2158c2ecf20Sopenharmony_ci		}
2168c2ecf20Sopenharmony_ci	}
2178c2ecf20Sopenharmony_ci#endif /* PECULIAR_486 */
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci	arg_signif.lsw = argSignif.lsw;
2208c2ecf20Sopenharmony_ci	XSIG_LL(arg_signif) = XSIG_LL(argSignif);
2218c2ecf20Sopenharmony_ci	adj = norm_Xsig(&argSignif);
2228c2ecf20Sopenharmony_ci	accumulator.lsw = argSignif.lsw;
2238c2ecf20Sopenharmony_ci	XSIG_LL(accumulator) = XSIG_LL(argSignif);
2248c2ecf20Sopenharmony_ci	mul_Xsig_Xsig(&accumulator, &accumulator);
2258c2ecf20Sopenharmony_ci	shr_Xsig(&accumulator, 2 * (-1 - (1 + exponent + adj)));
2268c2ecf20Sopenharmony_ci	Xsq = XSIG_LL(accumulator);
2278c2ecf20Sopenharmony_ci	if (accumulator.lsw & 0x80000000)
2288c2ecf20Sopenharmony_ci		Xsq++;
2298c2ecf20Sopenharmony_ci
2308c2ecf20Sopenharmony_ci	accumulator.msw = accumulator.midw = accumulator.lsw = 0;
2318c2ecf20Sopenharmony_ci	/* Do the basic fixed point polynomial evaluation */
2328c2ecf20Sopenharmony_ci	polynomial_Xsig(&accumulator, &Xsq, logterms, HIPOWER - 1);
2338c2ecf20Sopenharmony_ci
2348c2ecf20Sopenharmony_ci	mul_Xsig_Xsig(&accumulator, &argSignif);
2358c2ecf20Sopenharmony_ci	shr_Xsig(&accumulator, 6 - adj);
2368c2ecf20Sopenharmony_ci
2378c2ecf20Sopenharmony_ci	mul32_Xsig(&arg_signif, leadterm);
2388c2ecf20Sopenharmony_ci	add_two_Xsig(&accumulator, &arg_signif, &exponent);
2398c2ecf20Sopenharmony_ci
2408c2ecf20Sopenharmony_ci	*expon = exponent + 1;
2418c2ecf20Sopenharmony_ci	accum_result->lsw = accumulator.lsw;
2428c2ecf20Sopenharmony_ci	accum_result->midw = accumulator.midw;
2438c2ecf20Sopenharmony_ci	accum_result->msw = accumulator.msw;
2448c2ecf20Sopenharmony_ci
2458c2ecf20Sopenharmony_ci}
246