static void fsqrt_(void) { if (!(FPU_st0_tag ^ TW_Valid)) { int expon; if (FPU_st0_ptr->sign == SIGN_NEG) { arith_invalid(FPU_st0_ptr); /* sqrt(negative) is * invalid */ return; } #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ expon = FPU_st0_ptr->exp - EXP_BIAS; FPU_st0_ptr->exp = EXP_BIAS + (expon & 1); /* make st(0) in [1.0 * .. 4.0) */ wm_sqrt(FPU_st0_ptr, control_word); /* Do the computation */ FPU_st0_ptr->exp += expon >> 1; FPU_st0_ptr->sign = SIGN_POS; } else if (FPU_st0_tag == TW_Zero)
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; } }
static void ftst_(FPU_REG *st0_ptr, u_char st0tag) { switch (st0tag) { case TAG_Zero: setcc(SW_C3); break; case TAG_Valid: if (getsign(st0_ptr) == SIGN_POS) setcc(0); else setcc(SW_C0); break; case TAG_Special: switch ( FPU_Special(st0_ptr) ) { case TW_Denormal: if (getsign(st0_ptr) == SIGN_POS) setcc(0); else setcc(SW_C0); if ( denormal_operand() < 0 ) { #ifdef PECULIAR_486 /* This is weird! */ if (getsign(st0_ptr) == SIGN_POS) setcc(SW_C3); #endif /* PECULIAR_486 */ return; } break; case TW_NaN: setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ EXCEPTION(EX_Invalid); break; case TW_Infinity: if (getsign(st0_ptr) == SIGN_POS) setcc(0); else setcc(SW_C0); break; default: setcc(SW_C0|SW_C2|SW_C3); /* Operand is not comparable */ EXCEPTION(EX_INTERNAL|0x14); break; } break; case TAG_Empty: setcc(SW_C0|SW_C2|SW_C3); EXCEPTION(EX_StackUnder); break; } }
static int compare_u_st_st(int nr) { int f = 0, c; FPU_REG *st_ptr; if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { setcc(SW_C3 | SW_C2 | SW_C0); /* */ EXCEPTION(EX_StackUnder); return !(control_word & CW_Invalid); } st_ptr = &st(nr); c = compare(st_ptr, FPU_gettagi(nr)); if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); if (c & COMP_SNaN) { /* */ EXCEPTION(EX_Invalid); return !(control_word & CW_Invalid); } return 0; } else switch (c & 7) { case COMP_A_lt_B: f = SW_C0; break; case COMP_A_eq_B: f = SW_C3; break; case COMP_A_gt_B: f = 0; break; case COMP_No_Comp: f = SW_C3 | SW_C2 | SW_C0; break; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x123); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* */ } setcc(f); if (c & COMP_Denormal) { return denormal_operand() < 0; } return 0; }
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(); } }
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 ftst_(void) { switch (FPU_st0_tag) { case TW_Zero: setcc(SW_C3); break; case TW_Valid: #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return; #endif /* DENORM_OPERAND */ if (FPU_st0_ptr->sign == SIGN_POS) setcc(0); else setcc(SW_C0); break; case TW_NaN: setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */ EXCEPTION(EX_Invalid); break; case TW_Infinity: if (FPU_st0_ptr->sign == SIGN_POS) setcc(0); else setcc(SW_C0); EXCEPTION(EX_Invalid); break; case TW_Empty: setcc(SW_C0 | SW_C2 | SW_C3); EXCEPTION(EX_StackUnder); break; default: setcc(SW_C0 | SW_C2 | SW_C3); /* Operand is not comparable */ EXCEPTION(EX_INTERNAL | 0x14); break; } }
int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) { int f = 0, c; c = compare(loaded_data, loaded_tag); if (c & COMP_NaN) { EXCEPTION(EX_Invalid); f = SW_C3 | SW_C2 | SW_C0; } else switch (c & 7) { case COMP_A_lt_B: f = SW_C0; break; case COMP_A_eq_B: f = SW_C3; break; case COMP_A_gt_B: f = 0; break; case COMP_No_Comp: f = SW_C3 | SW_C2 | SW_C0; break; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x121); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* */ } setcc(f); if (c & COMP_Denormal) { return denormal_operand() < 0; } 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; }
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; }
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 { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); } return; } else if (b->tag == TW_Zero) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } if (a->sign == b->sign) { /* They are both + or * - infinity */ reg_move(a, dest); return; } arith_invalid(dest); /* Infinity-Infinity is * undefined. */ return; } else if (b->tag == TW_Infinity) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x101); #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); /* 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); }
/* 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); }
/* 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; }
/* 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) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ 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 { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); } return; } else if (a->tag == TW_Zero) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); dest->sign ^= SIGN_POS ^ SIGN_NEG; return; } else if (a->tag == TW_Infinity) { if (b->tag != TW_Infinity) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(a, dest); return; } /* Both args are Infinity */ if (a->sign == b->sign) { arith_invalid(dest); /* Infinity-Infinity is * undefined. */ return; } reg_move(a, dest); return; } else if (b->tag == TW_Infinity) { #ifdef DENORM_OPERAND if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) && denormal_operand()) return; #endif /* DENORM_OPERAND */ reg_move(b, dest); dest->sign ^= SIGN_POS ^ SIGN_NEG; return; } } #ifdef PARANOID EXCEPTION(EX_INTERNAL | 0x110); #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 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 */ }
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; }