staticvoid rem_kernel(unsignedlonglong st0, unsignedlonglong *y, unsignedlonglong st1, unsignedlonglong 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 */ staticint trig_arg(FPU_REG *st0_ptr, int even)
{
FPU_REG tmp;
u_char tmptag; unsignedlonglong q; int old_cw = control_word, saved_status = partial_status; int tag, st0_tag = TAG_Valid;
#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 */
/* Convert a long to register */ staticvoid convert_l2reg(longconst *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;
}
/* 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 */
staticvoid 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;
} elseif (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);
}
staticvoid 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 (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;
} elseif (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;
}
}
/* 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;
} elseif (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 (!f_sin(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.
*/ staticvoid rem_kernel(unsignedlonglong st0, unsignedlonglong *y, unsignedlonglong st1, unsignedlonglong q, int n)
{ int dummy; unsignedlonglong x;
x = st0 << n;
/* Do the required multiplication and subtraction in the one operation */
/* Remainder of st(0) / st(1) */ /* This routine produces exact results, i.e. there is never any
rounding or truncation, etc of the result. */ staticvoid 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; longlong q; unsignedshort saved_status; int cc;
/* 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 ((round == RC_RND)
&& (tmp.sigh & 0xc0000000)) { /* We may need to subtract st(1) once more,
to get a result <= 1/2 of st(1). */ unsignedlonglong 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;
/* 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;
}
/* 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;
} elseif ((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);
/* ST(1) <- ST(1) * log ST; pop ST */ staticvoid 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;
} elseif ((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);
}
} elseif (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;
} elseif ((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;
}
} elseif ((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 */ elseif (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 */ elseif (((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. */
/* 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 */
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;
}
} elseif (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;
}
} elseif (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;
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.