root/arch/i386/math-emu/reg_add_sub.c

/* [previous][next][first][last][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. reg_add
  2. reg_sub

   1 /*---------------------------------------------------------------------------+
   2  |  reg_add_sub.c                                                            |
   3  |                                                                           |
   4  | Functions to add or subtract two registers and put the result in a third. |
   5  |                                                                           |
   6  | Copyright (C) 1992,1993                                                   |
   7  |                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   8  |                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au    |
   9  |                                                                           |
  10  |                                                                           |
  11  +---------------------------------------------------------------------------*/
  12 
  13 /*---------------------------------------------------------------------------+
  14  | For each function, the destination may be any FPU_REG, including one of   |
  15  | the source FPU_REGs.                                                      |
  16  +---------------------------------------------------------------------------*/
  17 
  18 #include "exception.h"
  19 #include "reg_constant.h"
  20 #include "fpu_emu.h"
  21 #include "control_w.h"
  22 #include "fpu_system.h"
  23 
  24 
  25 int reg_add(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
     /* [previous][next][first][last][top][bottom][index][help] */
  26 {
  27   char saved_sign = dest->sign;
  28   int diff;
  29   
  30   if ( !(a->tag | b->tag) )
  31     {
  32       /* Both registers are valid */
  33       if (!(a->sign ^ b->sign))
  34         {
  35           /* signs are the same */
  36           dest->sign = a->sign;
  37           if ( reg_u_add(a, b, dest, control_w) )
  38             {
  39               dest->sign = saved_sign;
  40               return 1;
  41             }
  42           return 0;
  43         }
  44       
  45       /* The signs are different, so do a subtraction */
  46       diff = a->exp - b->exp;
  47       if (!diff)
  48         {
  49           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
  50           if (!diff)
  51             {
  52               diff = a->sigl > b->sigl;
  53               if (!diff)
  54                 diff = -(a->sigl < b->sigl);
  55             }
  56         }
  57       
  58       if (diff > 0)
  59         {
  60           dest->sign = a->sign;
  61           if ( reg_u_sub(a, b, dest, control_w) )
  62             {
  63               dest->sign = saved_sign;
  64               return 1;
  65             }
  66         }
  67       else if ( diff == 0 )
  68         {
  69 #ifdef DENORM_OPERAND
  70           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  71               denormal_operand() )
  72             return 1;
  73 #endif DENORM_OPERAND
  74           reg_move(&CONST_Z, dest);
  75           /* sign depends upon rounding mode */
  76           dest->sign = ((control_w & CW_RC) != RC_DOWN)
  77             ? SIGN_POS : SIGN_NEG;
  78         }
  79       else
  80         {
  81           dest->sign = b->sign;
  82           if ( reg_u_sub(b, a, dest, control_w) )
  83             {
  84               dest->sign = saved_sign;
  85               return 1;
  86             }
  87         }
  88       return 0;
  89     }
  90   else
  91     {
  92       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
  93         { return real_2op_NaN(a, b, dest); }
  94       else if (a->tag == TW_Zero)
  95         {
  96           if (b->tag == TW_Zero)
  97             {
  98               char different_signs = a->sign ^ b->sign;
  99               /* Both are zero, result will be zero. */
 100               reg_move(a, dest);
 101               if (different_signs)
 102                 {
 103                   /* Signs are different. */
 104                   /* Sign of answer depends upon rounding mode. */
 105                   dest->sign = ((control_w & CW_RC) != RC_DOWN)
 106                     ? SIGN_POS : SIGN_NEG;
 107                 }
 108             }
 109           else
 110             {
 111 #ifdef DENORM_OPERAND
 112               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 113                   denormal_operand() )
 114                 return 1;
 115 #endif DENORM_OPERAND
 116               reg_move(b, dest);
 117             }
 118           return 0;
 119         }
 120       else if (b->tag == TW_Zero)
 121         {
 122 #ifdef DENORM_OPERAND
 123           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 124               denormal_operand() )
 125             return 1;
 126 #endif DENORM_OPERAND
 127           reg_move(a, dest); return 0;
 128         }
 129       else if (a->tag == TW_Infinity)
 130         {
 131           if (b->tag != TW_Infinity)
 132             {
 133 #ifdef DENORM_OPERAND
 134               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 135                   denormal_operand() )
 136                 return 1;
 137 #endif DENORM_OPERAND
 138               reg_move(a, dest); return 0;
 139             }
 140           if (a->sign == b->sign)
 141             {
 142               /* They are both + or - infinity */
 143               reg_move(a, dest); return 0;
 144             }
 145           return arith_invalid(dest);   /* Infinity-Infinity is undefined. */
 146         }
 147       else if (b->tag == TW_Infinity)
 148         {
 149 #ifdef DENORM_OPERAND
 150           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 151               denormal_operand() )
 152             return 1;
 153 #endif DENORM_OPERAND
 154           reg_move(b, dest); return 0;
 155         }
 156     }
 157 #ifdef PARANOID
 158   EXCEPTION(EX_INTERNAL|0x101);
 159 #endif
 160   return 1;
 161 }
 162 
 163 
 164 /* Subtract b from a.  (a-b) -> dest */
 165 int reg_sub(FPU_REG const *a, FPU_REG const *b, FPU_REG *dest, int control_w)
     /* [previous][next][first][last][top][bottom][index][help] */
 166 {
 167   char saved_sign = dest->sign;
 168   int diff;
 169 
 170   if ( !(a->tag | b->tag) )
 171     {
 172       /* Both registers are valid */
 173       diff = a->exp - b->exp;
 174       if (!diff)
 175         {
 176           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
 177           if (!diff)
 178             {
 179               diff = a->sigl > b->sigl;
 180               if (!diff)
 181                 diff = -(a->sigl < b->sigl);
 182             }
 183         }
 184 
 185       switch (a->sign*2 + b->sign)
 186         {
 187         case 0: /* P - P */
 188         case 3: /* N - N */
 189           if (diff > 0)
 190             {
 191               /* |a| > |b| */
 192               dest->sign = a->sign;
 193               if ( reg_u_sub(a, b, dest, control_w) )
 194                 {
 195                   dest->sign = saved_sign;
 196                   return 1;
 197                 }
 198               return 0;
 199             }
 200           else if ( diff == 0 )
 201             {
 202 #ifdef DENORM_OPERAND
 203               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 204                   denormal_operand() )
 205                 return 1;
 206 #endif DENORM_OPERAND
 207               reg_move(&CONST_Z, dest);
 208               /* sign depends upon rounding mode */
 209               dest->sign = ((control_w & CW_RC) != RC_DOWN)
 210                 ? SIGN_POS : SIGN_NEG;
 211             }
 212           else
 213             {
 214               dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
 215               if ( reg_u_sub(b, a, dest, control_w) )
 216                 {
 217                   dest->sign = saved_sign;
 218                   return 1;
 219                 }
 220             }
 221           break;
 222         case 1: /* P - N */
 223           dest->sign = SIGN_POS;
 224           if ( reg_u_add(a, b, dest, control_w) )
 225             {
 226               dest->sign = saved_sign;
 227               return 1;
 228             }
 229           break;
 230         case 2: /* N - P */
 231           dest->sign = SIGN_NEG;
 232           if ( reg_u_add(a, b, dest, control_w) )
 233             {
 234               dest->sign = saved_sign;
 235               return 1;
 236             }
 237           break;
 238         }
 239       return 0;
 240     }
 241   else
 242     {
 243       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
 244         { return real_2op_NaN(b, a, dest); }
 245       else if (b->tag == TW_Zero)
 246         { 
 247           if (a->tag == TW_Zero)
 248             {
 249               char same_signs = !(a->sign ^ b->sign);
 250               /* Both are zero, result will be zero. */
 251               reg_move(a, dest); /* Answer for different signs. */
 252               if (same_signs)
 253                 {
 254                   /* Sign depends upon rounding mode */
 255                   dest->sign = ((control_w & CW_RC) != RC_DOWN)
 256                     ? SIGN_POS : SIGN_NEG;
 257                 }
 258             }
 259           else
 260             {
 261 #ifdef DENORM_OPERAND
 262               if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 263                   denormal_operand() )
 264                 return 1;
 265 #endif DENORM_OPERAND
 266               reg_move(a, dest);
 267             }
 268           return 0;
 269         }
 270       else if (a->tag == TW_Zero)
 271         {
 272 #ifdef DENORM_OPERAND
 273           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 274               denormal_operand() )
 275             return 1;
 276 #endif DENORM_OPERAND
 277           reg_move(b, dest);
 278           dest->sign ^= SIGN_POS^SIGN_NEG;
 279           return 0;
 280         }
 281       else if (a->tag == TW_Infinity)
 282         {
 283           if (b->tag != TW_Infinity)
 284             {
 285 #ifdef DENORM_OPERAND
 286               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 287                   denormal_operand() )
 288                 return 1;
 289 #endif DENORM_OPERAND
 290               reg_move(a, dest); return 0;
 291             }
 292           /* Both args are Infinity */
 293           if (a->sign == b->sign)
 294             {
 295               /* Infinity-Infinity is undefined. */
 296               return arith_invalid(dest);
 297             }
 298           reg_move(a, dest);
 299           return 0;
 300         }
 301       else if (b->tag == TW_Infinity)
 302         {
 303 #ifdef DENORM_OPERAND
 304           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 305               denormal_operand() )
 306             return 1;
 307 #endif DENORM_OPERAND
 308           reg_move(b, dest);
 309           dest->sign ^= SIGN_POS^SIGN_NEG;
 310           return 0;
 311         }
 312     }
 313 #ifdef PARANOID
 314   EXCEPTION(EX_INTERNAL|0x110);
 315 #endif
 316   return 1;
 317 }
 318 

/* [previous][next][first][last][top][bottom][index][help] */