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             reg_move(b, dest);
  93           return;
  94         }
  95       else if (b->tag == TW_Zero)
  96         { reg_move(a, dest); return; }
  97       else if (a->tag == TW_Infinity)
  98         {
  99           if (b->tag != TW_Infinity)
 100             { reg_move(a, dest); return; }
 101           /* They are both + or - infinity */
 102           if (a->sign == b->sign)
 103             { reg_move(a, dest); return; }
 104           reg_move(&CONST_QNaN, dest);  /* inf - inf is undefined. */
 105           return;
 106         }
 107       else if (b->tag == TW_Infinity)
 108         { reg_move(b, dest); return; }
 109     }
 110 #ifdef PARANOID
 111   EXCEPTION(EX_INTERNAL|0x101);
 112 #endif
 113 }
 114 
 115 
 116 /* Subtract b from a.  (a-b) -> dest */
 117 void reg_sub(FPU_REG *a, FPU_REG *b, FPU_REG *dest, int control_w)
     /* [previous][next][first][last][top][bottom][index][help] */
 118 {
 119   int diff;
 120 
 121   if ( !(a->tag | b->tag) )
 122     {
 123       /* Both registers are valid */
 124       diff = a->exp - b->exp;
 125       if (!diff)
 126         {
 127           diff = a->sigh - b->sigh;  /* Works only if ms bits are identical */
 128           if (!diff)
 129             {
 130               diff = a->sigl > b->sigl;
 131               if (!diff)
 132                 diff = -(a->sigl < b->sigl);
 133             }
 134         }
 135       
 136       switch (a->sign*2 + b->sign)
 137         {
 138         case 0: /* P - P */
 139         case 3: /* N - N */
 140           if (diff > 0)
 141             {
 142               reg_u_sub(a, b, dest, control_w);
 143               dest->sign = a->sign;
 144             }
 145           else if ( diff == 0 )
 146             {
 147               reg_move(&CONST_Z, dest);
 148               /* sign depends upon rounding mode */
 149               dest->sign = ((control_w & CW_RC) != RC_DOWN)
 150                 ? SIGN_POS : SIGN_NEG;
 151             }
 152           else
 153             {
 154               reg_u_sub(b, a, dest, control_w);
 155               dest->sign = a->sign ^ SIGN_POS^SIGN_NEG;
 156             }
 157           return;
 158         case 1: /* P - N */
 159           reg_u_add(a, b, dest, control_w);
 160           dest->sign = SIGN_POS;
 161           return;
 162         case 2: /* N - P */
 163           reg_u_add(a, b, dest, control_w);
 164           dest->sign = SIGN_NEG;
 165           return;
 166         }
 167     }
 168   else
 169     {
 170       if ( (a->tag == TW_NaN) || (b->tag == TW_NaN) )
 171         { real_2op_NaN(a, b, dest); return; }
 172       else if (b->tag == TW_Zero)
 173         { 
 174           if (a->tag == TW_Zero)
 175             {
 176               char same_signs = !(a->sign ^ b->sign);
 177               /* Both are zero, result will be zero. */
 178               reg_move(a, dest); /* Answer for different signs. */
 179               if (same_signs)
 180                 {
 181                   /* Sign depends upon rounding mode */
 182                   dest->sign = ((control_w & CW_RC) != RC_DOWN)
 183                     ? SIGN_POS : SIGN_NEG;
 184                 }
 185             }
 186           else
 187             reg_move(a, dest);
 188           return;
 189         }
 190       else if (a->tag == TW_Zero)
 191         {
 192           reg_move(b, dest);
 193           dest->sign ^= SIGN_POS^SIGN_NEG;
 194           return;
 195         }
 196       else if (a->tag == TW_Infinity)
 197         {
 198           if (b->tag != TW_Infinity)
 199             { reg_move(a, dest); return; }
 200           if (a->sign == b->sign)
 201             { reg_move(&CONST_QNaN, dest); return; }
 202           reg_move(a, dest);
 203           return;
 204         }
 205       else if (b->tag == TW_Infinity)
 206         {
 207           reg_move(b, dest);
 208           dest->sign ^= SIGN_POS^SIGN_NEG;
 209           return;
 210         }
 211     }
 212 #ifdef PARANOID
 213   EXCEPTION(EX_INTERNAL|0x110);
 214 #endif
 215 }
 216 

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