diff options
Diffstat (limited to 'sys/gnu/i386/fpemul/fpu_trig.c')
-rw-r--r-- | sys/gnu/i386/fpemul/fpu_trig.c | 1371 |
1 files changed, 0 insertions, 1371 deletions
diff --git a/sys/gnu/i386/fpemul/fpu_trig.c b/sys/gnu/i386/fpemul/fpu_trig.c deleted file mode 100644 index 9eaab4f..0000000 --- a/sys/gnu/i386/fpemul/fpu_trig.c +++ /dev/null @@ -1,1371 +0,0 @@ -/* - * fpu_trig.c - * - * Implementation of the FPU "transcendental" functions. - * - * - * Copyright (C) 1992,1993,1994 - * W. Metzenthen, 22 Parker St, Ormond, Vic 3163, - * Australia. E-mail billm@vaxc.cc.monash.edu.au - * All rights reserved. - * - * This copyright notice covers the redistribution and use of the - * FPU emulator developed by W. Metzenthen. It covers only its use - * in the 386BSD, FreeBSD and NetBSD operating systems. Any other - * use is not permitted under this copyright. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must include information specifying - * that source code for the emulator is freely available and include - * either: - * a) an offer to provide the source code for a nominal distribution - * fee, or - * b) list at least two alternative methods whereby the source - * can be obtained, e.g. a publically accessible bulletin board - * and an anonymous ftp site from which the software can be - * downloaded. - * 3. All advertising materials specifically mentioning features or use of - * this emulator must acknowledge that it was developed by W. Metzenthen. - * 4. The name of W. Metzenthen may not be used to endorse or promote - * products derived from this software without specific prior written - * permission. - * - * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, - * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY - * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL - * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, - * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, - * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR - * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF - * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING - * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS - * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. - * - * - * The purpose of this copyright, based upon the Berkeley copyright, is to - * ensure that the covered software remains freely available to everyone. - * - * The software (with necessary differences) is also available, but under - * the terms of the GNU copyleft, for the Linux operating system and for - * the djgpp ms-dos extender. - * - * W. Metzenthen June 1994. - * - * - * $FreeBSD$ - * - */ - - -#include <sys/param.h> -#ifdef DEBUG -#include <sys/systm.h> /* for printf() in EXCEPTION() */ -#endif -#include <sys/proc.h> -#include <machine/pcb.h> - -#include <gnu/i386/fpemul/fpu_emu.h> -#include <gnu/i386/fpemul/fpu_system.h> -#include <gnu/i386/fpemul/exception.h> -#include <gnu/i386/fpemul/status_w.h> -#include <gnu/i386/fpemul/reg_constant.h> -#include <gnu/i386/fpemul/control_w.h> - -static void convert_l2reg(long *arg, FPU_REG * dest); - -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; -} - - -/* 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); -} - - -static void -single_arg_error(void) -{ - switch (FPU_st0_tag) { - case TW_NaN: - if (!(FPU_st0_ptr->sigh & 0x40000000)) { /* Signaling ? */ - EXCEPTION(EX_Invalid); - /* Convert to a QNaN */ - FPU_st0_ptr->sigh |= 0x40000000; - } - break; /* return with a NaN in st(0) */ - case TW_Empty: - stack_underflow(); /* Puts a QNaN in st(0) */ - break; -#ifdef PARANOID - default: - EXCEPTION(EX_INTERNAL | 0x0112); -#endif /* PARANOID */ - } -} - - -/*---------------------------------------------------------------------------*/ - -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 -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 -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 -fdecstp(void) -{ - top--; /* FPU_st0_ptr will be fixed in math_emulate() - * before the next instr */ -} - -static void -fincstp(void) -{ - top++; /* FPU_st0_ptr will be fixed in math_emulate() - * before the next instr */ -} - - -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) - return; - else - if (FPU_st0_tag == TW_Infinity) { - if (FPU_st0_ptr->sign == SIGN_NEG) - arith_invalid(FPU_st0_ptr); /* sqrt(-Infinity) is - * invalid */ - return; - } else { - single_arg_error(); - return; - } - -} - - -static void -frndint_(void) -{ - if (!(FPU_st0_tag ^ TW_Valid)) { - if (FPU_st0_ptr->exp > EXP_BIAS + 63) - return; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - round_to_int(FPU_st0_ptr); /* Fortunately, this can't - * overflow to 2^64 */ - FPU_st0_ptr->exp = EXP_BIAS + 63; - normalize(FPU_st0_ptr); - return; - } else - if ((FPU_st0_tag == TW_Zero) || (FPU_st0_tag == TW_Infinity)) - return; - else - single_arg_error(); -} - - -static void -fsin(void) -{ - char arg_sign = FPU_st0_ptr->sign; - - if (FPU_st0_tag == TW_Valid) { - int q; - FPU_st0_ptr->sign = SIGN_POS; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if ((q = trig_arg(FPU_st0_ptr)) != -1) { - FPU_REG rv; - - if (q & 1) - reg_sub(&CONST_1, FPU_st0_ptr, FPU_st0_ptr, FULL_PRECISION); - - poly_sine(FPU_st0_ptr, &rv); - - setcc(0); - if (q & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - rv.sign ^= arg_sign; - reg_move(&rv, FPU_st0_ptr); - - if (FPU_st0_ptr->exp <= EXP_UNDER) - arith_underflow(FPU_st0_ptr); - - set_precision_flag_up(); /* We do not really know - * if up or down */ - - return; - } else { - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ - return; - } - } else - if (FPU_st0_tag == TW_Zero) { - setcc(0); - return; - } else - if (FPU_st0_tag == TW_Infinity) { - /* Operand is out of range */ - setcc(SW_C2); - FPU_st0_ptr->sign = arg_sign; /* restore st(0) */ - return; - } else - single_arg_error(); -} - - -static int -f_cos(FPU_REG * arg) -{ - char arg_sign = arg->sign; - - if (arg->tag == TW_Valid) { - int q; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return 1; -#endif /* DENORM_OPERAND */ - - arg->sign = SIGN_POS; - if ((q = trig_arg(arg)) != -1) { - FPU_REG rv; - - if (!(q & 1)) - reg_sub(&CONST_1, arg, arg, FULL_PRECISION); - - poly_sine(arg, &rv); - - setcc(0); - if ((q + 1) & 2) - rv.sign ^= SIGN_POS ^ SIGN_NEG; - reg_move(&rv, arg); - - set_precision_flag_up(); /* We do not really know - * if up or down */ - - return 0; - } else { - /* Operand is out of range */ - setcc(SW_C2); - arg->sign = arg_sign; /* restore st(0) */ - return 1; - } - } else - if (arg->tag == TW_Zero) { - reg_move(&CONST_1, arg); - setcc(0); - return 0; - } else - if (FPU_st0_tag == TW_Infinity) { - /* Operand is out of range */ - setcc(SW_C2); - arg->sign = arg_sign; /* restore st(0) */ - return 1; - } else { - single_arg_error(); /* requires arg == - * &st(0) */ - return 1; - } -} - - -static void -fcos(void) -{ - f_cos(FPU_st0_ptr); -} - - -static void -fsincos(void) -{ - FPU_REG *st_new_ptr; - FPU_REG arg; - - if (STACK_OVERFLOW) { - stack_overflow(); - return; - } - reg_move(FPU_st0_ptr, &arg); - if (!f_cos(&arg)) { - fsin(); - push(); - reg_move(&arg, FPU_st0_ptr); - } -} - - -/*---------------------------------------------------------------------------*/ -/* The following all require two arguments: st(0) and st(1) */ - -/* remainder of st(0) / st(1) */ -/* Assumes that st(0) and st(1) are both TW_Valid */ -static void -fprem_kernel(int round) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { - FPU_REG tmp; - int old_cw = control_word; - int expdif = FPU_st0_ptr->exp - (st1_ptr)->exp; - -#ifdef DENORM_OPERAND - if (((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - control_word &= ~CW_RC; - control_word |= round; - - if (expdif < 64) { - /* This should be the most common case */ - long long q; - int c = 0; - - reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); - - round_to_int(&tmp); /* Fortunately, this can't - * overflow to 2^64 */ - tmp.exp = EXP_BIAS + 63; - q = *(long long *) &(tmp.sigl); - normalize(&tmp); - - reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); - - if (q & 4) - c |= SW_C3; - if (q & 2) - c |= SW_C1; - if (q & 1) - c |= SW_C0; - - setcc(c); - } else { - /* There is a large exponent difference ( >= 64 ) */ - int N_exp; - - reg_div(FPU_st0_ptr, st1_ptr, &tmp, FULL_PRECISION); - /* N is 'a number between 32 and 63' (p26-113) */ - N_exp = (tmp.exp & 31) + 32; - tmp.exp = EXP_BIAS + N_exp; - - round_to_int(&tmp); /* Fortunately, this can't - * overflow to 2^64 */ - tmp.exp = EXP_BIAS + 63; - normalize(&tmp); - - tmp.exp = EXP_BIAS + expdif - N_exp; - - reg_mul(st1_ptr, &tmp, &tmp, FULL_PRECISION); - reg_sub(FPU_st0_ptr, &tmp, FPU_st0_ptr, FULL_PRECISION); - - setcc(SW_C2); - } - control_word = old_cw; - - if (FPU_st0_ptr->exp <= EXP_UNDER) - arith_underflow(FPU_st0_ptr); - return; - } else - if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) { - stack_underflow(); - return; - } else - if (FPU_st0_tag == TW_Zero) { - if (st1_tag == TW_Valid) { - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - setcc(0); - return; - } else - if (st1_tag == TW_Zero) { - arith_invalid(FPU_st0_ptr); - return; - } - /* fprem(?,0) always invalid */ - else - if (st1_tag == TW_Infinity) { - setcc(0); - return; - } - } else - if (FPU_st0_tag == TW_Valid) { - if (st1_tag == TW_Zero) { - arith_invalid(FPU_st0_ptr); /* fprem(Valid,Zero) is - * invalid */ - return; - } else - if (st1_tag != TW_NaN) { -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if (st1_tag == TW_Infinity) { - /* fprem(Valid, - * Infinity) - * is o.k. */ - setcc(0); - return; - } - } - } else - if (FPU_st0_tag == TW_Infinity) { - if (st1_tag != TW_NaN) { - arith_invalid(FPU_st0_ptr); /* fprem(Infinity,?) is - * invalid */ - return; - } - } - /* One of the registers must contain a NaN is we got here. */ - -#ifdef PARANOID - if ((FPU_st0_tag != TW_NaN) && (st1_tag != TW_NaN)) - EXCEPTION(EX_INTERNAL | 0x118); -#endif /* PARANOID */ - - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); - -} - - -/* ST(1) <- ST(1) * log ST; pop ST */ -static void -fyl2x(void) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { - if (FPU_st0_ptr->sign == SIGN_POS) { - int saved_control, saved_status; - -#ifdef DENORM_OPERAND - if (((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - /* We use the general purpose arithmetic, so we need - * to save these. */ - saved_status = status_word; - saved_control = control_word; - control_word = FULL_PRECISION; - - poly_l2(FPU_st0_ptr, FPU_st0_ptr); - - /* Enough of the basic arithmetic is done now */ - control_word = saved_control; - status_word = saved_status; - - /* Let the multiply set the flags */ - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); - - pop(); - FPU_st0_ptr = &st(0); - } else { - /* negative */ - pop(); - FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* st(0) cannot be - * negative */ - return; - } - } else - if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) { - stack_underflow_pop(1); - return; - } else - if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } else - if ((FPU_st0_tag <= TW_Zero) && (st1_tag <= TW_Zero)) { - /* one of the args is zero, the other - * valid, or both zero */ - if (FPU_st0_tag == TW_Zero) { - pop(); - FPU_st0_ptr = &st(0); - if (FPU_st0_ptr->tag == TW_Zero) - arith_invalid(FPU_st0_ptr); /* Both args zero is - * invalid */ -#ifdef PECULIAR_486 - /* This case is not - * specifically covered in the - * manual, but divide-by-zero - * would seem to be the best - * response. However, a real - * 80486 does it this way... */ - else - if (FPU_st0_ptr->tag == TW_Infinity) { - reg_move(&CONST_INF, FPU_st0_ptr); - return; - } -#endif /* PECULIAR_486 */ - else - divide_by_zero(st1_ptr->sign ^ SIGN_NEG ^ SIGN_POS, FPU_st0_ptr); - return; - } else { - /* st(1) contains zero, st(0) - * valid <> 0 */ - /* Zero is the valid answer */ - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - if (FPU_st0_ptr->sign == SIGN_NEG) { - pop(); - FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* log(negative) */ - return; - } - if (FPU_st0_ptr->exp < EXP_BIAS) - sign ^= SIGN_NEG ^ SIGN_POS; - pop(); - FPU_st0_ptr = &st(0); - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; - return; - } - } - /* One or both arg must be an infinity */ - else - if (FPU_st0_tag == TW_Infinity) { - if ((FPU_st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero)) { - pop(); - FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* log(-infinity) or - * 0*log(infinity) */ - return; - } else { - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - pop(); - FPU_st0_ptr = &st(0); - reg_move(&CONST_INF, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; - return; - } - } - /* st(1) must be infinity here */ - else - if ((FPU_st0_tag == TW_Valid) && (FPU_st0_ptr->sign == SIGN_POS)) { - if (FPU_st0_ptr->exp >= EXP_BIAS) { - if ((FPU_st0_ptr->exp == EXP_BIAS) && - (FPU_st0_ptr->sigh == 0x80000000) && - (FPU_st0_ptr->sigl == 0)) { - /* st(0 - * ) - * hold - * s - * 1.0 */ - pop(); - FPU_st0_ptr = &st(0); - arith_invalid(FPU_st0_ptr); /* infinity*log(1) */ - return; - } - /* st(0) is - * positive - * and > 1.0 */ - pop(); - } else { - /* st(0) is - * positive - * and < 1.0 */ - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - st1_ptr->sign ^= SIGN_NEG; - pop(); - } - return; - } else { - /* st(0) must be zero - * or negative */ - if (FPU_st0_ptr->tag == TW_Zero) { - pop(); - FPU_st0_ptr = st1_ptr; - st1_ptr->sign ^= SIGN_NEG ^ SIGN_POS; - /* This should - * be invalid, - * but a real - * 80486 is - * happy with - * it. */ -#ifndef PECULIAR_486 - divide_by_zero(st1_ptr->sign, FPU_st0_ptr); -#endif /* PECULIAR_486 */ - } else { - pop(); - FPU_st0_ptr = st1_ptr; - arith_invalid(FPU_st0_ptr); /* log(negative) */ - } - return; - } -} - - -static void -fpatan(void) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { - int saved_control, saved_status; - FPU_REG sum; - int quadrant = st1_ptr->sign | ((FPU_st0_ptr->sign) << 1); - -#ifdef DENORM_OPERAND - if (((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - /* We use the general purpose arithmetic so we need to save - * these. */ - saved_status = status_word; - saved_control = control_word; - control_word = FULL_PRECISION; - - st1_ptr->sign = FPU_st0_ptr->sign = SIGN_POS; - if (compare(st1_ptr) == COMP_A_lt_B) { - quadrant |= 4; - reg_div(FPU_st0_ptr, st1_ptr, &sum, FULL_PRECISION); - } else - reg_div(st1_ptr, FPU_st0_ptr, &sum, FULL_PRECISION); - - poly_atan(&sum); - - if (quadrant & 4) { - reg_sub(&CONST_PI2, &sum, &sum, FULL_PRECISION); - } - if (quadrant & 2) { - reg_sub(&CONST_PI, &sum, &sum, FULL_PRECISION); - } - if (quadrant & 1) - sum.sign ^= SIGN_POS ^ SIGN_NEG; - - /* All of the basic arithmetic is done now */ - control_word = saved_control; - status_word = saved_status; - - reg_move(&sum, st1_ptr); - } else - if ((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty)) { - stack_underflow_pop(1); - return; - } else - if ((FPU_st0_tag == TW_NaN) || (st1_tag == TW_NaN)) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } else - if ((FPU_st0_tag == TW_Infinity) || (st1_tag == TW_Infinity)) { - char sign = st1_ptr->sign; - if (FPU_st0_tag == TW_Infinity) { - if (st1_tag == TW_Infinity) { - if (FPU_st0_ptr->sign == SIGN_POS) { - reg_move(&CONST_PI4, st1_ptr); - } else - reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION); - } else { - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if (FPU_st0_ptr->sign == SIGN_POS) { - reg_move(&CONST_Z, st1_ptr); - pop(); - return; - } else - reg_move(&CONST_PI, st1_ptr); - } - } else { - /* st(1) is infinity, st(0) - * not infinity */ -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - reg_move(&CONST_PI2, st1_ptr); - } - st1_ptr->sign = sign; - } else - if (st1_tag == TW_Zero) { - /* st(0) must be valid or zero */ - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if (FPU_st0_ptr->sign == SIGN_POS) { - reg_move(&CONST_Z, st1_ptr); - pop(); - return; - } else - reg_move(&CONST_PI, st1_ptr); - st1_ptr->sign = sign; - } else - if (FPU_st0_tag == TW_Zero) { - /* st(1) must be - * TW_Valid here */ - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - reg_move(&CONST_PI2, st1_ptr); - st1_ptr->sign = sign; - } -#ifdef PARANOID - else - EXCEPTION(EX_INTERNAL | 0x220); -#endif /* PARANOID */ - - pop(); - set_precision_flag_up();/* We do not really know if up or down */ -} - - -static void -fprem(void) -{ - fprem_kernel(RC_CHOP); -} - - -static void -fprem1(void) -{ - fprem_kernel(RC_RND); -} - - -static void -fyl2xp1(void) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - - if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { - int saved_control, saved_status; - -#ifdef DENORM_OPERAND - if (((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - /* We use the general purpose arithmetic so we need to save - * these. */ - saved_status = status_word; - saved_control = control_word; - control_word = FULL_PRECISION; - - if (poly_l2p1(FPU_st0_ptr, FPU_st0_ptr)) { - arith_invalid(st1_ptr); /* poly_l2p1() returned - * invalid */ - pop(); - return; - } - /* Enough of the basic arithmetic is done now */ - control_word = saved_control; - status_word = saved_status; - - /* Let the multiply set the flags */ - reg_mul(FPU_st0_ptr, st1_ptr, st1_ptr, FULL_PRECISION); - - pop(); - } else - if ((FPU_st0_tag == TW_Empty) | (st1_tag == TW_Empty)) { - stack_underflow_pop(1); - return; - } else - if (FPU_st0_tag == TW_Zero) { - if (st1_tag <= TW_Zero) { - -#ifdef DENORM_OPERAND - if ((st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) && - (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - st1_ptr->sign ^= FPU_st0_ptr->sign; - reg_move(FPU_st0_ptr, st1_ptr); - } else - if (st1_tag == TW_Infinity) { - arith_invalid(st1_ptr); /* Infinity*log(1) */ - pop(); - return; - } else - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } -#ifdef PARANOID - else { - EXCEPTION(EX_INTERNAL | 0x116); - return; - } -#endif /* PARANOID */ - pop(); - return; - } else - if (FPU_st0_tag == TW_Valid) { - if (st1_tag == TW_Zero) { - if (FPU_st0_ptr->sign == SIGN_NEG) { - if (FPU_st0_ptr->exp >= EXP_BIAS) { - /* st(0) holds - * <= -1.0 */ - arith_invalid(st1_ptr); /* infinity*log(1) */ - pop(); - return; - } -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG; - pop(); - return; - } -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - pop(); - return; - } - if (st1_tag == TW_Infinity) { - if (FPU_st0_ptr->sign == SIGN_NEG) { - if ((FPU_st0_ptr->exp >= EXP_BIAS) && - !((FPU_st0_ptr->sigh == 0x80000000) && - (FPU_st0_ptr->sigl == 0))) { - /* st(0) holds - * < -1.0 */ - arith_invalid(st1_ptr); - pop(); - return; - } -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - st1_ptr->sign ^= SIGN_POS ^ SIGN_NEG; - pop(); - return; - } -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - pop(); - return; - } - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } - } else - if (FPU_st0_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } else - if (FPU_st0_tag == TW_Infinity) { - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, st1_ptr); - pop(); - return; - } else - if ((FPU_st0_ptr->sign == SIGN_NEG) || - (st1_tag == TW_Zero)) { - arith_invalid(st1_ptr); /* log(infinity) */ - pop(); - return; - } - /* st(1) must be valid - * here. */ - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - /* The Manual says - * that log(Infinity) - * is invalid, but a - * real 80486 sensibly - * says that it is - * o.k. */ - { - char sign = st1_ptr->sign; - reg_move(&CONST_INF, st1_ptr); - st1_ptr->sign = sign; - } - pop(); - return; - } -#ifdef PARANOID - else { - EXCEPTION(EX_INTERNAL | 0x117); - } -#endif /* PARANOID */ -} - - -static void -emu_fscale(void) -{ - FPU_REG *st1_ptr = &st(1); - char st1_tag = st1_ptr->tag; - int old_cw = control_word; - - if (!((FPU_st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid))) { - long scale; - FPU_REG tmp; - -#ifdef DENORM_OPERAND - if (((FPU_st0_ptr->exp <= EXP_UNDER) || - (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if (st1_ptr->exp > EXP_BIAS + 30) { - /* 2^31 is far too large, would require 2^(2^30) or - * 2^(-2^30) */ - char sign; - - if (st1_ptr->sign == SIGN_POS) { - EXCEPTION(EX_Overflow); - sign = FPU_st0_ptr->sign; - reg_move(&CONST_INF, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; - } else { - EXCEPTION(EX_Underflow); - sign = FPU_st0_ptr->sign; - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; - } - return; - } - control_word &= ~CW_RC; - control_word |= RC_CHOP; - reg_move(st1_ptr, &tmp); - round_to_int(&tmp); /* This can never overflow here */ - control_word = old_cw; - scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl; - scale += FPU_st0_ptr->exp; - FPU_st0_ptr->exp = scale; - - /* Use round_reg() to properly detect under/overflow etc */ - round_reg(FPU_st0_ptr, 0, control_word); - - return; - } else - if (FPU_st0_tag == TW_Valid) { - if (st1_tag == TW_Zero) { - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - return; - } - if (st1_tag == TW_Infinity) { - char sign = st1_ptr->sign; - -#ifdef DENORM_OPERAND - if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - if (sign == SIGN_POS) { - reg_move(&CONST_INF, FPU_st0_ptr); - } else - reg_move(&CONST_Z, FPU_st0_ptr); - FPU_st0_ptr->sign = sign; - return; - } - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); - return; - } - } else - if (FPU_st0_tag == TW_Zero) { - if (st1_tag == TW_Valid) { - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - return; - } else - if (st1_tag == TW_Zero) { - return; - } else - if (st1_tag == TW_Infinity) { - if (st1_ptr->sign == SIGN_NEG) - return; - else { - arith_invalid(FPU_st0_ptr); /* Zero scaled by - * +Infinity */ - return; - } - } else - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); - return; - } - } else - if (FPU_st0_tag == TW_Infinity) { - if (st1_tag == TW_Valid) { - -#ifdef DENORM_OPERAND - if ((st1_ptr->exp <= EXP_UNDER) && (denormal_operand())) - return; -#endif /* DENORM_OPERAND */ - - return; - } - if (((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS)) - || (st1_tag == TW_Zero)) - return; - else - if (st1_tag == TW_Infinity) { - arith_invalid(FPU_st0_ptr); /* Infinity scaled by - * -Infinity */ - return; - } else - if (st1_tag == TW_NaN) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); - return; - } - } else - if (FPU_st0_tag == TW_NaN) { - if (st1_tag != TW_Empty) { - real_2op_NaN(FPU_st0_ptr, st1_ptr, FPU_st0_ptr); - return; - } - } -#ifdef PARANOID - if (!((FPU_st0_tag == TW_Empty) || (st1_tag == TW_Empty))) { - EXCEPTION(EX_INTERNAL | 0x115); - return; - } -#endif - - /* At least one of st(0), st(1) must be empty */ - stack_underflow(); - -} - - -/*---------------------------------------------------------------------------*/ - -static FUNC trig_table_a[] = { - f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp -}; - -void -trig_a(void) -{ - (trig_table_a[FPU_rm]) (); -} - - -static FUNC trig_table_b[] = -{ - fprem, fyl2xp1, fsqrt_, fsincos, frndint_, emu_fscale, fsin, fcos -}; - -void -trig_b(void) -{ - (trig_table_b[FPU_rm]) (); -} |