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;
51 /* Both registers are valid */
52 if (!(signa ^ signb)) {
53 /* signs are the same */
55 FPU_u_add(a, b, dest, control_w, signa, expa, expb);
57 /* The signs are different, so do a subtraction */
60 diff = a->sigh - b->sigh; /* This works only if the ms bits
63 diff = a->sigl > b->sigl;
65 diff = -(a->sigl < b->sigl);
71 FPU_u_sub(a, b, dest, control_w, signa,
73 } else if (diff < 0) {
75 FPU_u_sub(b, a, dest, control_w, signb,
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);
87 setsign(dest, saved_sign);
90 FPU_settagi(deststnr, 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))) {
104 if (denormal_operand() < 0)
105 return FPU_Exception;
111 expa = exponent16(a);
112 expb = exponent16(b);
116 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
118 return real_2op_NaN(b, tagb, deststnr, a);
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;
132 u_char taga, tagb, signa, signb, saved_sign, sign;
133 int diff, tag = 0, expa, expb, deststnr;
136 taga = FPU_gettag0();
139 if (flags & LOADED) {
144 tagb = FPU_gettagi(rm);
158 dest = &st(deststnr);
159 saved_sign = getsign(dest);
161 if (!(taga | tagb)) {
166 /* Both registers are valid */
171 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
173 diff = a->sigl > b->sigl;
175 diff = -(a->sigl < b->sigl);
179 switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
185 FPU_u_sub(a, b, dest, control_w, signa,
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);
195 sign = signa ^ SIGN_NEG;
197 FPU_u_sub(b, a, dest, control_w, sign, expb,
203 FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
208 FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
213 EXCEPTION(EX_INTERNAL | 0x111);
218 setsign(dest, saved_sign);
221 FPU_settagi(deststnr, 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))) {
235 if (denormal_operand() < 0)
236 return FPU_Exception;
242 expa = exponent16(a);
243 expb = exponent16(b);
248 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
249 FPU_REG const *d1, *d2;
258 return real_2op_NaN(b, tagb, deststnr, d1);
260 return real_2op_NaN(a, taga, deststnr, d2);
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);
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);
290 setsign(dest, signa); /* signa may differ from the sign of a. */
294 if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
295 /* A pseudoDenormal, convert it. */
296 addexponent(dest, 1);
298 } else if (tagb > TAG_Empty)
300 setsign(dest, signb); /* signb may differ from the sign of b. */
301 FPU_settagi(deststnr, tagb);
304 } else if (tagb == TAG_Zero) {
306 if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
307 /* A pseudoDenormal */
308 addexponent(dest, 1);
310 } else if (taga > TAG_Empty)
312 setsign(dest, signa); /* signa may differ from the sign of a. */
313 FPU_settagi(deststnr, 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. */
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. */
329 EXCEPTION(EX_INTERNAL | 0x101);
332 return FPU_Exception;