Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
reg_add_sub.c
Go to the documentation of this file.
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,1997 |
7  | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8  | E-mail [email protected] |
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  | Each function returns 0 if the answer is o.k., otherwise a non-zero |
17  | value is returned, indicating either an exception condition or an |
18  | internal error. |
19  +---------------------------------------------------------------------------*/
20 
21 #include "exception.h"
22 #include "reg_constant.h"
23 #include "fpu_emu.h"
24 #include "control_w.h"
25 #include "fpu_system.h"
26 
27 static
28 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
29  FPU_REG const *b, u_char tagb, u_char signb,
30  FPU_REG * dest, int deststnr, int control_w);
31 
32 /*
33  Operates on st(0) and st(n), or on st(0) and temporary data.
34  The destination must be one of the source st(x).
35  */
36 int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
37 {
38  FPU_REG *a = &st(0);
39  FPU_REG *dest = &st(deststnr);
40  u_char signb = getsign(b);
41  u_char taga = FPU_gettag0();
42  u_char signa = getsign(a);
43  u_char saved_sign = getsign(dest);
44  int diff, tag, expa, expb;
45 
46  if (!(taga | tagb)) {
47  expa = exponent(a);
48  expb = exponent(b);
49 
50  valid_add:
51  /* Both registers are valid */
52  if (!(signa ^ signb)) {
53  /* signs are the same */
54  tag =
55  FPU_u_add(a, b, dest, control_w, signa, expa, expb);
56  } else {
57  /* The signs are different, so do a subtraction */
58  diff = expa - expb;
59  if (!diff) {
60  diff = a->sigh - b->sigh; /* This works only if the ms bits
61  are identical. */
62  if (!diff) {
63  diff = a->sigl > b->sigl;
64  if (!diff)
65  diff = -(a->sigl < b->sigl);
66  }
67  }
68 
69  if (diff > 0) {
70  tag =
71  FPU_u_sub(a, b, dest, control_w, signa,
72  expa, expb);
73  } else if (diff < 0) {
74  tag =
75  FPU_u_sub(b, a, dest, control_w, signb,
76  expb, expa);
77  } else {
78  FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
79  /* sign depends upon rounding mode */
80  setsign(dest, ((control_w & CW_RC) != RC_DOWN)
81  ? SIGN_POS : SIGN_NEG);
82  return TAG_Zero;
83  }
84  }
85 
86  if (tag < 0) {
87  setsign(dest, saved_sign);
88  return tag;
89  }
90  FPU_settagi(deststnr, tag);
91  return tag;
92  }
93 
94  if (taga == TAG_Special)
95  taga = FPU_Special(a);
96  if (tagb == TAG_Special)
97  tagb = FPU_Special(b);
98 
99  if (((taga == TAG_Valid) && (tagb == TW_Denormal))
100  || ((taga == TW_Denormal) && (tagb == TAG_Valid))
101  || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
102  FPU_REG x, y;
103 
104  if (denormal_operand() < 0)
105  return FPU_Exception;
106 
107  FPU_to_exp16(a, &x);
108  FPU_to_exp16(b, &y);
109  a = &x;
110  b = &y;
111  expa = exponent16(a);
112  expb = exponent16(b);
113  goto valid_add;
114  }
115 
116  if ((taga == TW_NaN) || (tagb == TW_NaN)) {
117  if (deststnr == 0)
118  return real_2op_NaN(b, tagb, deststnr, a);
119  else
120  return real_2op_NaN(a, taga, deststnr, a);
121  }
122 
123  return add_sub_specials(a, taga, signa, b, tagb, signb,
124  dest, deststnr, control_w);
125 }
126 
127 /* Subtract b from a. (a-b) -> dest */
128 int FPU_sub(int flags, int rm, int control_w)
129 {
130  FPU_REG const *a, *b;
131  FPU_REG *dest;
132  u_char taga, tagb, signa, signb, saved_sign, sign;
133  int diff, tag = 0, expa, expb, deststnr;
134 
135  a = &st(0);
136  taga = FPU_gettag0();
137 
138  deststnr = 0;
139  if (flags & LOADED) {
140  b = (FPU_REG *) rm;
141  tagb = flags & 0x0f;
142  } else {
143  b = &st(rm);
144  tagb = FPU_gettagi(rm);
145 
146  if (flags & DEST_RM)
147  deststnr = rm;
148  }
149 
150  signa = getsign(a);
151  signb = getsign(b);
152 
153  if (flags & REV) {
154  signa ^= SIGN_NEG;
155  signb ^= SIGN_NEG;
156  }
157 
158  dest = &st(deststnr);
159  saved_sign = getsign(dest);
160 
161  if (!(taga | tagb)) {
162  expa = exponent(a);
163  expb = exponent(b);
164 
165  valid_subtract:
166  /* Both registers are valid */
167 
168  diff = expa - expb;
169 
170  if (!diff) {
171  diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
172  if (!diff) {
173  diff = a->sigl > b->sigl;
174  if (!diff)
175  diff = -(a->sigl < b->sigl);
176  }
177  }
178 
179  switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
180  case 0: /* P - P */
181  case 3: /* N - N */
182  if (diff > 0) {
183  /* |a| > |b| */
184  tag =
185  FPU_u_sub(a, b, dest, control_w, signa,
186  expa, expb);
187  } else if (diff == 0) {
188  FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
189 
190  /* sign depends upon rounding mode */
191  setsign(dest, ((control_w & CW_RC) != RC_DOWN)
192  ? SIGN_POS : SIGN_NEG);
193  return TAG_Zero;
194  } else {
195  sign = signa ^ SIGN_NEG;
196  tag =
197  FPU_u_sub(b, a, dest, control_w, sign, expb,
198  expa);
199  }
200  break;
201  case 1: /* P - N */
202  tag =
203  FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
204  expb);
205  break;
206  case 2: /* N - P */
207  tag =
208  FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
209  expb);
210  break;
211 #ifdef PARANOID
212  default:
213  EXCEPTION(EX_INTERNAL | 0x111);
214  return -1;
215 #endif
216  }
217  if (tag < 0) {
218  setsign(dest, saved_sign);
219  return tag;
220  }
221  FPU_settagi(deststnr, tag);
222  return tag;
223  }
224 
225  if (taga == TAG_Special)
226  taga = FPU_Special(a);
227  if (tagb == TAG_Special)
228  tagb = FPU_Special(b);
229 
230  if (((taga == TAG_Valid) && (tagb == TW_Denormal))
231  || ((taga == TW_Denormal) && (tagb == TAG_Valid))
232  || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
233  FPU_REG x, y;
234 
235  if (denormal_operand() < 0)
236  return FPU_Exception;
237 
238  FPU_to_exp16(a, &x);
239  FPU_to_exp16(b, &y);
240  a = &x;
241  b = &y;
242  expa = exponent16(a);
243  expb = exponent16(b);
244 
245  goto valid_subtract;
246  }
247 
248  if ((taga == TW_NaN) || (tagb == TW_NaN)) {
249  FPU_REG const *d1, *d2;
250  if (flags & REV) {
251  d1 = b;
252  d2 = a;
253  } else {
254  d1 = a;
255  d2 = b;
256  }
257  if (flags & LOADED)
258  return real_2op_NaN(b, tagb, deststnr, d1);
259  if (flags & DEST_RM)
260  return real_2op_NaN(a, taga, deststnr, d2);
261  else
262  return real_2op_NaN(b, tagb, deststnr, d2);
263  }
264 
265  return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
266  dest, deststnr, control_w);
267 }
268 
269 static
270 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
271  FPU_REG const *b, u_char tagb, u_char signb,
272  FPU_REG * dest, int deststnr, int control_w)
273 {
274  if (((taga == TW_Denormal) || (tagb == TW_Denormal))
275  && (denormal_operand() < 0))
276  return FPU_Exception;
277 
278  if (taga == TAG_Zero) {
279  if (tagb == TAG_Zero) {
280  /* Both are zero, result will be zero. */
281  u_char different_signs = signa ^ signb;
282 
283  FPU_copy_to_regi(a, TAG_Zero, deststnr);
284  if (different_signs) {
285  /* Signs are different. */
286  /* Sign of answer depends upon rounding mode. */
287  setsign(dest, ((control_w & CW_RC) != RC_DOWN)
288  ? SIGN_POS : SIGN_NEG);
289  } else
290  setsign(dest, signa); /* signa may differ from the sign of a. */
291  return TAG_Zero;
292  } else {
293  reg_copy(b, dest);
294  if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
295  /* A pseudoDenormal, convert it. */
296  addexponent(dest, 1);
297  tagb = TAG_Valid;
298  } else if (tagb > TAG_Empty)
299  tagb = TAG_Special;
300  setsign(dest, signb); /* signb may differ from the sign of b. */
301  FPU_settagi(deststnr, tagb);
302  return tagb;
303  }
304  } else if (tagb == TAG_Zero) {
305  reg_copy(a, dest);
306  if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
307  /* A pseudoDenormal */
308  addexponent(dest, 1);
309  taga = TAG_Valid;
310  } else if (taga > TAG_Empty)
311  taga = TAG_Special;
312  setsign(dest, signa); /* signa may differ from the sign of a. */
313  FPU_settagi(deststnr, taga);
314  return taga;
315  } else if (taga == TW_Infinity) {
316  if ((tagb != TW_Infinity) || (signa == signb)) {
317  FPU_copy_to_regi(a, TAG_Special, deststnr);
318  setsign(dest, signa); /* signa may differ from the sign of a. */
319  return taga;
320  }
321  /* Infinity-Infinity is undefined. */
322  return arith_invalid(deststnr);
323  } else if (tagb == TW_Infinity) {
324  FPU_copy_to_regi(b, TAG_Special, deststnr);
325  setsign(dest, signb); /* signb may differ from the sign of b. */
326  return tagb;
327  }
328 #ifdef PARANOID
329  EXCEPTION(EX_INTERNAL | 0x101);
330 #endif
331 
332  return FPU_Exception;
333 }