root/kernel/FPU-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 apm233m@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 void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
     /* [previous][next][first][last][top][bottom][index][help] */
  26 {
  27   int diff;
  28   
  29   if ( !(a->tag | b->tag) )
  30     {
  31       /* Both registers are valid */
  32       if (!(a->sign ^ b->sign))
  33         {
  34           /* signs are the same */
  35           reg_u_add(a, b, dest, control_w);
  36           dest->sign = a->sign;
  37           return;
  38         }
  39       
  40       /* The signs are different, so do a subtraction */
  41       diff = a->exp - b->exp;
  42       if (!diff)
  43         {
  44           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
  45           if (!diff)
  46             {
  47               diff = a->sigl > b->sigl;
  48               if (!diff)
  49                 diff = -(a->sigl < b->sigl);
  50             }
  51         }
  52       
  53       if (diff > 0)
  54         {
  55           reg_u_sub(a, b, dest, control_w);
  56           dest->sign = a->sign;
  57         }
  58       else if ( diff == 0 )
  59         {
  60           reg_move(&CONST_Z, dest);
  61           /* sign depends upon rounding mode */
  62           dest->sign = ((control_w & CW_RC) != RC_DOWN)
  63             ? SIGN_POS : SIGN_NEG;
  64         }
  65       else
  66         {
  67           reg_u_sub(b, a, dest, control_w);
  68           dest->sign = b->sign;
  69         }
  70       return;
  71     }
  72   else
  73     {
  74       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
  75         { real_2op_NaN(a, b, dest); return; }
  76       else if (a->tag == TW_Zero)
  77         {
  78           if (b->tag == TW_Zero)
  79             {
  80               char different_signs = a->sign ^ b->sign;
  81               /* Both are zero, result will be zero. */
  82               reg_move(a, dest);
  83               if (different_signs)
  84                 {
  85                   /* Signs are different. */
  86                   /* Sign of answer depends upon rounding mode. */
  87                   dest->sign = ((control_w & CW_RC) != RC_DOWN)
  88                     ? SIGN_POS : SIGN_NEG;
  89                 }
  90             }
  91           else
  92             {
  93 #ifdef DENORM_OPERAND
  94               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
  95                   denormal_operand() )
  96                 return;
  97 #endif DENORM_OPERAND
  98               reg_move(b, dest);
  99             }
 100           return;
 101         }
 102       else if (b->tag == TW_Zero)
 103         {
 104 #ifdef DENORM_OPERAND
 105           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 106               denormal_operand() )
 107             return;
 108 #endif DENORM_OPERAND
 109           reg_move(a, dest); return;
 110         }
 111       else if (a->tag == TW_Infinity)
 112         {
 113           if (b->tag != TW_Infinity)
 114             {
 115 #ifdef DENORM_OPERAND
 116               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 117                   denormal_operand() )
 118                 return;
 119 #endif DENORM_OPERAND
 120               reg_move(a, dest); return;
 121             }
 122           if (a->sign == b->sign)
 123             {
 124               /* They are both + or - infinity */
 125               reg_move(a, dest); return;
 126             }
 127           arith_invalid(dest);  /* Infinity-Infinity is undefined. */
 128           return;
 129         }
 130       else if (b->tag == TW_Infinity)
 131         {
 132 #ifdef DENORM_OPERAND
 133           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 134               denormal_operand() )
 135             return;
 136 #endif DENORM_OPERAND
 137           reg_move(b, dest); return;
 138         }
 139     }
 140 #ifdef PARANOID
 141   EXCEPTION(EX_INTERNAL|0x101);
 142 #endif
 143 }
 144 
 145 
 146 /* Subtract b from a.  (a-b) -> dest */
 147 void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
     /* [previous][next][first][last][top][bottom][index][help] */
 148 {
 149   int diff;
 150 
 151   if ( !(a->tag | b->tag) )
 152     {
 153       /* Both registers are valid */
 154       diff = a->exp - b->exp;
 155       if (!diff)
 156         {
 157           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
 158           if (!diff)
 159             {
 160               diff = a->sigl > b->sigl;
 161               if (!diff)
 162                 diff = -(a->sigl < b->sigl);
 163             }
 164         }
 165       
 166       switch (a->sign*2 + b->sign)
 167         {
 168         case 0: /* P - P */
 169         case 3: /* N - N */
 170           if (diff > 0)
 171             {
 172               reg_u_sub(a, b, dest, control_w);
 173               dest->sign = a->sign;
 174             }
 175           else if ( diff == 0 )
 176             {
 177 #ifdef DENORM_OPERAND
 178               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 179                   denormal_operand() )
 180                 return;
 181 #endif DENORM_OPERAND
 182               reg_move(&CONST_Z, dest);
 183               /* sign depends upon rounding mode */
 184               dest->sign = ((control_w & CW_RC) != RC_DOWN)
 185                 ? SIGN_POS : SIGN_NEG;
 186             }
 187           else
 188             {
 189               reg_u_sub(b, a, dest, control_w);
 190               dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
 191             }
 192           return;
 193         case 1: /* P - N */
 194           reg_u_add(a, b, dest, control_w);
 195           dest->sign = SIGN_POS;
 196           return;
 197         case 2: /* N - P */
 198           reg_u_add(a, b, dest, control_w);
 199           dest->sign = SIGN_NEG;
 200           return;
 201         }
 202     }
 203   else
 204     {
 205       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
 206         { real_2op_NaN(a, b, dest); return; }
 207       else if (b->tag == TW_Zero)
 208         { 
 209           if (a->tag == TW_Zero)
 210             {
 211               char same_signs = !(a->sign ^ b->sign);
 212               /* Both are zero, result will be zero. */
 213               reg_move(a, dest); /* Answer for different signs. */
 214               if (same_signs)
 215                 {
 216                   /* Sign depends upon rounding mode */
 217                   dest->sign = ((control_w & CW_RC) != RC_DOWN)
 218                     ? SIGN_POS : SIGN_NEG;
 219                 }
 220             }
 221           else
 222             {
 223 #ifdef DENORM_OPERAND
 224               if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 225                   denormal_operand() )
 226                 return;
 227 #endif DENORM_OPERAND
 228               reg_move(a, dest);
 229             }
 230           return;
 231         }
 232       else if (a->tag == TW_Zero)
 233         {
 234 #ifdef DENORM_OPERAND
 235           if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 236               denormal_operand() )
 237             return;
 238 #endif DENORM_OPERAND
 239           reg_move(b, dest);
 240           dest->sign ^= SIGN_POS^SIGN_NEG;
 241           return;
 242         }
 243       else if (a->tag == TW_Infinity)
 244         {
 245           if (b->tag != TW_Infinity)
 246             {
 247 #ifdef DENORM_OPERAND
 248               if ( (b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
 249                   denormal_operand() )
 250                 return;
 251 #endif DENORM_OPERAND
 252               reg_move(a, dest); return;
 253             }
 254           /* Both args are Infinity */
 255           if (a->sign == b->sign)
 256             {
 257               arith_invalid(dest);      /* Infinity-Infinity is undefined. */
 258               return;
 259             }
 260           reg_move(a, dest);
 261           return;
 262         }
 263       else if (b->tag == TW_Infinity)
 264         {
 265 #ifdef DENORM_OPERAND
 266           if ( (a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
 267               denormal_operand() )
 268             return;
 269 #endif DENORM_OPERAND
 270           reg_move(b, dest);
 271           dest->sign ^= SIGN_POS^SIGN_NEG;
 272           return;
 273         }
 274     }
 275 #ifdef PARANOID
 276   EXCEPTION(EX_INTERNAL|0x110);
 277 #endif
 278 }
 279 

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