OpenCores
URL https://opencores.org/ocsvn/or1k/or1k/trunk

Subversion Repositories or1k

[/] [or1k/] [trunk/] [rc203soc/] [sw/] [uClinux/] [arch/] [i386/] [math-emu/] [reg_add_sub.c] - Blame information for rev 1777

Go to most recent revision | Details | Compare with Previous | View Log

Line No. Rev Author Line
1 1623 jcastillo
/*---------------------------------------------------------------------------+
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)
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)
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
 

powered by: WebSVN 2.1.0

© copyright 1999-2024 OpenCores.org, equivalent to Oliscience, all rights reserved. OpenCores®, registered trademark.