mirror of
https://github.com/torvalds/linux.git
synced 2024-11-17 09:31:50 +00:00
b24413180f
Many source files in the tree are missing licensing information, which makes it harder for compliance tools to determine the correct license. By default all files without license information are under the default license of the kernel, which is GPL version 2. Update the files which contain no license information with the 'GPL-2.0' SPDX license identifier. The SPDX identifier is a legally binding shorthand, which can be used instead of the full boiler plate text. This patch is based on work done by Thomas Gleixner and Kate Stewart and Philippe Ombredanne. How this work was done: Patches were generated and checked against linux-4.14-rc6 for a subset of the use cases: - file had no licensing information it it. - file was a */uapi/* one with no licensing information in it, - file was a */uapi/* one with existing licensing information, Further patches will be generated in subsequent months to fix up cases where non-standard license headers were used, and references to license had to be inferred by heuristics based on keywords. The analysis to determine which SPDX License Identifier to be applied to a file was done in a spreadsheet of side by side results from of the output of two independent scanners (ScanCode & Windriver) producing SPDX tag:value files created by Philippe Ombredanne. Philippe prepared the base worksheet, and did an initial spot review of a few 1000 files. The 4.13 kernel was the starting point of the analysis with 60,537 files assessed. Kate Stewart did a file by file comparison of the scanner results in the spreadsheet to determine which SPDX license identifier(s) to be applied to the file. She confirmed any determination that was not immediately clear with lawyers working with the Linux Foundation. Criteria used to select files for SPDX license identifier tagging was: - Files considered eligible had to be source code files. - Make and config files were included as candidates if they contained >5 lines of source - File already had some variant of a license header in it (even if <5 lines). All documentation files were explicitly excluded. The following heuristics were used to determine which SPDX license identifiers to apply. - when both scanners couldn't find any license traces, file was considered to have no license information in it, and the top level COPYING file license applied. For non */uapi/* files that summary was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 11139 and resulted in the first patch in this series. If that file was a */uapi/* path one, it was "GPL-2.0 WITH Linux-syscall-note" otherwise it was "GPL-2.0". Results of that was: SPDX license identifier # files ---------------------------------------------------|------- GPL-2.0 WITH Linux-syscall-note 930 and resulted in the second patch in this series. - if a file had some form of licensing information in it, and was one of the */uapi/* ones, it was denoted with the Linux-syscall-note if any GPL family license was found in the file or had no licensing in it (per prior point). Results summary: SPDX license identifier # files ---------------------------------------------------|------ GPL-2.0 WITH Linux-syscall-note 270 GPL-2.0+ WITH Linux-syscall-note 169 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-2-Clause) 21 ((GPL-2.0 WITH Linux-syscall-note) OR BSD-3-Clause) 17 LGPL-2.1+ WITH Linux-syscall-note 15 GPL-1.0+ WITH Linux-syscall-note 14 ((GPL-2.0+ WITH Linux-syscall-note) OR BSD-3-Clause) 5 LGPL-2.0+ WITH Linux-syscall-note 4 LGPL-2.1 WITH Linux-syscall-note 3 ((GPL-2.0 WITH Linux-syscall-note) OR MIT) 3 ((GPL-2.0 WITH Linux-syscall-note) AND MIT) 1 and that resulted in the third patch in this series. - when the two scanners agreed on the detected license(s), that became the concluded license(s). - when there was disagreement between the two scanners (one detected a license but the other didn't, or they both detected different licenses) a manual inspection of the file occurred. - In most cases a manual inspection of the information in the file resulted in a clear resolution of the license that should apply (and which scanner probably needed to revisit its heuristics). - When it was not immediately clear, the license identifier was confirmed with lawyers working with the Linux Foundation. - If there was any question as to the appropriate license identifier, the file was flagged for further research and to be revisited later in time. In total, over 70 hours of logged manual review was done on the spreadsheet to determine the SPDX license identifiers to apply to the source files by Kate, Philippe, Thomas and, in some cases, confirmation by lawyers working with the Linux Foundation. Kate also obtained a third independent scan of the 4.13 code base from FOSSology, and compared selected files where the other two scanners disagreed against that SPDX file, to see if there was new insights. The Windriver scanner is based on an older version of FOSSology in part, so they are related. Thomas did random spot checks in about 500 files from the spreadsheets for the uapi headers and agreed with SPDX license identifier in the files he inspected. For the non-uapi files Thomas did random spot checks in about 15000 files. In initial set of patches against 4.14-rc6, 3 files were found to have copy/paste license identifier errors, and have been fixed to reflect the correct identifier. Additionally Philippe spent 10 hours this week doing a detailed manual inspection and review of the 12,461 patched files from the initial patch version early this week with: - a full scancode scan run, collecting the matched texts, detected license ids and scores - reviewing anything where there was a license detected (about 500+ files) to ensure that the applied SPDX license was correct - reviewing anything where there was no detection but the patch license was not GPL-2.0 WITH Linux-syscall-note to ensure that the applied SPDX license was correct This produced a worksheet with 20 files needing minor correction. This worksheet was then exported into 3 different .csv files for the different types of files to be modified. These .csv files were then reviewed by Greg. Thomas wrote a script to parse the csv files and add the proper SPDX tag to the file, in the format that the file expected. This script was further refined by Greg based on the output to detect more types of files automatically and to distinguish between header and source .c files (which need different comment types.) Finally Greg ran the script using the .csv files to generate the patches. Reviewed-by: Kate Stewart <kstewart@linuxfoundation.org> Reviewed-by: Philippe Ombredanne <pombredanne@nexb.com> Reviewed-by: Thomas Gleixner <tglx@linutronix.de> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
1645 lines
38 KiB
C
1645 lines
38 KiB
C
// SPDX-License-Identifier: GPL-2.0
|
|
/*---------------------------------------------------------------------------+
|
|
| fpu_trig.c |
|
|
| |
|
|
| Implementation of the FPU "transcendental" functions. |
|
|
| |
|
|
| Copyright (C) 1992,1993,1994,1997,1999 |
|
|
| W. Metzenthen, 22 Parker St, Ormond, Vic 3163, |
|
|
| Australia. E-mail billm@melbpc.org.au |
|
|
| |
|
|
| |
|
|
+---------------------------------------------------------------------------*/
|
|
|
|
#include "fpu_system.h"
|
|
#include "exception.h"
|
|
#include "fpu_emu.h"
|
|
#include "status_w.h"
|
|
#include "control_w.h"
|
|
#include "reg_constant.h"
|
|
|
|
static void rem_kernel(unsigned long long st0, unsigned long long *y,
|
|
unsigned long long st1, unsigned long long q, int n);
|
|
|
|
#define BETTER_THAN_486
|
|
|
|
#define FCOS 4
|
|
|
|
/* Used only by fptan, fsin, fcos, and fsincos. */
|
|
/* This routine produces very accurate results, similar to
|
|
using a value of pi with more than 128 bits precision. */
|
|
/* Limited measurements show no results worse than 64 bit precision
|
|
except for the results for arguments close to 2^63, where the
|
|
precision of the result sometimes degrades to about 63.9 bits */
|
|
static int trig_arg(FPU_REG *st0_ptr, int even)
|
|
{
|
|
FPU_REG tmp;
|
|
u_char tmptag;
|
|
unsigned long long q;
|
|
int old_cw = control_word, saved_status = partial_status;
|
|
int tag, st0_tag = TAG_Valid;
|
|
|
|
if (exponent(st0_ptr) >= 63) {
|
|
partial_status |= SW_C2; /* Reduction incomplete. */
|
|
return -1;
|
|
}
|
|
|
|
control_word &= ~CW_RC;
|
|
control_word |= RC_CHOP;
|
|
|
|
setpositive(st0_ptr);
|
|
tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
|
|
SIGN_POS);
|
|
|
|
FPU_round_to_int(&tmp, tag); /* Fortunately, this can't overflow
|
|
to 2^64 */
|
|
q = significand(&tmp);
|
|
if (q) {
|
|
rem_kernel(significand(st0_ptr),
|
|
&significand(&tmp),
|
|
significand(&CONST_PI2),
|
|
q, exponent(st0_ptr) - exponent(&CONST_PI2));
|
|
setexponent16(&tmp, exponent(&CONST_PI2));
|
|
st0_tag = FPU_normalize(&tmp);
|
|
FPU_copy_to_reg0(&tmp, st0_tag);
|
|
}
|
|
|
|
if ((even && !(q & 1)) || (!even && (q & 1))) {
|
|
st0_tag =
|
|
FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2,
|
|
FULL_PRECISION);
|
|
|
|
#ifdef BETTER_THAN_486
|
|
/* So far, the results are exact but based upon a 64 bit
|
|
precision approximation to pi/2. The technique used
|
|
now is equivalent to using an approximation to pi/2 which
|
|
is accurate to about 128 bits. */
|
|
if ((exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)
|
|
|| (q > 1)) {
|
|
/* This code gives the effect of having pi/2 to better than
|
|
128 bits precision. */
|
|
|
|
significand(&tmp) = q + 1;
|
|
setexponent16(&tmp, 63);
|
|
FPU_normalize(&tmp);
|
|
tmptag =
|
|
FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
|
|
FULL_PRECISION, SIGN_POS,
|
|
exponent(&CONST_PI2extra) +
|
|
exponent(&tmp));
|
|
setsign(&tmp, getsign(&CONST_PI2extra));
|
|
st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
|
|
if (signnegative(st0_ptr)) {
|
|
/* CONST_PI2extra is negative, so the result of the addition
|
|
can be negative. This means that the argument is actually
|
|
in a different quadrant. The correction is always < pi/2,
|
|
so it can't overflow into yet another quadrant. */
|
|
setpositive(st0_ptr);
|
|
q++;
|
|
}
|
|
}
|
|
#endif /* BETTER_THAN_486 */
|
|
}
|
|
#ifdef BETTER_THAN_486
|
|
else {
|
|
/* So far, the results are exact but based upon a 64 bit
|
|
precision approximation to pi/2. The technique used
|
|
now is equivalent to using an approximation to pi/2 which
|
|
is accurate to about 128 bits. */
|
|
if (((q > 0)
|
|
&& (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64))
|
|
|| (q > 1)) {
|
|
/* This code gives the effect of having p/2 to better than
|
|
128 bits precision. */
|
|
|
|
significand(&tmp) = q;
|
|
setexponent16(&tmp, 63);
|
|
FPU_normalize(&tmp); /* This must return TAG_Valid */
|
|
tmptag =
|
|
FPU_u_mul(&CONST_PI2extra, &tmp, &tmp,
|
|
FULL_PRECISION, SIGN_POS,
|
|
exponent(&CONST_PI2extra) +
|
|
exponent(&tmp));
|
|
setsign(&tmp, getsign(&CONST_PI2extra));
|
|
st0_tag = FPU_sub(LOADED | (tmptag & 0x0f), (int)&tmp,
|
|
FULL_PRECISION);
|
|
if ((exponent(st0_ptr) == exponent(&CONST_PI2)) &&
|
|
((st0_ptr->sigh > CONST_PI2.sigh)
|
|
|| ((st0_ptr->sigh == CONST_PI2.sigh)
|
|
&& (st0_ptr->sigl > CONST_PI2.sigl)))) {
|
|
/* CONST_PI2extra is negative, so the result of the
|
|
subtraction can be larger than pi/2. This means
|
|
that the argument is actually in a different quadrant.
|
|
The correction is always < pi/2, so it can't overflow
|
|
into yet another quadrant. */
|
|
st0_tag =
|
|
FPU_sub(REV | LOADED | TAG_Valid,
|
|
(int)&CONST_PI2, FULL_PRECISION);
|
|
q++;
|
|
}
|
|
}
|
|
}
|
|
#endif /* BETTER_THAN_486 */
|
|
|
|
FPU_settag0(st0_tag);
|
|
control_word = old_cw;
|
|
partial_status = saved_status & ~SW_C2; /* Reduction complete. */
|
|
|
|
return (q & 3) | even;
|
|
}
|
|
|
|
/* Convert a long to register */
|
|
static void convert_l2reg(long const *arg, int deststnr)
|
|
{
|
|
int tag;
|
|
long num = *arg;
|
|
u_char sign;
|
|
FPU_REG *dest = &st(deststnr);
|
|
|
|
if (num == 0) {
|
|
FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
|
|
return;
|
|
}
|
|
|
|
if (num > 0) {
|
|
sign = SIGN_POS;
|
|
} else {
|
|
num = -num;
|
|
sign = SIGN_NEG;
|
|
}
|
|
|
|
dest->sigh = num;
|
|
dest->sigl = 0;
|
|
setexponent16(dest, 31);
|
|
tag = FPU_normalize(dest);
|
|
FPU_settagi(deststnr, tag);
|
|
setsign(dest, sign);
|
|
return;
|
|
}
|
|
|
|
static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
if (st0_tag == TAG_Empty)
|
|
FPU_stack_underflow(); /* Puts a QNaN in st(0) */
|
|
else if (st0_tag == TW_NaN)
|
|
real_1op_NaN(st0_ptr); /* return with a NaN in st(0) */
|
|
#ifdef PARANOID
|
|
else
|
|
EXCEPTION(EX_INTERNAL | 0x0112);
|
|
#endif /* PARANOID */
|
|
}
|
|
|
|
static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
int isNaN;
|
|
|
|
switch (st0_tag) {
|
|
case TW_NaN:
|
|
isNaN = (exponent(st0_ptr) == EXP_OVER)
|
|
&& (st0_ptr->sigh & 0x80000000);
|
|
if (isNaN && !(st0_ptr->sigh & 0x40000000)) { /* Signaling ? */
|
|
EXCEPTION(EX_Invalid);
|
|
if (control_word & CW_Invalid) {
|
|
/* The masked response */
|
|
/* Convert to a QNaN */
|
|
st0_ptr->sigh |= 0x40000000;
|
|
push();
|
|
FPU_copy_to_reg0(st0_ptr, TAG_Special);
|
|
}
|
|
} else if (isNaN) {
|
|
/* A QNaN */
|
|
push();
|
|
FPU_copy_to_reg0(st0_ptr, TAG_Special);
|
|
} else {
|
|
/* pseudoNaN or other unsupported */
|
|
EXCEPTION(EX_Invalid);
|
|
if (control_word & CW_Invalid) {
|
|
/* The masked response */
|
|
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
|
|
push();
|
|
FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
|
|
}
|
|
}
|
|
break; /* return with a NaN in st(0) */
|
|
#ifdef PARANOID
|
|
default:
|
|
EXCEPTION(EX_INTERNAL | 0x0112);
|
|
#endif /* PARANOID */
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static void f2xm1(FPU_REG *st0_ptr, u_char tag)
|
|
{
|
|
FPU_REG a;
|
|
|
|
clear_C1();
|
|
|
|
if (tag == TAG_Valid) {
|
|
/* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */
|
|
if (exponent(st0_ptr) < 0) {
|
|
denormal_arg:
|
|
|
|
FPU_to_exp16(st0_ptr, &a);
|
|
|
|
/* poly_2xm1(x) requires 0 < st(0) < 1. */
|
|
poly_2xm1(getsign(st0_ptr), &a, st0_ptr);
|
|
}
|
|
set_precision_flag_up(); /* 80486 appears to always do this */
|
|
return;
|
|
}
|
|
|
|
if (tag == TAG_Zero)
|
|
return;
|
|
|
|
if (tag == TAG_Special)
|
|
tag = FPU_Special(st0_ptr);
|
|
|
|
switch (tag) {
|
|
case TW_Denormal:
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
goto denormal_arg;
|
|
case TW_Infinity:
|
|
if (signnegative(st0_ptr)) {
|
|
/* -infinity gives -1 (p16-10) */
|
|
FPU_copy_to_reg0(&CONST_1, TAG_Valid);
|
|
setnegative(st0_ptr);
|
|
}
|
|
return;
|
|
default:
|
|
single_arg_error(st0_ptr, tag);
|
|
}
|
|
}
|
|
|
|
static void fptan(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st_new_ptr;
|
|
int q;
|
|
u_char arg_sign = getsign(st0_ptr);
|
|
|
|
/* Stack underflow has higher priority */
|
|
if (st0_tag == TAG_Empty) {
|
|
FPU_stack_underflow(); /* Puts a QNaN in st(0) */
|
|
if (control_word & CW_Invalid) {
|
|
st_new_ptr = &st(-1);
|
|
push();
|
|
FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (STACK_OVERFLOW) {
|
|
FPU_stack_overflow();
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Valid) {
|
|
if (exponent(st0_ptr) > -40) {
|
|
if ((q = trig_arg(st0_ptr, 0)) == -1) {
|
|
/* Operand is out of range */
|
|
return;
|
|
}
|
|
|
|
poly_tan(st0_ptr);
|
|
setsign(st0_ptr, (q & 1) ^ (arg_sign != 0));
|
|
set_precision_flag_up(); /* We do not really know if up or down */
|
|
} else {
|
|
/* For a small arg, the result == the argument */
|
|
/* Underflow may happen */
|
|
|
|
denormal_arg:
|
|
|
|
FPU_to_exp16(st0_ptr, st0_ptr);
|
|
|
|
st0_tag =
|
|
FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
|
|
FPU_settag0(st0_tag);
|
|
}
|
|
push();
|
|
FPU_copy_to_reg0(&CONST_1, TAG_Valid);
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Zero) {
|
|
push();
|
|
FPU_copy_to_reg0(&CONST_1, TAG_Valid);
|
|
setcc(0);
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
|
|
if (st0_tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
goto denormal_arg;
|
|
}
|
|
|
|
if (st0_tag == TW_Infinity) {
|
|
/* The 80486 treats infinity as an invalid operand */
|
|
if (arith_invalid(0) >= 0) {
|
|
st_new_ptr = &st(-1);
|
|
push();
|
|
arith_invalid(0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
single_arg_2_error(st0_ptr, st0_tag);
|
|
}
|
|
|
|
static void fxtract(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st_new_ptr;
|
|
u_char sign;
|
|
register FPU_REG *st1_ptr = st0_ptr; /* anticipate */
|
|
|
|
if (STACK_OVERFLOW) {
|
|
FPU_stack_overflow();
|
|
return;
|
|
}
|
|
|
|
clear_C1();
|
|
|
|
if (st0_tag == TAG_Valid) {
|
|
long e;
|
|
|
|
push();
|
|
sign = getsign(st1_ptr);
|
|
reg_copy(st1_ptr, st_new_ptr);
|
|
setexponent16(st_new_ptr, exponent(st_new_ptr));
|
|
|
|
denormal_arg:
|
|
|
|
e = exponent16(st_new_ptr);
|
|
convert_l2reg(&e, 1);
|
|
setexponentpos(st_new_ptr, 0);
|
|
setsign(st_new_ptr, sign);
|
|
FPU_settag0(TAG_Valid); /* Needed if arg was a denormal */
|
|
return;
|
|
} else if (st0_tag == TAG_Zero) {
|
|
sign = getsign(st0_ptr);
|
|
|
|
if (FPU_divide_by_zero(0, SIGN_NEG) < 0)
|
|
return;
|
|
|
|
push();
|
|
FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
|
|
setsign(st_new_ptr, sign);
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
|
|
if (st0_tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
push();
|
|
sign = getsign(st1_ptr);
|
|
FPU_to_exp16(st1_ptr, st_new_ptr);
|
|
goto denormal_arg;
|
|
} else if (st0_tag == TW_Infinity) {
|
|
sign = getsign(st0_ptr);
|
|
setpositive(st0_ptr);
|
|
push();
|
|
FPU_copy_to_reg0(&CONST_INF, TAG_Special);
|
|
setsign(st_new_ptr, sign);
|
|
return;
|
|
} else if (st0_tag == TW_NaN) {
|
|
if (real_1op_NaN(st0_ptr) < 0)
|
|
return;
|
|
|
|
push();
|
|
FPU_copy_to_reg0(st0_ptr, TAG_Special);
|
|
return;
|
|
} else if (st0_tag == TAG_Empty) {
|
|
/* Is this the correct behaviour? */
|
|
if (control_word & EX_Invalid) {
|
|
FPU_stack_underflow();
|
|
push();
|
|
FPU_stack_underflow();
|
|
} else
|
|
EXCEPTION(EX_StackUnder);
|
|
}
|
|
#ifdef PARANOID
|
|
else
|
|
EXCEPTION(EX_INTERNAL | 0x119);
|
|
#endif /* PARANOID */
|
|
}
|
|
|
|
static void fdecstp(void)
|
|
{
|
|
clear_C1();
|
|
top--;
|
|
}
|
|
|
|
static void fincstp(void)
|
|
{
|
|
clear_C1();
|
|
top++;
|
|
}
|
|
|
|
static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
int expon;
|
|
|
|
clear_C1();
|
|
|
|
if (st0_tag == TAG_Valid) {
|
|
u_char tag;
|
|
|
|
if (signnegative(st0_ptr)) {
|
|
arith_invalid(0); /* sqrt(negative) is invalid */
|
|
return;
|
|
}
|
|
|
|
/* make st(0) in [1.0 .. 4.0) */
|
|
expon = exponent(st0_ptr);
|
|
|
|
denormal_arg:
|
|
|
|
setexponent16(st0_ptr, (expon & 1));
|
|
|
|
/* Do the computation, the sign of the result will be positive. */
|
|
tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS);
|
|
addexponent(st0_ptr, expon >> 1);
|
|
FPU_settag0(tag);
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Zero)
|
|
return;
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
|
|
if (st0_tag == TW_Infinity) {
|
|
if (signnegative(st0_ptr))
|
|
arith_invalid(0); /* sqrt(-Infinity) is invalid */
|
|
return;
|
|
} else if (st0_tag == TW_Denormal) {
|
|
if (signnegative(st0_ptr)) {
|
|
arith_invalid(0); /* sqrt(negative) is invalid */
|
|
return;
|
|
}
|
|
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
FPU_to_exp16(st0_ptr, st0_ptr);
|
|
|
|
expon = exponent16(st0_ptr);
|
|
|
|
goto denormal_arg;
|
|
}
|
|
|
|
single_arg_error(st0_ptr, st0_tag);
|
|
|
|
}
|
|
|
|
static void frndint_(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
int flags, tag;
|
|
|
|
if (st0_tag == TAG_Valid) {
|
|
u_char sign;
|
|
|
|
denormal_arg:
|
|
|
|
sign = getsign(st0_ptr);
|
|
|
|
if (exponent(st0_ptr) > 63)
|
|
return;
|
|
|
|
if (st0_tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
}
|
|
|
|
/* Fortunately, this can't overflow to 2^64 */
|
|
if ((flags = FPU_round_to_int(st0_ptr, st0_tag)))
|
|
set_precision_flag(flags);
|
|
|
|
setexponent16(st0_ptr, 63);
|
|
tag = FPU_normalize(st0_ptr);
|
|
setsign(st0_ptr, sign);
|
|
FPU_settag0(tag);
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Zero)
|
|
return;
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
|
|
if (st0_tag == TW_Denormal)
|
|
goto denormal_arg;
|
|
else if (st0_tag == TW_Infinity)
|
|
return;
|
|
else
|
|
single_arg_error(st0_ptr, st0_tag);
|
|
}
|
|
|
|
static int fsin(FPU_REG *st0_ptr, u_char tag)
|
|
{
|
|
u_char arg_sign = getsign(st0_ptr);
|
|
|
|
if (tag == TAG_Valid) {
|
|
int q;
|
|
|
|
if (exponent(st0_ptr) > -40) {
|
|
if ((q = trig_arg(st0_ptr, 0)) == -1) {
|
|
/* Operand is out of range */
|
|
return 1;
|
|
}
|
|
|
|
poly_sine(st0_ptr);
|
|
|
|
if (q & 2)
|
|
changesign(st0_ptr);
|
|
|
|
setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign);
|
|
|
|
/* We do not really know if up or down */
|
|
set_precision_flag_up();
|
|
return 0;
|
|
} else {
|
|
/* For a small arg, the result == the argument */
|
|
set_precision_flag_up(); /* Must be up. */
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
if (tag == TAG_Zero) {
|
|
setcc(0);
|
|
return 0;
|
|
}
|
|
|
|
if (tag == TAG_Special)
|
|
tag = FPU_Special(st0_ptr);
|
|
|
|
if (tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return 1;
|
|
|
|
/* For a small arg, the result == the argument */
|
|
/* Underflow may happen */
|
|
FPU_to_exp16(st0_ptr, st0_ptr);
|
|
|
|
tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
|
|
|
|
FPU_settag0(tag);
|
|
|
|
return 0;
|
|
} else if (tag == TW_Infinity) {
|
|
/* The 80486 treats infinity as an invalid operand */
|
|
arith_invalid(0);
|
|
return 1;
|
|
} else {
|
|
single_arg_error(st0_ptr, tag);
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static int f_cos(FPU_REG *st0_ptr, u_char tag)
|
|
{
|
|
u_char st0_sign;
|
|
|
|
st0_sign = getsign(st0_ptr);
|
|
|
|
if (tag == TAG_Valid) {
|
|
int q;
|
|
|
|
if (exponent(st0_ptr) > -40) {
|
|
if ((exponent(st0_ptr) < 0)
|
|
|| ((exponent(st0_ptr) == 0)
|
|
&& (significand(st0_ptr) <=
|
|
0xc90fdaa22168c234LL))) {
|
|
poly_cos(st0_ptr);
|
|
|
|
/* We do not really know if up or down */
|
|
set_precision_flag_down();
|
|
|
|
return 0;
|
|
} else if ((q = trig_arg(st0_ptr, FCOS)) != -1) {
|
|
poly_sine(st0_ptr);
|
|
|
|
if ((q + 1) & 2)
|
|
changesign(st0_ptr);
|
|
|
|
/* We do not really know if up or down */
|
|
set_precision_flag_down();
|
|
|
|
return 0;
|
|
} else {
|
|
/* Operand is out of range */
|
|
return 1;
|
|
}
|
|
} else {
|
|
denormal_arg:
|
|
|
|
setcc(0);
|
|
FPU_copy_to_reg0(&CONST_1, TAG_Valid);
|
|
#ifdef PECULIAR_486
|
|
set_precision_flag_down(); /* 80486 appears to do this. */
|
|
#else
|
|
set_precision_flag_up(); /* Must be up. */
|
|
#endif /* PECULIAR_486 */
|
|
return 0;
|
|
}
|
|
} else if (tag == TAG_Zero) {
|
|
FPU_copy_to_reg0(&CONST_1, TAG_Valid);
|
|
setcc(0);
|
|
return 0;
|
|
}
|
|
|
|
if (tag == TAG_Special)
|
|
tag = FPU_Special(st0_ptr);
|
|
|
|
if (tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return 1;
|
|
|
|
goto denormal_arg;
|
|
} else if (tag == TW_Infinity) {
|
|
/* The 80486 treats infinity as an invalid operand */
|
|
arith_invalid(0);
|
|
return 1;
|
|
} else {
|
|
single_arg_error(st0_ptr, tag); /* requires st0_ptr == &st(0) */
|
|
return 1;
|
|
}
|
|
}
|
|
|
|
static void fcos(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
f_cos(st0_ptr, st0_tag);
|
|
}
|
|
|
|
static void fsincos(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st_new_ptr;
|
|
FPU_REG arg;
|
|
u_char tag;
|
|
|
|
/* Stack underflow has higher priority */
|
|
if (st0_tag == TAG_Empty) {
|
|
FPU_stack_underflow(); /* Puts a QNaN in st(0) */
|
|
if (control_word & CW_Invalid) {
|
|
st_new_ptr = &st(-1);
|
|
push();
|
|
FPU_stack_underflow(); /* Puts a QNaN in the new st(0) */
|
|
}
|
|
return;
|
|
}
|
|
|
|
if (STACK_OVERFLOW) {
|
|
FPU_stack_overflow();
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
tag = FPU_Special(st0_ptr);
|
|
else
|
|
tag = st0_tag;
|
|
|
|
if (tag == TW_NaN) {
|
|
single_arg_2_error(st0_ptr, TW_NaN);
|
|
return;
|
|
} else if (tag == TW_Infinity) {
|
|
/* The 80486 treats infinity as an invalid operand */
|
|
if (arith_invalid(0) >= 0) {
|
|
/* Masked response */
|
|
push();
|
|
arith_invalid(0);
|
|
}
|
|
return;
|
|
}
|
|
|
|
reg_copy(st0_ptr, &arg);
|
|
if (!fsin(st0_ptr, st0_tag)) {
|
|
push();
|
|
FPU_copy_to_reg0(&arg, st0_tag);
|
|
f_cos(&st(0), st0_tag);
|
|
} else {
|
|
/* An error, so restore st(0) */
|
|
FPU_copy_to_reg0(&arg, st0_tag);
|
|
}
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
/* The following all require two arguments: st(0) and st(1) */
|
|
|
|
/* A lean, mean kernel for the fprem instructions. This relies upon
|
|
the division and rounding to an integer in do_fprem giving an
|
|
exact result. Because of this, rem_kernel() needs to deal only with
|
|
the least significant 64 bits, the more significant bits of the
|
|
result must be zero.
|
|
*/
|
|
static void rem_kernel(unsigned long long st0, unsigned long long *y,
|
|
unsigned long long st1, unsigned long long q, int n)
|
|
{
|
|
int dummy;
|
|
unsigned long long x;
|
|
|
|
x = st0 << n;
|
|
|
|
/* Do the required multiplication and subtraction in the one operation */
|
|
|
|
/* lsw x -= lsw st1 * lsw q */
|
|
asm volatile ("mull %4; subl %%eax,%0; sbbl %%edx,%1":"=m"
|
|
(((unsigned *)&x)[0]), "=m"(((unsigned *)&x)[1]),
|
|
"=a"(dummy)
|
|
:"2"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[0])
|
|
:"%dx");
|
|
/* msw x -= msw st1 * lsw q */
|
|
asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
|
|
"=a"(dummy)
|
|
:"1"(((unsigned *)&st1)[1]), "m"(((unsigned *)&q)[0])
|
|
:"%dx");
|
|
/* msw x -= lsw st1 * msw q */
|
|
asm volatile ("mull %3; subl %%eax,%0":"=m" (((unsigned *)&x)[1]),
|
|
"=a"(dummy)
|
|
:"1"(((unsigned *)&st1)[0]), "m"(((unsigned *)&q)[1])
|
|
:"%dx");
|
|
|
|
*y = x;
|
|
}
|
|
|
|
/* Remainder of st(0) / st(1) */
|
|
/* This routine produces exact results, i.e. there is never any
|
|
rounding or truncation, etc of the result. */
|
|
static void do_fprem(FPU_REG *st0_ptr, u_char st0_tag, int round)
|
|
{
|
|
FPU_REG *st1_ptr = &st(1);
|
|
u_char st1_tag = FPU_gettagi(1);
|
|
|
|
if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
|
|
FPU_REG tmp, st0, st1;
|
|
u_char st0_sign, st1_sign;
|
|
u_char tmptag;
|
|
int tag;
|
|
int old_cw;
|
|
int expdif;
|
|
long long q;
|
|
unsigned short saved_status;
|
|
int cc;
|
|
|
|
fprem_valid:
|
|
/* Convert registers for internal use. */
|
|
st0_sign = FPU_to_exp16(st0_ptr, &st0);
|
|
st1_sign = FPU_to_exp16(st1_ptr, &st1);
|
|
expdif = exponent16(&st0) - exponent16(&st1);
|
|
|
|
old_cw = control_word;
|
|
cc = 0;
|
|
|
|
/* We want the status following the denorm tests, but don't want
|
|
the status changed by the arithmetic operations. */
|
|
saved_status = partial_status;
|
|
control_word &= ~CW_RC;
|
|
control_word |= RC_CHOP;
|
|
|
|
if (expdif < 64) {
|
|
/* This should be the most common case */
|
|
|
|
if (expdif > -2) {
|
|
u_char sign = st0_sign ^ st1_sign;
|
|
tag = FPU_u_div(&st0, &st1, &tmp,
|
|
PR_64_BITS | RC_CHOP | 0x3f,
|
|
sign);
|
|
setsign(&tmp, sign);
|
|
|
|
if (exponent(&tmp) >= 0) {
|
|
FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
|
|
overflow to 2^64 */
|
|
q = significand(&tmp);
|
|
|
|
rem_kernel(significand(&st0),
|
|
&significand(&tmp),
|
|
significand(&st1),
|
|
q, expdif);
|
|
|
|
setexponent16(&tmp, exponent16(&st1));
|
|
} else {
|
|
reg_copy(&st0, &tmp);
|
|
q = 0;
|
|
}
|
|
|
|
if ((round == RC_RND)
|
|
&& (tmp.sigh & 0xc0000000)) {
|
|
/* We may need to subtract st(1) once more,
|
|
to get a result <= 1/2 of st(1). */
|
|
unsigned long long x;
|
|
expdif =
|
|
exponent16(&st1) - exponent16(&tmp);
|
|
if (expdif <= 1) {
|
|
if (expdif == 0)
|
|
x = significand(&st1) -
|
|
significand(&tmp);
|
|
else /* expdif is 1 */
|
|
x = (significand(&st1)
|
|
<< 1) -
|
|
significand(&tmp);
|
|
if ((x < significand(&tmp)) ||
|
|
/* or equi-distant (from 0 & st(1)) and q is odd */
|
|
((x == significand(&tmp))
|
|
&& (q & 1))) {
|
|
st0_sign = !st0_sign;
|
|
significand(&tmp) = x;
|
|
q++;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (q & 4)
|
|
cc |= SW_C0;
|
|
if (q & 2)
|
|
cc |= SW_C3;
|
|
if (q & 1)
|
|
cc |= SW_C1;
|
|
} else {
|
|
control_word = old_cw;
|
|
setcc(0);
|
|
return;
|
|
}
|
|
} else {
|
|
/* There is a large exponent difference ( >= 64 ) */
|
|
/* To make much sense, the code in this section should
|
|
be done at high precision. */
|
|
int exp_1, N;
|
|
u_char sign;
|
|
|
|
/* prevent overflow here */
|
|
/* N is 'a number between 32 and 63' (p26-113) */
|
|
reg_copy(&st0, &tmp);
|
|
tmptag = st0_tag;
|
|
N = (expdif & 0x0000001f) + 32; /* This choice gives results
|
|
identical to an AMD 486 */
|
|
setexponent16(&tmp, N);
|
|
exp_1 = exponent16(&st1);
|
|
setexponent16(&st1, 0);
|
|
expdif -= N;
|
|
|
|
sign = getsign(&tmp) ^ st1_sign;
|
|
tag =
|
|
FPU_u_div(&tmp, &st1, &tmp,
|
|
PR_64_BITS | RC_CHOP | 0x3f, sign);
|
|
setsign(&tmp, sign);
|
|
|
|
FPU_round_to_int(&tmp, tag); /* Fortunately, this can't
|
|
overflow to 2^64 */
|
|
|
|
rem_kernel(significand(&st0),
|
|
&significand(&tmp),
|
|
significand(&st1),
|
|
significand(&tmp), exponent(&tmp)
|
|
);
|
|
setexponent16(&tmp, exp_1 + expdif);
|
|
|
|
/* It is possible for the operation to be complete here.
|
|
What does the IEEE standard say? The Intel 80486 manual
|
|
implies that the operation will never be completed at this
|
|
point, and the behaviour of a real 80486 confirms this.
|
|
*/
|
|
if (!(tmp.sigh | tmp.sigl)) {
|
|
/* The result is zero */
|
|
control_word = old_cw;
|
|
partial_status = saved_status;
|
|
FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
|
|
setsign(&st0, st0_sign);
|
|
#ifdef PECULIAR_486
|
|
setcc(SW_C2);
|
|
#else
|
|
setcc(0);
|
|
#endif /* PECULIAR_486 */
|
|
return;
|
|
}
|
|
cc = SW_C2;
|
|
}
|
|
|
|
control_word = old_cw;
|
|
partial_status = saved_status;
|
|
tag = FPU_normalize_nuo(&tmp);
|
|
reg_copy(&tmp, st0_ptr);
|
|
|
|
/* The only condition to be looked for is underflow,
|
|
and it can occur here only if underflow is unmasked. */
|
|
if ((exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero)
|
|
&& !(control_word & CW_Underflow)) {
|
|
setcc(cc);
|
|
tag = arith_underflow(st0_ptr);
|
|
setsign(st0_ptr, st0_sign);
|
|
FPU_settag0(tag);
|
|
return;
|
|
} else if ((exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero)) {
|
|
stdexp(st0_ptr);
|
|
setsign(st0_ptr, st0_sign);
|
|
} else {
|
|
tag =
|
|
FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign);
|
|
}
|
|
FPU_settag0(tag);
|
|
setcc(cc);
|
|
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
if (st1_tag == TAG_Special)
|
|
st1_tag = FPU_Special(st1_ptr);
|
|
|
|
if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
goto fprem_valid;
|
|
} else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
|
|
FPU_stack_underflow();
|
|
return;
|
|
} else if (st0_tag == TAG_Zero) {
|
|
if (st1_tag == TAG_Valid) {
|
|
setcc(0);
|
|
return;
|
|
} else if (st1_tag == TW_Denormal) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
setcc(0);
|
|
return;
|
|
} else if (st1_tag == TAG_Zero) {
|
|
arith_invalid(0);
|
|
return;
|
|
} /* fprem(?,0) always invalid */
|
|
else if (st1_tag == TW_Infinity) {
|
|
setcc(0);
|
|
return;
|
|
}
|
|
} else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
|
|
if (st1_tag == TAG_Zero) {
|
|
arith_invalid(0); /* fprem(Valid,Zero) is invalid */
|
|
return;
|
|
} else if (st1_tag != TW_NaN) {
|
|
if (((st0_tag == TW_Denormal)
|
|
|| (st1_tag == TW_Denormal))
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
if (st1_tag == TW_Infinity) {
|
|
/* fprem(Valid,Infinity) is o.k. */
|
|
setcc(0);
|
|
return;
|
|
}
|
|
}
|
|
} else if (st0_tag == TW_Infinity) {
|
|
if (st1_tag != TW_NaN) {
|
|
arith_invalid(0); /* fprem(Infinity,?) is invalid */
|
|
return;
|
|
}
|
|
}
|
|
|
|
/* One of the registers must contain a NaN if we got here. */
|
|
|
|
#ifdef PARANOID
|
|
if ((st0_tag != TW_NaN) && (st1_tag != TW_NaN))
|
|
EXCEPTION(EX_INTERNAL | 0x118);
|
|
#endif /* PARANOID */
|
|
|
|
real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr);
|
|
|
|
}
|
|
|
|
/* ST(1) <- ST(1) * log ST; pop ST */
|
|
static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st1_ptr = &st(1), exponent;
|
|
u_char st1_tag = FPU_gettagi(1);
|
|
u_char sign;
|
|
int e, tag;
|
|
|
|
clear_C1();
|
|
|
|
if ((st0_tag == TAG_Valid) && (st1_tag == TAG_Valid)) {
|
|
both_valid:
|
|
/* Both regs are Valid or Denormal */
|
|
if (signpositive(st0_ptr)) {
|
|
if (st0_tag == TW_Denormal)
|
|
FPU_to_exp16(st0_ptr, st0_ptr);
|
|
else
|
|
/* Convert st(0) for internal use. */
|
|
setexponent16(st0_ptr, exponent(st0_ptr));
|
|
|
|
if ((st0_ptr->sigh == 0x80000000)
|
|
&& (st0_ptr->sigl == 0)) {
|
|
/* Special case. The result can be precise. */
|
|
u_char esign;
|
|
e = exponent16(st0_ptr);
|
|
if (e >= 0) {
|
|
exponent.sigh = e;
|
|
esign = SIGN_POS;
|
|
} else {
|
|
exponent.sigh = -e;
|
|
esign = SIGN_NEG;
|
|
}
|
|
exponent.sigl = 0;
|
|
setexponent16(&exponent, 31);
|
|
tag = FPU_normalize_nuo(&exponent);
|
|
stdexp(&exponent);
|
|
setsign(&exponent, esign);
|
|
tag =
|
|
FPU_mul(&exponent, tag, 1, FULL_PRECISION);
|
|
if (tag >= 0)
|
|
FPU_settagi(1, tag);
|
|
} else {
|
|
/* The usual case */
|
|
sign = getsign(st1_ptr);
|
|
if (st1_tag == TW_Denormal)
|
|
FPU_to_exp16(st1_ptr, st1_ptr);
|
|
else
|
|
/* Convert st(1) for internal use. */
|
|
setexponent16(st1_ptr,
|
|
exponent(st1_ptr));
|
|
poly_l2(st0_ptr, st1_ptr, sign);
|
|
}
|
|
} else {
|
|
/* negative */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
}
|
|
|
|
FPU_pop();
|
|
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
if (st1_tag == TAG_Special)
|
|
st1_tag = FPU_Special(st1_ptr);
|
|
|
|
if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
|
|
FPU_stack_underflow_pop(1);
|
|
return;
|
|
} else if ((st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal)) {
|
|
if (st0_tag == TAG_Zero) {
|
|
if (st1_tag == TAG_Zero) {
|
|
/* Both args zero is invalid */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
} else {
|
|
u_char sign;
|
|
sign = getsign(st1_ptr) ^ SIGN_NEG;
|
|
if (FPU_divide_by_zero(1, sign) < 0)
|
|
return;
|
|
|
|
setsign(st1_ptr, sign);
|
|
}
|
|
} else if (st1_tag == TAG_Zero) {
|
|
/* st(1) contains zero, st(0) valid <> 0 */
|
|
/* Zero is the valid answer */
|
|
sign = getsign(st1_ptr);
|
|
|
|
if (signnegative(st0_ptr)) {
|
|
/* log(negative) */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
} else if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
else {
|
|
if (exponent(st0_ptr) < 0)
|
|
sign ^= SIGN_NEG;
|
|
|
|
FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
|
|
setsign(st1_ptr, sign);
|
|
}
|
|
} else {
|
|
/* One or both operands are denormals. */
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
goto both_valid;
|
|
}
|
|
} else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
|
|
return;
|
|
}
|
|
/* One or both arg must be an infinity */
|
|
else if (st0_tag == TW_Infinity) {
|
|
if ((signnegative(st0_ptr)) || (st1_tag == TAG_Zero)) {
|
|
/* log(-infinity) or 0*log(infinity) */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
} else {
|
|
u_char sign = getsign(st1_ptr);
|
|
|
|
if ((st1_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
FPU_copy_to_reg1(&CONST_INF, TAG_Special);
|
|
setsign(st1_ptr, sign);
|
|
}
|
|
}
|
|
/* st(1) must be infinity here */
|
|
else if (((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
|
|
&& (signpositive(st0_ptr))) {
|
|
if (exponent(st0_ptr) >= 0) {
|
|
if ((exponent(st0_ptr) == 0) &&
|
|
(st0_ptr->sigh == 0x80000000) &&
|
|
(st0_ptr->sigl == 0)) {
|
|
/* st(0) holds 1.0 */
|
|
/* infinity*log(1) */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
}
|
|
/* else st(0) is positive and > 1.0 */
|
|
} else {
|
|
/* st(0) is positive and < 1.0 */
|
|
|
|
if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
changesign(st1_ptr);
|
|
}
|
|
} else {
|
|
/* st(0) must be zero or negative */
|
|
if (st0_tag == TAG_Zero) {
|
|
/* This should be invalid, but a real 80486 is happy with it. */
|
|
|
|
#ifndef PECULIAR_486
|
|
sign = getsign(st1_ptr);
|
|
if (FPU_divide_by_zero(1, sign) < 0)
|
|
return;
|
|
#endif /* PECULIAR_486 */
|
|
|
|
changesign(st1_ptr);
|
|
} else if (arith_invalid(1) < 0) /* log(negative) */
|
|
return;
|
|
}
|
|
|
|
FPU_pop();
|
|
}
|
|
|
|
static void fpatan(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st1_ptr = &st(1);
|
|
u_char st1_tag = FPU_gettagi(1);
|
|
int tag;
|
|
|
|
clear_C1();
|
|
if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
|
|
valid_atan:
|
|
|
|
poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag);
|
|
|
|
FPU_pop();
|
|
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
if (st1_tag == TAG_Special)
|
|
st1_tag = FPU_Special(st1_ptr);
|
|
|
|
if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
goto valid_atan;
|
|
} else if ((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) {
|
|
FPU_stack_underflow_pop(1);
|
|
return;
|
|
} else if ((st0_tag == TW_NaN) || (st1_tag == TW_NaN)) {
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0)
|
|
FPU_pop();
|
|
return;
|
|
} else if ((st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) {
|
|
u_char sign = getsign(st1_ptr);
|
|
if (st0_tag == TW_Infinity) {
|
|
if (st1_tag == TW_Infinity) {
|
|
if (signpositive(st0_ptr)) {
|
|
FPU_copy_to_reg1(&CONST_PI4, TAG_Valid);
|
|
} else {
|
|
setpositive(st1_ptr);
|
|
tag =
|
|
FPU_u_add(&CONST_PI4, &CONST_PI2,
|
|
st1_ptr, FULL_PRECISION,
|
|
SIGN_POS,
|
|
exponent(&CONST_PI4),
|
|
exponent(&CONST_PI2));
|
|
if (tag >= 0)
|
|
FPU_settagi(1, tag);
|
|
}
|
|
} else {
|
|
if ((st1_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
if (signpositive(st0_ptr)) {
|
|
FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
|
|
setsign(st1_ptr, sign); /* An 80486 preserves the sign */
|
|
FPU_pop();
|
|
return;
|
|
} else {
|
|
FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
|
|
}
|
|
}
|
|
} else {
|
|
/* st(1) is infinity, st(0) not infinity */
|
|
if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
|
|
}
|
|
setsign(st1_ptr, sign);
|
|
} else if (st1_tag == TAG_Zero) {
|
|
/* st(0) must be valid or zero */
|
|
u_char sign = getsign(st1_ptr);
|
|
|
|
if ((st0_tag == TW_Denormal) && (denormal_operand() < 0))
|
|
return;
|
|
|
|
if (signpositive(st0_ptr)) {
|
|
/* An 80486 preserves the sign */
|
|
FPU_pop();
|
|
return;
|
|
}
|
|
|
|
FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
|
|
setsign(st1_ptr, sign);
|
|
} else if (st0_tag == TAG_Zero) {
|
|
/* st(1) must be TAG_Valid here */
|
|
u_char sign = getsign(st1_ptr);
|
|
|
|
if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
|
|
return;
|
|
|
|
FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
|
|
setsign(st1_ptr, sign);
|
|
}
|
|
#ifdef PARANOID
|
|
else
|
|
EXCEPTION(EX_INTERNAL | 0x125);
|
|
#endif /* PARANOID */
|
|
|
|
FPU_pop();
|
|
set_precision_flag_up(); /* We do not really know if up or down */
|
|
}
|
|
|
|
static void fprem(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
do_fprem(st0_ptr, st0_tag, RC_CHOP);
|
|
}
|
|
|
|
static void fprem1(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
do_fprem(st0_ptr, st0_tag, RC_RND);
|
|
}
|
|
|
|
static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
u_char sign, sign1;
|
|
FPU_REG *st1_ptr = &st(1), a, b;
|
|
u_char st1_tag = FPU_gettagi(1);
|
|
|
|
clear_C1();
|
|
if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
|
|
valid_yl2xp1:
|
|
|
|
sign = getsign(st0_ptr);
|
|
sign1 = getsign(st1_ptr);
|
|
|
|
FPU_to_exp16(st0_ptr, &a);
|
|
FPU_to_exp16(st1_ptr, &b);
|
|
|
|
if (poly_l2p1(sign, sign1, &a, &b, st1_ptr))
|
|
return;
|
|
|
|
FPU_pop();
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
if (st1_tag == TAG_Special)
|
|
st1_tag = FPU_Special(st1_ptr);
|
|
|
|
if (((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
|
|
|| ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal))) {
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
goto valid_yl2xp1;
|
|
} else if ((st0_tag == TAG_Empty) | (st1_tag == TAG_Empty)) {
|
|
FPU_stack_underflow_pop(1);
|
|
return;
|
|
} else if (st0_tag == TAG_Zero) {
|
|
switch (st1_tag) {
|
|
case TW_Denormal:
|
|
if (denormal_operand() < 0)
|
|
return;
|
|
|
|
case TAG_Zero:
|
|
case TAG_Valid:
|
|
setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr));
|
|
FPU_copy_to_reg1(st0_ptr, st0_tag);
|
|
break;
|
|
|
|
case TW_Infinity:
|
|
/* Infinity*log(1) */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
break;
|
|
|
|
case TW_NaN:
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
|
|
return;
|
|
break;
|
|
|
|
default:
|
|
#ifdef PARANOID
|
|
EXCEPTION(EX_INTERNAL | 0x116);
|
|
return;
|
|
#endif /* PARANOID */
|
|
break;
|
|
}
|
|
} else if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
|
|
switch (st1_tag) {
|
|
case TAG_Zero:
|
|
if (signnegative(st0_ptr)) {
|
|
if (exponent(st0_ptr) >= 0) {
|
|
/* st(0) holds <= -1.0 */
|
|
#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
|
|
changesign(st1_ptr);
|
|
#else
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
#endif /* PECULIAR_486 */
|
|
} else if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
else
|
|
changesign(st1_ptr);
|
|
} else if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
break;
|
|
|
|
case TW_Infinity:
|
|
if (signnegative(st0_ptr)) {
|
|
if ((exponent(st0_ptr) >= 0) &&
|
|
!((st0_ptr->sigh == 0x80000000) &&
|
|
(st0_ptr->sigl == 0))) {
|
|
/* st(0) holds < -1.0 */
|
|
#ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */
|
|
changesign(st1_ptr);
|
|
#else
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
#endif /* PECULIAR_486 */
|
|
} else if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
else
|
|
changesign(st1_ptr);
|
|
} else if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
break;
|
|
|
|
case TW_NaN:
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
|
|
return;
|
|
}
|
|
|
|
} else if (st0_tag == TW_NaN) {
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
|
|
return;
|
|
} else if (st0_tag == TW_Infinity) {
|
|
if (st1_tag == TW_NaN) {
|
|
if (real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0)
|
|
return;
|
|
} else if (signnegative(st0_ptr)) {
|
|
#ifndef PECULIAR_486
|
|
/* This should have higher priority than denormals, but... */
|
|
if (arith_invalid(1) < 0) /* log(-infinity) */
|
|
return;
|
|
#endif /* PECULIAR_486 */
|
|
if ((st1_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
#ifdef PECULIAR_486
|
|
/* Denormal operands actually get higher priority */
|
|
if (arith_invalid(1) < 0) /* log(-infinity) */
|
|
return;
|
|
#endif /* PECULIAR_486 */
|
|
} else if (st1_tag == TAG_Zero) {
|
|
/* log(infinity) */
|
|
if (arith_invalid(1) < 0)
|
|
return;
|
|
}
|
|
|
|
/* st(1) must be valid here. */
|
|
|
|
else if ((st1_tag == TW_Denormal) && (denormal_operand() < 0))
|
|
return;
|
|
|
|
/* The Manual says that log(Infinity) is invalid, but a real
|
|
80486 sensibly says that it is o.k. */
|
|
else {
|
|
u_char sign = getsign(st1_ptr);
|
|
FPU_copy_to_reg1(&CONST_INF, TAG_Special);
|
|
setsign(st1_ptr, sign);
|
|
}
|
|
}
|
|
#ifdef PARANOID
|
|
else {
|
|
EXCEPTION(EX_INTERNAL | 0x117);
|
|
return;
|
|
}
|
|
#endif /* PARANOID */
|
|
|
|
FPU_pop();
|
|
return;
|
|
|
|
}
|
|
|
|
static void fscale(FPU_REG *st0_ptr, u_char st0_tag)
|
|
{
|
|
FPU_REG *st1_ptr = &st(1);
|
|
u_char st1_tag = FPU_gettagi(1);
|
|
int old_cw = control_word;
|
|
u_char sign = getsign(st0_ptr);
|
|
|
|
clear_C1();
|
|
if (!((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid))) {
|
|
long scale;
|
|
FPU_REG tmp;
|
|
|
|
/* Convert register for internal use. */
|
|
setexponent16(st0_ptr, exponent(st0_ptr));
|
|
|
|
valid_scale:
|
|
|
|
if (exponent(st1_ptr) > 30) {
|
|
/* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
|
|
|
|
if (signpositive(st1_ptr)) {
|
|
EXCEPTION(EX_Overflow);
|
|
FPU_copy_to_reg0(&CONST_INF, TAG_Special);
|
|
} else {
|
|
EXCEPTION(EX_Underflow);
|
|
FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
|
|
}
|
|
setsign(st0_ptr, sign);
|
|
return;
|
|
}
|
|
|
|
control_word &= ~CW_RC;
|
|
control_word |= RC_CHOP;
|
|
reg_copy(st1_ptr, &tmp);
|
|
FPU_round_to_int(&tmp, st1_tag); /* This can never overflow here */
|
|
control_word = old_cw;
|
|
scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl;
|
|
scale += exponent16(st0_ptr);
|
|
|
|
setexponent16(st0_ptr, scale);
|
|
|
|
/* Use FPU_round() to properly detect under/overflow etc */
|
|
FPU_round(st0_ptr, 0, 0, control_word, sign);
|
|
|
|
return;
|
|
}
|
|
|
|
if (st0_tag == TAG_Special)
|
|
st0_tag = FPU_Special(st0_ptr);
|
|
if (st1_tag == TAG_Special)
|
|
st1_tag = FPU_Special(st1_ptr);
|
|
|
|
if ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal)) {
|
|
switch (st1_tag) {
|
|
case TAG_Valid:
|
|
/* st(0) must be a denormal */
|
|
if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
FPU_to_exp16(st0_ptr, st0_ptr); /* Will not be left on stack */
|
|
goto valid_scale;
|
|
|
|
case TAG_Zero:
|
|
if (st0_tag == TW_Denormal)
|
|
denormal_operand();
|
|
return;
|
|
|
|
case TW_Denormal:
|
|
denormal_operand();
|
|
return;
|
|
|
|
case TW_Infinity:
|
|
if ((st0_tag == TW_Denormal)
|
|
&& (denormal_operand() < 0))
|
|
return;
|
|
|
|
if (signpositive(st1_ptr))
|
|
FPU_copy_to_reg0(&CONST_INF, TAG_Special);
|
|
else
|
|
FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
|
|
setsign(st0_ptr, sign);
|
|
return;
|
|
|
|
case TW_NaN:
|
|
real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
|
|
return;
|
|
}
|
|
} else if (st0_tag == TAG_Zero) {
|
|
switch (st1_tag) {
|
|
case TAG_Valid:
|
|
case TAG_Zero:
|
|
return;
|
|
|
|
case TW_Denormal:
|
|
denormal_operand();
|
|
return;
|
|
|
|
case TW_Infinity:
|
|
if (signpositive(st1_ptr))
|
|
arith_invalid(0); /* Zero scaled by +Infinity */
|
|
return;
|
|
|
|
case TW_NaN:
|
|
real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
|
|
return;
|
|
}
|
|
} else if (st0_tag == TW_Infinity) {
|
|
switch (st1_tag) {
|
|
case TAG_Valid:
|
|
case TAG_Zero:
|
|
return;
|
|
|
|
case TW_Denormal:
|
|
denormal_operand();
|
|
return;
|
|
|
|
case TW_Infinity:
|
|
if (signnegative(st1_ptr))
|
|
arith_invalid(0); /* Infinity scaled by -Infinity */
|
|
return;
|
|
|
|
case TW_NaN:
|
|
real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
|
|
return;
|
|
}
|
|
} else if (st0_tag == TW_NaN) {
|
|
if (st1_tag != TAG_Empty) {
|
|
real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
|
|
return;
|
|
}
|
|
}
|
|
#ifdef PARANOID
|
|
if (!((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty))) {
|
|
EXCEPTION(EX_INTERNAL | 0x115);
|
|
return;
|
|
}
|
|
#endif
|
|
|
|
/* At least one of st(0), st(1) must be empty */
|
|
FPU_stack_underflow();
|
|
|
|
}
|
|
|
|
/*---------------------------------------------------------------------------*/
|
|
|
|
static FUNC_ST0 const trig_table_a[] = {
|
|
f2xm1, fyl2x, fptan, fpatan,
|
|
fxtract, fprem1, (FUNC_ST0) fdecstp, (FUNC_ST0) fincstp
|
|
};
|
|
|
|
void FPU_triga(void)
|
|
{
|
|
(trig_table_a[FPU_rm]) (&st(0), FPU_gettag0());
|
|
}
|
|
|
|
static FUNC_ST0 const trig_table_b[] = {
|
|
fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0) fsin, fcos
|
|
};
|
|
|
|
void FPU_trigb(void)
|
|
{
|
|
(trig_table_b[FPU_rm]) (&st(0), FPU_gettag0());
|
|
}
|