/* Convert a s32 to register */ static void convert_l2reg(s32 const *arg, int deststnr) { int tag; s32 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_nuo(dest, EXTENDED_Ebias); /* No underflow or overflow is possible */ FPU_settagi(deststnr, tag); setsign(dest, sign); return; }
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 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; 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); 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 if ((exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1)) { 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)) { setpositive(st0_ptr); q++; } } #endif } #ifdef BETTER_THAN_486 else { if (((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64)) || (q > 1)) { significand(&tmp) = q; 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_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)))) { st0_tag = FPU_sub(REV | LOADED | TAG_Valid, (int)&CONST_PI2, FULL_PRECISION); q++; } } } #endif FPU_settag0(st0_tag); control_word = old_cw; partial_status = saved_status & ~SW_C2; return (q & 3) | even; }
static int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, FPU_REG const *b, u_char tagb, u_char signb, FPU_REG * dest, int deststnr, int control_w) { if (((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0)) return FPU_Exception; if (taga == TAG_Zero) { if (tagb == TAG_Zero) { u_char different_signs = signa ^ signb; FPU_copy_to_regi(a, TAG_Zero, deststnr); if (different_signs) { setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); } else setsign(dest, signa); return TAG_Zero; } else { reg_copy(b, dest); if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) { addexponent(dest, 1); tagb = TAG_Valid; } else if (tagb > TAG_Empty) tagb = TAG_Special; setsign(dest, signb); FPU_settagi(deststnr, tagb); return tagb; } } else if (tagb == TAG_Zero) { reg_copy(a, dest); if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) { addexponent(dest, 1); taga = TAG_Valid; } else if (taga > TAG_Empty) taga = TAG_Special; setsign(dest, signa); FPU_settagi(deststnr, taga); return taga; } else if (taga == TW_Infinity) { if ((tagb != TW_Infinity) || (signa == signb)) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, signa); return taga; } return arith_invalid(deststnr); } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, signb); return tagb; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x101); #endif return FPU_Exception; }
int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w) { FPU_REG *a = &st(0); FPU_REG *dest = &st(deststnr); u_char signb = getsign(b); u_char taga = FPU_gettag0(); u_char signa = getsign(a); u_char saved_sign = getsign(dest); int diff, tag, expa, expb; if (!(taga | tagb)) { expa = exponent(a); expb = exponent(b); valid_add: if (!(signa ^ signb)) { tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb); } else { diff = expa - expb; if (!diff) { diff = a->sigh - b->sigh; if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } if (diff > 0) { tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); } else if (diff < 0) { tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa); } else { FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); return TAG_Zero; } } if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { FPU_REG x, y; if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); a = &x; b = &y; expa = exponent16(a); expb = exponent16(b); goto valid_add; } if ((taga == TW_NaN) || (tagb == TW_NaN)) { if (deststnr == 0) return real_2op_NaN(b, tagb, deststnr, a); else return real_2op_NaN(a, taga, deststnr, a); } return add_sub_specials(a, taga, signa, b, tagb, signb, dest, deststnr, control_w); }
int FPU_sub(int flags, int rm, int control_w) { FPU_REG const *a, *b; FPU_REG *dest; u_char taga, tagb, signa, signb, saved_sign, sign; int diff, tag = 0, expa, expb, deststnr; a = &st(0); taga = FPU_gettag0(); deststnr = 0; if (flags & LOADED) { b = (FPU_REG *) rm; tagb = flags & 0x0f; } else { b = &st(rm); tagb = FPU_gettagi(rm); if (flags & DEST_RM) deststnr = rm; } signa = getsign(a); signb = getsign(b); if (flags & REV) { signa ^= SIGN_NEG; signb ^= SIGN_NEG; } dest = &st(deststnr); saved_sign = getsign(dest); if (!(taga | tagb)) { expa = exponent(a); expb = exponent(b); valid_subtract: diff = expa - expb; if (!diff) { diff = a->sigh - b->sigh; if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } switch ((((int)signa) * 2 + signb) / SIGN_NEG) { case 0: case 3: if (diff > 0) { tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); } else if (diff == 0) { FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); return TAG_Zero; } else { sign = signa ^ SIGN_NEG; tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa); } break; case 1: tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb); break; case 2: tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb); break; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x111); return -1; #endif } if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { FPU_REG x, y; if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); a = &x; b = &y; expa = exponent16(a); expb = exponent16(b); goto valid_subtract; } if ((taga == TW_NaN) || (tagb == TW_NaN)) { FPU_REG const *d1, *d2; if (flags & REV) { d1 = b; d2 = a; } else { d1 = a; d2 = b; } if (flags & LOADED) return real_2op_NaN(b, tagb, deststnr, d1); if (flags & DEST_RM) return real_2op_NaN(a, taga, deststnr, d2); else return real_2op_NaN(b, tagb, deststnr, d2); } return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG, dest, deststnr, control_w); }
static int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa, FPU_REG const *b, u_char tagb, u_char signb, FPU_REG *dest, int deststnr, int control_w) { if ( ((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0) ) return FPU_Exception; if (taga == TAG_Zero) { if (tagb == TAG_Zero) { /* Both are zero, result will be zero. */ u_char different_signs = signa ^ signb; FPU_copy_to_regi(a, TAG_Zero, deststnr); if ( different_signs ) { /* Signs are different. */ /* Sign of answer depends upon rounding mode. */ setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); } else setsign(dest, signa); /* signa may differ from the sign of a. */ return TAG_Zero; } else { reg_copy(b, dest); if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) ) { /* A pseudoDenormal, convert it. */ addexponent(dest, 1); tagb = TAG_Valid; } else if ( tagb > TAG_Empty ) tagb = TAG_Special; setsign(dest, signb); /* signb may differ from the sign of b. */ FPU_settagi(deststnr, tagb); return tagb; } } else if (tagb == TAG_Zero) { reg_copy(a, dest); if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) ) { /* A pseudoDenormal */ addexponent(dest, 1); taga = TAG_Valid; } else if ( taga > TAG_Empty ) taga = TAG_Special; setsign(dest, signa); /* signa may differ from the sign of a. */ FPU_settagi(deststnr, taga); return taga; } else if (taga == TW_Infinity) { if ( (tagb != TW_Infinity) || (signa == signb) ) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, signa); /* signa may differ from the sign of a. */ return taga; } /* Infinity-Infinity is undefined. */ return arith_invalid(deststnr); } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, signb); /* signb may differ from the sign of b. */ return tagb; } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x101); #endif return FPU_Exception; }
/* Operates on st(0) and st(n), or on st(0) and temporary data. The destination must be one of the source st(x). */ int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, u16 control_w) { FPU_REG *a = &st(0); FPU_REG *dest = &st(deststnr); u_char signb = getsign(b); u_char taga = FPU_gettag0(); u_char signa = getsign(a); u_char saved_sign = getsign(dest); int diff, tag, expa, expb; if ( !(taga | tagb) ) { expa = exponent(a); expb = exponent(b); valid_add: /* Both registers are valid */ if (!(signa ^ signb)) { /* signs are the same */ tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb); } else { /* The signs are different, so do a subtraction */ diff = expa - expb; if (!diff) { diff = a->sigh - b->sigh; /* This works only if the ms bits are identical. */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } if (diff > 0) { tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb); } else if ( diff < 0 ) { tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa); } else { FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); /* sign depends upon rounding mode */ setsign(dest, ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG); return TAG_Zero; } } if ( tag < 0 ) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } if ( taga == TAG_Special ) taga = FPU_Special(a); if ( tagb == TAG_Special ) tagb = FPU_Special(b); if ( ((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal)) ) { FPU_REG x, y; if ( denormal_operand() < 0 ) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); a = &x; b = &y; expa = exponent16(a); expb = exponent16(b); goto valid_add; } if ( (taga == TW_NaN) || (tagb == TW_NaN) ) { if ( deststnr == 0 ) return real_2op_NaN(b, tagb, deststnr, a); else return real_2op_NaN(a, taga, deststnr, a); } return add_sub_specials(a, taga, signa, b, tagb, signb, dest, deststnr, control_w); }
/* 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; }
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); }
int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result) { long int exponent, shift; unsigned long long Xll; Xsig accumulator, Denom, argSignif; u_char tag; exponent = exponent16(arg); #ifdef PARANOID if (exponent >= 0) { /* */ /* */ EXCEPTION(EX_INTERNAL | 0x127); return 1; } #endif /* */ argSignif.lsw = 0; XSIG_LL(argSignif) = Xll = significand(arg); if (exponent == -1) { shift = (argSignif.msw & 0x40000000) ? 3 : 2; /* */ exponent -= 2; XSIG_LL(argSignif) <<= 2; Xll <<= 2; } else if (exponent == -2) { shift = 1; /* */ exponent--; XSIG_LL(argSignif) <<= 1; Xll <<= 1; } else shift = 0; if (exponent < -2) { /* */ if (FPU_shrx(&Xll, -2 - exponent) >= 0x80000000U) Xll++; /* */ } accumulator.lsw = accumulator.midw = accumulator.msw = 0; polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER - 1); mul_Xsig_Xsig(&accumulator, &argSignif); shr_Xsig(&accumulator, 3); mul_Xsig_Xsig(&argSignif, &hiterm); /* */ add_two_Xsig(&accumulator, &argSignif, &exponent); if (shift) { /* */ shr_Xsig(&accumulator, -exponent); accumulator.msw |= 0x80000000; /* */ mul_Xsig_Xsig(&accumulator, shiftterm[shift]); accumulator.msw &= 0x3fffffff; /* */ exponent = 1; } if (sign != SIGN_POS) { /* */ Denom.lsw = accumulator.lsw; XSIG_LL(Denom) = XSIG_LL(accumulator); if (exponent < 0) shr_Xsig(&Denom, -exponent); else if (exponent > 0) { /* */ XSIG_LL(Denom) <<= 1; if (Denom.lsw & 0x80000000) XSIG_LL(Denom) |= 1; (Denom.lsw) <<= 1; } Denom.msw |= 0x80000000; /* */ div_Xsig(&accumulator, &Denom, &accumulator); } /* */ exponent += round_Xsig(&accumulator); result = &st(0); significand(result) = XSIG_LL(accumulator); setexponent16(result, exponent); tag = FPU_round(result, 1, 0, FULL_PRECISION, sign); setsign(result, sign); FPU_settag0(tag); return 0; }
/* Divide one register by another and put the result into a third register. */ int FPU_div(int flags, int rm, int control_w) { FPU_REG x, y; FPU_REG const *a, *b, *st0_ptr, *st_ptr; FPU_REG *dest; u_char taga, tagb, signa, signb, sign, saved_sign; int tag, deststnr; if (flags & DEST_RM) deststnr = rm; else deststnr = 0; if (flags & REV) { b = &st(0); st0_ptr = b; tagb = FPU_gettag0(); if (flags & LOADED) { a = (FPU_REG *) rm; taga = flags & 0x0f; } else { a = &st(rm); st_ptr = a; taga = FPU_gettagi(rm); } } else { a = &st(0); st0_ptr = a; taga = FPU_gettag0(); if (flags & LOADED) { b = (FPU_REG *) rm; tagb = flags & 0x0f; } else { b = &st(rm); st_ptr = b; tagb = FPU_gettagi(rm); } } signa = getsign(a); signb = getsign(b); sign = signa ^ signb; dest = &st(deststnr); saved_sign = getsign(dest); if (!(taga | tagb)) { /* Both regs Valid, this should be the most common case. */ reg_copy(a, &x); reg_copy(b, &y); setpositive(&x); setpositive(&y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (tagb != TAG_Zero) { /* Want to find Zero/Valid */ if (tagb == TW_Denormal) { if (denormal_operand() < 0) return FPU_Exception; } /* The result is zero. */ FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } /* We have an exception condition, either 0/0 or Valid/Zero. */ if (taga == TAG_Zero) { /* 0/0 */ return arith_invalid(deststnr); } /* Valid/Zero */ return FPU_divide_by_zero(deststnr, sign); } /* Must have infinities, NaNs, etc */ else if ((taga == TW_NaN) || (tagb == TW_NaN)) { if (flags & LOADED) return real_2op_NaN((FPU_REG *) rm, flags & 0x0f, 0, st0_ptr); if (flags & DEST_RM) { int tag; tag = FPU_gettag0(); if (tag == TAG_Special) tag = FPU_Special(st0_ptr); return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm)); } else { int tag; tag = FPU_gettagi(rm); if (tag == TAG_Special) tag = FPU_Special(&st(rm)); return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm)); } } else if (taga == TW_Infinity) { if (tagb == TW_Infinity) { /* infinity/infinity */ return arith_invalid(deststnr); } else { /* tagb must be Valid or Zero */ if ((tagb == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; /* Infinity divided by Zero or Valid does not raise and exception, but returns Infinity */ FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return taga; } } else if (tagb == TW_Infinity) { if ((taga == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; /* The result is zero. */ FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif /* PARANOID */ return 0; }
int FPU_mul(FPU_REG const *b, u_char tagb, int deststnr, int control_w) { FPU_REG *a = &st(deststnr); FPU_REG *dest = a; u_char taga = FPU_gettagi(deststnr); u_char saved_sign = getsign(dest); u_char sign = (getsign(a) ^ getsign(b)); int tag; if (!(taga | tagb)) { tag = FPU_u_mul(a, b, dest, control_w, sign, exponent(a) + exponent(b)); if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { FPU_REG x, y; if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_mul(&x, &y, dest, control_w, sign, exponent16(&x) + exponent16(&y)); if (tag < 0) { setsign(dest, saved_sign); return tag; } FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (((tagb == TW_Denormal) || (taga == TW_Denormal)) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } else if ((taga == TW_NaN) || (tagb == TW_NaN)) { return real_2op_NaN(b, tagb, deststnr, &st(0)); } else if (((taga == TW_Infinity) && (tagb == TAG_Zero)) || ((tagb == TW_Infinity) && (taga == TAG_Zero))) { return arith_invalid(deststnr); } else if (((taga == TW_Denormal) || (tagb == TW_Denormal)) && (denormal_operand() < 0)) { return FPU_Exception; } else if (taga == TW_Infinity) { FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return TAG_Special; } else if (tagb == TW_Infinity) { FPU_copy_to_regi(b, TAG_Special, deststnr); setsign(dest, sign); return TAG_Special; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif return 0; }
void poly_sine(FPU_REG *st0_ptr) { int exponent, echange; Xsig accumulator, argSqrd, argTo4; unsigned long fix_up, adj; unsigned long long fixed_arg; FPU_REG result; exponent = exponent(st0_ptr); accumulator.lsw = accumulator.midw = accumulator.msw = 0; if ((exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xe21240aa))) { argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &significand(st0_ptr)); shr_Xsig(&argSqrd, 2 * (-1 - exponent)); argTo4.msw = argSqrd.msw; argTo4.midw = argSqrd.midw; argTo4.lsw = argSqrd.lsw; mul_Xsig_Xsig(&argTo4, &argTo4); polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_l, N_COEFF_N - 1); mul_Xsig_Xsig(&accumulator, &argSqrd); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_l, N_COEFF_P - 1); shr_Xsig(&accumulator, 2); accumulator.msw |= 0x80000000; mul64_Xsig(&accumulator, &significand(st0_ptr)); mul64_Xsig(&accumulator, &significand(st0_ptr)); mul64_Xsig(&accumulator, &significand(st0_ptr)); exponent = 3 * exponent; shr_Xsig(&accumulator, exponent(st0_ptr) - exponent); negate_Xsig(&accumulator); XSIG_LL(accumulator) += significand(st0_ptr); echange = round_Xsig(&accumulator); setexponentpos(&result, exponent(st0_ptr) + echange); } else { fixed_arg = significand(st0_ptr); if (exponent == 0) { fixed_arg <<= 1; } fixed_arg = 0x921fb54442d18469LL - fixed_arg; if (fixed_arg == 0xffffffffffffffffLL) fixed_arg = 0; XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &fixed_arg); XSIG_LL(argTo4) = XSIG_LL(argSqrd); argTo4.lsw = argSqrd.lsw; mul_Xsig_Xsig(&argTo4, &argTo4); polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), neg_terms_h, N_COEFF_NH - 1); mul_Xsig_Xsig(&accumulator, &argSqrd); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argTo4), pos_terms_h, N_COEFF_PH - 1); negate_Xsig(&accumulator); mul64_Xsig(&accumulator, &fixed_arg); mul64_Xsig(&accumulator, &fixed_arg); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSqrd); shr_Xsig(&accumulator, 1); accumulator.lsw |= 1; negate_Xsig(&accumulator); fix_up = 0x898cc517; if (argSqrd.msw & 0xffc00000) { fix_up -= mul_32_32(0x898cc517, argSqrd.msw) / 6; } fix_up = mul_32_32(fix_up, LL_MSW(fixed_arg)); adj = accumulator.lsw; accumulator.lsw -= fix_up; if (accumulator.lsw > adj) XSIG_LL(accumulator)--; echange = round_Xsig(&accumulator); setexponentpos(&result, echange - 1); } significand(&result) = XSIG_LL(accumulator); setsign(&result, getsign(st0_ptr)); FPU_copy_to_reg0(&result, TAG_Valid); #ifdef PARANOID if ((exponent(&result) >= 0) && (significand(&result) > 0x8000000000000000LL)) { EXCEPTION(EX_INTERNAL | 0x150); } #endif }
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); if (st0_tag == TAG_Empty) { FPU_stack_underflow(); if (control_word & CW_Invalid) { st_new_ptr = &st(-1); push(); FPU_stack_underflow(); } 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) { return; } poly_tan(st0_ptr); setsign(st0_ptr, (q & 1) ^ (arg_sign != 0)); set_precision_flag_up(); } else { 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) { if (arith_invalid(0) >= 0) { st_new_ptr = &st(-1); push(); arith_invalid(0); } return; } single_arg_2_error(st0_ptr, st0_tag); }
/*--- poly_2xm1() -----------------------------------------------------------+ | Requires st(0) which is TAG_Valid and < 1. | +---------------------------------------------------------------------------*/ int poly_2xm1(u_char sign, FPU_REG *arg, FPU_REG *result) { s32 exponent, shift; u64 Xll; Xsig accumulator, Denom, argSignif; u_char tag; exponent = exponent16(arg); #ifdef PARANOID if ( exponent >= 0 ) /* Don't want a |number| >= 1.0 */ { /* Number negative, too large, or not Valid. */ EXCEPTION(EX_INTERNAL|0x127); return 1; } #endif /* PARANOID */ argSignif.lsw = 0; XSIG_LL(argSignif) = Xll = significand(arg); if ( exponent == -1 ) { shift = (argSignif.msw & 0x40000000) ? 3 : 2; /* subtract 0.5 or 0.75 */ exponent -= 2; XSIG_LL(argSignif) <<= 2; Xll <<= 2; } else if ( exponent == -2 ) { shift = 1; /* subtract 0.25 */ exponent--; XSIG_LL(argSignif) <<= 1; Xll <<= 1; } else shift = 0; if ( exponent < -2 ) { /* Shift the argument right by the required places. */ if ( FPU_shrx(&Xll, -2-exponent) >= 0x80000000U ) Xll++; /* round up */ } accumulator.lsw = accumulator.midw = accumulator.msw = 0; polynomial_Xsig(&accumulator, &Xll, lterms, HIPOWER-1); mul_Xsig_Xsig(&accumulator, &argSignif); shr_Xsig(&accumulator, 3); mul_Xsig_Xsig(&argSignif, &hiterm); /* The leading term */ add_two_Xsig(&accumulator, &argSignif, &exponent); if ( shift ) { /* The argument is large, use the identity: f(x+a) = f(a) * (f(x) + 1) - 1; */ shr_Xsig(&accumulator, - exponent); accumulator.msw |= 0x80000000; /* add 1.0 */ mul_Xsig_Xsig(&accumulator, shiftterm[shift]); accumulator.msw &= 0x3fffffff; /* subtract 1.0 */ exponent = 1; } if ( sign != SIGN_POS ) { /* The argument is negative, use the identity: f(-x) = -f(x) / (1 + f(x)) */ Denom.lsw = accumulator.lsw; XSIG_LL(Denom) = XSIG_LL(accumulator); if ( exponent < 0 ) shr_Xsig(&Denom, - exponent); else if ( exponent > 0 ) { /* exponent must be 1 here */ XSIG_LL(Denom) <<= 1; if ( Denom.lsw & 0x80000000 ) XSIG_LL(Denom) |= 1; (Denom.lsw) <<= 1; } Denom.msw |= 0x80000000; /* add 1.0 */ div_Xsig(&accumulator, &Denom, &accumulator); } /* Convert to 64 bit signed-compatible */ exponent += round_Xsig(&accumulator); result = &st(0); significand(result) = XSIG_LL(accumulator); setexponent16(result, exponent); tag = FPU_round(result, 1, 0, FULL_PRECISION, sign); setsign(result, sign); FPU_settag0(tag); return 0; }
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; 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); 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) { if (control_word & EX_Invalid) { FPU_stack_underflow(); push(); FPU_stack_underflow(); } else EXCEPTION(EX_StackUnder); } #ifdef PARANOID else EXCEPTION(EX_INTERNAL | 0x119); #endif }
int FPU_div(int flags, int rm, int control_w) { FPU_REG x, y; FPU_REG const *a, *b, *st0_ptr, *st_ptr; FPU_REG *dest; u_char taga, tagb, signa, signb, sign, saved_sign; int tag, deststnr; if (flags & DEST_RM) deststnr = rm; else deststnr = 0; if (flags & REV) { b = &st(0); st0_ptr = b; tagb = FPU_gettag0(); if (flags & LOADED) { a = (FPU_REG *) rm; taga = flags & 0x0f; } else { a = &st(rm); st_ptr = a; taga = FPU_gettagi(rm); } } else { a = &st(0); st0_ptr = a; taga = FPU_gettag0(); if (flags & LOADED) { b = (FPU_REG *) rm; tagb = flags & 0x0f; } else { b = &st(rm); st_ptr = b; tagb = FPU_gettagi(rm); } } signa = getsign(a); signb = getsign(b); sign = signa ^ signb; dest = &st(deststnr); saved_sign = getsign(dest); if (!(taga | tagb)) { reg_copy(a, &x); reg_copy(b, &y); setpositive(&x); setpositive(&y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } if (taga == TAG_Special) taga = FPU_Special(a); if (tagb == TAG_Special) tagb = FPU_Special(b); if (((taga == TAG_Valid) && (tagb == TW_Denormal)) || ((taga == TW_Denormal) && (tagb == TAG_Valid)) || ((taga == TW_Denormal) && (tagb == TW_Denormal))) { if (denormal_operand() < 0) return FPU_Exception; FPU_to_exp16(a, &x); FPU_to_exp16(b, &y); tag = FPU_u_div(&x, &y, dest, control_w, sign); if (tag < 0) return tag; FPU_settagi(deststnr, tag); return tag; } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) { if (tagb != TAG_Zero) { if (tagb == TW_Denormal) { if (denormal_operand() < 0) return FPU_Exception; } FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } if (taga == TAG_Zero) { return arith_invalid(deststnr); } return FPU_divide_by_zero(deststnr, sign); } else if ((taga == TW_NaN) || (tagb == TW_NaN)) { if (flags & LOADED) return real_2op_NaN((FPU_REG *) rm, flags & 0x0f, 0, st0_ptr); if (flags & DEST_RM) { int tag; tag = FPU_gettag0(); if (tag == TAG_Special) tag = FPU_Special(st0_ptr); return real_2op_NaN(st0_ptr, tag, rm, (flags & REV) ? st0_ptr : &st(rm)); } else { int tag; tag = FPU_gettagi(rm); if (tag == TAG_Special) tag = FPU_Special(&st(rm)); return real_2op_NaN(&st(rm), tag, 0, (flags & REV) ? st0_ptr : &st(rm)); } } else if (taga == TW_Infinity) { if (tagb == TW_Infinity) { return arith_invalid(deststnr); } else { if ((tagb == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(a, TAG_Special, deststnr); setsign(dest, sign); return taga; } } else if (tagb == TW_Infinity) { if ((taga == TW_Denormal) && (denormal_operand() < 0)) return FPU_Exception; FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr); setsign(dest, sign); return TAG_Zero; } #ifdef PARANOID else { EXCEPTION(EX_INTERNAL | 0x102); return FPU_Exception; } #endif return 0; }