OpenCores
URL https://opencores.org/ocsvn/or1k_old/or1k_old/trunk

Subversion Repositories or1k_old

[/] [or1k_old/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [i386/] [math-emu/] [fpu_trig.c] - Diff between revs 1765 and 1782

Only display areas with differences | Details | Blame | View Log

Rev 1765 Rev 1782
/*---------------------------------------------------------------------------+
/*---------------------------------------------------------------------------+
 |  fpu_trig.c                                                               |
 |  fpu_trig.c                                                               |
 |                                                                           |
 |                                                                           |
 | Implementation of the FPU "transcendental" functions.                     |
 | Implementation of the FPU "transcendental" functions.                     |
 |                                                                           |
 |                                                                           |
 | Copyright (C) 1992,1993,1994                                              |
 | Copyright (C) 1992,1993,1994                                              |
 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
 |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
 |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
 |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
 |                                                                           |
 |                                                                           |
 |                                                                           |
 |                                                                           |
 +---------------------------------------------------------------------------*/
 +---------------------------------------------------------------------------*/
 
 
#include "fpu_system.h"
#include "fpu_system.h"
#include "exception.h"
#include "exception.h"
#include "fpu_emu.h"
#include "fpu_emu.h"
#include "status_w.h"
#include "status_w.h"
#include "control_w.h"
#include "control_w.h"
#include "reg_constant.h"       
#include "reg_constant.h"       
 
 
 
 
static void rem_kernel(unsigned long long st0, unsigned long long *y,
static void rem_kernel(unsigned long long st0, unsigned long long *y,
                       unsigned long long st1,
                       unsigned long long st1,
                       unsigned long long q, int n);
                       unsigned long long q, int n);
 
 
#define BETTER_THAN_486
#define BETTER_THAN_486
 
 
#define FCOS  4
#define FCOS  4
/* Not needed now with new code
/* Not needed now with new code
#define FPTAN 1
#define FPTAN 1
 */
 */
 
 
/* Used only by fptan, fsin, fcos, and fsincos. */
/* Used only by fptan, fsin, fcos, and fsincos. */
/* This routine produces very accurate results, similar to
/* This routine produces very accurate results, similar to
   using a value of pi with more than 128 bits precision. */
   using a value of pi with more than 128 bits precision. */
/* Limited measurements show no results worse than 64 bit precision
/* Limited measurements show no results worse than 64 bit precision
   except for the results for arguments close to 2^63, where the
   except for the results for arguments close to 2^63, where the
   precision of the result sometimes degrades to about 63.9 bits */
   precision of the result sometimes degrades to about 63.9 bits */
static int trig_arg(FPU_REG *X, int even)
static int trig_arg(FPU_REG *X, int even)
{
{
  FPU_REG tmp;
  FPU_REG tmp;
  unsigned long long q;
  unsigned long long q;
  int old_cw = control_word, saved_status = partial_status;
  int old_cw = control_word, saved_status = partial_status;
 
 
  if ( X->exp >= EXP_BIAS + 63 )
  if ( X->exp >= EXP_BIAS + 63 )
    {
    {
      partial_status |= SW_C2;     /* Reduction incomplete. */
      partial_status |= SW_C2;     /* Reduction incomplete. */
      return -1;
      return -1;
    }
    }
 
 
  control_word &= ~CW_RC;
  control_word &= ~CW_RC;
  control_word |= RC_CHOP;
  control_word |= RC_CHOP;
 
 
  reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
  reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
  round_to_int(&tmp);  /* Fortunately, this can't overflow
  round_to_int(&tmp);  /* Fortunately, this can't overflow
                          to 2^64 */
                          to 2^64 */
  q = significand(&tmp);
  q = significand(&tmp);
  if ( q )
  if ( q )
    {
    {
      rem_kernel(significand(X),
      rem_kernel(significand(X),
                 &significand(&tmp),
                 &significand(&tmp),
                 significand(&CONST_PI2),
                 significand(&CONST_PI2),
                 q, X->exp - CONST_PI2.exp);
                 q, X->exp - CONST_PI2.exp);
      tmp.exp = CONST_PI2.exp;
      tmp.exp = CONST_PI2.exp;
      normalize(&tmp);
      normalize(&tmp);
      reg_move(&tmp, X);
      reg_move(&tmp, X);
    }
    }
 
 
#ifdef FPTAN
#ifdef FPTAN
  if ( even == FPTAN )
  if ( even == FPTAN )
    {
    {
      if ( ((X->exp >= EXP_BIAS) ||
      if ( ((X->exp >= EXP_BIAS) ||
            ((X->exp == EXP_BIAS-1)
            ((X->exp == EXP_BIAS-1)
             && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
             && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
        even = FCOS;
        even = FCOS;
      else
      else
        even = 0;
        even = 0;
    }
    }
#endif FPTAN
#endif FPTAN
 
 
  if ( (even && !(q & 1)) || (!even && (q & 1)) )
  if ( (even && !(q & 1)) || (!even && (q & 1)) )
    {
    {
      reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
      reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
#ifdef BETTER_THAN_486
#ifdef BETTER_THAN_486
      /* So far, the results are exact but based upon a 64 bit
      /* So far, the results are exact but based upon a 64 bit
         precision approximation to pi/2. The technique used
         precision approximation to pi/2. The technique used
         now is equivalent to using an approximation to pi/2 which
         now is equivalent to using an approximation to pi/2 which
         is accurate to about 128 bits. */
         is accurate to about 128 bits. */
      if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
      if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
        {
        {
          /* This code gives the effect of having p/2 to better than
          /* This code gives the effect of having p/2 to better than
             128 bits precision. */
             128 bits precision. */
          significand(&tmp) = q + 1;
          significand(&tmp) = q + 1;
          tmp.exp = EXP_BIAS + 63;
          tmp.exp = EXP_BIAS + 63;
          tmp.tag = TW_Valid;
          tmp.tag = TW_Valid;
          normalize(&tmp);
          normalize(&tmp);
          reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
          reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
          reg_add(X, &tmp,  X, FULL_PRECISION);
          reg_add(X, &tmp,  X, FULL_PRECISION);
          if ( X->sign == SIGN_NEG )
          if ( X->sign == SIGN_NEG )
            {
            {
              /* CONST_PI2extra is negative, so the result of the addition
              /* CONST_PI2extra is negative, so the result of the addition
                 can be negative. This means that the argument is actually
                 can be negative. This means that the argument is actually
                 in a different quadrant. The correction is always < pi/2,
                 in a different quadrant. The correction is always < pi/2,
                 so it can't overflow into yet another quadrant. */
                 so it can't overflow into yet another quadrant. */
              X->sign = SIGN_POS;
              X->sign = SIGN_POS;
              q++;
              q++;
            }
            }
        }
        }
#endif BETTER_THAN_486
#endif BETTER_THAN_486
    }
    }
#ifdef BETTER_THAN_486
#ifdef BETTER_THAN_486
  else
  else
    {
    {
      /* So far, the results are exact but based upon a 64 bit
      /* So far, the results are exact but based upon a 64 bit
         precision approximation to pi/2. The technique used
         precision approximation to pi/2. The technique used
         now is equivalent to using an approximation to pi/2 which
         now is equivalent to using an approximation to pi/2 which
         is accurate to about 128 bits. */
         is accurate to about 128 bits. */
      if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
      if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
        {
        {
          /* This code gives the effect of having p/2 to better than
          /* This code gives the effect of having p/2 to better than
             128 bits precision. */
             128 bits precision. */
          significand(&tmp) = q;
          significand(&tmp) = q;
          tmp.exp = EXP_BIAS + 63;
          tmp.exp = EXP_BIAS + 63;
          tmp.tag = TW_Valid;
          tmp.tag = TW_Valid;
          normalize(&tmp);
          normalize(&tmp);
          reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
          reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
          reg_sub(X, &tmp, X, FULL_PRECISION);
          reg_sub(X, &tmp, X, FULL_PRECISION);
          if ( (X->exp == CONST_PI2.exp) &&
          if ( (X->exp == CONST_PI2.exp) &&
              ((X->sigh > CONST_PI2.sigh)
              ((X->sigh > CONST_PI2.sigh)
               || ((X->sigh == CONST_PI2.sigh)
               || ((X->sigh == CONST_PI2.sigh)
                   && (X->sigl > CONST_PI2.sigl))) )
                   && (X->sigl > CONST_PI2.sigl))) )
            {
            {
              /* CONST_PI2extra is negative, so the result of the
              /* CONST_PI2extra is negative, so the result of the
                 subtraction can be larger than pi/2. This means
                 subtraction can be larger than pi/2. This means
                 that the argument is actually in a different quadrant.
                 that the argument is actually in a different quadrant.
                 The correction is always < pi/2, so it can't overflow
                 The correction is always < pi/2, so it can't overflow
                 into yet another quadrant. */
                 into yet another quadrant. */
              reg_sub(&CONST_PI, X, X, FULL_PRECISION);
              reg_sub(&CONST_PI, X, X, FULL_PRECISION);
              q++;
              q++;
            }
            }
        }
        }
    }
    }
#endif BETTER_THAN_486
#endif BETTER_THAN_486
 
 
  control_word = old_cw;
  control_word = old_cw;
  partial_status = saved_status & ~SW_C2;     /* Reduction complete. */
  partial_status = saved_status & ~SW_C2;     /* Reduction complete. */
 
 
  return (q & 3) | even;
  return (q & 3) | even;
}
}
 
 
 
 
/* Convert a long to register */
/* Convert a long to register */
void convert_l2reg(long const *arg, FPU_REG *dest)
void convert_l2reg(long const *arg, FPU_REG *dest)
{
{
  long num = *arg;
  long num = *arg;
 
 
  if (num == 0)
  if (num == 0)
    { reg_move(&CONST_Z, dest); return; }
    { reg_move(&CONST_Z, dest); return; }
 
 
  if (num > 0)
  if (num > 0)
    dest->sign = SIGN_POS;
    dest->sign = SIGN_POS;
  else
  else
    { num = -num; dest->sign = SIGN_NEG; }
    { num = -num; dest->sign = SIGN_NEG; }
 
 
  dest->sigh = num;
  dest->sigh = num;
  dest->sigl = 0;
  dest->sigl = 0;
  dest->exp = EXP_BIAS + 31;
  dest->exp = EXP_BIAS + 31;
  dest->tag = TW_Valid;
  dest->tag = TW_Valid;
  normalize(dest);
  normalize(dest);
}
}
 
 
 
 
static void single_arg_error(FPU_REG *st0_ptr)
static void single_arg_error(FPU_REG *st0_ptr)
{
{
  switch ( st0_ptr->tag )
  switch ( st0_ptr->tag )
    {
    {
    case TW_NaN:
    case TW_NaN:
      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
        {
        {
          EXCEPTION(EX_Invalid);
          EXCEPTION(EX_Invalid);
          if ( control_word & CW_Invalid )
          if ( control_word & CW_Invalid )
            st0_ptr->sigh |= 0x40000000;          /* Convert to a QNaN */
            st0_ptr->sigh |= 0x40000000;          /* Convert to a QNaN */
        }
        }
      break;              /* return with a NaN in st(0) */
      break;              /* return with a NaN in st(0) */
    case TW_Empty:
    case TW_Empty:
      stack_underflow();  /* Puts a QNaN in st(0) */
      stack_underflow();  /* Puts a QNaN in st(0) */
      break;
      break;
#ifdef PARANOID
#ifdef PARANOID
    default:
    default:
      EXCEPTION(EX_INTERNAL|0x0112);
      EXCEPTION(EX_INTERNAL|0x0112);
#endif PARANOID
#endif PARANOID
    }
    }
}
}
 
 
 
 
static void single_arg_2_error(FPU_REG *st0_ptr)
static void single_arg_2_error(FPU_REG *st0_ptr)
{
{
  FPU_REG *st_new_ptr;
  FPU_REG *st_new_ptr;
 
 
  switch ( st0_ptr->tag )
  switch ( st0_ptr->tag )
    {
    {
    case TW_NaN:
    case TW_NaN:
      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
        {
        {
          EXCEPTION(EX_Invalid);
          EXCEPTION(EX_Invalid);
          if ( control_word & CW_Invalid )
          if ( control_word & CW_Invalid )
            {
            {
              /* The masked response */
              /* The masked response */
              /* Convert to a QNaN */
              /* Convert to a QNaN */
              st0_ptr->sigh |= 0x40000000;
              st0_ptr->sigh |= 0x40000000;
              st_new_ptr = &st(-1);
              st_new_ptr = &st(-1);
              push();
              push();
              reg_move(&st(1), st_new_ptr);
              reg_move(&st(1), st_new_ptr);
            }
            }
        }
        }
      else
      else
        {
        {
          /* A QNaN */
          /* A QNaN */
          st_new_ptr = &st(-1);
          st_new_ptr = &st(-1);
          push();
          push();
          reg_move(&st(1), st_new_ptr);
          reg_move(&st(1), st_new_ptr);
        }
        }
      break;              /* return with a NaN in st(0) */
      break;              /* return with a NaN in st(0) */
#ifdef PARANOID
#ifdef PARANOID
    default:
    default:
      EXCEPTION(EX_INTERNAL|0x0112);
      EXCEPTION(EX_INTERNAL|0x0112);
#endif PARANOID
#endif PARANOID
    }
    }
}
}
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
 
 
static void f2xm1(FPU_REG *st0_ptr)
static void f2xm1(FPU_REG *st0_ptr)
{
{
  clear_C1();
  clear_C1();
  switch ( st0_ptr->tag )
  switch ( st0_ptr->tag )
    {
    {
    case TW_Valid:
    case TW_Valid:
      {
      {
        if ( st0_ptr->exp >= 0 )
        if ( st0_ptr->exp >= 0 )
          {
          {
            /* For an 80486 FPU, the result is undefined. */
            /* For an 80486 FPU, the result is undefined. */
          }
          }
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
        else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          return;
          return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
        else
        else
          {
          {
            /* poly_2xm1(x) requires 0 < x < 1. */
            /* poly_2xm1(x) requires 0 < x < 1. */
            poly_2xm1(st0_ptr, st0_ptr);
            poly_2xm1(st0_ptr, st0_ptr);
          }
          }
        if ( st0_ptr->exp <= EXP_UNDER )
        if ( st0_ptr->exp <= EXP_UNDER )
          {
          {
            /* A denormal result has been produced.
            /* A denormal result has been produced.
               Precision must have been lost, this is always
               Precision must have been lost, this is always
               an underflow. */
               an underflow. */
            arith_underflow(st0_ptr);
            arith_underflow(st0_ptr);
          }
          }
        set_precision_flag_up();   /* 80486 appears to always do this */
        set_precision_flag_up();   /* 80486 appears to always do this */
        return;
        return;
      }
      }
    case TW_Zero:
    case TW_Zero:
      return;
      return;
    case TW_Infinity:
    case TW_Infinity:
      if ( st0_ptr->sign == SIGN_NEG )
      if ( st0_ptr->sign == SIGN_NEG )
        {
        {
          /* -infinity gives -1 (p16-10) */
          /* -infinity gives -1 (p16-10) */
          reg_move(&CONST_1, st0_ptr);
          reg_move(&CONST_1, st0_ptr);
          st0_ptr->sign = SIGN_NEG;
          st0_ptr->sign = SIGN_NEG;
        }
        }
      return;
      return;
    default:
    default:
      single_arg_error(st0_ptr);
      single_arg_error(st0_ptr);
    }
    }
}
}
 
 
 
 
static void fptan(FPU_REG *st0_ptr)
static void fptan(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st_new_ptr;
  FPU_REG *st_new_ptr;
  int q;
  int q;
  char arg_sign = st0_ptr->sign;
  char arg_sign = st0_ptr->sign;
 
 
  /* Stack underflow has higher priority */
  /* Stack underflow has higher priority */
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      stack_underflow();  /* Puts a QNaN in st(0) */
      stack_underflow();  /* Puts a QNaN in st(0) */
      if ( control_word & CW_Invalid )
      if ( control_word & CW_Invalid )
        {
        {
          st_new_ptr = &st(-1);
          st_new_ptr = &st(-1);
          push();
          push();
          stack_underflow();  /* Puts a QNaN in the new st(0) */
          stack_underflow();  /* Puts a QNaN in the new st(0) */
        }
        }
      return;
      return;
    }
    }
 
 
  if ( STACK_OVERFLOW )
  if ( STACK_OVERFLOW )
    { stack_overflow(); return; }
    { stack_overflow(); return; }
 
 
  switch ( st0_tag )
  switch ( st0_tag )
    {
    {
    case TW_Valid:
    case TW_Valid:
      if ( st0_ptr->exp > EXP_BIAS - 40 )
      if ( st0_ptr->exp > EXP_BIAS - 40 )
        {
        {
          st0_ptr->sign = SIGN_POS;
          st0_ptr->sign = SIGN_POS;
          if ( (q = trig_arg(st0_ptr, 0)) != -1 )
          if ( (q = trig_arg(st0_ptr, 0)) != -1 )
            {
            {
              poly_tan(st0_ptr, st0_ptr);
              poly_tan(st0_ptr, st0_ptr);
              st0_ptr->sign = (q & 1) ^ arg_sign;
              st0_ptr->sign = (q & 1) ^ arg_sign;
            }
            }
          else
          else
            {
            {
              /* Operand is out of range */
              /* Operand is out of range */
              st0_ptr->sign = arg_sign;         /* restore st(0) */
              st0_ptr->sign = arg_sign;         /* restore st(0) */
              return;
              return;
            }
            }
          set_precision_flag_up();  /* We do not really know if up or down */
          set_precision_flag_up();  /* We do not really know if up or down */
        }
        }
      else
      else
        {
        {
          /* For a small arg, the result == the argument */
          /* For a small arg, the result == the argument */
          /* Underflow may happen */
          /* Underflow may happen */
 
 
          if ( st0_ptr->exp <= EXP_UNDER )
          if ( st0_ptr->exp <= EXP_UNDER )
            {
            {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
              if ( denormal_operand() )
              if ( denormal_operand() )
                return;
                return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
              /* A denormal result has been produced.
              /* A denormal result has been produced.
                 Precision must have been lost, this is always
                 Precision must have been lost, this is always
                 an underflow. */
                 an underflow. */
              if ( arith_underflow(st0_ptr) )
              if ( arith_underflow(st0_ptr) )
                return;
                return;
            }
            }
          set_precision_flag_down();  /* Must be down. */
          set_precision_flag_down();  /* Must be down. */
        }
        }
      push();
      push();
      reg_move(&CONST_1, st_new_ptr);
      reg_move(&CONST_1, st_new_ptr);
      return;
      return;
      break;
      break;
    case TW_Infinity:
    case TW_Infinity:
      /* The 80486 treats infinity as an invalid operand */
      /* The 80486 treats infinity as an invalid operand */
      arith_invalid(st0_ptr);
      arith_invalid(st0_ptr);
      if ( control_word & CW_Invalid )
      if ( control_word & CW_Invalid )
        {
        {
          st_new_ptr = &st(-1);
          st_new_ptr = &st(-1);
          push();
          push();
          arith_invalid(st_new_ptr);
          arith_invalid(st_new_ptr);
        }
        }
      return;
      return;
    case TW_Zero:
    case TW_Zero:
      push();
      push();
      reg_move(&CONST_1, st_new_ptr);
      reg_move(&CONST_1, st_new_ptr);
      setcc(0);
      setcc(0);
      break;
      break;
    default:
    default:
      single_arg_2_error(st0_ptr);
      single_arg_2_error(st0_ptr);
      break;
      break;
    }
    }
}
}
 
 
 
 
static void fxtract(FPU_REG *st0_ptr)
static void fxtract(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st_new_ptr;
  FPU_REG *st_new_ptr;
  register FPU_REG *st1_ptr = st0_ptr;  /* anticipate */
  register FPU_REG *st1_ptr = st0_ptr;  /* anticipate */
 
 
  if ( STACK_OVERFLOW )
  if ( STACK_OVERFLOW )
    {  stack_overflow(); return; }
    {  stack_overflow(); return; }
  clear_C1();
  clear_C1();
  if ( !(st0_tag ^ TW_Valid) )
  if ( !(st0_tag ^ TW_Valid) )
    {
    {
      long e;
      long e;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      push();
      push();
      reg_move(st1_ptr, st_new_ptr);
      reg_move(st1_ptr, st_new_ptr);
      st_new_ptr->exp = EXP_BIAS;
      st_new_ptr->exp = EXP_BIAS;
      e = st1_ptr->exp - EXP_BIAS;
      e = st1_ptr->exp - EXP_BIAS;
      convert_l2reg(&e, st1_ptr);
      convert_l2reg(&e, st1_ptr);
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      char sign = st0_ptr->sign;
      char sign = st0_ptr->sign;
      if ( divide_by_zero(SIGN_NEG, st0_ptr) )
      if ( divide_by_zero(SIGN_NEG, st0_ptr) )
        return;
        return;
      push();
      push();
      reg_move(&CONST_Z, st_new_ptr);
      reg_move(&CONST_Z, st_new_ptr);
      st_new_ptr->sign = sign;
      st_new_ptr->sign = sign;
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      char sign = st0_ptr->sign;
      char sign = st0_ptr->sign;
      st0_ptr->sign = SIGN_POS;
      st0_ptr->sign = SIGN_POS;
      push();
      push();
      reg_move(&CONST_INF, st_new_ptr);
      reg_move(&CONST_INF, st_new_ptr);
      st_new_ptr->sign = sign;
      st_new_ptr->sign = sign;
      return;
      return;
    }
    }
  else if ( st0_tag == TW_NaN )
  else if ( st0_tag == TW_NaN )
    {
    {
      if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
      if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
        return;
        return;
      push();
      push();
      reg_move(st1_ptr, st_new_ptr);
      reg_move(st1_ptr, st_new_ptr);
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Empty )
  else if ( st0_tag == TW_Empty )
    {
    {
      /* Is this the correct behaviour? */
      /* Is this the correct behaviour? */
      if ( control_word & EX_Invalid )
      if ( control_word & EX_Invalid )
        {
        {
          stack_underflow();
          stack_underflow();
          push();
          push();
          stack_underflow();
          stack_underflow();
        }
        }
      else
      else
        EXCEPTION(EX_StackUnder);
        EXCEPTION(EX_StackUnder);
    }
    }
#ifdef PARANOID
#ifdef PARANOID
  else
  else
    EXCEPTION(EX_INTERNAL | 0x119);
    EXCEPTION(EX_INTERNAL | 0x119);
#endif PARANOID
#endif PARANOID
}
}
 
 
 
 
static void fdecstp(FPU_REG *st0_ptr)
static void fdecstp(FPU_REG *st0_ptr)
{
{
  clear_C1();
  clear_C1();
  top--;  /* st0_ptr will be fixed in math_emulate() before the next instr */
  top--;  /* st0_ptr will be fixed in math_emulate() before the next instr */
}
}
 
 
static void fincstp(FPU_REG *st0_ptr)
static void fincstp(FPU_REG *st0_ptr)
{
{
  clear_C1();
  clear_C1();
  top++;  /* st0_ptr will be fixed in math_emulate() before the next instr */
  top++;  /* st0_ptr will be fixed in math_emulate() before the next instr */
}
}
 
 
 
 
static void fsqrt_(FPU_REG *st0_ptr)
static void fsqrt_(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
 
 
  clear_C1();
  clear_C1();
  if ( !(st0_tag ^ TW_Valid) )
  if ( !(st0_tag ^ TW_Valid) )
    {
    {
      int expon;
      int expon;
 
 
      if (st0_ptr->sign == SIGN_NEG)
      if (st0_ptr->sign == SIGN_NEG)
        {
        {
          arith_invalid(st0_ptr);  /* sqrt(negative) is invalid */
          arith_invalid(st0_ptr);  /* sqrt(negative) is invalid */
          return;
          return;
        }
        }
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      expon = st0_ptr->exp - EXP_BIAS;
      expon = st0_ptr->exp - EXP_BIAS;
      st0_ptr->exp = EXP_BIAS + (expon & 1);  /* make st(0) in  [1.0 .. 4.0) */
      st0_ptr->exp = EXP_BIAS + (expon & 1);  /* make st(0) in  [1.0 .. 4.0) */
 
 
      wm_sqrt(st0_ptr, control_word);   /* Do the computation */
      wm_sqrt(st0_ptr, control_word);   /* Do the computation */
 
 
      st0_ptr->exp += expon >> 1;
      st0_ptr->exp += expon >> 1;
      st0_ptr->sign = SIGN_POS;
      st0_ptr->sign = SIGN_POS;
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    return;
    return;
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      if ( st0_ptr->sign == SIGN_NEG )
      if ( st0_ptr->sign == SIGN_NEG )
        arith_invalid(st0_ptr);  /* sqrt(-Infinity) is invalid */
        arith_invalid(st0_ptr);  /* sqrt(-Infinity) is invalid */
      return;
      return;
    }
    }
  else
  else
    { single_arg_error(st0_ptr); return; }
    { single_arg_error(st0_ptr); return; }
 
 
}
}
 
 
 
 
static void frndint_(FPU_REG *st0_ptr)
static void frndint_(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  int flags;
  int flags;
 
 
  if ( !(st0_tag ^ TW_Valid) )
  if ( !(st0_tag ^ TW_Valid) )
    {
    {
      if (st0_ptr->exp > EXP_BIAS+63)
      if (st0_ptr->exp > EXP_BIAS+63)
        return;
        return;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      /* Fortunately, this can't overflow to 2^64 */
      /* Fortunately, this can't overflow to 2^64 */
      if ( (flags = round_to_int(st0_ptr)) )
      if ( (flags = round_to_int(st0_ptr)) )
        set_precision_flag(flags);
        set_precision_flag(flags);
 
 
      st0_ptr->exp = EXP_BIAS + 63;
      st0_ptr->exp = EXP_BIAS + 63;
      normalize(st0_ptr);
      normalize(st0_ptr);
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
  else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
    return;
    return;
  else
  else
    single_arg_error(st0_ptr);
    single_arg_error(st0_ptr);
}
}
 
 
 
 
static void fsin(FPU_REG *st0_ptr)
static void fsin(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  char arg_sign = st0_ptr->sign;
  char arg_sign = st0_ptr->sign;
 
 
  if ( st0_tag == TW_Valid )
  if ( st0_tag == TW_Valid )
    {
    {
      FPU_REG rv;
      FPU_REG rv;
      int q;
      int q;
 
 
      if ( st0_ptr->exp > EXP_BIAS - 40 )
      if ( st0_ptr->exp > EXP_BIAS - 40 )
        {
        {
          st0_ptr->sign = SIGN_POS;
          st0_ptr->sign = SIGN_POS;
          if ( (q = trig_arg(st0_ptr, 0)) != -1 )
          if ( (q = trig_arg(st0_ptr, 0)) != -1 )
            {
            {
 
 
              poly_sine(st0_ptr, &rv);
              poly_sine(st0_ptr, &rv);
 
 
              if (q & 2)
              if (q & 2)
                rv.sign ^= SIGN_POS ^ SIGN_NEG;
                rv.sign ^= SIGN_POS ^ SIGN_NEG;
              rv.sign ^= arg_sign;
              rv.sign ^= arg_sign;
              reg_move(&rv, st0_ptr);
              reg_move(&rv, st0_ptr);
 
 
              /* We do not really know if up or down */
              /* We do not really know if up or down */
              set_precision_flag_up();
              set_precision_flag_up();
              return;
              return;
            }
            }
          else
          else
            {
            {
              /* Operand is out of range */
              /* Operand is out of range */
              st0_ptr->sign = arg_sign;         /* restore st(0) */
              st0_ptr->sign = arg_sign;         /* restore st(0) */
              return;
              return;
            }
            }
        }
        }
      else
      else
        {
        {
          /* For a small arg, the result == the argument */
          /* For a small arg, the result == the argument */
          /* Underflow may happen */
          /* Underflow may happen */
 
 
          if ( st0_ptr->exp <= EXP_UNDER )
          if ( st0_ptr->exp <= EXP_UNDER )
            {
            {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
              if ( denormal_operand() )
              if ( denormal_operand() )
                return;
                return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
              /* A denormal result has been produced.
              /* A denormal result has been produced.
                 Precision must have been lost, this is always
                 Precision must have been lost, this is always
                 an underflow. */
                 an underflow. */
              arith_underflow(st0_ptr);
              arith_underflow(st0_ptr);
              return;
              return;
            }
            }
 
 
          set_precision_flag_up();  /* Must be up. */
          set_precision_flag_up();  /* Must be up. */
        }
        }
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      setcc(0);
      setcc(0);
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      /* The 80486 treats infinity as an invalid operand */
      /* The 80486 treats infinity as an invalid operand */
      arith_invalid(st0_ptr);
      arith_invalid(st0_ptr);
      return;
      return;
    }
    }
  else
  else
    single_arg_error(st0_ptr);
    single_arg_error(st0_ptr);
}
}
 
 
 
 
static int f_cos(FPU_REG *arg)
static int f_cos(FPU_REG *arg)
{
{
  char arg_sign = arg->sign;
  char arg_sign = arg->sign;
 
 
  if ( arg->tag == TW_Valid )
  if ( arg->tag == TW_Valid )
    {
    {
      FPU_REG rv;
      FPU_REG rv;
      int q;
      int q;
 
 
      if ( arg->exp > EXP_BIAS - 40 )
      if ( arg->exp > EXP_BIAS - 40 )
        {
        {
          arg->sign = SIGN_POS;
          arg->sign = SIGN_POS;
          if ( (arg->exp < EXP_BIAS)
          if ( (arg->exp < EXP_BIAS)
              || ((arg->exp == EXP_BIAS)
              || ((arg->exp == EXP_BIAS)
                  && (significand(arg) <= 0xc90fdaa22168c234LL)) )
                  && (significand(arg) <= 0xc90fdaa22168c234LL)) )
            {
            {
              poly_cos(arg, &rv);
              poly_cos(arg, &rv);
              reg_move(&rv, arg);
              reg_move(&rv, arg);
 
 
              /* We do not really know if up or down */
              /* We do not really know if up or down */
              set_precision_flag_down();
              set_precision_flag_down();
 
 
              return 0;
              return 0;
            }
            }
          else if ( (q = trig_arg(arg, FCOS)) != -1 )
          else if ( (q = trig_arg(arg, FCOS)) != -1 )
            {
            {
              poly_sine(arg, &rv);
              poly_sine(arg, &rv);
 
 
              if ((q+1) & 2)
              if ((q+1) & 2)
                rv.sign ^= SIGN_POS ^ SIGN_NEG;
                rv.sign ^= SIGN_POS ^ SIGN_NEG;
              reg_move(&rv, arg);
              reg_move(&rv, arg);
 
 
              /* We do not really know if up or down */
              /* We do not really know if up or down */
              set_precision_flag_down();
              set_precision_flag_down();
 
 
              return 0;
              return 0;
            }
            }
          else
          else
            {
            {
              /* Operand is out of range */
              /* Operand is out of range */
              arg->sign = arg_sign;         /* restore st(0) */
              arg->sign = arg_sign;         /* restore st(0) */
              return 1;
              return 1;
            }
            }
        }
        }
      else
      else
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
            return 1;
            return 1;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          setcc(0);
          setcc(0);
          reg_move(&CONST_1, arg);
          reg_move(&CONST_1, arg);
#ifdef PECULIAR_486
#ifdef PECULIAR_486
          set_precision_flag_down();  /* 80486 appears to do this. */
          set_precision_flag_down();  /* 80486 appears to do this. */
#else
#else
          set_precision_flag_up();  /* Must be up. */
          set_precision_flag_up();  /* Must be up. */
#endif PECULIAR_486
#endif PECULIAR_486
          return 0;
          return 0;
        }
        }
    }
    }
  else if ( arg->tag == TW_Zero )
  else if ( arg->tag == TW_Zero )
    {
    {
      reg_move(&CONST_1, arg);
      reg_move(&CONST_1, arg);
      setcc(0);
      setcc(0);
      return 0;
      return 0;
    }
    }
  else if ( arg->tag == TW_Infinity )
  else if ( arg->tag == TW_Infinity )
    {
    {
      /* The 80486 treats infinity as an invalid operand */
      /* The 80486 treats infinity as an invalid operand */
      arith_invalid(arg);
      arith_invalid(arg);
      return 1;
      return 1;
    }
    }
  else
  else
    {
    {
      single_arg_error(arg);  /* requires arg == &st(0) */
      single_arg_error(arg);  /* requires arg == &st(0) */
      return 1;
      return 1;
    }
    }
}
}
 
 
 
 
static void fcos(FPU_REG *st0_ptr)
static void fcos(FPU_REG *st0_ptr)
{
{
  f_cos(st0_ptr);
  f_cos(st0_ptr);
}
}
 
 
 
 
static void fsincos(FPU_REG *st0_ptr)
static void fsincos(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st_new_ptr;
  FPU_REG *st_new_ptr;
  FPU_REG arg;
  FPU_REG arg;
 
 
  /* Stack underflow has higher priority */
  /* Stack underflow has higher priority */
  if ( st0_tag == TW_Empty )
  if ( st0_tag == TW_Empty )
    {
    {
      stack_underflow();  /* Puts a QNaN in st(0) */
      stack_underflow();  /* Puts a QNaN in st(0) */
      if ( control_word & CW_Invalid )
      if ( control_word & CW_Invalid )
        {
        {
          st_new_ptr = &st(-1);
          st_new_ptr = &st(-1);
          push();
          push();
          stack_underflow();  /* Puts a QNaN in the new st(0) */
          stack_underflow();  /* Puts a QNaN in the new st(0) */
        }
        }
      return;
      return;
    }
    }
 
 
  if ( STACK_OVERFLOW )
  if ( STACK_OVERFLOW )
    { stack_overflow(); return; }
    { stack_overflow(); return; }
 
 
  if ( st0_tag == TW_NaN )
  if ( st0_tag == TW_NaN )
    {
    {
      single_arg_2_error(st0_ptr);
      single_arg_2_error(st0_ptr);
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      /* The 80486 treats infinity as an invalid operand */
      /* The 80486 treats infinity as an invalid operand */
      if ( !arith_invalid(st0_ptr) )
      if ( !arith_invalid(st0_ptr) )
        {
        {
          /* unmasked response */
          /* unmasked response */
          push();
          push();
          arith_invalid(st_new_ptr);
          arith_invalid(st_new_ptr);
        }
        }
      return;
      return;
    }
    }
 
 
  reg_move(st0_ptr,&arg);
  reg_move(st0_ptr,&arg);
  if ( !f_cos(&arg) )
  if ( !f_cos(&arg) )
    {
    {
      fsin(st0_ptr);
      fsin(st0_ptr);
      push();
      push();
      reg_move(&arg,st_new_ptr);
      reg_move(&arg,st_new_ptr);
    }
    }
 
 
}
}
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
/* The following all require two arguments: st(0) and st(1) */
/* The following all require two arguments: st(0) and st(1) */
 
 
/* A lean, mean kernel for the fprem instructions. This relies upon
/* A lean, mean kernel for the fprem instructions. This relies upon
   the division and rounding to an integer in do_fprem giving an
   the division and rounding to an integer in do_fprem giving an
   exact result. Because of this, rem_kernel() needs to deal only with
   exact result. Because of this, rem_kernel() needs to deal only with
   the least significant 64 bits, the more significant bits of the
   the least significant 64 bits, the more significant bits of the
   result must be zero.
   result must be zero.
 */
 */
static void rem_kernel(unsigned long long st0, unsigned long long *y,
static void rem_kernel(unsigned long long st0, unsigned long long *y,
                       unsigned long long st1,
                       unsigned long long st1,
                       unsigned long long q, int n)
                       unsigned long long q, int n)
{
{
  unsigned long long x;
  unsigned long long x;
 
 
  x = st0 << n;
  x = st0 << n;
 
 
  /* Do the required multiplication and subtraction in the one operation */
  /* Do the required multiplication and subtraction in the one operation */
  asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
  asm volatile ("movl %2,%%eax; mull %4; subl %%eax,%0; sbbl %%edx,%1;
                 movl %3,%%eax; mull %4; subl %%eax,%1;
                 movl %3,%%eax; mull %4; subl %%eax,%1;
                 movl %2,%%eax; mull %5; subl %%eax,%1;"
                 movl %2,%%eax; mull %5; subl %%eax,%1;"
                :"=m" (x), "=m" (((unsigned *)&x)[1])
                :"=m" (x), "=m" (((unsigned *)&x)[1])
                :"m" (st1),"m" (((unsigned *)&st1)[1]),
                :"m" (st1),"m" (((unsigned *)&st1)[1]),
                 "m" (q),"m" (((unsigned *)&q)[1])
                 "m" (q),"m" (((unsigned *)&q)[1])
                :"%ax","%dx");
                :"%ax","%dx");
 
 
  *y = x;
  *y = x;
}
}
 
 
 
 
/* Remainder of st(0) / st(1) */
/* Remainder of st(0) / st(1) */
/* This routine produces exact results, i.e. there is never any
/* This routine produces exact results, i.e. there is never any
   rounding or truncation, etc of the result. */
   rounding or truncation, etc of the result. */
static void do_fprem(FPU_REG *st0_ptr, int round)
static void do_fprem(FPU_REG *st0_ptr, int round)
{
{
  FPU_REG *st1_ptr = &st(1);
  FPU_REG *st1_ptr = &st(1);
  char st1_tag = st1_ptr->tag;
  char st1_tag = st1_ptr->tag;
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  char sign = st0_ptr->sign;
  char sign = st0_ptr->sign;
 
 
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
    {
    {
      FPU_REG tmp;
      FPU_REG tmp;
      int old_cw = control_word;
      int old_cw = control_word;
      int expdif = st0_ptr->exp - st1_ptr->exp;
      int expdif = st0_ptr->exp - st1_ptr->exp;
      long long q;
      long long q;
      unsigned short saved_status;
      unsigned short saved_status;
      int cc = 0;
      int cc = 0;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      /* We want the status following the denorm tests, but don't want
      /* We want the status following the denorm tests, but don't want
         the status changed by the arithmetic operations. */
         the status changed by the arithmetic operations. */
      saved_status = partial_status;
      saved_status = partial_status;
      control_word &= ~CW_RC;
      control_word &= ~CW_RC;
      control_word |= RC_CHOP;
      control_word |= RC_CHOP;
 
 
      if (expdif < 64)
      if (expdif < 64)
        {
        {
          /* This should be the most common case */
          /* This should be the most common case */
 
 
          if ( expdif > -2 )
          if ( expdif > -2 )
            {
            {
              reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
              reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
 
 
              if ( tmp.exp >= EXP_BIAS )
              if ( tmp.exp >= EXP_BIAS )
                {
                {
                  round_to_int(&tmp);  /* Fortunately, this can't overflow
                  round_to_int(&tmp);  /* Fortunately, this can't overflow
                                          to 2^64 */
                                          to 2^64 */
                  q = significand(&tmp);
                  q = significand(&tmp);
 
 
                  rem_kernel(significand(st0_ptr),
                  rem_kernel(significand(st0_ptr),
                             &significand(&tmp),
                             &significand(&tmp),
                             significand(st1_ptr),
                             significand(st1_ptr),
                             q, expdif);
                             q, expdif);
 
 
                  tmp.exp = st1_ptr->exp;
                  tmp.exp = st1_ptr->exp;
                }
                }
              else
              else
                {
                {
                  reg_move(st0_ptr, &tmp);
                  reg_move(st0_ptr, &tmp);
                  q = 0;
                  q = 0;
                }
                }
              tmp.sign = sign;
              tmp.sign = sign;
 
 
              if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
              if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
                {
                {
                  /* We may need to subtract st(1) once more,
                  /* We may need to subtract st(1) once more,
                     to get a result <= 1/2 of st(1). */
                     to get a result <= 1/2 of st(1). */
                  unsigned long long x;
                  unsigned long long x;
                  expdif = st1_ptr->exp - tmp.exp;
                  expdif = st1_ptr->exp - tmp.exp;
                  if ( expdif <= 1 )
                  if ( expdif <= 1 )
                    {
                    {
                      if ( expdif == 0 )
                      if ( expdif == 0 )
                        x = significand(st1_ptr) - significand(&tmp);
                        x = significand(st1_ptr) - significand(&tmp);
                      else /* expdif is 1 */
                      else /* expdif is 1 */
                        x = (significand(st1_ptr) << 1) - significand(&tmp);
                        x = (significand(st1_ptr) << 1) - significand(&tmp);
                      if ( (x < significand(&tmp)) ||
                      if ( (x < significand(&tmp)) ||
                          /* or equi-distant (from 0 & st(1)) and q is odd */
                          /* or equi-distant (from 0 & st(1)) and q is odd */
                          ((x == significand(&tmp)) && (q & 1) ) )
                          ((x == significand(&tmp)) && (q & 1) ) )
                        {
                        {
                          tmp.sign ^= (SIGN_POS^SIGN_NEG);
                          tmp.sign ^= (SIGN_POS^SIGN_NEG);
                          significand(&tmp) = x;
                          significand(&tmp) = x;
                          q++;
                          q++;
                        }
                        }
                    }
                    }
                }
                }
 
 
              if (q & 4) cc |= SW_C0;
              if (q & 4) cc |= SW_C0;
              if (q & 2) cc |= SW_C3;
              if (q & 2) cc |= SW_C3;
              if (q & 1) cc |= SW_C1;
              if (q & 1) cc |= SW_C1;
            }
            }
          else
          else
            {
            {
              control_word = old_cw;
              control_word = old_cw;
              setcc(0);
              setcc(0);
              return;
              return;
            }
            }
        }
        }
      else
      else
        {
        {
          /* There is a large exponent difference ( >= 64 ) */
          /* There is a large exponent difference ( >= 64 ) */
          /* To make much sense, the code in this section should
          /* To make much sense, the code in this section should
             be done at high precision. */
             be done at high precision. */
          int exp_1;
          int exp_1;
 
 
          /* prevent overflow here */
          /* prevent overflow here */
          /* N is 'a number between 32 and 63' (p26-113) */
          /* N is 'a number between 32 and 63' (p26-113) */
          reg_move(st0_ptr, &tmp);
          reg_move(st0_ptr, &tmp);
          tmp.exp = EXP_BIAS + 56;
          tmp.exp = EXP_BIAS + 56;
          exp_1 = st1_ptr->exp;      st1_ptr->exp = EXP_BIAS;
          exp_1 = st1_ptr->exp;      st1_ptr->exp = EXP_BIAS;
          expdif -= 56;
          expdif -= 56;
 
 
          reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
          reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
          st1_ptr->exp = exp_1;
          st1_ptr->exp = exp_1;
 
 
          round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
          round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
 
 
          rem_kernel(significand(st0_ptr),
          rem_kernel(significand(st0_ptr),
                     &significand(&tmp),
                     &significand(&tmp),
                     significand(st1_ptr),
                     significand(st1_ptr),
                     significand(&tmp),
                     significand(&tmp),
                     tmp.exp - EXP_BIAS
                     tmp.exp - EXP_BIAS
                     );
                     );
          tmp.exp = exp_1 + expdif;
          tmp.exp = exp_1 + expdif;
          tmp.sign = sign;
          tmp.sign = sign;
 
 
          /* It is possible for the operation to be complete here.
          /* It is possible for the operation to be complete here.
             What does the IEEE standard say? The Intel 80486 manual
             What does the IEEE standard say? The Intel 80486 manual
             implies that the operation will never be completed at this
             implies that the operation will never be completed at this
             point, and the behaviour of a real 80486 confirms this.
             point, and the behaviour of a real 80486 confirms this.
           */
           */
          if ( !(tmp.sigh | tmp.sigl) )
          if ( !(tmp.sigh | tmp.sigl) )
            {
            {
              /* The result is zero */
              /* The result is zero */
              control_word = old_cw;
              control_word = old_cw;
              partial_status = saved_status;
              partial_status = saved_status;
              reg_move(&CONST_Z, st0_ptr);
              reg_move(&CONST_Z, st0_ptr);
              st0_ptr->sign = sign;
              st0_ptr->sign = sign;
#ifdef PECULIAR_486
#ifdef PECULIAR_486
              setcc(SW_C2);
              setcc(SW_C2);
#else
#else
              setcc(0);
              setcc(0);
#endif PECULIAR_486
#endif PECULIAR_486
              return;
              return;
            }
            }
          cc = SW_C2;
          cc = SW_C2;
        }
        }
 
 
      control_word = old_cw;
      control_word = old_cw;
      partial_status = saved_status;
      partial_status = saved_status;
      normalize_nuo(&tmp);
      normalize_nuo(&tmp);
      reg_move(&tmp, st0_ptr);
      reg_move(&tmp, st0_ptr);
      setcc(cc);
      setcc(cc);
 
 
      /* The only condition to be looked for is underflow,
      /* The only condition to be looked for is underflow,
         and it can occur here only if underflow is unmasked. */
         and it can occur here only if underflow is unmasked. */
      if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
      if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
          && !(control_word & CW_Underflow) )
          && !(control_word & CW_Underflow) )
        arith_underflow(st0_ptr);
        arith_underflow(st0_ptr);
 
 
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
    {
    {
      stack_underflow();
      stack_underflow();
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      if ( st1_tag == TW_Valid )
      if ( st1_tag == TW_Valid )
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          setcc(0); return;
          setcc(0); return;
        }
        }
      else if ( st1_tag == TW_Zero )
      else if ( st1_tag == TW_Zero )
        { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
        { arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
      else if ( st1_tag == TW_Infinity )
      else if ( st1_tag == TW_Infinity )
        { setcc(0); return; }
        { setcc(0); return; }
    }
    }
  else if ( st0_tag == TW_Valid )
  else if ( st0_tag == TW_Valid )
    {
    {
      if ( st1_tag == TW_Zero )
      if ( st1_tag == TW_Zero )
        {
        {
          arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
          arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
          return;
          return;
        }
        }
      else if ( st1_tag != TW_NaN )
      else if ( st1_tag != TW_NaN )
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          if ( st1_tag == TW_Infinity )
          if ( st1_tag == TW_Infinity )
            {
            {
              /* fprem(Valid,Infinity) is o.k. */
              /* fprem(Valid,Infinity) is o.k. */
              setcc(0); return;
              setcc(0); return;
            }
            }
        }
        }
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      if ( st1_tag != TW_NaN )
      if ( st1_tag != TW_NaN )
        {
        {
          arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
          arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
          return;
          return;
        }
        }
    }
    }
 
 
  /* One of the registers must contain a NaN is we got here. */
  /* One of the registers must contain a NaN is we got here. */
 
 
#ifdef PARANOID
#ifdef PARANOID
  if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
  if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
      EXCEPTION(EX_INTERNAL | 0x118);
      EXCEPTION(EX_INTERNAL | 0x118);
#endif PARANOID
#endif PARANOID
 
 
  real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
  real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
 
 
}
}
 
 
 
 
/* ST(1) <- ST(1) * log ST;  pop ST */
/* ST(1) <- ST(1) * log ST;  pop ST */
static void fyl2x(FPU_REG *st0_ptr)
static void fyl2x(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st1_ptr = &st(1), exponent;
  FPU_REG *st1_ptr = &st(1), exponent;
  char st1_tag = st1_ptr->tag;
  char st1_tag = st1_ptr->tag;
  int e;
  int e;
 
 
  clear_C1();
  clear_C1();
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
    {
    {
      if ( st0_ptr->sign == SIGN_POS )
      if ( st0_ptr->sign == SIGN_POS )
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( ((st0_ptr->exp <= EXP_UNDER) ||
          if ( ((st0_ptr->exp <= EXP_UNDER) ||
                (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
                (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
          if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
            {
            {
              /* Special case. The result can be precise. */
              /* Special case. The result can be precise. */
              e = st0_ptr->exp - EXP_BIAS;
              e = st0_ptr->exp - EXP_BIAS;
              if ( e > 0 )
              if ( e > 0 )
                {
                {
                  exponent.sigh = e;
                  exponent.sigh = e;
                  exponent.sign = SIGN_POS;
                  exponent.sign = SIGN_POS;
                }
                }
              else
              else
                {
                {
                  exponent.sigh = -e;
                  exponent.sigh = -e;
                  exponent.sign = SIGN_NEG;
                  exponent.sign = SIGN_NEG;
                }
                }
              exponent.sigl = 0;
              exponent.sigl = 0;
              exponent.exp = EXP_BIAS + 31;
              exponent.exp = EXP_BIAS + 31;
              exponent.tag = TW_Valid;
              exponent.tag = TW_Valid;
              normalize_nuo(&exponent);
              normalize_nuo(&exponent);
              reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
              reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
            }
            }
          else
          else
            {
            {
              /* The usual case */
              /* The usual case */
              poly_l2(st0_ptr, st1_ptr, st1_ptr);
              poly_l2(st0_ptr, st1_ptr, st1_ptr);
              if ( st1_ptr->exp <= EXP_UNDER )
              if ( st1_ptr->exp <= EXP_UNDER )
                {
                {
                  /* A denormal result has been produced.
                  /* A denormal result has been produced.
                     Precision must have been lost, this is always
                     Precision must have been lost, this is always
                     an underflow. */
                     an underflow. */
                  arith_underflow(st1_ptr);
                  arith_underflow(st1_ptr);
                }
                }
              else
              else
                set_precision_flag_up();  /* 80486 appears to always do this */
                set_precision_flag_up();  /* 80486 appears to always do this */
            }
            }
          pop();
          pop();
          return;
          return;
        }
        }
      else
      else
        {
        {
          /* negative */
          /* negative */
          if ( !arith_invalid(st1_ptr) )
          if ( !arith_invalid(st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
    }
    }
  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
    {
    {
      stack_underflow_pop(1);
      stack_underflow_pop(1);
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
    {
    {
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
        pop();
        pop();
      return;
      return;
    }
    }
  else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
  else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
    {
    {
      /* one of the args is zero, the other valid, or both zero */
      /* one of the args is zero, the other valid, or both zero */
      if ( st0_tag == TW_Zero )
      if ( st0_tag == TW_Zero )
        {
        {
          if ( st1_tag == TW_Zero )
          if ( st1_tag == TW_Zero )
            {
            {
              /* Both args zero is invalid */
              /* Both args zero is invalid */
              if ( !arith_invalid(st1_ptr) )
              if ( !arith_invalid(st1_ptr) )
                pop();
                pop();
            }
            }
#ifdef PECULIAR_486
#ifdef PECULIAR_486
          /* This case is not specifically covered in the manual,
          /* This case is not specifically covered in the manual,
             but divide-by-zero would seem to be the best response.
             but divide-by-zero would seem to be the best response.
             However, a real 80486 does it this way... */
             However, a real 80486 does it this way... */
          else if ( st0_ptr->tag == TW_Infinity )
          else if ( st0_ptr->tag == TW_Infinity )
            {
            {
              reg_move(&CONST_INF, st1_ptr);
              reg_move(&CONST_INF, st1_ptr);
              pop();
              pop();
            }
            }
#endif PECULIAR_486
#endif PECULIAR_486
          else
          else
            {
            {
              if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
              if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
                pop();
                pop();
            }
            }
          return;
          return;
        }
        }
      else
      else
        {
        {
          /* st(1) contains zero, st(0) valid <> 0 */
          /* st(1) contains zero, st(0) valid <> 0 */
          /* Zero is the valid answer */
          /* Zero is the valid answer */
          char sign = st1_ptr->sign;
          char sign = st1_ptr->sign;
 
 
          if ( st0_ptr->sign == SIGN_NEG )
          if ( st0_ptr->sign == SIGN_NEG )
            {
            {
              /* log(negative) */
              /* log(negative) */
              if ( !arith_invalid(st1_ptr) )
              if ( !arith_invalid(st1_ptr) )
                pop();
                pop();
              return;
              return;
            }
            }
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
          if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
          pop(); st0_ptr = &st(0);
          pop(); st0_ptr = &st(0);
          reg_move(&CONST_Z, st0_ptr);
          reg_move(&CONST_Z, st0_ptr);
          st0_ptr->sign = sign;
          st0_ptr->sign = sign;
          return;
          return;
        }
        }
    }
    }
  /* One or both arg must be an infinity */
  /* One or both arg must be an infinity */
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
      if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
        {
        {
          /* log(-infinity) or 0*log(infinity) */
          /* log(-infinity) or 0*log(infinity) */
          if ( !arith_invalid(st1_ptr) )
          if ( !arith_invalid(st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
      else
      else
        {
        {
          char sign = st1_ptr->sign;
          char sign = st1_ptr->sign;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          pop(); st0_ptr = &st(0);
          pop(); st0_ptr = &st(0);
          reg_move(&CONST_INF, st0_ptr);
          reg_move(&CONST_INF, st0_ptr);
          st0_ptr->sign = sign;
          st0_ptr->sign = sign;
          return;
          return;
        }
        }
    }
    }
  /* st(1) must be infinity here */
  /* st(1) must be infinity here */
  else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
  else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
    {
    {
      if ( st0_ptr->exp >= EXP_BIAS )
      if ( st0_ptr->exp >= EXP_BIAS )
        {
        {
          if ( (st0_ptr->exp == EXP_BIAS) &&
          if ( (st0_ptr->exp == EXP_BIAS) &&
              (st0_ptr->sigh == 0x80000000) &&
              (st0_ptr->sigh == 0x80000000) &&
              (st0_ptr->sigl == 0) )
              (st0_ptr->sigl == 0) )
            {
            {
              /* st(0) holds 1.0 */
              /* st(0) holds 1.0 */
              /* infinity*log(1) */
              /* infinity*log(1) */
              if ( !arith_invalid(st1_ptr) )
              if ( !arith_invalid(st1_ptr) )
                pop();
                pop();
              return;
              return;
            }
            }
          /* st(0) is positive and > 1.0 */
          /* st(0) is positive and > 1.0 */
          pop();
          pop();
        }
        }
      else
      else
        {
        {
          /* st(0) is positive and < 1.0 */
          /* st(0) is positive and < 1.0 */
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          st1_ptr->sign ^= SIGN_NEG;
          st1_ptr->sign ^= SIGN_NEG;
          pop();
          pop();
        }
        }
      return;
      return;
    }
    }
  else
  else
    {
    {
      /* st(0) must be zero or negative */
      /* st(0) must be zero or negative */
      if ( st0_ptr->tag == TW_Zero )
      if ( st0_ptr->tag == TW_Zero )
        {
        {
          /* This should be invalid, but a real 80486 is happy with it. */
          /* This should be invalid, but a real 80486 is happy with it. */
#ifndef PECULIAR_486
#ifndef PECULIAR_486
          if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
          if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
#endif PECULIAR_486
#endif PECULIAR_486
            {
            {
              st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
              st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
              pop();
              pop();
            }
            }
        }
        }
      else
      else
        {
        {
          /* log(negative) */
          /* log(negative) */
          if ( !arith_invalid(st1_ptr) )
          if ( !arith_invalid(st1_ptr) )
            pop();
            pop();
        }
        }
      return;
      return;
    }
    }
}
}
 
 
 
 
static void fpatan(FPU_REG *st0_ptr)
static void fpatan(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st1_ptr = &st(1);
  FPU_REG *st1_ptr = &st(1);
  char st1_tag = st1_ptr->tag;
  char st1_tag = st1_ptr->tag;
 
 
  clear_C1();
  clear_C1();
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
    {
    {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      poly_atan(st0_ptr, st1_ptr, st1_ptr);
      poly_atan(st0_ptr, st1_ptr, st1_ptr);
 
 
      if ( st1_ptr->exp <= EXP_UNDER )
      if ( st1_ptr->exp <= EXP_UNDER )
        {
        {
          /* A denormal result has been produced.
          /* A denormal result has been produced.
             Precision must have been lost.
             Precision must have been lost.
             This is by definition an underflow. */
             This is by definition an underflow. */
          arith_underflow(st1_ptr);
          arith_underflow(st1_ptr);
          pop();
          pop();
          return;
          return;
        }
        }
    }
    }
  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
    {
    {
      stack_underflow_pop(1);
      stack_underflow_pop(1);
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
    {
    {
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
          pop();
          pop();
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
  else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
    {
    {
      char sign = st1_ptr->sign;
      char sign = st1_ptr->sign;
      if ( st0_tag == TW_Infinity )
      if ( st0_tag == TW_Infinity )
        {
        {
          if ( st1_tag == TW_Infinity )
          if ( st1_tag == TW_Infinity )
            {
            {
              if ( st0_ptr->sign == SIGN_POS )
              if ( st0_ptr->sign == SIGN_POS )
                { reg_move(&CONST_PI4, st1_ptr); }
                { reg_move(&CONST_PI4, st1_ptr); }
              else
              else
                reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
                reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
            }
            }
          else
          else
            {
            {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
              if ( st1_tag != TW_Zero )
              if ( st1_tag != TW_Zero )
                {
                {
                  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
                  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
                    return;
                    return;
                }
                }
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
              if ( st0_ptr->sign == SIGN_POS )
              if ( st0_ptr->sign == SIGN_POS )
                {
                {
                  reg_move(&CONST_Z, st1_ptr);
                  reg_move(&CONST_Z, st1_ptr);
                  st1_ptr->sign = sign;   /* An 80486 preserves the sign */
                  st1_ptr->sign = sign;   /* An 80486 preserves the sign */
                  pop();
                  pop();
                  return;
                  return;
                }
                }
              else
              else
                reg_move(&CONST_PI, st1_ptr);
                reg_move(&CONST_PI, st1_ptr);
            }
            }
        }
        }
      else
      else
        {
        {
          /* st(1) is infinity, st(0) not infinity */
          /* st(1) is infinity, st(0) not infinity */
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( st0_tag != TW_Zero )
          if ( st0_tag != TW_Zero )
            {
            {
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
                return;
                return;
            }
            }
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          reg_move(&CONST_PI2, st1_ptr);
          reg_move(&CONST_PI2, st1_ptr);
        }
        }
      st1_ptr->sign = sign;
      st1_ptr->sign = sign;
    }
    }
  else if ( st1_tag == TW_Zero )
  else if ( st1_tag == TW_Zero )
    {
    {
      /* st(0) must be valid or zero */
      /* st(0) must be valid or zero */
      char sign = st1_ptr->sign;
      char sign = st1_ptr->sign;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( st0_tag != TW_Zero )
      if ( st0_tag != TW_Zero )
        {
        {
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
        }
        }
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      if ( st0_ptr->sign == SIGN_POS )
      if ( st0_ptr->sign == SIGN_POS )
        { /* An 80486 preserves the sign */ pop(); return; }
        { /* An 80486 preserves the sign */ pop(); return; }
      else
      else
        reg_move(&CONST_PI, st1_ptr);
        reg_move(&CONST_PI, st1_ptr);
      st1_ptr->sign = sign;
      st1_ptr->sign = sign;
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      /* st(1) must be TW_Valid here */
      /* st(1) must be TW_Valid here */
      char sign = st1_ptr->sign;
      char sign = st1_ptr->sign;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      reg_move(&CONST_PI2, st1_ptr);
      reg_move(&CONST_PI2, st1_ptr);
      st1_ptr->sign = sign;
      st1_ptr->sign = sign;
    }
    }
#ifdef PARANOID
#ifdef PARANOID
  else
  else
    EXCEPTION(EX_INTERNAL | 0x125);
    EXCEPTION(EX_INTERNAL | 0x125);
#endif PARANOID
#endif PARANOID
 
 
  pop();
  pop();
  set_precision_flag_up();  /* We do not really know if up or down */
  set_precision_flag_up();  /* We do not really know if up or down */
}
}
 
 
 
 
static void fprem(FPU_REG *st0_ptr)
static void fprem(FPU_REG *st0_ptr)
{
{
  do_fprem(st0_ptr, RC_CHOP);
  do_fprem(st0_ptr, RC_CHOP);
}
}
 
 
 
 
static void fprem1(FPU_REG *st0_ptr)
static void fprem1(FPU_REG *st0_ptr)
{
{
  do_fprem(st0_ptr, RC_RND);
  do_fprem(st0_ptr, RC_RND);
}
}
 
 
 
 
static void fyl2xp1(FPU_REG *st0_ptr)
static void fyl2xp1(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag, sign;
  char st0_tag = st0_ptr->tag, sign;
  FPU_REG *st1_ptr = &st(1);
  FPU_REG *st1_ptr = &st(1);
  char st1_tag = st1_ptr->tag;
  char st1_tag = st1_ptr->tag;
 
 
  clear_C1();
  clear_C1();
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
    {
    {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
            (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
      if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
        {
        {
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
          st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
          st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
#else
#else
          if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
          if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
            return;
            return;
#endif PECULIAR_486
#endif PECULIAR_486
        }
        }
      if ( st1_ptr->exp <= EXP_UNDER )
      if ( st1_ptr->exp <= EXP_UNDER )
        {
        {
          /* A denormal result has been produced.
          /* A denormal result has been produced.
             Precision must have been lost, this is always
             Precision must have been lost, this is always
             an underflow. */
             an underflow. */
          sign = st1_ptr->sign;
          sign = st1_ptr->sign;
          arith_underflow(st1_ptr);
          arith_underflow(st1_ptr);
          st1_ptr->sign = sign;
          st1_ptr->sign = sign;
        }
        }
      else
      else
        set_precision_flag_up();   /* 80486 appears to always do this */
        set_precision_flag_up();   /* 80486 appears to always do this */
      pop();
      pop();
      return;
      return;
    }
    }
  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
    {
    {
      stack_underflow_pop(1);
      stack_underflow_pop(1);
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      if ( st1_tag <= TW_Zero )
      if ( st1_tag <= TW_Zero )
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
          if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
              (denormal_operand()) )
              (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          st0_ptr->sign ^= st1_ptr->sign;
          st0_ptr->sign ^= st1_ptr->sign;
          reg_move(st0_ptr, st1_ptr);
          reg_move(st0_ptr, st1_ptr);
        }
        }
      else if ( st1_tag == TW_Infinity )
      else if ( st1_tag == TW_Infinity )
        {
        {
          /* Infinity*log(1) */
          /* Infinity*log(1) */
          if ( !arith_invalid(st1_ptr) )
          if ( !arith_invalid(st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
      else if ( st1_tag == TW_NaN )
      else if ( st1_tag == TW_NaN )
        {
        {
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
#ifdef PARANOID
#ifdef PARANOID
      else
      else
        {
        {
          EXCEPTION(EX_INTERNAL | 0x116);
          EXCEPTION(EX_INTERNAL | 0x116);
          return;
          return;
        }
        }
#endif PARANOID
#endif PARANOID
      pop(); return;
      pop(); return;
    }
    }
  else if ( st0_tag == TW_Valid )
  else if ( st0_tag == TW_Valid )
    {
    {
      if ( st1_tag == TW_Zero )
      if ( st1_tag == TW_Zero )
        {
        {
          if ( st0_ptr->sign == SIGN_NEG )
          if ( st0_ptr->sign == SIGN_NEG )
            {
            {
              if ( st0_ptr->exp >= EXP_BIAS )
              if ( st0_ptr->exp >= EXP_BIAS )
                {
                {
                  /* st(0) holds <= -1.0 */
                  /* st(0) holds <= -1.0 */
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
                  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
                  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
#else
#else
                  if ( arith_invalid(st1_ptr) ) return;
                  if ( arith_invalid(st1_ptr) ) return;
#endif PECULIAR_486
#endif PECULIAR_486
                  pop(); return;
                  pop(); return;
                }
                }
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
                return;
                return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
              st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
              st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
              pop(); return;
              pop(); return;
            }
            }
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
          pop(); return;
          pop(); return;
        }
        }
      if ( st1_tag == TW_Infinity )
      if ( st1_tag == TW_Infinity )
        {
        {
          if ( st0_ptr->sign == SIGN_NEG )
          if ( st0_ptr->sign == SIGN_NEG )
            {
            {
              if ( (st0_ptr->exp >= EXP_BIAS) &&
              if ( (st0_ptr->exp >= EXP_BIAS) &&
                  !((st0_ptr->sigh == 0x80000000) &&
                  !((st0_ptr->sigh == 0x80000000) &&
                    (st0_ptr->sigl == 0)) )
                    (st0_ptr->sigl == 0)) )
                {
                {
                  /* st(0) holds < -1.0 */
                  /* st(0) holds < -1.0 */
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
                  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
                  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
#else
#else
                  if ( arith_invalid(st1_ptr) ) return;
                  if ( arith_invalid(st1_ptr) ) return;
#endif PECULIAR_486
#endif PECULIAR_486
                  pop(); return;
                  pop(); return;
                }
                }
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
              if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
                return;
                return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
              st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
              st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
              pop(); return;
              pop(); return;
            }
            }
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
          pop(); return;
          pop(); return;
        }
        }
      if ( st1_tag == TW_NaN )
      if ( st1_tag == TW_NaN )
        {
        {
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
    }
    }
  else if ( st0_tag == TW_NaN )
  else if ( st0_tag == TW_NaN )
    {
    {
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
        pop();
        pop();
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      if ( st1_tag == TW_NaN )
      if ( st1_tag == TW_NaN )
        {
        {
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
          if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
      else if ( st0_ptr->sign == SIGN_NEG )
      else if ( st0_ptr->sign == SIGN_NEG )
        {
        {
          int exponent = st1_ptr->exp;
          int exponent = st1_ptr->exp;
#ifndef PECULIAR_486
#ifndef PECULIAR_486
          /* This should have higher priority than denormals, but... */
          /* This should have higher priority than denormals, but... */
          if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
          if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
            return;
            return;
#endif PECULIAR_486
#endif PECULIAR_486
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( st1_tag != TW_Zero )
          if ( st1_tag != TW_Zero )
            {
            {
              if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
              if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
                return;
                return;
            }
            }
#endif DENORM_OPERAND
#endif DENORM_OPERAND
#ifdef PECULIAR_486
#ifdef PECULIAR_486
          /* Denormal operands actually get higher priority */
          /* Denormal operands actually get higher priority */
          if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
          if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
            return;
            return;
#endif PECULIAR_486
#endif PECULIAR_486
          pop();
          pop();
          return;
          return;
        }
        }
      else if ( st1_tag == TW_Zero )
      else if ( st1_tag == TW_Zero )
        {
        {
          /* log(infinity) */
          /* log(infinity) */
          if ( !arith_invalid(st1_ptr) )
          if ( !arith_invalid(st1_ptr) )
            pop();
            pop();
          return;
          return;
        }
        }
 
 
      /* st(1) must be valid here. */
      /* st(1) must be valid here. */
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      /* The Manual says that log(Infinity) is invalid, but a real
      /* The Manual says that log(Infinity) is invalid, but a real
         80486 sensibly says that it is o.k. */
         80486 sensibly says that it is o.k. */
      { char sign = st1_ptr->sign;
      { char sign = st1_ptr->sign;
        reg_move(&CONST_INF, st1_ptr);
        reg_move(&CONST_INF, st1_ptr);
        st1_ptr->sign = sign;
        st1_ptr->sign = sign;
      }
      }
      pop();
      pop();
      return;
      return;
    }
    }
#ifdef PARANOID
#ifdef PARANOID
  else
  else
    {
    {
      EXCEPTION(EX_INTERNAL | 0x117);
      EXCEPTION(EX_INTERNAL | 0x117);
    }
    }
#endif PARANOID
#endif PARANOID
}
}
 
 
 
 
static void fscale(FPU_REG *st0_ptr)
static void fscale(FPU_REG *st0_ptr)
{
{
  char st0_tag = st0_ptr->tag;
  char st0_tag = st0_ptr->tag;
  FPU_REG *st1_ptr = &st(1);
  FPU_REG *st1_ptr = &st(1);
  char st1_tag = st1_ptr->tag;
  char st1_tag = st1_ptr->tag;
  int old_cw = control_word;
  int old_cw = control_word;
  char sign = st0_ptr->sign;
  char sign = st0_ptr->sign;
 
 
  clear_C1();
  clear_C1();
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
    {
    {
      long scale;
      long scale;
      FPU_REG tmp;
      FPU_REG tmp;
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
      if ( ((st0_ptr->exp <= EXP_UNDER) ||
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
            (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
        return;
        return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
      if ( st1_ptr->exp > EXP_BIAS + 30 )
      if ( st1_ptr->exp > EXP_BIAS + 30 )
        {
        {
          /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
          /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
          char sign;
          char sign;
 
 
          if ( st1_ptr->sign == SIGN_POS )
          if ( st1_ptr->sign == SIGN_POS )
            {
            {
              EXCEPTION(EX_Overflow);
              EXCEPTION(EX_Overflow);
              sign = st0_ptr->sign;
              sign = st0_ptr->sign;
              reg_move(&CONST_INF, st0_ptr);
              reg_move(&CONST_INF, st0_ptr);
              st0_ptr->sign = sign;
              st0_ptr->sign = sign;
            }
            }
          else
          else
            {
            {
              EXCEPTION(EX_Underflow);
              EXCEPTION(EX_Underflow);
              sign = st0_ptr->sign;
              sign = st0_ptr->sign;
              reg_move(&CONST_Z, st0_ptr);
              reg_move(&CONST_Z, st0_ptr);
              st0_ptr->sign = sign;
              st0_ptr->sign = sign;
            }
            }
          return;
          return;
        }
        }
 
 
      control_word &= ~CW_RC;
      control_word &= ~CW_RC;
      control_word |= RC_CHOP;
      control_word |= RC_CHOP;
      reg_move(st1_ptr, &tmp);
      reg_move(st1_ptr, &tmp);
      round_to_int(&tmp);               /* This can never overflow here */
      round_to_int(&tmp);               /* This can never overflow here */
      control_word = old_cw;
      control_word = old_cw;
      scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
      scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
      scale += st0_ptr->exp;
      scale += st0_ptr->exp;
      st0_ptr->exp = scale;
      st0_ptr->exp = scale;
 
 
      /* Use round_reg() to properly detect under/overflow etc */
      /* Use round_reg() to properly detect under/overflow etc */
      round_reg(st0_ptr, 0, control_word);
      round_reg(st0_ptr, 0, control_word);
 
 
      return;
      return;
    }
    }
  else if ( st0_tag == TW_Valid )
  else if ( st0_tag == TW_Valid )
    {
    {
      if ( st1_tag == TW_Zero )
      if ( st1_tag == TW_Zero )
        {
        {
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          return;
          return;
        }
        }
      if ( st1_tag == TW_Infinity )
      if ( st1_tag == TW_Infinity )
        {
        {
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          if ( st1_ptr->sign == SIGN_POS )
          if ( st1_ptr->sign == SIGN_POS )
            { reg_move(&CONST_INF, st0_ptr); }
            { reg_move(&CONST_INF, st0_ptr); }
          else
          else
              reg_move(&CONST_Z, st0_ptr);
              reg_move(&CONST_Z, st0_ptr);
          st0_ptr->sign = sign;
          st0_ptr->sign = sign;
          return;
          return;
        }
        }
      if ( st1_tag == TW_NaN )
      if ( st1_tag == TW_NaN )
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
    }
    }
  else if ( st0_tag == TW_Zero )
  else if ( st0_tag == TW_Zero )
    {
    {
      if ( st1_tag == TW_Valid )
      if ( st1_tag == TW_Valid )
        {
        {
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          return;
          return;
        }
        }
      else if ( st1_tag == TW_Zero ) { return; }
      else if ( st1_tag == TW_Zero ) { return; }
      else if ( st1_tag == TW_Infinity )
      else if ( st1_tag == TW_Infinity )
        {
        {
          if ( st1_ptr->sign == SIGN_NEG )
          if ( st1_ptr->sign == SIGN_NEG )
            return;
            return;
          else
          else
            {
            {
              arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
              arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
              return;
              return;
            }
            }
        }
        }
      else if ( st1_tag == TW_NaN )
      else if ( st1_tag == TW_NaN )
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
    }
    }
  else if ( st0_tag == TW_Infinity )
  else if ( st0_tag == TW_Infinity )
    {
    {
      if ( st1_tag == TW_Valid )
      if ( st1_tag == TW_Valid )
        {
        {
 
 
#ifdef DENORM_OPERAND
#ifdef DENORM_OPERAND
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
          if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
            return;
            return;
#endif DENORM_OPERAND
#endif DENORM_OPERAND
 
 
          return;
          return;
        }
        }
      if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
      if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
          || (st1_tag == TW_Zero) )
          || (st1_tag == TW_Zero) )
        return;
        return;
      else if ( st1_tag == TW_Infinity )
      else if ( st1_tag == TW_Infinity )
        {
        {
          arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
          arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
          return;
          return;
        }
        }
      else if ( st1_tag == TW_NaN )
      else if ( st1_tag == TW_NaN )
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
    }
    }
  else if ( st0_tag == TW_NaN )
  else if ( st0_tag == TW_NaN )
    {
    {
      if ( st1_tag != TW_Empty )
      if ( st1_tag != TW_Empty )
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
        { real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
    }
    }
 
 
#ifdef PARANOID
#ifdef PARANOID
  if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
  if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
    {
    {
      EXCEPTION(EX_INTERNAL | 0x115);
      EXCEPTION(EX_INTERNAL | 0x115);
      return;
      return;
    }
    }
#endif
#endif
 
 
  /* At least one of st(0), st(1) must be empty */
  /* At least one of st(0), st(1) must be empty */
  stack_underflow();
  stack_underflow();
 
 
}
}
 
 
 
 
/*---------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------*/
 
 
static FUNC_ST0 const trig_table_a[] = {
static FUNC_ST0 const trig_table_a[] = {
  f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
  f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
};
};
 
 
void trig_a(void)
void trig_a(void)
{
{
  (trig_table_a[FPU_rm])(&st(0));
  (trig_table_a[FPU_rm])(&st(0));
}
}
 
 
 
 
static FUNC_ST0 const trig_table_b[] =
static FUNC_ST0 const trig_table_b[] =
  {
  {
    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
  };
  };
 
 
void trig_b(void)
void trig_b(void)
{
{
  (trig_table_b[FPU_rm])(&st(0));
  (trig_table_b[FPU_rm])(&st(0));
}
}
 
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.