Import 2.3.13pre1
[davej-history.git] / arch / i386 / math-emu / reg_add_sub.c
blob05e86d624a30450c9feeb94b09204e92bb6b35fa
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) )
48 expa = exponent(a);
49 expb = exponent(b);
51 valid_add:
52 /* Both registers are valid */
53 if (!(signa ^ signb))
55 /* signs are the same */
56 tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb);
58 else
60 /* The signs are different, so do a subtraction */
61 diff = expa - expb;
62 if (!diff)
64 diff = a->sigh - b->sigh; /* This works only if the ms bits
65 are identical. */
66 if (!diff)
68 diff = a->sigl > b->sigl;
69 if (!diff)
70 diff = -(a->sigl < b->sigl);
74 if (diff > 0)
76 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
78 else if ( diff < 0 )
80 tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa);
82 else
84 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
85 /* sign depends upon rounding mode */
86 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
87 ? SIGN_POS : SIGN_NEG);
88 return TAG_Zero;
92 if ( tag < 0 )
94 setsign(dest, saved_sign);
95 return tag;
97 FPU_settagi(deststnr, tag);
98 return tag;
101 if ( taga == TAG_Special )
102 taga = FPU_Special(a);
103 if ( tagb == TAG_Special )
104 tagb = FPU_Special(b);
106 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
107 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
108 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
110 FPU_REG x, y;
112 if ( denormal_operand() < 0 )
113 return FPU_Exception;
115 FPU_to_exp16(a, &x);
116 FPU_to_exp16(b, &y);
117 a = &x;
118 b = &y;
119 expa = exponent16(a);
120 expb = exponent16(b);
121 goto valid_add;
124 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
126 if ( deststnr == 0 )
127 return real_2op_NaN(b, tagb, deststnr, a);
128 else
129 return real_2op_NaN(a, taga, deststnr, a);
132 return add_sub_specials(a, taga, signa, b, tagb, signb,
133 dest, deststnr, control_w);
137 /* Subtract b from a. (a-b) -> dest */
138 int FPU_sub(int flags, int rm, int control_w)
140 FPU_REG const *a, *b;
141 FPU_REG *dest;
142 u_char taga, tagb, signa, signb, saved_sign, sign;
143 int diff, tag, expa, expb, deststnr;
145 a = &st(0);
146 taga = FPU_gettag0();
148 deststnr = 0;
149 if ( flags & LOADED )
151 b = (FPU_REG *)rm;
152 tagb = flags & 0x0f;
154 else
156 b = &st(rm);
157 tagb = FPU_gettagi(rm);
159 if ( flags & DEST_RM )
160 deststnr = rm;
163 signa = getsign(a);
164 signb = getsign(b);
166 if ( flags & REV )
168 signa ^= SIGN_NEG;
169 signb ^= SIGN_NEG;
172 dest = &st(deststnr);
173 saved_sign = getsign(dest);
175 if ( !(taga | tagb) )
177 expa = exponent(a);
178 expb = exponent(b);
180 valid_subtract:
181 /* Both registers are valid */
183 diff = expa - expb;
185 if (!diff)
187 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
188 if (!diff)
190 diff = a->sigl > b->sigl;
191 if (!diff)
192 diff = -(a->sigl < b->sigl);
196 switch ( (((int)signa)*2 + signb) / SIGN_NEG )
198 case 0: /* P - P */
199 case 3: /* N - N */
200 if (diff > 0)
202 /* |a| > |b| */
203 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
205 else if ( diff == 0 )
207 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
209 /* sign depends upon rounding mode */
210 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
211 ? SIGN_POS : SIGN_NEG);
212 return TAG_Zero;
214 else
216 sign = signa ^ SIGN_NEG;
217 tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa);
219 break;
220 case 1: /* P - N */
221 tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb);
222 break;
223 case 2: /* N - P */
224 tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb);
225 break;
226 #ifdef PARANOID
227 default:
228 EXCEPTION(EX_INTERNAL|0x111);
229 return -1;
230 #endif
232 if ( tag < 0 )
234 setsign(dest, saved_sign);
235 return tag;
237 FPU_settagi(deststnr, tag);
238 return tag;
241 if ( taga == TAG_Special )
242 taga = FPU_Special(a);
243 if ( tagb == TAG_Special )
244 tagb = FPU_Special(b);
246 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
247 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
248 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
250 FPU_REG x, y;
252 if ( denormal_operand() < 0 )
253 return FPU_Exception;
255 FPU_to_exp16(a, &x);
256 FPU_to_exp16(b, &y);
257 a = &x;
258 b = &y;
259 expa = exponent16(a);
260 expb = exponent16(b);
262 goto valid_subtract;
265 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
267 FPU_REG const *d1, *d2;
268 if ( flags & REV )
270 d1 = b;
271 d2 = a;
273 else
275 d1 = a;
276 d2 = b;
278 if ( flags & LOADED )
279 return real_2op_NaN(b, tagb, deststnr, d1);
280 if ( flags & DEST_RM )
281 return real_2op_NaN(a, taga, deststnr, d2);
282 else
283 return real_2op_NaN(b, tagb, deststnr, d2);
286 return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
287 dest, deststnr, control_w);
291 static
292 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
293 FPU_REG const *b, u_char tagb, u_char signb,
294 FPU_REG *dest, int deststnr, int control_w)
296 if ( ((taga == TW_Denormal) || (tagb == TW_Denormal))
297 && (denormal_operand() < 0) )
298 return FPU_Exception;
300 if (taga == TAG_Zero)
302 if (tagb == TAG_Zero)
304 /* Both are zero, result will be zero. */
305 u_char different_signs = signa ^ signb;
307 FPU_copy_to_regi(a, TAG_Zero, deststnr);
308 if ( different_signs )
310 /* Signs are different. */
311 /* Sign of answer depends upon rounding mode. */
312 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
313 ? SIGN_POS : SIGN_NEG);
315 else
316 setsign(dest, signa); /* signa may differ from the sign of a. */
317 return TAG_Zero;
319 else
321 reg_copy(b, dest);
322 if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) )
324 /* A pseudoDenormal, convert it. */
325 addexponent(dest, 1);
326 tagb = TAG_Valid;
328 else if ( tagb > TAG_Empty )
329 tagb = TAG_Special;
330 setsign(dest, signb); /* signb may differ from the sign of b. */
331 FPU_settagi(deststnr, tagb);
332 return tagb;
335 else if (tagb == TAG_Zero)
337 reg_copy(a, dest);
338 if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) )
340 /* A pseudoDenormal */
341 addexponent(dest, 1);
342 taga = TAG_Valid;
344 else if ( taga > TAG_Empty )
345 taga = TAG_Special;
346 setsign(dest, signa); /* signa may differ from the sign of a. */
347 FPU_settagi(deststnr, taga);
348 return taga;
350 else if (taga == TW_Infinity)
352 if ( (tagb != TW_Infinity) || (signa == signb) )
354 FPU_copy_to_regi(a, TAG_Special, deststnr);
355 setsign(dest, signa); /* signa may differ from the sign of a. */
356 return taga;
358 /* Infinity-Infinity is undefined. */
359 return arith_invalid(deststnr);
361 else if (tagb == TW_Infinity)
363 FPU_copy_to_regi(b, TAG_Special, deststnr);
364 setsign(dest, signb); /* signb may differ from the sign of b. */
365 return tagb;
368 #ifdef PARANOID
369 EXCEPTION(EX_INTERNAL|0x101);
370 #endif
372 return FPU_Exception;