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; }
/* 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; }
/*--- poly_l2p1() -----------------------------------------------------------+ | Base 2 logarithm by a polynomial approximation. | | log2(x+1) | +---------------------------------------------------------------------------*/ int poly_l2p1(u_char sign0, u_char sign1, FPU_REG *st0_ptr, FPU_REG *st1_ptr, FPU_REG *dest) { u_char tag; s32 exponent; Xsig accumulator, yaccum; if ( exponent16(st0_ptr) < 0 ) { log2_kernel(st0_ptr, sign0, &accumulator, &exponent); yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); exponent += round_Xsig(&accumulator); exponent += exponent16(st1_ptr) + 1; if ( exponent < EXP_WAY_UNDER ) exponent = EXP_WAY_UNDER; significand(dest) = XSIG_LL(accumulator); setexponent16(dest, exponent); tag = FPU_round(dest, 1, 0, FULL_PRECISION, sign0 ^ sign1); FPU_settagi(1, tag); if ( tag == TAG_Valid ) set_precision_flag_up(); /* 80486 appears to always do this */ } else { /* The magnitude of st0_ptr is far too large. */ if ( sign0 != SIGN_POS ) { /* Trying to get the log of a negative number. */ #ifdef PECULIAR_486 /* Stupid 80486 doesn't worry about log(negative). */ changesign(st1_ptr); #else if ( arith_invalid(1) < 0 ) return 1; #endif /* PECULIAR_486 */ } /* 80486 appears to do this */ if ( sign0 == SIGN_NEG ) set_precision_flag_down(); else set_precision_flag_up(); } if ( exponent(dest) <= EXP_UNDER ) EXCEPTION(EX_Underflow); return 0; }
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); }
/*--- poly_atan() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr, u_char st1_tag) { u_char transformed, inverted, sign1, sign2; int exponent; long int dummy_exp; Xsig accumulator, Numer, Denom, accumulatore, argSignif, argSq, argSqSq; u_char tag; sign1 = getsign(st0_ptr); sign2 = getsign(st1_ptr); if ( st0_tag == TAG_Valid ) { exponent = exponent(st0_ptr); } else { /* This gives non-compatible stack contents... */ FPU_to_exp16(st0_ptr, st0_ptr); exponent = exponent16(st0_ptr); } if ( st1_tag == TAG_Valid ) { exponent -= exponent(st1_ptr); } else { /* This gives non-compatible stack contents... */ FPU_to_exp16(st1_ptr, st1_ptr); exponent -= exponent16(st1_ptr); } if ( (exponent < 0) || ((exponent == 0) && ((st0_ptr->sigh < st1_ptr->sigh) || ((st0_ptr->sigh == st1_ptr->sigh) && (st0_ptr->sigl < st1_ptr->sigl))) ) ) { inverted = 1; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st0_ptr); XSIG_LL(Denom) = significand(st1_ptr); } else { inverted = 0; exponent = -exponent; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st1_ptr); XSIG_LL(Denom) = significand(st0_ptr); } div_Xsig(&Numer, &Denom, &argSignif); exponent += norm_Xsig(&argSignif); if ( (exponent >= -1) || ((exponent == -2) && (argSignif.msw > 0xd413ccd0)) ) { /* The argument is greater than sqrt(2)-1 (=0.414213562...) */ /* Convert the argument by an identity for atan */ transformed = 1; if ( exponent >= 0 ) { #ifdef PARANOID if ( !( (exponent == 0) && (argSignif.lsw == 0) && (argSignif.midw == 0) && (argSignif.msw == 0x80000000) ) ) { EXCEPTION(EX_INTERNAL|0x104); /* There must be a logic error */ return; } #endif /* PARANOID */ argSignif.msw = 0; /* Make the transformed arg -> 0.0 */ } else { Numer.lsw = Denom.lsw = argSignif.lsw; XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); if ( exponent < -1 ) shr_Xsig(&Numer, -1-exponent); negate_Xsig(&Numer); shr_Xsig(&Denom, -exponent); Denom.msw |= 0x80000000; div_Xsig(&Numer, &Denom, &argSignif); exponent = -1 + norm_Xsig(&argSignif); } } else { transformed = 0; } argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; argSq.msw = argSignif.msw; mul_Xsig_Xsig(&argSq, &argSq); argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; mul_Xsig_Xsig(&argSqSq, &argSqSq); accumulatore.lsw = argSq.lsw; XSIG_LL(accumulatore) = XSIG_LL(argSq); shr_Xsig(&argSq, 2*(-1-exponent-1)); shr_Xsig(&argSqSq, 4*(-1-exponent-1)); /* Now have argSq etc with binary point at the left .1xxxxxxxx */ /* Do the basic fixed point polynomial evaluation */ accumulator.msw = accumulator.midw = accumulator.lsw = 0; polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddplterms, HIPOWERop-1); mul64_Xsig(&accumulator, &XSIG_LL(argSq)); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon-1); negate_Xsig(&accumulator); add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); mul64_Xsig(&accumulatore, &denomterm); shr_Xsig(&accumulatore, 1 + 2*(-1-exponent)); accumulatore.msw |= 0x80000000; div_Xsig(&accumulator, &accumulatore, &accumulator); mul_Xsig_Xsig(&accumulator, &argSignif); mul_Xsig_Xsig(&accumulator, &argSq); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSignif); if ( transformed ) { /* compute pi/4 - accumulator */ shr_Xsig(&accumulator, -1-exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = -1; } if ( inverted ) { /* compute pi/2 - accumulator */ shr_Xsig(&accumulator, -exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 0; } if ( sign1 ) { /* compute pi - accumulator */ shr_Xsig(&accumulator, 1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 1; } exponent += round_Xsig(&accumulator); significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, exponent); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2); FPU_settagi(1, tag); set_precision_flag_up(); /* We do not really know if up or down, use this as the default. */ }
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; }
void poly_atan(FPU_REG *st0_ptr, u_char st0_tag, FPU_REG *st1_ptr, u_char st1_tag) { u_char transformed, inverted, sign1, sign2; int exponent; long int dummy_exp; Xsig accumulator, Numer, Denom, accumulatore, argSignif, argSq, argSqSq; u_char tag; sign1 = getsign(st0_ptr); sign2 = getsign(st1_ptr); if (st0_tag == TAG_Valid) { exponent = exponent(st0_ptr); } else { FPU_to_exp16(st0_ptr, st0_ptr); exponent = exponent16(st0_ptr); } if (st1_tag == TAG_Valid) { exponent -= exponent(st1_ptr); } else { FPU_to_exp16(st1_ptr, st1_ptr); exponent -= exponent16(st1_ptr); } if ((exponent < 0) || ((exponent == 0) && ((st0_ptr->sigh < st1_ptr->sigh) || ((st0_ptr->sigh == st1_ptr->sigh) && (st0_ptr->sigl < st1_ptr->sigl))))) { inverted = 1; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st0_ptr); XSIG_LL(Denom) = significand(st1_ptr); } else { inverted = 0; exponent = -exponent; Numer.lsw = Denom.lsw = 0; XSIG_LL(Numer) = significand(st1_ptr); XSIG_LL(Denom) = significand(st0_ptr); } div_Xsig(&Numer, &Denom, &argSignif); exponent += norm_Xsig(&argSignif); if ((exponent >= -1) || ((exponent == -2) && (argSignif.msw > 0xd413ccd0))) { transformed = 1; if (exponent >= 0) { #ifdef PARANOID if (!((exponent == 0) && (argSignif.lsw == 0) && (argSignif.midw == 0) && (argSignif.msw == 0x80000000))) { EXCEPTION(EX_INTERNAL | 0x104); return; } #endif argSignif.msw = 0; } else { Numer.lsw = Denom.lsw = argSignif.lsw; XSIG_LL(Numer) = XSIG_LL(Denom) = XSIG_LL(argSignif); if (exponent < -1) shr_Xsig(&Numer, -1 - exponent); negate_Xsig(&Numer); shr_Xsig(&Denom, -exponent); Denom.msw |= 0x80000000; div_Xsig(&Numer, &Denom, &argSignif); exponent = -1 + norm_Xsig(&argSignif); } } else { transformed = 0; } argSq.lsw = argSignif.lsw; argSq.midw = argSignif.midw; argSq.msw = argSignif.msw; mul_Xsig_Xsig(&argSq, &argSq); argSqSq.lsw = argSq.lsw; argSqSq.midw = argSq.midw; argSqSq.msw = argSq.msw; mul_Xsig_Xsig(&argSqSq, &argSqSq); accumulatore.lsw = argSq.lsw; XSIG_LL(accumulatore) = XSIG_LL(argSq); shr_Xsig(&argSq, 2 * (-1 - exponent - 1)); shr_Xsig(&argSqSq, 4 * (-1 - exponent - 1)); accumulator.msw = accumulator.midw = accumulator.lsw = 0; polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddplterms, HIPOWERop - 1); mul64_Xsig(&accumulator, &XSIG_LL(argSq)); negate_Xsig(&accumulator); polynomial_Xsig(&accumulator, &XSIG_LL(argSqSq), oddnegterms, HIPOWERon - 1); negate_Xsig(&accumulator); add_two_Xsig(&accumulator, &fixedpterm, &dummy_exp); mul64_Xsig(&accumulatore, &denomterm); shr_Xsig(&accumulatore, 1 + 2 * (-1 - exponent)); accumulatore.msw |= 0x80000000; div_Xsig(&accumulator, &accumulatore, &accumulator); mul_Xsig_Xsig(&accumulator, &argSignif); mul_Xsig_Xsig(&accumulator, &argSq); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSignif); if (transformed) { shr_Xsig(&accumulator, -1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = -1; } if (inverted) { shr_Xsig(&accumulator, -exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 0; } if (sign1) { shr_Xsig(&accumulator, 1 - exponent); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &pi_signif); exponent = 1; } exponent += round_Xsig(&accumulator); significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, exponent); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign2); FPU_settagi(1, tag); set_precision_flag_up(); }
/* 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 FPU_copy_to_reg1(FPU_REG const *r, u_char tag) { reg_copy(r, &st(1)); FPU_settagi(1, tag); }
void FPU_copy_to_regi(FPU_REG const *r, u_char tag, int stnr) { reg_copy(r, &st(stnr)); FPU_settagi(stnr, tag); }
/*--- poly_l2() -------------------------------------------------------------+ | Base 2 logarithm by a polynomial approximation. | +---------------------------------------------------------------------------*/ void poly_l2(FPU_REG *st0_ptr, FPU_REG *st1_ptr, u_char st1_sign) { long int exponent, expon, expon_expon; Xsig accumulator, expon_accum, yaccum; u_char sign, argsign; FPU_REG x; int tag; exponent = exponent16(st0_ptr); /* From st0_ptr, make a number > sqrt(2)/2 and < sqrt(2) */ if (st0_ptr->sigh > (unsigned)0xb504f334) { /* Treat as sqrt(2)/2 < st0_ptr < 1 */ significand(&x) = -significand(st0_ptr); setexponent16(&x, -1); exponent++; argsign = SIGN_NEG; } else { /* Treat as 1 <= st0_ptr < sqrt(2) */ x.sigh = st0_ptr->sigh - 0x80000000; x.sigl = st0_ptr->sigl; setexponent16(&x, 0); argsign = SIGN_POS; } tag = FPU_normalize_nuo(&x); if (tag == TAG_Zero) { expon = 0; accumulator.msw = accumulator.midw = accumulator.lsw = 0; } else { log2_kernel(&x, argsign, &accumulator, &expon); } if (exponent < 0) { sign = SIGN_NEG; exponent = -exponent; } else sign = SIGN_POS; expon_accum.msw = exponent; expon_accum.midw = expon_accum.lsw = 0; if (exponent) { expon_expon = 31 + norm_Xsig(&expon_accum); shr_Xsig(&accumulator, expon_expon - expon); if (sign ^ argsign) negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &expon_accum); } else { expon_expon = expon; sign = argsign; } yaccum.lsw = 0; XSIG_LL(yaccum) = significand(st1_ptr); mul_Xsig_Xsig(&accumulator, &yaccum); expon_expon += round_Xsig(&accumulator); if (accumulator.msw == 0) { FPU_copy_to_reg1(&CONST_Z, TAG_Zero); return; } significand(st1_ptr) = XSIG_LL(accumulator); setexponent16(st1_ptr, expon_expon + exponent16(st1_ptr) + 1); tag = FPU_round(st1_ptr, 1, 0, FULL_PRECISION, sign ^ st1_sign); FPU_settagi(1, tag); set_precision_flag_up(); /* 80486 appears to always do this */ return; }
/* 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); }
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; }