1 /*---------------------------------------------------------------------------+
4 | Functions to add or subtract two registers and put the result in a third. |
6 | Copyright (C) 1992,1993,1997 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8 | E-mail billm@suburbia.net |
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 |
19 +---------------------------------------------------------------------------*/
21 #include "exception.h"
22 #include "reg_constant.h"
24 #include "control_w.h"
25 #include "fpu_system.h"
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)
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;
52 /* Both registers are valid */
55 /* signs are the same */
56 tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb);
60 /* The signs are different, so do a subtraction */
64 diff = a->sigh - b->sigh; /* This works only if the ms bits
68 diff = a->sigl > b->sigl;
70 diff = -(a->sigl < b->sigl);
76 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
80 tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa);
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);
94 setsign(dest, saved_sign);
97 FPU_settagi(deststnr, 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)) )
112 if ( denormal_operand() < 0 )
113 return FPU_Exception;
119 expa = exponent16(a);
120 expb = exponent16(b);
124 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
127 return real_2op_NaN(b, tagb, deststnr, a);
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;
142 u_char taga, tagb, signa, signb, saved_sign, sign;
143 int diff, tag = 0, expa, expb, deststnr;
146 taga = FPU_gettag0();
149 if ( flags & LOADED )
157 tagb = FPU_gettagi(rm);
159 if ( flags & DEST_RM )
172 dest = &st(deststnr);
173 saved_sign = getsign(dest);
175 if ( !(taga | tagb) )
181 /* Both registers are valid */
187 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
190 diff = a->sigl > b->sigl;
192 diff = -(a->sigl < b->sigl);
196 switch ( (((int)signa)*2 + signb) / SIGN_NEG )
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);
216 sign = signa ^ SIGN_NEG;
217 tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa);
221 tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb);
224 tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb);
228 EXCEPTION(EX_INTERNAL|0x111);
234 setsign(dest, saved_sign);
237 FPU_settagi(deststnr, 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)) )
252 if ( denormal_operand() < 0 )
253 return FPU_Exception;
259 expa = exponent16(a);
260 expb = exponent16(b);
265 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
267 FPU_REG const *d1, *d2;
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);
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);
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);
316 setsign(dest, signa); /* signa may differ from the sign of a. */
322 if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) )
324 /* A pseudoDenormal, convert it. */
325 addexponent(dest, 1);
328 else if ( tagb > TAG_Empty )
330 setsign(dest, signb); /* signb may differ from the sign of b. */
331 FPU_settagi(deststnr, tagb);
335 else if (tagb == TAG_Zero)
338 if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) )
340 /* A pseudoDenormal */
341 addexponent(dest, 1);
344 else if ( taga > TAG_Empty )
346 setsign(dest, signa); /* signa may differ from the sign of a. */
347 FPU_settagi(deststnr, 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. */
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. */
369 EXCEPTION(EX_INTERNAL|0x101);
372 return FPU_Exception;