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
;