patch-2.1.73 linux/arch/i386/math-emu/fpu_trig.c

Next file: linux/arch/i386/math-emu/get_address.c
Previous file: linux/arch/i386/math-emu/fpu_tags.c
Back to the patch index
Back to the overall index

diff -u --recursive --new-file v2.1.72/linux/arch/i386/math-emu/fpu_trig.c linux/arch/i386/math-emu/fpu_trig.c
@@ -3,9 +3,9 @@
  |                                                                           |
  | Implementation of the FPU "transcendental" functions.                     |
  |                                                                           |
- | Copyright (C) 1992,1993,1994                                              |
+ | Copyright (C) 1992,1993,1994,1997                                         |
  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
- |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
+ |                       Australia.  E-mail   billm@suburbia.net             |
  |                                                                           |
  |                                                                           |
  +---------------------------------------------------------------------------*/
@@ -17,7 +17,6 @@
 #include "control_w.h"
 #include "reg_constant.h"	
 
-
 static void rem_kernel(unsigned long long st0, unsigned long long *y,
 		       unsigned long long st1,
 		       unsigned long long q, int n);
@@ -25,9 +24,6 @@
 #define BETTER_THAN_486
 
 #define FCOS  4
-/* Not needed now with new code
-#define FPTAN 1
- */
 
 /* Used only by fptan, fsin, fcos, and fsincos. */
 /* This routine produces very accurate results, similar to
@@ -35,13 +31,15 @@
 /* Limited measurements show no results worse than 64 bit precision
    except for the results for arguments close to 2^63, where the
    precision of the result sometimes degrades to about 63.9 bits */
-static int trig_arg(FPU_REG *X, int even)
+static int trig_arg(FPU_REG *st0_ptr, int even)
 {
   FPU_REG tmp;
+  u_char tmptag;
   unsigned long long q;
   int old_cw = control_word, saved_status = partial_status;
+  int tag, st0_tag = TAG_Valid;
 
-  if ( X->exp >= EXP_BIAS + 63 )
+  if ( exponent(st0_ptr) >= 63 )
     {
       partial_status |= SW_C2;     /* Reduction incomplete. */
       return -1;
@@ -50,58 +48,52 @@
   control_word &= ~CW_RC;
   control_word |= RC_CHOP;
 
-  reg_div(X, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
-  round_to_int(&tmp);  /* Fortunately, this can't overflow
-			  to 2^64 */
+  setpositive(st0_ptr);
+  tag = FPU_u_div(st0_ptr, &CONST_PI2, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
+		  SIGN_POS);
+
+  FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't overflow
+				   to 2^64 */
   q = significand(&tmp);
   if ( q )
     {
-      rem_kernel(significand(X),
+      rem_kernel(significand(st0_ptr),
 		 &significand(&tmp),
 		 significand(&CONST_PI2),
-		 q, X->exp - CONST_PI2.exp);
-      tmp.exp = CONST_PI2.exp;
-      normalize(&tmp);
-      reg_move(&tmp, X);
+		 q, exponent(st0_ptr) - exponent(&CONST_PI2));
+      setexponent16(&tmp, exponent(&CONST_PI2));
+      st0_tag = FPU_normalize(&tmp);
+      FPU_copy_to_reg0(&tmp, st0_tag);
     }
 
-#ifdef FPTAN
-  if ( even == FPTAN )
-    {
-      if ( ((X->exp >= EXP_BIAS) ||
-	    ((X->exp == EXP_BIAS-1)
-	     && (X->sigh >= 0xc90fdaa2))) ^ (q & 1) )
-	even = FCOS;
-      else
-	even = 0;
-    }
-#endif FPTAN
-
   if ( (even && !(q & 1)) || (!even && (q & 1)) )
     {
-      reg_sub(&CONST_PI2, X, X, FULL_PRECISION);
+      st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2, FULL_PRECISION);
+
 #ifdef BETTER_THAN_486
       /* So far, the results are exact but based upon a 64 bit
 	 precision approximation to pi/2. The technique used
 	 now is equivalent to using an approximation to pi/2 which
 	 is accurate to about 128 bits. */
-      if ( (X->exp <= CONST_PI2extra.exp + 64) || (q > 1) )
+      if ( (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64) || (q > 1) )
 	{
-	  /* This code gives the effect of having p/2 to better than
+	  /* This code gives the effect of having pi/2 to better than
 	     128 bits precision. */
+
 	  significand(&tmp) = q + 1;
-	  tmp.exp = EXP_BIAS + 63;
-	  tmp.tag = TW_Valid;
-	  normalize(&tmp);
-	  reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
-	  reg_add(X, &tmp,  X, FULL_PRECISION);
-	  if ( X->sign == SIGN_NEG )
+	  setexponent16(&tmp, 63);
+	  FPU_normalize(&tmp);
+	  tmptag =
+	    FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION, SIGN_POS,
+		      exponent16(&CONST_PI2extra) + exponent16(&tmp));
+	  st0_tag = FPU_add(&tmp, tmptag, 0, FULL_PRECISION);
+	  if ( signnegative(st0_ptr) )
 	    {
 	      /* CONST_PI2extra is negative, so the result of the addition
 		 can be negative. This means that the argument is actually
 		 in a different quadrant. The correction is always < pi/2,
 		 so it can't overflow into yet another quadrant. */
-	      X->sign = SIGN_POS;
+	      setpositive(st0_ptr);
 	      q++;
 	    }
 	}
@@ -114,33 +106,39 @@
 	 precision approximation to pi/2. The technique used
 	 now is equivalent to using an approximation to pi/2 which
 	 is accurate to about 128 bits. */
-      if ( ((q > 0) && (X->exp <= CONST_PI2extra.exp + 64)) || (q > 1) )
+      if ( ((q > 0) && (exponent(st0_ptr) <= exponent(&CONST_PI2extra) + 64))
+	   || (q > 1) )
 	{
 	  /* This code gives the effect of having p/2 to better than
 	     128 bits precision. */
+
 	  significand(&tmp) = q;
-	  tmp.exp = EXP_BIAS + 63;
-	  tmp.tag = TW_Valid;
-	  normalize(&tmp);
-	  reg_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION);
-	  reg_sub(X, &tmp, X, FULL_PRECISION);
-	  if ( (X->exp == CONST_PI2.exp) &&
-	      ((X->sigh > CONST_PI2.sigh)
-	       || ((X->sigh == CONST_PI2.sigh)
-		   && (X->sigl > CONST_PI2.sigl))) )
+	  setexponent16(&tmp, 63);
+	  FPU_normalize(&tmp);         /* This must return TAG_Valid */
+	  tmptag = FPU_u_mul(&CONST_PI2extra, &tmp, &tmp, FULL_PRECISION,
+			     SIGN_POS,
+			     exponent16(&CONST_PI2extra) + exponent16(&tmp));
+	  st0_tag = FPU_sub(LOADED|(tmptag & 0x0f), (int)&tmp,
+			    FULL_PRECISION);
+	  if ( (exponent(st0_ptr) == exponent(&CONST_PI2)) &&
+	      ((st0_ptr->sigh > CONST_PI2.sigh)
+	       || ((st0_ptr->sigh == CONST_PI2.sigh)
+		   && (st0_ptr->sigl > CONST_PI2.sigl))) )
 	    {
 	      /* CONST_PI2extra is negative, so the result of the
 		 subtraction can be larger than pi/2. This means
 		 that the argument is actually in a different quadrant.
 		 The correction is always < pi/2, so it can't overflow
 		 into yet another quadrant. */
-	      reg_sub(&CONST_PI, X, X, FULL_PRECISION);
+	      st0_tag = FPU_sub(REV|LOADED|TAG_Valid, (int)&CONST_PI2,
+				FULL_PRECISION);
 	      q++;
 	    }
 	}
     }
 #endif BETTER_THAN_486
 
+  FPU_settag0(st0_tag);
   control_word = old_cw;
   partial_status = saved_status & ~SW_C2;     /* Reduction complete. */
 
@@ -149,57 +147,56 @@
 
 
 /* Convert a long to register */
-void convert_l2reg(long const *arg, FPU_REG *dest)
+static void convert_l2reg(long const *arg, int deststnr)
 {
+  int tag;
   long num = *arg;
+  u_char sign;
+  FPU_REG *dest = &st(deststnr);
 
   if (num == 0)
-    { reg_move(&CONST_Z, dest); return; }
+    {
+      FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
+      return;
+    }
 
   if (num > 0)
-    dest->sign = SIGN_POS;
+    { sign = SIGN_POS; }
   else
-    { num = -num; dest->sign = SIGN_NEG; }
+    { num = -num; sign = SIGN_NEG; }
 
   dest->sigh = num;
   dest->sigl = 0;
-  dest->exp = EXP_BIAS + 31;
-  dest->tag = TW_Valid;
-  normalize(dest);
+  setexponent16(dest, 31);
+  tag = FPU_normalize(dest);
+  FPU_settagi(deststnr, tag);
+  setsign(dest, sign);
+  return;
 }
 
 
-static void single_arg_error(FPU_REG *st0_ptr)
+static void single_arg_error(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  switch ( st0_ptr->tag )
-    {
-    case TW_NaN:
-      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
-	{
-	  EXCEPTION(EX_Invalid);
-	  if ( control_word & CW_Invalid )
-	    st0_ptr->sigh |= 0x40000000;	  /* Convert to a QNaN */
-	}
-      break;              /* return with a NaN in st(0) */
-    case TW_Empty:
-      stack_underflow();  /* Puts a QNaN in st(0) */
-      break;
+  if ( st0_tag == TAG_Empty )
+    FPU_stack_underflow();  /* Puts a QNaN in st(0) */
+  else if ( st0_tag == TW_NaN )
+    real_1op_NaN(st0_ptr);       /* return with a NaN in st(0) */
 #ifdef PARANOID
-    default:
-      EXCEPTION(EX_INTERNAL|0x0112);
+  else
+    EXCEPTION(EX_INTERNAL|0x0112);
 #endif PARANOID
-    }
 }
 
 
-static void single_arg_2_error(FPU_REG *st0_ptr)
+static void single_arg_2_error(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  FPU_REG *st_new_ptr;
+  int isNaN;
 
-  switch ( st0_ptr->tag )
+  switch ( st0_tag )
     {
     case TW_NaN:
-      if ( !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
+      isNaN = (exponent(st0_ptr) == EXP_OVER) && (st0_ptr->sigh & 0x80000000);
+      if ( isNaN && !(st0_ptr->sigh & 0x40000000) )   /* Signaling ? */
 	{
 	  EXCEPTION(EX_Invalid);
 	  if ( control_word & CW_Invalid )
@@ -207,17 +204,27 @@
 	      /* The masked response */
 	      /* Convert to a QNaN */
 	      st0_ptr->sigh |= 0x40000000;
-	      st_new_ptr = &st(-1);
 	      push();
-	      reg_move(&st(1), st_new_ptr);
+	      FPU_copy_to_reg0(st0_ptr, TAG_Special);
 	    }
 	}
-      else
+      else if ( isNaN )
 	{
 	  /* A QNaN */
-	  st_new_ptr = &st(-1);
 	  push();
-	  reg_move(&st(1), st_new_ptr);
+	  FPU_copy_to_reg0(st0_ptr, TAG_Special);
+	}
+      else
+	{
+	  /* pseudoNaN or other unsupported */
+	  EXCEPTION(EX_Invalid);
+	  if ( control_word & CW_Invalid )
+	    {
+	      /* The masked response */
+	      FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
+	      push();
+	      FPU_copy_to_reg0(&CONST_QNaN, TAG_Special);
+	    }
 	}
       break;              /* return with a NaN in st(0) */
 #ifdef PARANOID
@@ -230,92 +237,88 @@
 
 /*---------------------------------------------------------------------------*/
 
-static void f2xm1(FPU_REG *st0_ptr)
+static void f2xm1(FPU_REG *st0_ptr, u_char tag)
 {
+  FPU_REG a;
+
   clear_C1();
-  switch ( st0_ptr->tag )
+
+  if ( tag == TAG_Valid )
     {
-    case TW_Valid:
-      {
-	if ( st0_ptr->exp >= 0 )
-	  {
-	    /* For an 80486 FPU, the result is undefined. */
-	  }
-#ifdef DENORM_OPERAND
-	else if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	  return;
-#endif DENORM_OPERAND
-	else
-	  {
-	    /* poly_2xm1(x) requires 0 < x < 1. */
-	    poly_2xm1(st0_ptr, st0_ptr);
-	  }
-	if ( st0_ptr->exp <= EXP_UNDER )
-	  {
-	    /* A denormal result has been produced.
-	       Precision must have been lost, this is always
-	       an underflow. */
-	    arith_underflow(st0_ptr);
-	  }
-	set_precision_flag_up();   /* 80486 appears to always do this */
-	return;
-      }
-    case TW_Zero:
+      /* For an 80486 FPU, the result is undefined if the arg is >= 1.0 */
+      if ( exponent(st0_ptr) < 0 )
+	{
+	denormal_arg:
+
+	  FPU_to_exp16(st0_ptr, &a);
+
+	  /* poly_2xm1(x) requires 0 < st(0) < 1. */
+	  poly_2xm1(getsign(st0_ptr), &a, st0_ptr);
+	}
+      set_precision_flag_up();   /* 80486 appears to always do this */
       return;
+    }
+
+  if ( tag == TAG_Zero )
+    return;
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  switch ( tag )
+    {
+    case TW_Denormal:
+      if ( denormal_operand() < 0 )
+	return;
+      goto denormal_arg;
     case TW_Infinity:
-      if ( st0_ptr->sign == SIGN_NEG )
+      if ( signnegative(st0_ptr) )
 	{
 	  /* -infinity gives -1 (p16-10) */
-	  reg_move(&CONST_1, st0_ptr);
-	  st0_ptr->sign = SIGN_NEG;
+	  FPU_copy_to_reg0(&CONST_1, TAG_Valid);
+	  setnegative(st0_ptr);
 	}
       return;
     default:
-      single_arg_error(st0_ptr);
+      single_arg_error(st0_ptr, tag);
     }
 }
 
 
-static void fptan(FPU_REG *st0_ptr)
+static void fptan(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
   int q;
-  char arg_sign = st0_ptr->sign;
+  u_char arg_sign = getsign(st0_ptr);
 
   /* Stack underflow has higher priority */
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
-      stack_underflow();  /* Puts a QNaN in st(0) */
+      FPU_stack_underflow();  /* Puts a QNaN in st(0) */
       if ( control_word & CW_Invalid )
 	{
 	  st_new_ptr = &st(-1);
 	  push();
-	  stack_underflow();  /* Puts a QNaN in the new st(0) */
+	  FPU_stack_underflow();  /* Puts a QNaN in the new st(0) */
 	}
       return;
     }
 
   if ( STACK_OVERFLOW )
-    { stack_overflow(); return; }
+    { FPU_stack_overflow(); return; }
 
-  switch ( st0_tag )
+  if ( st0_tag == TAG_Valid )
     {
-    case TW_Valid:
-      if ( st0_ptr->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
 	{
-	  st0_ptr->sign = SIGN_POS;
-	  if ( (q = trig_arg(st0_ptr, 0)) != -1 )
-	    {
-	      poly_tan(st0_ptr, st0_ptr);
-	      st0_ptr->sign = (q & 1) ^ arg_sign;
-	    }
-	  else
+	  if ( (q = trig_arg(st0_ptr, 0)) == -1 )
 	    {
 	      /* Operand is out of range */
-	      st0_ptr->sign = arg_sign;         /* restore st(0) */
 	      return;
 	    }
+
+	  poly_tan(st0_ptr);
+	  setsign(st0_ptr, (q & 1) ^ (arg_sign != 0));
 	  set_precision_flag_up();  /* We do not really know if up or down */
 	}
       else
@@ -323,106 +326,134 @@
 	  /* For a small arg, the result == the argument */
 	  /* Underflow may happen */
 
-	  if ( st0_ptr->exp <= EXP_UNDER )
-	    {
-#ifdef DENORM_OPERAND
-	      if ( denormal_operand() )
-		return;
-#endif DENORM_OPERAND
-	      /* A denormal result has been produced.
-		 Precision must have been lost, this is always
-		 an underflow. */
-	      if ( arith_underflow(st0_ptr) )
-		return;
-	    }
-	  set_precision_flag_down();  /* Must be down. */
+	denormal_arg:
+
+	  FPU_to_exp16(st0_ptr, st0_ptr);
+      
+	  st0_tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
+	  FPU_settag0(st0_tag);
 	}
       push();
-      reg_move(&CONST_1, st_new_ptr);
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
       return;
-      break;
-    case TW_Infinity:
+    }
+
+  if ( st0_tag == TAG_Zero )
+    {
+      push();
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
+      setcc(0);
+      return;
+    }
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+	return;
+
+      goto denormal_arg;
+    }
+
+  if ( st0_tag == TW_Infinity )
+    {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(st0_ptr);
-      if ( control_word & CW_Invalid )
+      if ( arith_invalid(0) >= 0 )
 	{
 	  st_new_ptr = &st(-1);
 	  push();
-	  arith_invalid(st_new_ptr);
+	  arith_invalid(0);
 	}
       return;
-    case TW_Zero:
-      push();
-      reg_move(&CONST_1, st_new_ptr);
-      setcc(0);
-      break;
-    default:
-      single_arg_2_error(st0_ptr);
-      break;
     }
+
+  single_arg_2_error(st0_ptr, st0_tag);
 }
 
 
-static void fxtract(FPU_REG *st0_ptr)
+static void fxtract(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
+  u_char sign;
   register FPU_REG *st1_ptr = st0_ptr;  /* anticipate */
 
   if ( STACK_OVERFLOW )
-    {  stack_overflow(); return; }
+    {  FPU_stack_overflow(); return; }
+
   clear_C1();
-  if ( !(st0_tag ^ TW_Valid) )
+
+  if ( st0_tag == TAG_Valid )
     {
       long e;
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-	  
       push();
-      reg_move(st1_ptr, st_new_ptr);
-      st_new_ptr->exp = EXP_BIAS;
-      e = st1_ptr->exp - EXP_BIAS;
-      convert_l2reg(&e, st1_ptr);
+      sign = getsign(st1_ptr);
+      reg_copy(st1_ptr, st_new_ptr);
+      setexponent16(st_new_ptr, exponent(st_new_ptr));
+
+    denormal_arg:
+
+      e = exponent16(st_new_ptr);
+      convert_l2reg(&e, 1);
+      setexponentpos(st_new_ptr, 0);
+      setsign(st_new_ptr, sign);
+      FPU_settag0(TAG_Valid);       /* Needed if arg was a denormal */
       return;
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      char sign = st0_ptr->sign;
-      if ( divide_by_zero(SIGN_NEG, st0_ptr) )
+      sign = getsign(st0_ptr);
+
+      if ( FPU_divide_by_zero(0, SIGN_NEG) < 0 )
 	return;
+
       push();
-      reg_move(&CONST_Z, st_new_ptr);
-      st_new_ptr->sign = sign;
+      FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+      setsign(st_new_ptr, sign);
       return;
     }
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
+    {
+      if (denormal_operand() < 0 )
+	return;
+
+      push();
+      sign = getsign(st1_ptr);
+      FPU_to_exp16(st1_ptr, st_new_ptr);
+      goto denormal_arg;
+    }
   else if ( st0_tag == TW_Infinity )
     {
-      char sign = st0_ptr->sign;
-      st0_ptr->sign = SIGN_POS;
+      sign = getsign(st0_ptr);
+      setpositive(st0_ptr);
       push();
-      reg_move(&CONST_INF, st_new_ptr);
-      st_new_ptr->sign = sign;
+      FPU_copy_to_reg0(&CONST_INF, TAG_Special);
+      setsign(st_new_ptr, sign);
       return;
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( real_2op_NaN(st0_ptr, st0_ptr, st0_ptr) )
+      if ( real_1op_NaN(st0_ptr) < 0 )
 	return;
+
       push();
-      reg_move(st1_ptr, st_new_ptr);
+      FPU_copy_to_reg0(st0_ptr, TAG_Special);
       return;
     }
-  else if ( st0_tag == TW_Empty )
+  else if ( st0_tag == TAG_Empty )
     {
       /* Is this the correct behaviour? */
       if ( control_word & EX_Invalid )
 	{
-	  stack_underflow();
+	  FPU_stack_underflow();
 	  push();
-	  stack_underflow();
+	  FPU_stack_underflow();
 	}
       else
 	EXCEPTION(EX_StackUnder);
@@ -434,193 +465,233 @@
 }
 
 
-static void fdecstp(FPU_REG *st0_ptr)
+static void fdecstp(void)
 {
   clear_C1();
-  top--;  /* st0_ptr will be fixed in math_emulate() before the next instr */
+  top--;
 }
 
-static void fincstp(FPU_REG *st0_ptr)
+static void fincstp(void)
 {
   clear_C1();
-  top++;  /* st0_ptr will be fixed in math_emulate() before the next instr */
+  top++;
 }
 
 
-static void fsqrt_(FPU_REG *st0_ptr)
+static void fsqrt_(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
+  int expon;
 
   clear_C1();
-  if ( !(st0_tag ^ TW_Valid) )
+
+  if ( st0_tag == TAG_Valid )
     {
-      int expon;
+      u_char tag;
       
-      if (st0_ptr->sign == SIGN_NEG)
+      if (signnegative(st0_ptr))
 	{
-	  arith_invalid(st0_ptr);  /* sqrt(negative) is invalid */
+	  arith_invalid(0);  /* sqrt(negative) is invalid */
 	  return;
 	}
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
+      /* make st(0) in  [1.0 .. 4.0) */
+      expon = exponent(st0_ptr);
 
-      expon = st0_ptr->exp - EXP_BIAS;
-      st0_ptr->exp = EXP_BIAS + (expon & 1);  /* make st(0) in  [1.0 .. 4.0) */
-      
-      wm_sqrt(st0_ptr, control_word);	/* Do the computation */
-      
-      st0_ptr->exp += expon >> 1;
-      st0_ptr->sign = SIGN_POS;
+    denormal_arg:
+
+      setexponent16(st0_ptr, (expon & 1));
+
+      /* Do the computation, the sign of the result will be positive. */
+      tag = wm_sqrt(st0_ptr, 0, 0, control_word, SIGN_POS);
+      addexponent(st0_ptr, expon >> 1);
+      FPU_settag0(tag);
+      return;
     }
-  else if ( st0_tag == TW_Zero )
+
+  if ( st0_tag == TAG_Zero )
     return;
-  else if ( st0_tag == TW_Infinity )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Infinity )
     {
-      if ( st0_ptr->sign == SIGN_NEG )
-	arith_invalid(st0_ptr);  /* sqrt(-Infinity) is invalid */
+      if ( signnegative(st0_ptr) )
+	arith_invalid(0);  /* sqrt(-Infinity) is invalid */
       return;
     }
-  else
-    { single_arg_error(st0_ptr); return; }
+  else if ( st0_tag == TW_Denormal )
+    {
+      if (signnegative(st0_ptr))
+	{
+	  arith_invalid(0);  /* sqrt(negative) is invalid */
+	  return;
+	}
+
+      if ( denormal_operand() < 0 )
+	return;
+
+      FPU_to_exp16(st0_ptr, st0_ptr);
+
+      expon = exponent16(st0_ptr);
+
+      goto denormal_arg;
+    }
+
+  single_arg_error(st0_ptr, st0_tag);
 
 }
 
 
-static void frndint_(FPU_REG *st0_ptr)
+static void frndint_(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
-  int flags;
+  int flags, tag;
 
-  if ( !(st0_tag ^ TW_Valid) )
+  if ( st0_tag == TAG_Valid )
     {
-      if (st0_ptr->exp > EXP_BIAS+63)
-	return;
+      u_char sign;
 
-#ifdef DENORM_OPERAND
-      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+    denormal_arg:
+
+      sign = getsign(st0_ptr);
+
+      if (exponent(st0_ptr) > 63)
 	return;
-#endif DENORM_OPERAND
+
+      if ( st0_tag == TW_Denormal )
+	{
+	  if (denormal_operand() < 0 )
+	    return;
+	}
 
       /* Fortunately, this can't overflow to 2^64 */
-      if ( (flags = round_to_int(st0_ptr)) )
+      if ( (flags = FPU_round_to_int(st0_ptr, st0_tag)) )
 	set_precision_flag(flags);
 
-      st0_ptr->exp = EXP_BIAS + 63;
-      normalize(st0_ptr);
+      setexponent16(st0_ptr, 63);
+      tag = FPU_normalize(st0_ptr);
+      setsign(st0_ptr, sign);
+      FPU_settag0(tag);
       return;
     }
-  else if ( (st0_tag == TW_Zero) || (st0_tag == TW_Infinity) )
+
+  if ( st0_tag == TAG_Zero )
+    return;
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+
+  if ( st0_tag == TW_Denormal )
+    goto denormal_arg;
+  else if ( st0_tag == TW_Infinity )
     return;
   else
-    single_arg_error(st0_ptr);
+    single_arg_error(st0_ptr, st0_tag);
 }
 
 
-static void fsin(FPU_REG *st0_ptr)
+static int fsin(FPU_REG *st0_ptr, u_char tag)
 {
-  char st0_tag = st0_ptr->tag;
-  char arg_sign = st0_ptr->sign;
+  u_char arg_sign = getsign(st0_ptr);
 
-  if ( st0_tag == TW_Valid )
+  if ( tag == TAG_Valid )
     {
-      FPU_REG rv;
       int q;
 
-      if ( st0_ptr->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
 	{
-	  st0_ptr->sign = SIGN_POS;
-	  if ( (q = trig_arg(st0_ptr, 0)) != -1 )
+	  if ( (q = trig_arg(st0_ptr, 0)) == -1 )
 	    {
+	      /* Operand is out of range */
+	      return 1;
+	    }
 
-	      poly_sine(st0_ptr, &rv);
+	  poly_sine(st0_ptr);
+	  
+	  if (q & 2)
+	    changesign(st0_ptr);
 
-	      if (q & 2)
-		rv.sign ^= SIGN_POS ^ SIGN_NEG;
-	      rv.sign ^= arg_sign;
-	      reg_move(&rv, st0_ptr);
+	  setsign(st0_ptr, getsign(st0_ptr) ^ arg_sign);
 
-	      /* We do not really know if up or down */
-	      set_precision_flag_up();
-	      return;
-	    }
-	  else
-	    {
-	      /* Operand is out of range */
-	      st0_ptr->sign = arg_sign;         /* restore st(0) */
-	      return;
-	    }
+	  /* We do not really know if up or down */
+	  set_precision_flag_up();
+	  return 0;
 	}
       else
 	{
 	  /* For a small arg, the result == the argument */
-	  /* Underflow may happen */
-
-	  if ( st0_ptr->exp <= EXP_UNDER )
-	    {
-#ifdef DENORM_OPERAND
-	      if ( denormal_operand() )
-		return;
-#endif DENORM_OPERAND
-	      /* A denormal result has been produced.
-		 Precision must have been lost, this is always
-		 an underflow. */
-	      arith_underflow(st0_ptr);
-	      return;
-	    }
-
 	  set_precision_flag_up();  /* Must be up. */
+	  return 0;
 	}
     }
-  else if ( st0_tag == TW_Zero )
+
+  if ( tag == TAG_Zero )
     {
       setcc(0);
-      return;
+      return 0;
     }
-  else if ( st0_tag == TW_Infinity )
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  if ( tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+	return 1;
+
+      /* For a small arg, the result == the argument */
+      /* Underflow may happen */
+      FPU_to_exp16(st0_ptr, st0_ptr);
+      
+      tag = FPU_round(st0_ptr, 1, 0, FULL_PRECISION, arg_sign);
+
+      FPU_settag0(tag);
+
+      return 0;
+    }
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(st0_ptr);
-      return;
+      arith_invalid(0);
+      return 1;
     }
   else
-    single_arg_error(st0_ptr);
+    {
+      single_arg_error(st0_ptr, tag);
+      return 1;
+    }
 }
 
 
-static int f_cos(FPU_REG *arg)
+static int f_cos(FPU_REG *st0_ptr, u_char tag)
 {
-  char arg_sign = arg->sign;
+  u_char st0_sign;
 
-  if ( arg->tag == TW_Valid )
+  st0_sign = getsign(st0_ptr);
+
+  if ( tag == TAG_Valid )
     {
-      FPU_REG rv;
       int q;
 
-      if ( arg->exp > EXP_BIAS - 40 )
+      if ( exponent(st0_ptr) > -40 )
 	{
-	  arg->sign = SIGN_POS;
-	  if ( (arg->exp < EXP_BIAS)
-	      || ((arg->exp == EXP_BIAS)
-		  && (significand(arg) <= 0xc90fdaa22168c234LL)) )
+	  if ( (exponent(st0_ptr) < 0)
+	      || ((exponent(st0_ptr) == 0)
+		  && (significand(st0_ptr) <= 0xc90fdaa22168c234LL)) )
 	    {
-	      poly_cos(arg, &rv);
-	      reg_move(&rv, arg);
+	      poly_cos(st0_ptr);
 
 	      /* We do not really know if up or down */
 	      set_precision_flag_down();
 	  
 	      return 0;
 	    }
-	  else if ( (q = trig_arg(arg, FCOS)) != -1 )
+	  else if ( (q = trig_arg(st0_ptr, FCOS)) != -1 )
 	    {
-	      poly_sine(arg, &rv);
+	      poly_sine(st0_ptr);
 
 	      if ((q+1) & 2)
-		rv.sign ^= SIGN_POS ^ SIGN_NEG;
-	      reg_move(&rv, arg);
+		changesign(st0_ptr);
 
 	      /* We do not really know if up or down */
 	      set_precision_flag_down();
@@ -630,19 +701,15 @@
 	  else
 	    {
 	      /* Operand is out of range */
-	      arg->sign = arg_sign;         /* restore st(0) */
 	      return 1;
 	    }
 	}
       else
 	{
-#ifdef DENORM_OPERAND
-	  if ( (arg->exp <= EXP_UNDER) && (denormal_operand()) )
-	    return 1;
-#endif DENORM_OPERAND
+	denormal_arg:
 
 	  setcc(0);
-	  reg_move(&CONST_1, arg);
+	  FPU_copy_to_reg0(&CONST_1, TAG_Valid);
 #ifdef PECULIAR_486
 	  set_precision_flag_down();  /* 80486 appears to do this. */
 #else
@@ -651,79 +718,99 @@
 	  return 0;
 	}
     }
-  else if ( arg->tag == TW_Zero )
+  else if ( tag == TAG_Zero )
     {
-      reg_move(&CONST_1, arg);
+      FPU_copy_to_reg0(&CONST_1, TAG_Valid);
       setcc(0);
       return 0;
     }
-  else if ( arg->tag == TW_Infinity )
+
+  if ( tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+
+  if ( tag == TW_Denormal )
+    {
+      if ( denormal_operand() < 0 )
+	return 1;
+
+      goto denormal_arg;
+    }
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      arith_invalid(arg);
+      arith_invalid(0);
       return 1;
     }
   else
     {
-      single_arg_error(arg);  /* requires arg == &st(0) */
+      single_arg_error(st0_ptr, tag);  /* requires st0_ptr == &st(0) */
       return 1;
     }
 }
 
 
-static void fcos(FPU_REG *st0_ptr)
+static void fcos(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  f_cos(st0_ptr);
+  f_cos(st0_ptr, st0_tag);
 }
 
 
-static void fsincos(FPU_REG *st0_ptr)
+static void fsincos(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st_new_ptr;
   FPU_REG arg;
+  u_char tag;
 
   /* Stack underflow has higher priority */
-  if ( st0_tag == TW_Empty )
+  if ( st0_tag == TAG_Empty )
     {
-      stack_underflow();  /* Puts a QNaN in st(0) */
+      FPU_stack_underflow();  /* Puts a QNaN in st(0) */
       if ( control_word & CW_Invalid )
 	{
 	  st_new_ptr = &st(-1);
 	  push();
-	  stack_underflow();  /* Puts a QNaN in the new st(0) */
+	  FPU_stack_underflow();  /* Puts a QNaN in the new st(0) */
 	}
       return;
     }
 
   if ( STACK_OVERFLOW )
-    { stack_overflow(); return; }
+    { FPU_stack_overflow(); return; }
 
-  if ( st0_tag == TW_NaN )
+  if ( st0_tag == TAG_Special )
+    tag = FPU_Special(st0_ptr);
+  else
+    tag = st0_tag;
+
+  if ( tag == TW_NaN )
     {
-      single_arg_2_error(st0_ptr);
+      single_arg_2_error(st0_ptr, TW_NaN);
       return;
     }
-  else if ( st0_tag == TW_Infinity )
+  else if ( tag == TW_Infinity )
     {
       /* The 80486 treats infinity as an invalid operand */
-      if ( !arith_invalid(st0_ptr) )
+      if ( arith_invalid(0) >= 0 )
 	{
-	  /* unmasked response */
+	  /* Masked response */
 	  push();
-	  arith_invalid(st_new_ptr);
+	  arith_invalid(0);
 	}
       return;
     }
 
-  reg_move(st0_ptr,&arg);
-  if ( !f_cos(&arg) )
+  reg_copy(st0_ptr, &arg);
+  if ( !fsin(st0_ptr, st0_tag) )
     {
-      fsin(st0_ptr);
       push();
-      reg_move(&arg,st_new_ptr);
+      FPU_copy_to_reg0(&arg, st0_tag);
+      f_cos(&st(0), st0_tag);
+    }
+  else
+    {
+      /* An error, so restore st(0) */
+      FPU_copy_to_reg0(&arg, st0_tag);
     }
-
 }
 
 
@@ -760,79 +847,86 @@
 /* Remainder of st(0) / st(1) */
 /* This routine produces exact results, i.e. there is never any
    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, u_char st0_tag, int round)
 {
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
-  char st0_tag = st0_ptr->tag;
-  char sign = st0_ptr->sign;
+  u_char st1_tag = FPU_gettagi(1);
 
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-      FPU_REG tmp;
-      int old_cw = control_word;
-      int expdif = st0_ptr->exp - st1_ptr->exp;
+      FPU_REG tmp, st0, st1;
+      u_char st0_sign, st1_sign;
+      u_char tmptag;
+      int tag;
+      int old_cw;
+      int expdif;
       long long q;
       unsigned short saved_status;
-      int cc = 0;
+      int cc;
+
+    fprem_valid:
+      /* Convert registers for internal use. */
+      st0_sign = FPU_to_exp16(st0_ptr, &st0);
+      st1_sign = FPU_to_exp16(st1_ptr, &st1);
+      expdif = exponent16(&st0) - exponent16(&st1);
+
+      old_cw = control_word;
+      cc = 0;
 
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-	    (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
-      
       /* We want the status following the denorm tests, but don't want
 	 the status changed by the arithmetic operations. */
       saved_status = partial_status;
       control_word &= ~CW_RC;
       control_word |= RC_CHOP;
 
-      if (expdif < 64)
+      if ( expdif < 64 )
 	{
 	  /* This should be the most common case */
 
 	  if ( expdif > -2 )
 	    {
-	      reg_div(st0_ptr, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
+	      u_char sign = st0_sign ^ st1_sign;
+	      tag = FPU_u_div(&st0, &st1, &tmp,
+			      PR_64_BITS | RC_CHOP | 0x3f,
+			      sign);
+	      setsign(&tmp, sign);
 
-	      if ( tmp.exp >= EXP_BIAS )
+	      if ( exponent(&tmp) >= 0 )
 		{
-		  round_to_int(&tmp);  /* Fortunately, this can't overflow
-					  to 2^64 */
+		  FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't
+						   overflow to 2^64 */
 		  q = significand(&tmp);
 
-		  rem_kernel(significand(st0_ptr),
+		  rem_kernel(significand(&st0),
 			     &significand(&tmp),
-			     significand(st1_ptr),
+			     significand(&st1),
 			     q, expdif);
 
-		  tmp.exp = st1_ptr->exp;
+		  setexponent16(&tmp, exponent16(&st1));
 		}
 	      else
 		{
-		  reg_move(st0_ptr, &tmp);
+		  reg_copy(&st0, &tmp);
 		  q = 0;
 		}
-	      tmp.sign = sign;
 
 	      if ( (round == RC_RND) && (tmp.sigh & 0xc0000000) )
 		{
 		  /* We may need to subtract st(1) once more,
 		     to get a result <= 1/2 of st(1). */
 		  unsigned long long x;
-		  expdif = st1_ptr->exp - tmp.exp;
+		  expdif = exponent16(&st1) - exponent16(&tmp);
 		  if ( expdif <= 1 )
 		    {
 		      if ( expdif == 0 )
-			x = significand(st1_ptr) - significand(&tmp);
+			x = significand(&st1) - significand(&tmp);
 		      else /* expdif is 1 */
-			x = (significand(st1_ptr) << 1) - significand(&tmp);
+			x = (significand(&st1) << 1) - significand(&tmp);
 		      if ( (x < significand(&tmp)) ||
 			  /* or equi-distant (from 0 & st(1)) and q is odd */
 			  ((x == significand(&tmp)) && (q & 1) ) )
 			{
-			  tmp.sign ^= (SIGN_POS^SIGN_NEG);
+			  st0_sign = ! st0_sign;
 			  significand(&tmp) = x;
 			  q++;
 			}
@@ -855,28 +949,35 @@
 	  /* There is a large exponent difference ( >= 64 ) */
 	  /* To make much sense, the code in this section should
 	     be done at high precision. */
-	  int exp_1;
+	  int exp_1, N;
+	  u_char sign;
 
 	  /* prevent overflow here */
 	  /* N is 'a number between 32 and 63' (p26-113) */
-	  reg_move(st0_ptr, &tmp);
-	  tmp.exp = EXP_BIAS + 56;
-	  exp_1 = st1_ptr->exp;      st1_ptr->exp = EXP_BIAS;
-	  expdif -= 56;
-
-	  reg_div(&tmp, st1_ptr, &tmp, PR_64_BITS | RC_CHOP | 0x3f);
-	  st1_ptr->exp = exp_1;
+	  reg_copy(&st0, &tmp);
+	  tmptag = st0_tag;
+	  N = (expdif & 0x0000001f) + 32;  /* This choice gives results
+					      identical to an AMD 486 */
+	  setexponent16(&tmp, N);
+	  exp_1 = exponent16(&st1);
+	  setexponent16(&st1, 0);
+	  expdif -= N;
+
+	  sign = getsign(&tmp) ^ st1_sign;
+	  tag = FPU_u_div(&tmp, &st1, &tmp, PR_64_BITS | RC_CHOP | 0x3f,
+			  sign);
+	  setsign(&tmp, sign);
 
-	  round_to_int(&tmp);  /* Fortunately, this can't overflow to 2^64 */
+	  FPU_round_to_int(&tmp, tag);  /* Fortunately, this can't
+					   overflow to 2^64 */
 
-	  rem_kernel(significand(st0_ptr),
+	  rem_kernel(significand(&st0),
 		     &significand(&tmp),
-		     significand(st1_ptr),
+		     significand(&st1),
 		     significand(&tmp),
-		     tmp.exp - EXP_BIAS
+		     exponent(&tmp)
 		     ); 
-	  tmp.exp = exp_1 + expdif;
-	  tmp.sign = sign;
+	  setexponent16(&tmp, exp_1 + expdif);
 
 	  /* It is possible for the operation to be complete here.
 	     What does the IEEE standard say? The Intel 80486 manual
@@ -888,8 +989,8 @@
 	      /* The result is zero */
 	      control_word = old_cw;
 	      partial_status = saved_status;
-	      reg_move(&CONST_Z, st0_ptr);
-	      st0_ptr->sign = sign;
+	      FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+	      setsign(&st0, st0_sign);
 #ifdef PECULIAR_486
 	      setcc(SW_C2);
 #else
@@ -902,52 +1003,82 @@
 
       control_word = old_cw;
       partial_status = saved_status;
-      normalize_nuo(&tmp);
-      reg_move(&tmp, st0_ptr);
-      setcc(cc);
+      tag = FPU_normalize_nuo(&tmp);
+      reg_copy(&tmp, st0_ptr);
 
       /* The only condition to be looked for is underflow,
 	 and it can occur here only if underflow is unmasked. */
-      if ( (st0_ptr->exp <= EXP_UNDER) && (st0_ptr->tag != TW_Zero)
+      if ( (exponent16(&tmp) <= EXP_UNDER) && (tag != TAG_Zero)
 	  && !(control_word & CW_Underflow) )
-	arith_underflow(st0_ptr);
+	{
+	  setcc(cc);
+	  tag = arith_underflow(st0_ptr);
+	  setsign(st0_ptr, st0_sign);
+	  FPU_settag0(tag);
+	  return;
+	}
+      else if ( (exponent16(&tmp) > EXP_UNDER) || (tag == TAG_Zero) )
+	{
+	  stdexp(st0_ptr);
+	  setsign(st0_ptr, st0_sign);
+	}
+      else
+	{
+	  tag = FPU_round(st0_ptr, 0, 0, FULL_PRECISION, st0_sign);
+	}
+      FPU_settag0(tag);
+      setcc(cc);
 
       return;
     }
-  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
     {
-      stack_underflow();
+      if ( denormal_operand() < 0 )
+	return;
+      goto fprem_valid;
+    }
+  else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) )
+    {
+      FPU_stack_underflow();
       return;
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag == TW_Valid )
+      if ( st1_tag == TAG_Valid )
 	{
-#ifdef DENORM_OPERAND
-	  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  setcc(0); return;
+	}
+      else if ( st1_tag == TW_Denormal )
+	{
+	  if ( denormal_operand() < 0 )
 	    return;
-#endif DENORM_OPERAND
-
 	  setcc(0); return;
 	}
-      else if ( st1_tag == TW_Zero )
-	{ arith_invalid(st0_ptr); return; } /* fprem(?,0) always invalid */
+      else if ( st1_tag == TAG_Zero )
+	{ arith_invalid(0); return; } /* fprem(?,0) always invalid */
       else if ( st1_tag == TW_Infinity )
 	{ setcc(0); return; }
     }
-  else if ( st0_tag == TW_Valid )
+  else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      if ( st1_tag == TAG_Zero )
 	{
-	  arith_invalid(st0_ptr); /* fprem(Valid,Zero) is invalid */
+	  arith_invalid(0); /* fprem(Valid,Zero) is invalid */
 	  return;
 	}
       else if ( st1_tag != TW_NaN )
 	{
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  if ( ((st0_tag == TW_Denormal) || (st1_tag == TW_Denormal))
+	       && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
 
 	  if ( st1_tag == TW_Infinity )
 	    {
@@ -960,729 +1091,710 @@
     {
       if ( st1_tag != TW_NaN )
 	{
-	  arith_invalid(st0_ptr); /* fprem(Infinity,?) is invalid */
+	  arith_invalid(0); /* fprem(Infinity,?) is invalid */
 	  return;
 	}
     }
 
-  /* One of the registers must contain a NaN is we got here. */
+  /* One of the registers must contain a NaN if we got here. */
 
 #ifdef PARANOID
   if ( (st0_tag != TW_NaN) && (st1_tag != TW_NaN) )
       EXCEPTION(EX_INTERNAL | 0x118);
 #endif PARANOID
 
-  real_2op_NaN(st1_ptr, st0_ptr, st0_ptr);
+  real_2op_NaN(st1_ptr, st1_tag, 0, st1_ptr);
 
 }
 
 
 /* ST(1) <- ST(1) * log ST;  pop ST */
-static void fyl2x(FPU_REG *st0_ptr)
+static void fyl2x(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1), exponent;
-  char st1_tag = st1_ptr->tag;
-  int e;
+  u_char st1_tag = FPU_gettagi(1);
+  u_char sign;
+  int e, tag;
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+
+  if ( (st0_tag == TAG_Valid) && (st1_tag == TAG_Valid) )
     {
-      if ( st0_ptr->sign == SIGN_POS )
+    both_valid:
+      /* Both regs are Valid or Denormal */
+      if ( signpositive(st0_ptr) )
 	{
-#ifdef DENORM_OPERAND
-	  if ( ((st0_ptr->exp <= EXP_UNDER) ||
-		(st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-	    return;
-#endif DENORM_OPERAND
+	  if ( st0_tag == TW_Denormal )
+	    FPU_to_exp16(st0_ptr, st0_ptr);
+	  else
+	    /* Convert st(0) for internal use. */
+	    setexponent16(st0_ptr, exponent(st0_ptr));
 
 	  if ( (st0_ptr->sigh == 0x80000000) && (st0_ptr->sigl == 0) )
 	    {
 	      /* Special case. The result can be precise. */
-	      e = st0_ptr->exp - EXP_BIAS;
-	      if ( e > 0 )
+	      u_char esign;
+	      e = exponent16(st0_ptr);
+	      if ( e >= 0 )
 		{
 		  exponent.sigh = e;
-		  exponent.sign = SIGN_POS;
+		  esign = SIGN_POS;
 		}
 	      else
 		{
 		  exponent.sigh = -e;
-		  exponent.sign = SIGN_NEG;
+		  esign = SIGN_NEG;
 		}
 	      exponent.sigl = 0;
-	      exponent.exp = EXP_BIAS + 31;
-	      exponent.tag = TW_Valid;
-	      normalize_nuo(&exponent);
-	      reg_mul(&exponent, st1_ptr, st1_ptr, FULL_PRECISION);
+	      setexponent16(&exponent, 31);
+	      tag = FPU_normalize_nuo(&exponent);
+	      stdexp(&exponent);
+	      setsign(&exponent, esign);
+	      tag = FPU_mul(&exponent, tag, 1, FULL_PRECISION);
+	      if ( tag >= 0 )
+		FPU_settagi(1, tag);
 	    }
 	  else
 	    {
 	      /* The usual case */
-	      poly_l2(st0_ptr, st1_ptr, st1_ptr);
-	      if ( st1_ptr->exp <= EXP_UNDER )
-		{
-		  /* A denormal result has been produced.
-		     Precision must have been lost, this is always
-		     an underflow. */
-		  arith_underflow(st1_ptr);
-		}
+	      sign = getsign(st1_ptr);
+	      if ( st1_tag == TW_Denormal )
+		FPU_to_exp16(st1_ptr, st1_ptr);
 	      else
-		set_precision_flag_up();  /* 80486 appears to always do this */
+		/* Convert st(1) for internal use. */
+		setexponent16(st1_ptr, exponent(st1_ptr));
+	      poly_l2(st0_ptr, st1_ptr, sign);
 	    }
-	  pop();
-	  return;
 	}
       else
 	{
 	  /* negative */
-	  if ( !arith_invalid(st1_ptr) )
-	    pop();
-	  return;
+	  if ( arith_invalid(1) < 0 )
+	    return;
 	}
-    }
-  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
-    {
-      stack_underflow_pop(1);
+
+      FPU_pop();
+
       return;
     }
-  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	pop();
+      FPU_stack_underflow_pop(1);
       return;
     }
-  else if ( (st0_tag <= TW_Zero) && (st1_tag <= TW_Zero) )
+  else if ( (st0_tag <= TW_Denormal) && (st1_tag <= TW_Denormal) )
     {
-      /* one of the args is zero, the other valid, or both zero */
-      if ( st0_tag == TW_Zero )
+      if ( st0_tag == TAG_Zero )
 	{
-	  if ( st1_tag == TW_Zero )
+	  if ( st1_tag == TAG_Zero )
 	    {
 	      /* Both args zero is invalid */
-	      if ( !arith_invalid(st1_ptr) )
-		pop();
-	    }
-#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 ( st0_ptr->tag == TW_Infinity )
-	    {
-	      reg_move(&CONST_INF, st1_ptr);
-	      pop();
+	      if ( arith_invalid(1) < 0 )
+		return;
 	    }
-#endif PECULIAR_486
 	  else
 	    {
-	      if ( !divide_by_zero(st1_ptr->sign^SIGN_NEG^SIGN_POS, st1_ptr) )
-		pop();
+	      u_char sign;
+	      sign = getsign(st1_ptr)^SIGN_NEG;
+	      if ( FPU_divide_by_zero(1, sign) < 0 )
+		return;
+
+	      setsign(st1_ptr, sign);
 	    }
-	  return;
 	}
-      else
+      else if ( st1_tag == TAG_Zero )
 	{
 	  /* st(1) contains zero, st(0) valid <> 0 */
 	  /* Zero is the valid answer */
-	  char sign = st1_ptr->sign;
-
-	  if ( st0_ptr->sign == SIGN_NEG )
+	  sign = getsign(st1_ptr);
+	  
+	  if ( signnegative(st0_ptr) )
 	    {
 	      /* log(negative) */
-	      if ( !arith_invalid(st1_ptr) )
-		pop();
-	      return;
+	      if ( arith_invalid(1) < 0 )
+		return;
 	    }
-
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
+	  else
+	    {
+	      if ( exponent(st0_ptr) < 0 )
+		sign ^= SIGN_NEG;
 
-	  if ( st0_ptr->exp < EXP_BIAS ) sign ^= SIGN_NEG^SIGN_POS;
-	  pop(); st0_ptr = &st(0);
-	  reg_move(&CONST_Z, st0_ptr);
-	  st0_ptr->sign = sign;
-	  return;
+	      FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
+	      setsign(st1_ptr, sign);
+	    }
+	}
+      else
+	{
+	  /* One or both operands are denormals. */
+	  if ( denormal_operand() < 0 )
+	    return;
+	  goto both_valid;
 	}
     }
+  else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
+    {
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+	return;
+    }
   /* One or both arg must be an infinity */
   else if ( st0_tag == TW_Infinity )
     {
-      if ( (st0_ptr->sign == SIGN_NEG) || (st1_tag == TW_Zero) )
+      if ( (signnegative(st0_ptr)) || (st1_tag == TAG_Zero) )
 	{
 	  /* log(-infinity) or 0*log(infinity) */
-	  if ( !arith_invalid(st1_ptr) )
-	    pop();
-	  return;
+	  if ( arith_invalid(1) < 0 )
+	    return;
 	}
       else
 	{
-	  char sign = st1_ptr->sign;
+	  u_char sign = getsign(st1_ptr);
 
-#ifdef DENORM_OPERAND
-	  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
 
-	  pop(); st0_ptr = &st(0);
-	  reg_move(&CONST_INF, st0_ptr);
-	  st0_ptr->sign = sign;
-	  return;
+	  FPU_copy_to_reg1(&CONST_INF, TAG_Special);
+	  setsign(st1_ptr, sign);
 	}
     }
   /* st(1) must be infinity here */
-  else if ( (st0_tag == TW_Valid) && (st0_ptr->sign == SIGN_POS) )
+  else if ( ((st0_tag == TAG_Valid) || (st0_tag == TW_Denormal))
+	    && ( signpositive(st0_ptr) ) )
     {
-      if ( st0_ptr->exp >= EXP_BIAS )
+      if ( exponent(st0_ptr) >= 0 )
 	{
-	  if ( (st0_ptr->exp == EXP_BIAS) &&
+	  if ( (exponent(st0_ptr) == 0) &&
 	      (st0_ptr->sigh == 0x80000000) &&
 	      (st0_ptr->sigl == 0) )
 	    {
 	      /* st(0) holds 1.0 */
 	      /* infinity*log(1) */
-	      if ( !arith_invalid(st1_ptr) )
-		pop();
-	      return;
+	      if ( arith_invalid(1) < 0 )
+		return;
 	    }
-	  /* st(0) is positive and > 1.0 */
-	  pop();
+	  /* else st(0) is positive and > 1.0 */
 	}
       else
 	{
 	  /* st(0) is positive and < 1.0 */
 
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
 
-	  st1_ptr->sign ^= SIGN_NEG;
-	  pop();
+	  changesign(st1_ptr);
 	}
-      return;
     }
   else
     {
       /* st(0) must be zero or negative */
-      if ( st0_ptr->tag == TW_Zero )
+      if ( st0_tag == TAG_Zero )
 	{
 	  /* This should be invalid, but a real 80486 is happy with it. */
+
 #ifndef PECULIAR_486
-	  if ( !divide_by_zero(st1_ptr->sign, st1_ptr) )
+	  sign = getsign(st1_ptr);
+	  if ( FPU_divide_by_zero(1, sign) < 0 )
+	    return;
 #endif PECULIAR_486
-	    {
-	      st1_ptr->sign ^= SIGN_NEG^SIGN_POS;
-	      pop();
-	    }
-	}
-      else
-	{
-	  /* log(negative) */
-	  if ( !arith_invalid(st1_ptr) )
-	    pop();
+
+	  changesign(st1_ptr);
 	}
-      return;
+      else if ( arith_invalid(1) < 0 )	  /* log(negative) */
+	return;
     }
+
+  FPU_pop();
 }
 
 
-static void fpatan(FPU_REG *st0_ptr)
+static void fpatan(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char st1_tag = FPU_gettagi(1);
+  int tag;
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-	    (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
+    valid_atan:
 
-      poly_atan(st0_ptr, st1_ptr, st1_ptr);
+      poly_atan(st0_ptr, st0_tag, st1_ptr, st1_tag);
 
-      if ( st1_ptr->exp <= EXP_UNDER )
-	{
-	  /* A denormal result has been produced.
-	     Precision must have been lost.
-	     This is by definition an underflow. */
-	  arith_underflow(st1_ptr);
-	  pop();
-	  return;
-	}
+      FPU_pop();
+
+      return;
     }
-  else if ( (st0_tag == TW_Empty) || (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
     {
-      stack_underflow_pop(1);
+      if ( denormal_operand() < 0 )
+	return;
+
+      goto valid_atan;
+    }
+  else if ( (st0_tag == TAG_Empty) || (st1_tag == TAG_Empty) )
+    {
+      FPU_stack_underflow_pop(1);
       return;
     }
   else if ( (st0_tag == TW_NaN) || (st1_tag == TW_NaN) )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	  pop();
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) >= 0 )
+	  FPU_pop();
       return;
     }
   else if ( (st0_tag == TW_Infinity) || (st1_tag == TW_Infinity) )
     {
-      char sign = st1_ptr->sign;
+      u_char sign = getsign(st1_ptr);
       if ( st0_tag == TW_Infinity )
 	{
 	  if ( st1_tag == TW_Infinity )
 	    {
-	      if ( st0_ptr->sign == SIGN_POS )
-		{ reg_move(&CONST_PI4, st1_ptr); }
+	      if ( signpositive(st0_ptr) )
+		{
+		  FPU_copy_to_reg1(&CONST_PI4, TAG_Valid);
+		}
 	      else
-		reg_add(&CONST_PI4, &CONST_PI2, st1_ptr, FULL_PRECISION);
+		{
+		  setpositive(st1_ptr);
+		  tag = FPU_u_add(&CONST_PI4, &CONST_PI2, st1_ptr,
+				  FULL_PRECISION, SIGN_POS,
+				  exponent(&CONST_PI4), exponent(&CONST_PI2));
+		  if ( tag >= 0 )
+		    FPU_settagi(1, tag);
+		}
 	    }
 	  else
 	    {
-#ifdef DENORM_OPERAND
-	      if ( st1_tag != TW_Zero )
-		{
-		  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-		    return;
-		}
-#endif DENORM_OPERAND
+	      if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
+		return;
 
-	      if ( st0_ptr->sign == SIGN_POS )
+	      if ( signpositive(st0_ptr) )
 		{
-		  reg_move(&CONST_Z, st1_ptr);
-		  st1_ptr->sign = sign;   /* An 80486 preserves the sign */
-		  pop();
+		  FPU_copy_to_reg1(&CONST_Z, TAG_Zero);
+		  setsign(st1_ptr, sign);   /* An 80486 preserves the sign */
+		  FPU_pop();
 		  return;
 		}
 	      else
-		reg_move(&CONST_PI, st1_ptr);
+		{
+		  FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
+		}
 	    }
 	}
       else
 	{
 	  /* st(1) is infinity, st(0) not infinity */
-#ifdef DENORM_OPERAND
-	  if ( st0_tag != TW_Zero )
-	    {
-	      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-		return;
-	    }
-#endif DENORM_OPERAND
+	  if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+	    return;
 
-	  reg_move(&CONST_PI2, st1_ptr);
+	  FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
 	}
-      st1_ptr->sign = sign;
+      setsign(st1_ptr, sign);
     }
-  else if ( st1_tag == TW_Zero )
+  else if ( st1_tag == TAG_Zero )
     {
       /* st(0) must be valid or zero */
-      char sign = st1_ptr->sign;
+      u_char sign = getsign(st1_ptr);
 
-#ifdef DENORM_OPERAND
-      if ( st0_tag != TW_Zero )
+      if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+	return;
+
+      if ( signpositive(st0_ptr) )
 	{
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	    return;
+	  /* An 80486 preserves the sign */
+	  FPU_pop();
+	  return;
 	}
-#endif DENORM_OPERAND
 
-      if ( st0_ptr->sign == SIGN_POS )
-	{ /* An 80486 preserves the sign */ pop(); return; }
-      else
-	reg_move(&CONST_PI, st1_ptr);
-      st1_ptr->sign = sign;
+      FPU_copy_to_reg1(&CONST_PI, TAG_Valid);
+      setsign(st1_ptr, sign);
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      /* st(1) must be TW_Valid here */
-      char sign = st1_ptr->sign;
+      /* st(1) must be TAG_Valid here */
+      u_char sign = getsign(st1_ptr);
 
-#ifdef DENORM_OPERAND
-      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
 	return;
-#endif DENORM_OPERAND
 
-      reg_move(&CONST_PI2, st1_ptr);
-      st1_ptr->sign = sign;
+      FPU_copy_to_reg1(&CONST_PI2, TAG_Valid);
+      setsign(st1_ptr, sign);
     }
 #ifdef PARANOID
   else
     EXCEPTION(EX_INTERNAL | 0x125);
 #endif PARANOID
 
-  pop();
+  FPU_pop();
   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, u_char st0_tag)
 {
-  do_fprem(st0_ptr, RC_CHOP);
+  do_fprem(st0_ptr, st0_tag, RC_CHOP);
 }
 
 
-static void fprem1(FPU_REG *st0_ptr)
+static void fprem1(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  do_fprem(st0_ptr, RC_RND);
+  do_fprem(st0_ptr, st0_tag, RC_RND);
 }
 
 
-static void fyl2xp1(FPU_REG *st0_ptr)
+static void fyl2xp1(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag, sign;
-  FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char sign, sign1;
+  FPU_REG *st1_ptr = &st(1), a, b;
+  u_char st1_tag = FPU_gettagi(1);
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-	    (st1_ptr->exp <= EXP_UNDER)) && denormal_operand() )
+    valid_yl2xp1:
+
+      sign = getsign(st0_ptr);
+      sign1 = getsign(st1_ptr);
+
+      FPU_to_exp16(st0_ptr, &a);
+      FPU_to_exp16(st1_ptr, &b);
+
+      if ( poly_l2p1(sign, sign1, &a, &b, st1_ptr) )
 	return;
-#endif DENORM_OPERAND
 
-      if ( poly_l2p1(st0_ptr, st1_ptr, st1_ptr) )
-	{
-#ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-	  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-#else
-	  if ( arith_invalid(st1_ptr) )  /* poly_l2p1() returned invalid */
-	    return;
-#endif PECULIAR_486
-	}
-      if ( st1_ptr->exp <= EXP_UNDER )
-	{
-	  /* A denormal result has been produced.
-	     Precision must have been lost, this is always
-	     an underflow. */
-	  sign = st1_ptr->sign;
-	  arith_underflow(st1_ptr);
-	  st1_ptr->sign = sign;
-	}
-      else
-	set_precision_flag_up();   /* 80486 appears to always do this */
-      pop();
+      FPU_pop();
       return;
     }
-  else if ( (st0_tag == TW_Empty) | (st1_tag == TW_Empty) )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( ((st0_tag == TAG_Valid) && (st1_tag == TW_Denormal))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TAG_Valid))
+	    || ((st0_tag == TW_Denormal) && (st1_tag == TW_Denormal)) )
+    {
+      if ( denormal_operand() < 0 )
+	return;
+
+      goto valid_yl2xp1;
+    }
+  else if ( (st0_tag == TAG_Empty) | (st1_tag == TAG_Empty) )
     {
-      stack_underflow_pop(1);
+      FPU_stack_underflow_pop(1);
       return;
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag <= TW_Zero )
+      switch ( st1_tag )
 	{
-#ifdef DENORM_OPERAND
-	  if ( (st1_tag == TW_Valid) && (st1_ptr->exp <= EXP_UNDER) &&
-	      (denormal_operand()) )
+	case TW_Denormal:
+	  if ( denormal_operand() < 0 )
 	    return;
-#endif DENORM_OPERAND
-	  
-	  st0_ptr->sign ^= st1_ptr->sign;
-	  reg_move(st0_ptr, st1_ptr);
-	}
-      else if ( st1_tag == TW_Infinity )
-	{
+
+	case TAG_Zero:
+	case TAG_Valid:
+	  setsign(st0_ptr, getsign(st0_ptr) ^ getsign(st1_ptr));
+	  FPU_copy_to_reg1(st0_ptr, st0_tag);
+	  break;
+
+	case TW_Infinity:
 	  /* Infinity*log(1) */
-	  if ( !arith_invalid(st1_ptr) )
-	    pop();
-	  return;
-	}
-      else if ( st1_tag == TW_NaN )
-	{
-	  if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	    pop();
-	  return;
-	}
+	  if ( arith_invalid(1) < 0 )
+	    return;
+	  break;
+
+	case TW_NaN:
+	  if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+	    return;
+	  break;
+
+	default:
 #ifdef PARANOID
-      else
-	{
 	  EXCEPTION(EX_INTERNAL | 0x116);
 	  return;
-	}
 #endif PARANOID
-      pop(); return;
+	}
     }
-  else if ( st0_tag == TW_Valid )
+  else if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      switch ( st1_tag )
 	{
-	  if ( st0_ptr->sign == SIGN_NEG )
+	case TAG_Zero:
+	  if ( signnegative(st0_ptr) )
 	    {
-	      if ( st0_ptr->exp >= EXP_BIAS )
+	      if ( exponent(st0_ptr) >= 0 )
 		{
 		  /* st(0) holds <= -1.0 */
 #ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-		  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+		  changesign(st1_ptr);
 #else
-		  if ( arith_invalid(st1_ptr) ) return;
+		  if ( arith_invalid(1) < 0 )
+		    return;
 #endif PECULIAR_486
-		  pop(); return;
 		}
-#ifdef DENORM_OPERAND
-	      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	      else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 		return;
-#endif DENORM_OPERAND
-	      st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-	      pop(); return;
+	      else
+		changesign(st1_ptr);
 	    }
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
-	  pop(); return;
-	}
-      if ( st1_tag == TW_Infinity )
-	{
-	  if ( st0_ptr->sign == SIGN_NEG )
+	  break;
+
+	case TW_Infinity:
+	  if ( signnegative(st0_ptr) )
 	    {
-	      if ( (st0_ptr->exp >= EXP_BIAS) &&
+	      if ( (exponent(st0_ptr) >= 0) &&
 		  !((st0_ptr->sigh == 0x80000000) &&
 		    (st0_ptr->sigl == 0)) )
 		{
 		  /* st(0) holds < -1.0 */
 #ifdef PECULIAR_486   /* Stupid 80486 doesn't worry about log(negative). */
-		  st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
+		  changesign(st1_ptr);
 #else
-		  if ( arith_invalid(st1_ptr) ) return;
+		  if ( arith_invalid(1) < 0 ) return;
 #endif PECULIAR_486
-		  pop(); return;
 		}
-#ifdef DENORM_OPERAND
-	      if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	      else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 		return;
-#endif DENORM_OPERAND
-	      st1_ptr->sign ^= SIGN_POS^SIGN_NEG;
-	      pop(); return;
+	      else
+		changesign(st1_ptr);
 	    }
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	  else if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
+	    return;
+	  break;
+
+	case TW_NaN:
+	  if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
 	    return;
-#endif DENORM_OPERAND
-	  pop(); return;
-	}
-      if ( st1_tag == TW_NaN )
-	{
-	  if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	    pop();
-	  return;
 	}
+
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	pop();
-      return;
+      if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+	return;
     }
   else if ( st0_tag == TW_Infinity )
     {
       if ( st1_tag == TW_NaN )
 	{
-	  if ( !real_2op_NaN(st0_ptr, st1_ptr, st1_ptr) )
-	    pop();
-	  return;
+	  if ( real_2op_NaN(st0_ptr, st0_tag, 1, st0_ptr) < 0 )
+	    return;
 	}
-      else if ( st0_ptr->sign == SIGN_NEG )
+      else if ( signnegative(st0_ptr) )
 	{
-	  int exponent = st1_ptr->exp;
 #ifndef PECULIAR_486
 	  /* This should have higher priority than denormals, but... */
-	  if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
+	  if ( arith_invalid(1) < 0 )  /* log(-infinity) */
 	    return;
 #endif PECULIAR_486
-#ifdef DENORM_OPERAND
-	  if ( st1_tag != TW_Zero )
-	    {
-	      if ( (exponent <= EXP_UNDER) && (denormal_operand()) )
-		return;
-	    }
-#endif DENORM_OPERAND
+	  if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
+	    return;
 #ifdef PECULIAR_486
 	  /* Denormal operands actually get higher priority */
-	  if ( arith_invalid(st1_ptr) )  /* log(-infinity) */
+	  if ( arith_invalid(1) < 0 )  /* log(-infinity) */
 	    return;
 #endif PECULIAR_486
-	  pop();
-	  return;
 	}
-      else if ( st1_tag == TW_Zero )
+      else if ( st1_tag == TAG_Zero )
 	{
 	  /* log(infinity) */
-	  if ( !arith_invalid(st1_ptr) )
-	    pop();
-	  return;
+	  if ( arith_invalid(1) < 0 )
+	    return;
 	}
 	
       /* st(1) must be valid here. */
 
-#ifdef DENORM_OPERAND
-      if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+      else if ( (st1_tag == TW_Denormal) && (denormal_operand() < 0) )
 	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;
+      else
+	{
+	  u_char sign = getsign(st1_ptr);
+	  FPU_copy_to_reg1(&CONST_INF, TAG_Special);
+	  setsign(st1_ptr, sign);
+	}
     }
 #ifdef PARANOID
   else
     {
       EXCEPTION(EX_INTERNAL | 0x117);
+      return;
     }
 #endif PARANOID
+
+  FPU_pop();
+  return;
+
 }
 
 
-static void fscale(FPU_REG *st0_ptr)
+static void fscale(FPU_REG *st0_ptr, u_char st0_tag)
 {
-  char st0_tag = st0_ptr->tag;
   FPU_REG *st1_ptr = &st(1);
-  char st1_tag = st1_ptr->tag;
+  u_char st1_tag = FPU_gettagi(1);
   int old_cw = control_word;
-  char sign = st0_ptr->sign;
+  u_char sign = getsign(st0_ptr);
 
   clear_C1();
-  if ( !((st0_tag ^ TW_Valid) | (st1_tag ^ TW_Valid)) )
+  if ( !((st0_tag ^ TAG_Valid) | (st1_tag ^ TAG_Valid)) )
     {
       long scale;
       FPU_REG tmp;
 
-#ifdef DENORM_OPERAND
-      if ( ((st0_ptr->exp <= EXP_UNDER) ||
-	    (st1_ptr->exp <= EXP_UNDER)) && (denormal_operand()) )
-	return;
-#endif DENORM_OPERAND
+      /* Convert register for internal use. */
+      setexponent16(st0_ptr, exponent(st0_ptr));
+
+    valid_scale:
 
-      if ( st1_ptr->exp > EXP_BIAS + 30 )
+      if ( exponent(st1_ptr) > 30 )
 	{
 	  /* 2^31 is far too large, would require 2^(2^30) or 2^(-2^30) */
-	  char sign;
 
-	  if ( st1_ptr->sign == SIGN_POS )
+	  if ( signpositive(st1_ptr) )
 	    {
 	      EXCEPTION(EX_Overflow);
-	      sign = st0_ptr->sign;
-	      reg_move(&CONST_INF, st0_ptr);
-	      st0_ptr->sign = sign;
+	      FPU_copy_to_reg0(&CONST_INF, TAG_Special);
 	    }
 	  else
 	    {
 	      EXCEPTION(EX_Underflow);
-	      sign = st0_ptr->sign;
-	      reg_move(&CONST_Z, st0_ptr);
-	      st0_ptr->sign = sign;
+	      FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
 	    }
+	  setsign(st0_ptr, sign);
 	  return;
 	}
 
       control_word &= ~CW_RC;
       control_word |= RC_CHOP;
-      reg_move(st1_ptr, &tmp);
-      round_to_int(&tmp);               /* This can never overflow here */
+      reg_copy(st1_ptr, &tmp);
+      FPU_round_to_int(&tmp, st1_tag);      /* This can never overflow here */
       control_word = old_cw;
-      scale = st1_ptr->sign ? -tmp.sigl : tmp.sigl;
-      scale += st0_ptr->exp;
-      st0_ptr->exp = scale;
+      scale = signnegative(st1_ptr) ? -tmp.sigl : tmp.sigl;
+      scale += exponent16(st0_ptr);
 
-      /* Use round_reg() to properly detect under/overflow etc */
-      round_reg(st0_ptr, 0, control_word);
+      setexponent16(st0_ptr, scale);
+
+      /* Use FPU_round() to properly detect under/overflow etc */
+      FPU_round(st0_ptr, 0, 0, control_word, sign);
 
       return;
     }
-  else if ( st0_tag == TW_Valid )
+
+  if ( st0_tag == TAG_Special )
+    st0_tag = FPU_Special(st0_ptr);
+  if ( st1_tag == TAG_Special )
+    st1_tag = FPU_Special(st1_ptr);
+
+  if ( (st0_tag == TAG_Valid) || (st0_tag == TW_Denormal) )
     {
-      if ( st1_tag == TW_Zero )
+      switch ( st1_tag )
 	{
-
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+	case TAG_Valid:
+	  /* st(0) must be a denormal */
+	  if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
 
+	  FPU_to_exp16(st0_ptr, st0_ptr);  /* Will not be left on stack */
+	  goto valid_scale;
+
+	case TAG_Zero:
+	  if ( st0_tag == TW_Denormal )
+	    denormal_operand();
 	  return;
-	}
-      if ( st1_tag == TW_Infinity )
-	{
-#ifdef DENORM_OPERAND
-	  if ( (st0_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
+
+	case TW_Denormal:
+	  denormal_operand();
+	  return;
+
+	case TW_Infinity:
+	  if ( (st0_tag == TW_Denormal) && (denormal_operand() < 0) )
 	    return;
-#endif DENORM_OPERAND
 
-	  if ( st1_ptr->sign == SIGN_POS )
-	    { reg_move(&CONST_INF, st0_ptr); }
+	  if ( signpositive(st1_ptr) )
+	    FPU_copy_to_reg0(&CONST_INF, TAG_Special);
 	  else
-	      reg_move(&CONST_Z, st0_ptr);
-	  st0_ptr->sign = sign;
+	    FPU_copy_to_reg0(&CONST_Z, TAG_Zero);
+	  setsign(st0_ptr, sign);
+	  return;
+
+	case TW_NaN:
+	  real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
 	  return;
 	}
-      if ( st1_tag == TW_NaN )
-	{ real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
-  else if ( st0_tag == TW_Zero )
+  else if ( st0_tag == TAG_Zero )
     {
-      if ( st1_tag == TW_Valid )
+      switch ( st1_tag )
 	{
+	case TAG_Valid:
+	case TAG_Zero:
+	  return;
 
-#ifdef DENORM_OPERAND
-	  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	    return;
-#endif DENORM_OPERAND
+	case TW_Denormal:
+	  denormal_operand();
+	  return;
 
+	case TW_Infinity:
+	  if ( signpositive(st1_ptr) )
+	    arith_invalid(0); /* Zero scaled by +Infinity */
+	  return;
+
+	case TW_NaN:
+	  real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
 	  return;
 	}
-      else if ( st1_tag == TW_Zero ) { return; }
-      else if ( st1_tag == TW_Infinity )
-	{
-	  if ( st1_ptr->sign == SIGN_NEG )
-	    return;
-	  else
-	    {
-	      arith_invalid(st0_ptr); /* Zero scaled by +Infinity */
-	      return;
-	    }
-	}
-      else if ( st1_tag == TW_NaN )
-	{ real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
   else if ( st0_tag == TW_Infinity )
     {
-      if ( st1_tag == TW_Valid )
+      switch ( st1_tag )
 	{
+	case TAG_Valid:
+	case TAG_Zero:
+	  return;
 
-#ifdef DENORM_OPERAND
-	  if ( (st1_ptr->exp <= EXP_UNDER) && (denormal_operand()) )
-	    return;
-#endif DENORM_OPERAND
+	case TW_Denormal:
+	  denormal_operand();
+	  return;
 
+	case TW_Infinity:
+	  if ( signnegative(st1_ptr) )
+	    arith_invalid(0); /* Infinity scaled by -Infinity */
 	  return;
-	}
-      if ( ((st1_tag == TW_Infinity) && (st1_ptr->sign == SIGN_POS))
-	  || (st1_tag == TW_Zero) )
-	return;
-      else if ( st1_tag == TW_Infinity )
-	{
-	  arith_invalid(st0_ptr); /* Infinity scaled by -Infinity */
+
+	case TW_NaN:
+	  real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr);
 	  return;
 	}
-      else if ( st1_tag == TW_NaN )
-	{ real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
     }
   else if ( st0_tag == TW_NaN )
     {
-      if ( st1_tag != TW_Empty )
-	{ real_2op_NaN(st0_ptr, st1_ptr, st0_ptr); return; }
+      if ( st1_tag != TAG_Empty )
+	{ real_2op_NaN(st1_ptr, st1_tag, 0, st0_ptr); return; }
     }
 
 #ifdef PARANOID
-  if ( !((st0_tag == TW_Empty) || (st1_tag == TW_Empty)) )
+  if ( !((st0_tag == TAG_Empty) || (st1_tag == TAG_Empty)) )
     {
       EXCEPTION(EX_INTERNAL | 0x115);
       return;
@@ -1690,7 +1802,7 @@
 #endif
 
   /* At least one of st(0), st(1) must be empty */
-  stack_underflow();
+  FPU_stack_underflow();
 
 }
 
@@ -1698,21 +1810,22 @@
 /*---------------------------------------------------------------------------*/
 
 static FUNC_ST0 const trig_table_a[] = {
-  f2xm1, fyl2x, fptan, fpatan, fxtract, fprem1, fdecstp, fincstp
+  f2xm1, fyl2x, fptan, fpatan,
+  fxtract, fprem1, (FUNC_ST0)fdecstp, (FUNC_ST0)fincstp
 };
 
-void trig_a(void)
+void FPU_triga(void)
 {
-  (trig_table_a[FPU_rm])(&st(0));
+  (trig_table_a[FPU_rm])(&st(0), FPU_gettag0());
 }
 
 
 static FUNC_ST0 const trig_table_b[] =
   {
-    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, fsin, fcos
+    fprem, fyl2xp1, fsqrt_, fsincos, frndint_, fscale, (FUNC_ST0)fsin, fcos
   };
 
-void trig_b(void)
+void FPU_trigb(void)
 {
-  (trig_table_b[FPU_rm])(&st(0));
+  (trig_table_b[FPU_rm])(&st(0), FPU_gettag0());
 }

FUNET's LINUX-ADM group, linux-adm@nic.funet.fi
TCL-scripts by Sam Shen, slshen@lbl.gov