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 exceptfortheresultsforargumentscloseto2^63,wherethe
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 precisionapproximationtopi/2.Thetechniqueused nowisequivalenttousinganapproximationtopi/2which
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 canbenegative.Thismeansthattheargumentisactually inadifferentquadrant.Thecorrectionisalways<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 precisionapproximationtopi/2.Thetechniqueused nowisequivalenttousinganapproximationtopi/2which
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 subtractioncanbelargerthanpi/2.Thismeans thattheargumentisactuallyinadifferentquadrant. Thecorrectionisalways<pi/2,soitcan'toverflow
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 */ return1;
}
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(); return0;
} else { /* For a small arg, the result == the argument */
set_precision_flag_up(); /* Must be up. */ return0;
}
}
if (tag == TAG_Zero) {
setcc(0); return0;
}
if (tag == TAG_Special)
tag = FPU_Special(st0_ptr);
if (tag == TW_Denormal) { if (denormal_operand() < 0) return1;
/* 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);
return0;
} elseif (tag == TW_Infinity) { /* The 80486 treats infinity as an invalid operand */
arith_invalid(0); return1;
} else {
single_arg_error(st0_ptr, tag); return1;
}
}
/* 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 thedivisionandroundingtoanintegerindo_fpremgivingan exactresult.Becauseofthis,rem_kernel()needstodealonlywith theleastsignificant64bits,themoresignificantbitsofthe resultmustbezero.
*/ 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. WhatdoestheIEEEstandardsay?TheIntel80486manual impliesthattheoperationwillneverbecompletedatthis point,andthebehaviourofareal80486confirmsthis.
*/ 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.