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    W. Metzenthen, 22 Parker St, Ormond, Vic 3163,      |
   7  |                       Australia.  E-mail apm233m@vaxc.cc.monash.edu.au    |
   8  |                                                                           |
   9  |                                                                           |
  10  +---------------------------------------------------------------------------*/
  11 
  12 /*---------------------------------------------------------------------------+
  13  | For each function, the destination may be any FPU_REG, including one of   |
  14  | the source FPU_REGs.                                                      |
  15  +---------------------------------------------------------------------------*/
  16 
  17 #include "exception.h"
  18 #include "reg_constant.h"
  19 #include "fpu_emu.h"
  20 #include "control_w.h"
  21 #include "fpu_system.h"
  22 
  23 
  24 void reg_add(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
  25 {
  26   int diff;
  27   
  28   if ( !(a->tag | b->tag) )
  29     {
  30       /* Both registers are valid */
  31       if (!(a->sign ^ b->sign))
  32         {
  33           /* signs are the same */
  34           reg_u_add(a, b, dest);
  35           dest->sign = a->sign;
  36           return;
  37         }
  38       
  39       /* The signs are different, so do a subtraction */
  40       diff = a->exp - b->exp;
  41       if (!diff)
  42         {
  43           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
  44           if (!diff)
  45             {
  46               diff = a->sigl > b->sigl;
  47               if (!diff)
  48                 diff = -(a->sigl < b->sigl);
  49             }
  50         }
  51       
  52       if (diff > 0)
  53         {
  54           reg_u_sub(a, b, dest);
  55           dest->sign = a->sign;
  56         }
  57       else if ( diff == 0 )
  58         {
  59           reg_move(&CONST_Z, dest);
  60           /* sign depends upon rounding mode */
  61           dest->sign = ((control_word & CW_RC) != RC_DOWN)
  62             ? SIGN_POS : SIGN_NEG;
  63         }
  64       else
  65         {
  66           reg_u_sub(b, a, dest);
  67           dest->sign = b->sign;
  68         }
  69       return;
  70     }
  71   else
  72     {
  73       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
  74         { real_2op_NaN(a, b, dest); return; }
  75       else if (a->tag == TW_Zero)
  76         {
  77           if (b->tag == TW_Zero)
  78             {
  79               char different_signs = a->sign ^ b->sign;
  80               /* Both are zero, result will be zero. */
  81               reg_move(a, dest);
  82               if (different_signs)
  83                 {
  84                   /* Signs are different. */
  85                   /* Sign of answer depends upon rounding mode. */
  86                   dest->sign = ((control_word & CW_RC) != RC_DOWN)
  87                     ? SIGN_POS : SIGN_NEG;
  88                 }
  89             }
  90           else
  91             reg_move(b, dest);
  92           return;
  93         }
  94       else if (b->tag == TW_Zero)
  95         { reg_move(a, dest); return; }
  96       else if (a->tag == TW_Infinity)
  97         {
  98           if (b->tag != TW_Infinity)
  99             { reg_move(a, dest); return; }
 100           /* They are both + or - infinity */
 101           if (a->sign == b->sign)
 102             { reg_move(a, dest); return; }
 103           reg_move(&CONST_QNaN, dest);  /* inf - inf is undefined. */
 104           return;
 105         }
 106       else if (b->tag == TW_Infinity)
 107         { reg_move(b, dest); return; }
 108     }
 109 #ifdef PARANOID
 110   EXCEPTION(EX_INTERNAL|0x101);
 111 #endif
 112 }
 113 
 114 
 115 /* Subtract b from a.  (a-b) -> dest */
 116 void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest)
     /* [previous][next][first][last][top][bottom][index][help] */
 117 {
 118   int diff;
 119 
 120   if ( !(a->tag | b->tag) )
 121     {
 122       /* Both registers are valid */
 123       diff = a->exp - b->exp;
 124       if (!diff)
 125         {
 126           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
 127           if (!diff)
 128             {
 129               diff = a->sigl > b->sigl;
 130               if (!diff)
 131                 diff = -(a->sigl < b->sigl);
 132             }
 133         }
 134       
 135       switch (a->sign*2 + b->sign)
 136         {
 137         case 0: /* P - P */
 138         case 3: /* N - N */
 139           if (diff > 0)
 140             {
 141               reg_u_sub(a, b, dest);
 142               dest->sign = a->sign;
 143             }
 144           else if ( diff == 0 )
 145             {
 146               reg_move(&CONST_Z, dest);
 147               /* sign depends upon rounding mode */
 148               dest->sign = ((control_word & CW_RC) != RC_DOWN)
 149                 ? SIGN_POS : SIGN_NEG;
 150             }
 151           else
 152             {
 153               reg_u_sub(b, a, dest);
 154               dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
 155             }
 156           return;
 157         case 1: /* P - N */
 158           reg_u_add(a, b, dest);
 159           dest->sign = SIGN_POS;
 160           return;
 161         case 2: /* N - P */
 162           reg_u_add(a, b, dest);
 163           dest->sign = SIGN_NEG;
 164           return;
 165         }
 166     }
 167   else
 168     {
 169       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
 170         { real_2op_NaN(a, b, dest); return; }
 171       else if (b->tag == TW_Zero)
 172         { 
 173           if (a->tag == TW_Zero)
 174             {
 175               char same_signs = !(a->sign ^ b->sign);
 176               /* Both are zero, result will be zero. */
 177               reg_move(a, dest); /* Answer for different signs. */
 178               if (same_signs)
 179                 {
 180                   /* Sign depends upon rounding mode */
 181                   dest->sign = ((control_word & CW_RC) != RC_DOWN)
 182                     ? SIGN_POS : SIGN_NEG;
 183                 }
 184             }
 185           else
 186             reg_move(a, dest);
 187           return;
 188         }
 189       else if (a->tag == TW_Zero)
 190         {
 191           reg_move(b, dest);
 192           dest->sign ^= SIGN_POS^SIGN_NEG;
 193           return;
 194         }
 195       else if (a->tag == TW_Infinity)
 196         {
 197           if (b->tag != TW_Infinity)
 198             { reg_move(a, dest); return; }
 199           if (a->sign == b->sign)
 200             { reg_move(&CONST_QNaN, dest); return; }
 201           reg_move(a, dest);
 202           return;
 203         }
 204       else if (b->tag == TW_Infinity)
 205         {
 206           reg_move(b, dest);
 207           dest->sign ^= SIGN_POS^SIGN_NEG;
 208           return;
 209         }
 210     }
 211 #ifdef PARANOID
 212   EXCEPTION(EX_INTERNAL|0x110);
 213 #endif
 214 }
 215 

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