void fxch_i() { /* fxch st(i) */ FPU_REG t; register FPU_REG *sti_ptr = &st(FPU_rm); if (FPU_st0_tag == TW_Empty) { if (sti_ptr->tag == TW_Empty) { stack_underflow(); stack_underflow_i(FPU_rm); return; } reg_move(sti_ptr, FPU_st0_ptr); stack_underflow_i(FPU_rm); return; } if (sti_ptr->tag == TW_Empty) { reg_move(FPU_st0_ptr, sti_ptr); stack_underflow(); return; } reg_move(FPU_st0_ptr, &t); reg_move(sti_ptr, FPU_st0_ptr); reg_move(&t, sti_ptr); }
static int trig_arg(FPU_REG * X) { FPU_REG tmp, quot; int rv; long long q; int old_cw = control_word; control_word &= ~CW_RC; control_word |= RC_CHOP; reg_move(X, "); reg_div(", &CONST_PI2, ", FULL_PRECISION); reg_move(", &tmp); round_to_int(&tmp); if (tmp.sigh & 0x80000000) return -1; /* |Arg| is >= 2^63 */ tmp.exp = EXP_BIAS + 63; q = *(long long *) &(tmp.sigl); normalize(&tmp); reg_sub(", &tmp, X, FULL_PRECISION); rv = q & 7; control_word = old_cw; return rv;; }
static void fptan(void) { FPU_REG *st_new_ptr; int q; char arg_sign = FPU_st0_ptr->sign; if (STACK_OVERFLOW) { stack_overflow(); return; } switch (FPU_st0_tag) { case TW_Valid: #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ FPU_st0_ptr->sign = SIGN_POS; if ((q = trig_arg(FPU_st0_ptr)) != -1) { if (q & 1) reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); poly_tan(FPU_st0_ptr, FPU_st0_ptr); FPU_st0_ptr->sign = (q & 1) ^ arg_sign; if (FPU_st0_ptr->exp <= EXP_UNDER) arith_underflow(FPU_st0_ptr); push(); reg_move(&CONST_1, FPU_st0_ptr); setcc(0); } else { /* Operand is out of range */ setcc(SW_C2); FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ return; } break; case TW_Infinity: /* Operand is out of range */ setcc(SW_C2); FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ return; case TW_Zero: push(); reg_move(&CONST_1, FPU_st0_ptr); setcc(0); break; default: single_arg_error(); break; } }
void fstp_i() { /* fstp st(i) */ reg_move(FPU_st0_ptr, &st(FPU_rm)); pop(); }
void search(short n, boolean is_auto) { short s, i, j, row, col, t; short shown = 0, found = 0; static boolean reg_search; for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { row = rogue.row + i; col = rogue.col + j; if ((row < MIN_ROW) || (row >= (ROGUE_LINES - 1)) || (col < 0) || (col >= ROGUE_COLUMNS)) { continue; } if (dungeon[row][col] & HIDDEN) { found++; } } } for (s = 0; s < n; s++) { for (i = -1; i <= 1; i++) { for (j = -1; j <= 1; j++) { row = rogue.row + i; col = rogue.col + j; if ((row < MIN_ROW) || (row >= (ROGUE_LINES - 1)) || (col < 0) || (col >= ROGUE_COLUMNS)) { continue; } if (dungeon[row][col] & HIDDEN) { if (rand_percent(17 + (rogue.exp + ring_exp))) { dungeon[row][col] &= (~HIDDEN); if ((!blind) && ((row != rogue.row) || (col != rogue.col))) { mvaddch_rogue(row, col, get_dungeon_char(row, col)); } shown++; if (dungeon[row][col] & TRAP) { t = trap_at(row, col); message(trap_strings[t * 2], 1); } } } if (((shown == found) && (found > 0)) || interrupted) { return; } } } if ((!is_auto) && (reg_search = !reg_search)) { (void) reg_move(); } } }
static void fld_const(FPU_REG * c) { FPU_REG *st_new_ptr; if (STACK_OVERFLOW) { stack_overflow(); return; } push(); reg_move(c, FPU_st0_ptr); status_word &= ~SW_C1; }
static void f2xm1(void) { switch (FPU_st0_tag) { case TW_Valid: { FPU_REG rv, tmp; #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ if (FPU_st0_ptr->sign == SIGN_POS) { /* poly_2xm1(x) requires 0 < x < 1. */ if (poly_2xm1(FPU_st0_ptr, &rv)) return; /* error */ reg_mul(&rv, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); } else { /* **** Should change poly_2xm1() to at least handle numbers near 0 */ /* poly_2xm1(x) doesn't handle negative * numbers. */ /* So we compute (poly_2xm1(x+1)-1)/2, for -1 * < x < 0 */ reg_add(FPU_st0_ptr, &CONST_1, &tmp, FULL_PRECISION); poly_2xm1(&tmp, &rv); reg_mul(&rv, &tmp, &tmp, FULL_PRECISION); reg_sub(&tmp, &CONST_1, FPU_st0_ptr, FULL_PRECISION); FPU_st0_ptr->exp--; if (FPU_st0_ptr->exp <= EXP_UNDER) arith_underflow(FPU_st0_ptr); } return; } case TW_Zero: return; case TW_Infinity: if (FPU_st0_ptr->sign == SIGN_NEG) { /* -infinity gives -1 (p16-10) */ reg_move(&CONST_1, FPU_st0_ptr); FPU_st0_ptr->sign = SIGN_NEG; } return; default: single_arg_error(); } }
void vanish(object *obj, short rm, object *pack) { if (obj->quantity > 1) { obj->quantity--; } else { if (obj->in_use_flags & BEING_WIELDED) { unwield(obj); } else if (obj->in_use_flags & BEING_WORN) { unwear(obj); } else if (obj->in_use_flags & ON_EITHER_HAND) { un_put_on(obj); } take_from_pack(obj, pack); free_object(obj); } if (rm) { reg_move(); } }
/* Convert a long to register */ static void convert_l2reg(long *arg, FPU_REG * dest) { long num = *arg; if (num == 0) { reg_move(&CONST_Z, dest); return; } if (num > 0) dest->sign = SIGN_POS; else { num = -num; dest->sign = SIGN_NEG; } dest->sigh = num; dest->sigl = 0; dest->exp = EXP_BIAS + 31; dest->tag = TW_Valid; normalize(dest); }
void fld_i_() { FPU_REG *st_new_ptr; if (STACK_OVERFLOW) { stack_overflow(); return; } /* fld st(i) */ if (NOT_EMPTY(FPU_rm)) { reg_move(&st(FPU_rm), st_new_ptr); push(); } else { if (control_word & EX_Invalid) { /* The masked response */ push(); stack_underflow(); } else EXCEPTION(EX_StackUnder); } }
/*--- poly_atan() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_atan(FPU_REG * arg) { char recursions = 0; short exponent; FPU_REG odd_poly, even_poly, pos_poly, neg_poly; FPU_REG argSq; long long arg_signif, argSqSq; #ifdef PARANOID if (arg->sign != 0) { /* Can't hack a number < 0.0 */ arith_invalid(arg); return; } /* Need a positive number */ #endif /* PARANOID */ exponent = arg->exp - EXP_BIAS; if (arg->tag == TW_Zero) { /* Return 0.0 */ reg_move(&CONST_Z, arg); return; } if (exponent >= -2) { /* argument is in the range [0.25 .. 1.0] */ if (exponent >= 0) { #ifdef PARANOID if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) #endif /* PARANOID */ { reg_move(&CONST_PI4, arg); return; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic * error */ #endif /* PARANOID */ } /* If the argument is greater than sqrt(2)-1 (=0.414213562...) */ /* convert the argument by an identity for atan */ if ((exponent >= -1) || (arg->sigh > 0xd413ccd0)) { FPU_REG numerator, denom; recursions++; arg_signif = *(long long *) &(arg->sigl); if (exponent < -1) { if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ } *(long long *) &(numerator.sigl) = -arg_signif; numerator.exp = EXP_BIAS - 1; normalize(&numerator); /* 1 - arg */ arg_signif = *(long long *) &(arg->sigl); if (shrx(&arg_signif, -exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ *(long long *) &(denom.sigl) = arg_signif; denom.sigh |= 0x80000000; /* 1 + arg */ arg->exp = numerator.exp; reg_u_div(&numerator, &denom, arg, FULL_PRECISION); exponent = arg->exp - EXP_BIAS; } } *(long long *) &arg_signif = *(long long *) &(arg->sigl); #ifdef PARANOID /* This must always be true */ if (exponent >= -1) { EXCEPTION(EX_INTERNAL | 0x120); /* There must be a logic error */ } #endif /* PARANOID */ /* shift the argument right by the required places */ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ /* Now have arg_signif with binary point at the left .1xxxxxxxx */ mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial(&pos_poly.sigl, (unsigned *) &argSqSq, (unsigned short (*)[4]) oddplterms, HIPOWERop - 1); mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl)); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial(&neg_poly.sigl, (unsigned *) &argSqSq, (unsigned short (*)[4]) oddnegterms, HIPOWERon - 1); /* Subtract the mantissas */ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); reg_move(&pos_poly, &odd_poly); poly_add_1(&odd_poly); /* The complete odd polynomial */ reg_u_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* will be a valid positive nr with expon = 0 */ *(short *) &(even_poly.sign) = 0; mul64((long long *) (&argSq.sigl), (long long *) (&denomterm), (long long *) (&even_poly.sigl)); poly_add_1(&even_poly); reg_div(&odd_poly, &even_poly, arg, FULL_PRECISION); if (recursions) reg_sub(&CONST_PI4, arg, arg, FULL_PRECISION); }
/* Subtract b from a. (a-b) -> dest */ void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; if ( !(a->tag | b->tag) ) { /* Both registers are valid */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } switch (a->sign*2 + b->sign) { case 0: /* P - P */ case 3: /* N - N */ if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = a->sign ^ SIGN_POS^SIGN_NEG; } return; case 1: /* P - N */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_POS; return; case 2: /* N - P */ reg_u_add(a, b, dest, control_w); dest->sign = SIGN_NEG; return; } } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) { real_2op_NaN(a, b, dest); return; } else if (b->tag == TW_Zero) { if (a->tag == TW_Zero) { char same_signs = !(a->sign ^ b->sign); /* Both are zero, result will be zero. */ reg_move(a, dest); /* Answer for different signs. */ if (same_signs) { /* Sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else reg_move(a, dest); return; } else if (a->tag == TW_Zero) { reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { reg_move(a, dest); return; } if (a->sign == b->sign) { reg_move(&CONST_QNaN, dest); return; } reg_move(a, dest); return; } else if (b->tag == TW_Infinity) { reg_move(b, dest); dest->sign ^= SIGN_POS^SIGN_NEG; return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x110); #endif }
void load_store_instr(char type) { FPU_REG *pop_ptr; /* We need a version of FPU_st0_ptr which won't change. */ pop_ptr = NULL; /* Initialized just to stop compiler warnings. */ switch ( type_table[(int) (unsigned) type] ) { case _NONE_: break; case _REG0_: pop_ptr = &st(0); /* Some of these instructions pop after storing */ FPU_st0_ptr = pop_ptr; /* Set the global variables. */ FPU_st0_tag = FPU_st0_ptr->tag; break; case _PUSH_: { pop_ptr = &st(-1); if ( pop_ptr->tag != TW_Empty ) { stack_overflow(); return; } top--; } break; case _null_: return Un_impl(); #ifdef PARANOID default: return EXCEPTION(EX_INTERNAL); #endif PARANOID } switch ( type ) { case 000: /* fld m32real */ reg_load_single(); reg_move(&FPU_loaded_data, pop_ptr); break; case 001: /* fild m32int */ reg_load_int32(); reg_move(&FPU_loaded_data, pop_ptr); break; case 002: /* fld m64real */ reg_load_double(); reg_move(&FPU_loaded_data, pop_ptr); break; case 003: /* fild m16int */ reg_load_int16(); reg_move(&FPU_loaded_data, pop_ptr); break; case 010: /* fst m32real */ reg_store_single(); break; case 011: /* fist m32int */ reg_store_int32(); break; case 012: /* fst m64real */ reg_store_double(); break; case 013: /* fist m16int */ reg_store_int16(); break; case 014: /* fstp m32real */ if ( reg_store_single() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 015: /* fistp m32int */ if ( reg_store_int32() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 016: /* fstp m64real */ if ( reg_store_double() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 017: /* fistp m16int */ if ( reg_store_int16() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 020: /* fldenv m14/28byte */ fldenv(); break; case 022: /* frstor m94/108byte */ frstor(); break; case 023: /* fbld m80dec */ reg_load_bcd(); reg_move(&FPU_loaded_data, pop_ptr); break; case 024: /* fldcw */ RE_ENTRANT_CHECK_OFF control_word = get_fs_word((unsigned short *) FPU_data_address); RE_ENTRANT_CHECK_ON #ifdef NO_UNDERFLOW_TRAP if ( !(control_word & EX_Underflow) ) { control_word |= EX_Underflow; } #endif FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 025: /* fld m80real */ reg_load_extended(); reg_move(&FPU_loaded_data, pop_ptr); break; case 027: /* fild m64int */ reg_load_int64(); reg_move(&FPU_loaded_data, pop_ptr); break; case 030: /* fstenv m14/28byte */ fstenv(); FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 032: /* fsave */ fsave(); FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 033: /* fbstp m80dec */ if ( reg_store_bcd() ) 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 verify_area(VERIFY_WRITE,FPU_data_address,2); put_fs_word(control_word, (short *) FPU_data_address); RE_ENTRANT_CHECK_ON FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 035: /* fstp m80real */ if ( reg_store_extended() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; case 036: /* fstsw m2byte */ status_word &= ~SW_TOP; status_word |= (top&7) << SW_TOPS; RE_ENTRANT_CHECK_OFF verify_area(VERIFY_WRITE,FPU_data_address,2); put_fs_word(status_word,(short *) FPU_data_address); RE_ENTRANT_CHECK_ON FPU_data_address = (void *)data_operand_offset; /* We want no net effect */ FPU_entry_eip = ip_offset; /* We want no net effect */ break; case 037: /* fistp m64int */ if ( reg_store_int64() ) pop_0(); /* pop only if the number was actually stored (see the 80486 manual p16-28) */ break; } }
void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w) { int diff; if ( !(a->tag | b->tag) ) { /* Both registers are valid */ if (!(a->sign ^ b->sign)) { /* signs are the same */ reg_u_add(a, b, dest, control_w); dest->sign = a->sign; return; } /* The signs are different, so do a subtraction */ diff = a->exp - b->exp; if (!diff) { diff = a->sigh - b->sigh; /* Works only if ms bits are identical */ if (!diff) { diff = a->sigl > b->sigl; if (!diff) diff = -(a->sigl < b->sigl); } } if (diff > 0) { reg_u_sub(a, b, dest, control_w); dest->sign = a->sign; } else if ( diff == 0 ) { reg_move(&CONST_Z, dest); /* sign depends upon rounding mode */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } else { reg_u_sub(b, a, dest, control_w); dest->sign = b->sign; } return; } else { if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) ) { real_2op_NaN(a, b, dest); return; } else if (a->tag == TW_Zero) { if (b->tag == TW_Zero) { char different_signs = a->sign ^ b->sign; /* Both are zero, result will be zero. */ reg_move(a, dest); if (different_signs) { /* Signs are different. */ /* Sign of answer depends upon rounding mode. */ dest->sign = ((control_w & CW_RC) != RC_DOWN) ? SIGN_POS : SIGN_NEG; } } else reg_move(b, dest); return; } else if (b->tag == TW_Zero) { reg_move(a, dest); return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { reg_move(a, dest); return; } /* They are both + or - infinity */ if (a->sign == b->sign) { reg_move(a, dest); return; } reg_move(&CONST_QNaN, dest); /* inf - inf is undefined. */ return; } else if (b->tag == TW_Infinity) { reg_move(b, dest); return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL|0x101); #endif }
void fst_i_() { /* fst st(i) */ reg_move(FPU_st0_ptr, &st(FPU_rm)); }
/*--- poly_sine() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_sine(FPU_REG const *arg, FPU_REG *result) { int exponent, echange; Xsig accumulator, argSqrd, argTo4; unsigned long fix_up, adj; unsigned long long fixed_arg; #ifdef PARANOID if ( arg->tag == TW_Zero ) { /* Return 0.0 */ reg_move(&CONST_Z, result); return; } #endif PARANOID exponent = arg->exp - EXP_BIAS; accumulator.lsw = accumulator.midw = accumulator.msw = 0; /* Split into two ranges, for arguments below and above 1.0 */ /* The boundary between upper and lower is approx 0.88309101259 */ if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xe21240aa)) ) { /* The argument is <= 0.88309101259 */ argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &significand(arg)); 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); /* Divide by four */ accumulator.msw |= 0x80000000; /* Add 1.0 */ mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(arg)); mul64_Xsig(&accumulator, &significand(arg)); /* Divide by four, FPU_REG compatible, etc */ exponent = 3*exponent + EXP_BIAS; /* The minimum exponent difference is 3 */ shr_Xsig(&accumulator, arg->exp - exponent); negate_Xsig(&accumulator); XSIG_LL(accumulator) += significand(arg); echange = round_Xsig(&accumulator); result->exp = arg->exp + echange; } else { /* The argument is > 0.88309101259 */ /* We use sin(arg) = cos(pi/2-arg) */ fixed_arg = significand(arg); if ( exponent == 0 ) { /* The argument is >= 1.0 */ /* Put the binary point at the left. */ fixed_arg <<= 1; } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ fixed_arg = 0x921fb54442d18469LL - fixed_arg; 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; /* A zero accumulator here would cause problems */ negate_Xsig(&accumulator); /* The basic computation is complete. Now fix the answer to compensate for the error due to the approximation used for pi/2 */ /* This has an exponent of -65 */ fix_up = 0x898cc517; /* The fix-up needs to be improved for larger args */ if ( argSqrd.msw & 0xffc00000 ) { /* Get about 32 bit precision in these: */ mul_32_32(0x898cc517, argSqrd.msw, &adj); fix_up -= adj/6; } mul_32_32(fix_up, LL_MSW(fixed_arg), &fix_up); adj = accumulator.lsw; /* temp save */ accumulator.lsw -= fix_up; if ( accumulator.lsw > adj ) XSIG_LL(accumulator) --; echange = round_Xsig(&accumulator); result->exp = EXP_BIAS - 1 + echange; } significand(result) = XSIG_LL(accumulator); result->tag = TW_Valid; result->sign = arg->sign; #ifdef PARANOID if ( (result->exp >= EXP_BIAS) && (significand(result) > 0x8000000000000000LL) ) { EXCEPTION(EX_INTERNAL|0x150); } #endif PARANOID }
/*--- poly_sine() -----------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_sine(FPU_REG const *arg, FPU_REG *result) { short exponent; FPU_REG fixed_arg, arg_sqrd, arg_to_4, accum, negaccum; exponent = arg->exp - EXP_BIAS; if ( arg->tag == TW_Zero ) { /* Return 0.0 */ reg_move(&CONST_Z, result); return; } #ifdef PARANOID if ( arg->sign != 0 ) /* Can't hack a number < 0.0 */ { EXCEPTION(EX_Invalid); reg_move(&CONST_QNaN, result); return; } if ( exponent >= 0 ) /* Can't hack a number > 1.0 */ { if ( (exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000) ) { reg_move(&CONST_1, result); return; } EXCEPTION(EX_Invalid); reg_move(&CONST_QNaN, result); return; } #endif PARANOID fixed_arg.sigl = arg->sigl; fixed_arg.sigh = arg->sigh; if ( exponent < -1 ) { /* shift the argument right by the required places */ if ( shrx(&(fixed_arg.sigl), -1-exponent) >= 0x80000000U ) significand(&fixed_arg)++; /* round up */ } mul64(&significand(&fixed_arg), &significand(&fixed_arg), &significand(&arg_sqrd)); mul64(&significand(&arg_sqrd), &significand(&arg_sqrd), &significand(&arg_to_4)); /* will be a valid positive nr with expon = 0 */ *(short *)&(accum.sign) = 0; accum.exp = 0; /* Do the basic fixed point polynomial evaluation */ polynomial(&(accum.sigl), &(arg_to_4.sigl), lterms, HIPOWER-1); /* will be a valid positive nr with expon = 0 */ *(short *)&(negaccum.sign) = 0; negaccum.exp = 0; /* Do the basic fixed point polynomial evaluation */ polynomial(&(negaccum.sigl), &(arg_to_4.sigl), negterms, HIPOWER-1); mul64(&significand(&arg_sqrd), &significand(&negaccum), &significand(&negaccum)); /* Subtract the mantissas */ significand(&accum) -= significand(&negaccum); /* Convert to 64 bit signed-compatible */ accum.exp = EXP_BIAS - 1 + accum.exp; reg_move(&accum, result); normalize(result); reg_mul(result, arg, result, FULL_PRECISION); reg_u_add(result, arg, result, FULL_PRECISION); if ( result->exp >= EXP_BIAS ) { /* A small overflow may be possible... but an illegal result. */ if ( (result->exp > EXP_BIAS) /* Larger or equal 2.0 */ || (result->sigl > 1) /* Larger than 1.0+msb */ || (result->sigh != 0x80000000) /* Much > 1.0 */ ) { #ifdef DEBUGGING RE_ENTRANT_CHECK_OFF; printk("\nEXP=%d, MS=%08x, LS=%08x\n", result->exp, result->sigh, result->sigl); RE_ENTRANT_CHECK_ON; #endif DEBUGGING EXCEPTION(EX_INTERNAL|0x103); } #ifdef DEBUGGING RE_ENTRANT_CHECK_OFF; printk("\n***CORRECTING ILLEGAL RESULT*** in poly_sin() computation\n"); printk("EXP=%d, MS=%08x, LS=%08x\n", result->exp, result->sigh, result->sigl); RE_ENTRANT_CHECK_ON; #endif DEBUGGING result->sigl = 0; /* Truncate the result to 1.00 */ } }
static void fxtract(void) { FPU_REG *st_new_ptr; register FPU_REG *st1_ptr = FPU_st0_ptr; /* anticipate */ if (STACK_OVERFLOW) { stack_overflow(); return; } if (!(FPU_st0_tag ^ TW_Valid)) { long e; #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ push(); reg_move(st1_ptr, FPU_st0_ptr); FPU_st0_ptr->exp = EXP_BIAS; e = st1_ptr->exp - EXP_BIAS; convert_l2reg(&e, st1_ptr); return; } else if (FPU_st0_tag == TW_Zero) { char sign = FPU_st0_ptr->sign; divide_by_zero(SIGN_NEG, FPU_st0_ptr); push(); reg_move(&CONST_Z, FPU_st0_ptr); FPU_st0_ptr->sign = sign; return; } else if (FPU_st0_tag == TW_Infinity) { char sign = FPU_st0_ptr->sign; FPU_st0_ptr->sign = SIGN_POS; push(); reg_move(&CONST_INF, FPU_st0_ptr); FPU_st0_ptr->sign = sign; return; } else if (FPU_st0_tag == TW_NaN) { if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */ EXCEPTION(EX_Invalid); /* Convert to a QNaN */ FPU_st0_ptr->sigh |= 0x40000000; } push(); reg_move(st1_ptr, FPU_st0_ptr); return; } else if (FPU_st0_tag == TW_Empty) { /* Is this the correct * behaviour? */ if (control_word & EX_Invalid) { stack_underflow(); push(); stack_underflow(); } else EXCEPTION(EX_StackUnder); } #ifdef PARANOID else EXCEPTION(EX_INTERNAL | 0x119); #endif /* PARANOID */ }
/*--- poly_tan() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_tan(FPU_REG * arg, FPU_REG * y_reg) { char invert = 0; short exponent; FPU_REG odd_poly, even_poly, pos_poly, neg_poly; FPU_REG argSq; long long arg_signif, argSqSq; exponent = arg->exp - EXP_BIAS; if (arg->tag == TW_Zero) { /* Return 0.0 */ reg_move(&CONST_Z, y_reg); return; } if (exponent >= -1) { /* argument is in the range [0.5 .. 1.0] */ if (exponent >= 0) { #ifdef PARANOID if ((exponent == 0) && (arg->sigl == 0) && (arg->sigh == 0x80000000)) #endif /* PARANOID */ { arith_overflow(y_reg); return; } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x104); /* There must be a logic * error */ return; #endif /* PARANOID */ } /* The argument is in the range [0.5 .. 1.0) */ /* Convert the argument to a number in the range (0.0 .. 0.5] */ *((long long *) (&arg->sigl)) = -*((long long *) (&arg->sigl)); normalize(arg); /* Needed later */ exponent = arg->exp - EXP_BIAS; invert = 1; } #ifdef PARANOID if (arg->sign != 0) { /* Can't hack a number < 0.0 */ arith_invalid(y_reg); return; } /* Need a positive number */ #endif /* PARANOID */ *(long long *) &arg_signif = *(long long *) &(arg->sigl); if (exponent < -1) { /* shift the argument right by the required places */ if (shrx(&arg_signif, -1 - exponent) >= (unsigned)0x80000000) arg_signif++; /* round up */ } mul64(&arg_signif, &arg_signif, (long long *) (&argSq.sigl)); mul64((long long *) (&argSq.sigl), (long long *) (&argSq.sigl), &argSqSq); /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, oddplterms, HIPOWERop - 1); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, oddnegterms, HIPOWERon - 1); mul64((long long *) (&argSq.sigl), (long long *) (&neg_poly.sigl), (long long *) (&neg_poly.sigl)); /* Subtract the mantissas */ *((long long *) (&pos_poly.sigl)) -= *((long long *) (&neg_poly.sigl)); /* Convert to 64 bit signed-compatible */ pos_poly.exp -= 1; reg_move(&pos_poly, &odd_poly); normalize(&odd_poly); reg_mul(&odd_poly, arg, &odd_poly, FULL_PRECISION); reg_u_add(&odd_poly, arg, &odd_poly, FULL_PRECISION); /* This is just the odd * polynomial */ /* will be a valid positive nr with expon = 0 */ *(short *) &(pos_poly.sign) = 0; pos_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &pos_poly.sigl, (unsigned *) &argSqSq, evenplterms, HIPOWERep - 1); mul64((long long *) (&argSq.sigl), (long long *) (&pos_poly.sigl), (long long *) (&pos_poly.sigl)); /* will be a valid positive nr with expon = 0 */ *(short *) &(neg_poly.sign) = 0; neg_poly.exp = EXP_BIAS; /* Do the basic fixed point polynomial evaluation */ polynomial((u_int *) &neg_poly.sigl, (unsigned *) &argSqSq, evennegterms, HIPOWERen - 1); /* Subtract the mantissas */ *((long long *) (&neg_poly.sigl)) -= *((long long *) (&pos_poly.sigl)); /* and multiply by argSq */ /* Convert argSq to a valid reg number */ *(short *) &(argSq.sign) = 0; argSq.exp = EXP_BIAS - 1; normalize(&argSq); /* Convert to 64 bit signed-compatible */ neg_poly.exp -= 1; reg_move(&neg_poly, &even_poly); normalize(&even_poly); reg_mul(&even_poly, &argSq, &even_poly, FULL_PRECISION); reg_add(&even_poly, &argSq, &even_poly, FULL_PRECISION); reg_sub(&CONST_1, &even_poly, &even_poly, FULL_PRECISION); /* This is just the even * polynomial */ /* Now ready to copy the results */ if (invert) { reg_div(&even_poly, &odd_poly, y_reg, FULL_PRECISION); } else { reg_div(&odd_poly, &even_poly, y_reg, FULL_PRECISION); } }
/*--- poly_cos() ------------------------------------------------------------+ | | +---------------------------------------------------------------------------*/ void poly_cos(FPU_REG const *arg, FPU_REG *result) { long int exponent, exp2, echange; Xsig accumulator, argSqrd, fix_up, argTo4; unsigned long adj; unsigned long long fixed_arg; #ifdef PARANOID if ( arg->tag == TW_Zero ) { /* Return 1.0 */ reg_move(&CONST_1, result); return; } if ( (arg->exp > EXP_BIAS) || ((arg->exp == EXP_BIAS) && (significand(arg) > 0xc90fdaa22168c234LL)) ) { EXCEPTION(EX_Invalid); reg_move(&CONST_QNaN, result); return; } #endif PARANOID exponent = arg->exp - EXP_BIAS; accumulator.lsw = accumulator.midw = accumulator.msw = 0; if ( (exponent < -1) || ((exponent == -1) && (arg->sigh <= 0xb00d6f54)) ) { /* arg is < 0.687705 */ argSqrd.msw = arg->sigh; argSqrd.midw = arg->sigl; argSqrd.lsw = 0; mul64_Xsig(&argSqrd, &significand(arg)); if ( exponent < -1 ) { /* shift the argument right by the required places */ 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(arg)); mul64_Xsig(&accumulator, &significand(arg)); shr_Xsig(&accumulator, -2*(1+exponent)); shr_Xsig(&accumulator, 3); negate_Xsig(&accumulator); add_Xsig_Xsig(&accumulator, &argSqrd); shr_Xsig(&accumulator, 1); /* It doesn't matter if accumulator is all zero here, the following code will work ok */ negate_Xsig(&accumulator); if ( accumulator.lsw & 0x80000000 ) XSIG_LL(accumulator) ++; if ( accumulator.msw == 0 ) { /* The result is 1.0 */ reg_move(&CONST_1, result); } else { significand(result) = XSIG_LL(accumulator); /* will be a valid positive nr with expon = -1 */ *(short *)&(result->sign) = 0; result->exp = EXP_BIAS - 1; } } else { fixed_arg = significand(arg); if ( exponent == 0 ) { /* The argument is >= 1.0 */ /* Put the binary point at the left. */ fixed_arg <<= 1; } /* pi/2 in hex is: 1.921fb54442d18469 898CC51701B839A2 52049C1 */ fixed_arg = 0x921fb54442d18469LL - fixed_arg; exponent = -1; exp2 = -1; /* A shift is needed here only for a narrow range of arguments, i.e. for fixed_arg approx 2^-32, but we pick up more... */ 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 ) { /* shift the argument right by the required places */ 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); /* Divide by four */ accumulator.msw |= 0x80000000; /* Add 1.0 */ mul64_Xsig(&accumulator, &fixed_arg); mul64_Xsig(&accumulator, &fixed_arg); mul64_Xsig(&accumulator, &fixed_arg); /* Divide by four, FPU_REG compatible, etc */ exponent = 3*exponent; /* The minimum exponent difference is 3 */ shr_Xsig(&accumulator, exp2 - exponent); negate_Xsig(&accumulator); XSIG_LL(accumulator) += fixed_arg; /* The basic computation is complete. Now fix the answer to compensate for the error due to the approximation used for pi/2 */ /* This has an exponent of -65 */ XSIG_LL(fix_up) = 0x898cc51701b839a2ll; fix_up.lsw = 0; /* The fix-up needs to be improved for larger args */ if ( argSqrd.msw & 0xffc00000 ) { /* Get about 32 bit precision in these: */ mul_32_32(0x898cc517, argSqrd.msw, &adj); fix_up.msw -= adj/2; mul_32_32(0x898cc517, argTo4.msw, &adj); fix_up.msw += adj/24; } exp2 += norm_Xsig(&accumulator); shr_Xsig(&accumulator, 1); /* Prevent overflow */ exp2++; shr_Xsig(&fix_up, 65 + exp2); add_Xsig_Xsig(&accumulator, &fix_up); echange = round_Xsig(&accumulator); result->exp = exp2 + EXP_BIAS + echange; *(short *)&(result->sign) = 0; /* Is a valid positive nr */ significand(result) = XSIG_LL(accumulator); } #ifdef PARANOID if ( (result->exp >= EXP_BIAS) && (significand(result) > 0x8000000000000000LL) ) { EXCEPTION(EX_INTERNAL|0x151); } #endif PARANOID }