static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag) { int isNaN; switch ( st0_tag ) { case TW_NaN: isNaN = (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000); if ( isNaN && !(st0_ptr->sigh & 0x40000000) ) /* Signaling ? */ { EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) { /* The masked response */ /* Convert to a QNaN */ st0_ptr->sigh |= 0x40000000; push(); FPU_copy_to_reg0(st0_ptr, TAG_Special); } } else if ( isNaN ) { /* A QNaN */ push(); FPU_copy_to_reg0(st0_ptr, TAG_Special); } else { /* pseudoNaN or other unsupported */ EXCEPTION(EX_Invalid); if ( control_word & CW_Invalid ) { /* The masked response */ FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); push(); FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); } } break; /* return with a NaN in st(0) */ #ifdef PARANOID default: EXCEPTION(EX_INTERNAL|0x0112); #endif PARANOID } }
static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag) { int isNaN; switch (st0_tag) { case TW_NaN: isNaN = (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000); if (isNaN && !(st0_ptr->sigh & 0x40000000)) { EXCEPTION(EX_Invalid); if (control_word & CW_Invalid) { st0_ptr->sigh |= 0x40000000; push(); FPU_copy_to_reg0(st0_ptr, TAG_Special); } } else if (isNaN) { push(); FPU_copy_to_reg0(st0_ptr, TAG_Special); } else { EXCEPTION(EX_Invalid); if (control_word & CW_Invalid) { FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); push(); FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); } } break; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x0112); #endif } }
static void f2xm1(FPU_REG *st0_ptr, u_char tag) { FPU_REG a; clear_C1(); if ( tag == TAG_Valid ) { /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */ if ( exponent(st0_ptr) < 0 ) { denormal_arg: FPU_to_exp16(st0_ptr, &a); /* poly_2xm1(x) requires 0 < st(0) < 1. */ poly_2xm1(getsign(st0_ptr), &a, st0_ptr); } set_precision_flag_up(); /* 80486 appears to always do this */ return; } if ( tag == TAG_Zero ) return; if ( tag == TAG_Special ) tag = FPU_Special(st0_ptr); switch ( tag ) { case TW_Denormal: if ( denormal_operand() < 0 ) return; goto denormal_arg; case TW_Infinity: if ( signnegative(st0_ptr) ) { /* -infinity gives -1 (p16-10) */ FPU_copy_to_reg0(&CONST_1, TAG_Valid); setnegative(st0_ptr); } return; default: single_arg_error(st0_ptr, tag); } }
static void f2xm1(FPU_REG *st0_ptr, u_char tag) { FPU_REG a; clear_C1(); if (tag == TAG_Valid) { if (exponent(st0_ptr) < 0) { denormal_arg: FPU_to_exp16(st0_ptr, &a); poly_2xm1(getsign(st0_ptr), &a, st0_ptr); } set_precision_flag_up(); return; } if (tag == TAG_Zero) return; if (tag == TAG_Special) tag = FPU_Special(st0_ptr); switch (tag) { case TW_Denormal: if (denormal_operand() < 0) return; goto denormal_arg; case TW_Infinity: if (signnegative(st0_ptr)) { FPU_copy_to_reg0(&CONST_1, TAG_Valid); setnegative(st0_ptr); } return; default: single_arg_error(st0_ptr, tag); } }
static void fxtract(FPU_REG *st0_ptr, u_char st0_tag) { FPU_REG *st_new_ptr; u_char sign; register FPU_REG *st1_ptr = st0_ptr; 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 }
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); }
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; }
/* 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 FPU_load_store(u_char type, fpu_addr_modes addr_modes, void __user *data_address) { FPU_REG loaded_data; FPU_REG *st0_ptr; u_char st0_tag = TAG_Empty; /* This is just to stop a gcc warning. */ u_char loaded_tag; st0_ptr = NULL; /* Initialized just to stop compiler warnings. */ if ( addr_modes.default_mode & PROTECTED ) { if ( addr_modes.default_mode == SEG32 ) { if ( access_limit < data_sizes_32[type] ) math_abort(FPU_info,SIGSEGV); } else if ( addr_modes.default_mode == PM16 ) { if ( access_limit < data_sizes_16[type] ) math_abort(FPU_info,SIGSEGV); } #ifdef PARANOID else EXCEPTION(EX_INTERNAL|0x140); #endif /* PARANOID */ } switch ( type_table[type] ) { case _NONE_: break; case _REG0_: st0_ptr = &st(0); /* Some of these instructions pop after storing */ st0_tag = FPU_gettag0(); break; case _PUSH_: { if ( FPU_gettagi(-1) != TAG_Empty ) { FPU_stack_overflow(); return 0; } top--; st0_ptr = &st(0); } break; case _null_: FPU_illegal(); return 0; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL|0x141); return 0; #endif /* PARANOID */ } switch ( type ) { case 000: /* fld m32real */ clear_C1(); loaded_tag = FPU_load_single((float __user *)data_address, &loaded_data); if ( (loaded_tag == TAG_Special) && isNaN(&loaded_data) && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 001: /* fild m32int */ clear_C1(); loaded_tag = FPU_load_int32((long __user *)data_address, &loaded_data); FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 002: /* fld m64real */ clear_C1(); loaded_tag = FPU_load_double((double __user *)data_address, &loaded_data); if ( (loaded_tag == TAG_Special) && isNaN(&loaded_data) && (real_1op_NaN(&loaded_data) < 0) ) { top++; break; } FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 003: /* fild m16int */ clear_C1(); loaded_tag = FPU_load_int16((short __user *)data_address, &loaded_data); FPU_copy_to_reg0(&loaded_data, loaded_tag); break; case 010: /* fst m32real */ clear_C1(); FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address); break; case 011: /* fist m32int */ clear_C1(); FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address); break; case 012: /* fst m64real */ clear_C1(); FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address); break; case 013: /* fist m16int */ clear_C1(); FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address); break; case 014: /* fstp m32real */ clear_C1(); if ( FPU_store_single(st0_ptr, st0_tag, (float __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ clear_C1(); if ( FPU_store_int32(st0_ptr, st0_tag, (long __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ clear_C1(); if ( FPU_store_double(st0_ptr, st0_tag, (double __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ clear_C1(); if ( FPU_store_int16(st0_ptr, st0_tag, (short __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 020: /* fldenv m14/28byte */ fldenv(addr_modes, (u_char __user *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 022: /* frstor m94/108byte */ frstor(addr_modes, (u_char __user *)data_address); /* Ensure that the values just loaded are not changed by fix-up operations. */ return 1; case 023: /* fbld m80dec */ clear_C1(); loaded_tag = FPU_load_bcd((u_char __user *)data_address); FPU_settag0(loaded_tag); break; case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_READ, data_address, 2); FPU_get_user(control_word, (unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; if ( partial_status & ~control_word & CW_Exceptions ) partial_status |= (SW_Summary | SW_Backward); else partial_status &= ~(SW_Summary | SW_Backward); #ifdef PECULIAR_486 control_word |= 0x40; /* An 80486 appears to always set this bit */ #endif /* PECULIAR_486 */ return 1; case 025: /* fld m80real */ clear_C1(); loaded_tag = FPU_load_extended((long double __user *)data_address, 0); FPU_settag0(loaded_tag); break; case 027: /* fild m64int */ clear_C1(); loaded_tag = FPU_load_int64((long long __user *)data_address); FPU_settag0(loaded_tag); break; case 030: /* fstenv m14/28byte */ fstenv(addr_modes, (u_char __user *)data_address); return 1; case 032: /* fsave */ fsave(addr_modes, (u_char __user *)data_address); return 1; case 033: /* fbstp m80dec */ clear_C1(); if ( FPU_store_bcd(st0_ptr, st0_tag, (u_char __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 034: /* fstcw m16int */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_put_user(control_word, (unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 035: /* fstp m80real */ clear_C1(); if ( FPU_store_extended(st0_ptr, st0_tag, (long double __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ RE_ENTRANT_CHECK_OFF; FPU_verify_area(VERIFY_WRITE,data_address,2); FPU_put_user(status_word(),(unsigned short __user *) data_address); RE_ENTRANT_CHECK_ON; return 1; case 037: /* fistp m64int */ clear_C1(); if ( FPU_store_int64(st0_ptr, st0_tag, (long long __user *)data_address) ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; } 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 }
void poly_cos(FPU_REG *st0_ptr) { FPU_REG result; long int exponent, exp2, echange; Xsig accumulator, argSqrd, fix_up, argTo4; unsigned long long fixed_arg; #ifdef PARANOID if ((exponent(st0_ptr) > 0) || ((exponent(st0_ptr) == 0) && (significand(st0_ptr) > 0xc90fdaa22168c234LL))) { EXCEPTION(EX_Invalid); FPU_copy_to_reg0(&CONST_QNaN, TAG_Special); return; } #endif exponent = exponent(st0_ptr); accumulator.lsw = accumulator.midw = accumulator.msw = 0; if ((exponent < -1) || ((exponent == -1) && (st0_ptr->sigh <= 0xb00d6f54))) { argSqrd.msw = st0_ptr->sigh; argSqrd.midw = st0_ptr->sigl; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &significand(st0_ptr)); if (exponent < -1) { 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_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, &significand(st0_ptr)); mul64_Xsig(&accumulator, &significand(st0_ptr)); shr_Xsig(&accumulator, -2 * (1 + exponent)); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSqrd); shr_Xsig(&accumulator, 1); negate_Xsig(&accumulator); if (accumulator.lsw & 0x80000000) XSIG_LL(accumulator)++; if (accumulator.msw == 0) { FPU_copy_to_reg0(&CONST_1, TAG_Valid); return; } else { significand(&result) = XSIG_LL(accumulator); setexponentpos(&result, -1); } } else { fixed_arg = significand(st0_ptr); if (exponent == 0) { fixed_arg <<= 1; } fixed_arg = 0x921fb54442d18469LL - fixed_arg; if (fixed_arg == 0xffffffffffffffffLL) fixed_arg = 0; exponent = -1; exp2 = -1; if (!(LL_MSW(fixed_arg) & 0xffff0000)) { fixed_arg <<= 16; exponent -= 16; exp2 -= 16; } XSIG_LL(argSqrd) = fixed_arg; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &fixed_arg); if (exponent < -1) { 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, &fixed_arg); mul64_Xsig(&accumulator, &fixed_arg); mul64_Xsig(&accumulator, &fixed_arg); exponent = 3 * exponent; shr_Xsig(&accumulator, exp2 - exponent); negate_Xsig(&accumulator); XSIG_LL(accumulator) += fixed_arg; XSIG_LL(fix_up) = 0x898cc51701b839a2ll; fix_up.lsw = 0; if (argSqrd.msw & 0xffc00000) { fix_up.msw -= mul_32_32(0x898cc517, argSqrd.msw) / 2; fix_up.msw += mul_32_32(0x898cc517, argTo4.msw) / 24; } exp2 += norm_Xsig(&accumulator); shr_Xsig(&accumulator, 1); exp2++; shr_Xsig(&fix_up, 65 + exp2); add_Xsig_Xsig(&accumulator, &fix_up); echange = round_Xsig(&accumulator); setexponentpos(&result, exp2 + echange); significand(&result) = XSIG_LL(accumulator); } FPU_copy_to_reg0(&result, TAG_Valid); #ifdef PARANOID if ((exponent(&result) >= 0) && (significand(&result) > 0x8000000000000000LL)) { EXCEPTION(EX_INTERNAL | 0x151); } #endif }