Linux 3.11-rc3
[cris-mirror.git] / arch / x86 / math-emu / reg_add_sub.c
blobdeea48b9f13a41c6807ecde0ca0f7f9d4d39bd60
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 billm@suburbia.net |
9 | |
10 | |
11 +---------------------------------------------------------------------------*/
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 +---------------------------------------------------------------------------*/
21 #include "exception.h"
22 #include "reg_constant.h"
23 #include "fpu_emu.h"
24 #include "control_w.h"
25 #include "fpu_system.h"
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);
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).
36 int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
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;
46 if (!(taga | tagb)) {
47 expa = exponent(a);
48 expb = exponent(b);
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);
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;
86 if (tag < 0) {
87 setsign(dest, saved_sign);
88 return tag;
90 FPU_settagi(deststnr, tag);
91 return tag;
94 if (taga == TAG_Special)
95 taga = FPU_Special(a);
96 if (tagb == TAG_Special)
97 tagb = FPU_Special(b);
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;
104 if (denormal_operand() < 0)
105 return FPU_Exception;
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;
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);
123 return add_sub_specials(a, taga, signa, b, tagb, signb,
124 dest, deststnr, control_w);
127 /* Subtract b from a. (a-b) -> dest */
128 int FPU_sub(int flags, int rm, int control_w)
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;
135 a = &st(0);
136 taga = FPU_gettag0();
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);
146 if (flags & DEST_RM)
147 deststnr = rm;
150 signa = getsign(a);
151 signb = getsign(b);
153 if (flags & REV) {
154 signa ^= SIGN_NEG;
155 signb ^= SIGN_NEG;
158 dest = &st(deststnr);
159 saved_sign = getsign(dest);
161 if (!(taga | tagb)) {
162 expa = exponent(a);
163 expb = exponent(b);
165 valid_subtract:
166 /* Both registers are valid */
168 diff = expa - expb;
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);
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);
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);
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
217 if (tag < 0) {
218 setsign(dest, saved_sign);
219 return tag;
221 FPU_settagi(deststnr, tag);
222 return tag;
225 if (taga == TAG_Special)
226 taga = FPU_Special(a);
227 if (tagb == TAG_Special)
228 tagb = FPU_Special(b);
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;
235 if (denormal_operand() < 0)
236 return FPU_Exception;
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);
245 goto valid_subtract;
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;
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);
265 return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
266 dest, deststnr, control_w);
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)
274 if (((taga == TW_Denormal) || (tagb == TW_Denormal))
275 && (denormal_operand() < 0))
276 return FPU_Exception;
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;
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;
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;
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;
328 #ifdef PARANOID
329 EXCEPTION(EX_INTERNAL | 0x101);
330 #endif
332 return FPU_Exception;