1 // SPDX-License-Identifier: GPL-2.0
2 /*---------------------------------------------------------------------------+
5 | Functions to add or subtract two registers and put the result in a third. |
7 | Copyright (C) 1992,1993,1997 |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9 | E-mail billm@suburbia.net |
12 +---------------------------------------------------------------------------*/
14 /*---------------------------------------------------------------------------+
15 | For each function, the destination may be any FPU_REG, including one of |
16 | the source FPU_REGs. |
17 | Each function returns 0 if the answer is o.k., otherwise a non-zero |
18 | value is returned, indicating either an exception condition or an |
20 +---------------------------------------------------------------------------*/
22 #include "exception.h"
23 #include "reg_constant.h"
25 #include "control_w.h"
26 #include "fpu_system.h"
29 int add_sub_specials(FPU_REG
const *a
, u_char taga
, u_char signa
,
30 FPU_REG
const *b
, u_char tagb
, u_char signb
,
31 FPU_REG
* dest
, int deststnr
, int control_w
);
34 Operates on st(0) and st(n), or on st(0) and temporary data.
35 The destination must be one of the source st(x).
37 int FPU_add(FPU_REG
const *b
, u_char tagb
, int deststnr
, int control_w
)
40 FPU_REG
*dest
= &st(deststnr
);
41 u_char signb
= getsign(b
);
42 u_char taga
= FPU_gettag0();
43 u_char signa
= getsign(a
);
44 u_char saved_sign
= getsign(dest
);
45 int diff
, tag
, expa
, expb
;
52 /* Both registers are valid */
53 if (!(signa
^ signb
)) {
54 /* signs are the same */
56 FPU_u_add(a
, b
, dest
, control_w
, signa
, expa
, expb
);
58 /* The signs are different, so do a subtraction */
61 diff
= a
->sigh
- b
->sigh
; /* This works only if the ms bits
64 diff
= a
->sigl
> b
->sigl
;
66 diff
= -(a
->sigl
< b
->sigl
);
72 FPU_u_sub(a
, b
, dest
, control_w
, signa
,
74 } else if (diff
< 0) {
76 FPU_u_sub(b
, a
, dest
, control_w
, signb
,
79 FPU_copy_to_regi(&CONST_Z
, TAG_Zero
, deststnr
);
80 /* sign depends upon rounding mode */
81 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
82 ? SIGN_POS
: SIGN_NEG
);
88 setsign(dest
, saved_sign
);
91 FPU_settagi(deststnr
, tag
);
95 if (taga
== TAG_Special
)
96 taga
= FPU_Special(a
);
97 if (tagb
== TAG_Special
)
98 tagb
= FPU_Special(b
);
100 if (((taga
== TAG_Valid
) && (tagb
== TW_Denormal
))
101 || ((taga
== TW_Denormal
) && (tagb
== TAG_Valid
))
102 || ((taga
== TW_Denormal
) && (tagb
== TW_Denormal
))) {
105 if (denormal_operand() < 0)
106 return FPU_Exception
;
112 expa
= exponent16(a
);
113 expb
= exponent16(b
);
117 if ((taga
== TW_NaN
) || (tagb
== TW_NaN
)) {
119 return real_2op_NaN(b
, tagb
, deststnr
, a
);
121 return real_2op_NaN(a
, taga
, deststnr
, a
);
124 return add_sub_specials(a
, taga
, signa
, b
, tagb
, signb
,
125 dest
, deststnr
, control_w
);
128 /* Subtract b from a. (a-b) -> dest */
129 int FPU_sub(int flags
, int rm
, int control_w
)
131 FPU_REG
const *a
, *b
;
133 u_char taga
, tagb
, signa
, signb
, saved_sign
, sign
;
134 int diff
, tag
= 0, expa
, expb
, deststnr
;
137 taga
= FPU_gettag0();
140 if (flags
& LOADED
) {
145 tagb
= FPU_gettagi(rm
);
159 dest
= &st(deststnr
);
160 saved_sign
= getsign(dest
);
162 if (!(taga
| tagb
)) {
167 /* Both registers are valid */
172 diff
= a
->sigh
- b
->sigh
; /* Works only if ms bits are identical */
174 diff
= a
->sigl
> b
->sigl
;
176 diff
= -(a
->sigl
< b
->sigl
);
180 switch ((((int)signa
) * 2 + signb
) / SIGN_NEG
) {
186 FPU_u_sub(a
, b
, dest
, control_w
, signa
,
188 } else if (diff
== 0) {
189 FPU_copy_to_regi(&CONST_Z
, TAG_Zero
, deststnr
);
191 /* sign depends upon rounding mode */
192 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
193 ? SIGN_POS
: SIGN_NEG
);
196 sign
= signa
^ SIGN_NEG
;
198 FPU_u_sub(b
, a
, dest
, control_w
, sign
, expb
,
204 FPU_u_add(a
, b
, dest
, control_w
, SIGN_POS
, expa
,
209 FPU_u_add(a
, b
, dest
, control_w
, SIGN_NEG
, expa
,
214 EXCEPTION(EX_INTERNAL
| 0x111);
219 setsign(dest
, saved_sign
);
222 FPU_settagi(deststnr
, tag
);
226 if (taga
== TAG_Special
)
227 taga
= FPU_Special(a
);
228 if (tagb
== TAG_Special
)
229 tagb
= FPU_Special(b
);
231 if (((taga
== TAG_Valid
) && (tagb
== TW_Denormal
))
232 || ((taga
== TW_Denormal
) && (tagb
== TAG_Valid
))
233 || ((taga
== TW_Denormal
) && (tagb
== TW_Denormal
))) {
236 if (denormal_operand() < 0)
237 return FPU_Exception
;
243 expa
= exponent16(a
);
244 expb
= exponent16(b
);
249 if ((taga
== TW_NaN
) || (tagb
== TW_NaN
)) {
250 FPU_REG
const *d1
, *d2
;
259 return real_2op_NaN(b
, tagb
, deststnr
, d1
);
261 return real_2op_NaN(a
, taga
, deststnr
, d2
);
263 return real_2op_NaN(b
, tagb
, deststnr
, d2
);
266 return add_sub_specials(a
, taga
, signa
, b
, tagb
, signb
^ SIGN_NEG
,
267 dest
, deststnr
, control_w
);
271 int add_sub_specials(FPU_REG
const *a
, u_char taga
, u_char signa
,
272 FPU_REG
const *b
, u_char tagb
, u_char signb
,
273 FPU_REG
* dest
, int deststnr
, int control_w
)
275 if (((taga
== TW_Denormal
) || (tagb
== TW_Denormal
))
276 && (denormal_operand() < 0))
277 return FPU_Exception
;
279 if (taga
== TAG_Zero
) {
280 if (tagb
== TAG_Zero
) {
281 /* Both are zero, result will be zero. */
282 u_char different_signs
= signa
^ signb
;
284 FPU_copy_to_regi(a
, TAG_Zero
, deststnr
);
285 if (different_signs
) {
286 /* Signs are different. */
287 /* Sign of answer depends upon rounding mode. */
288 setsign(dest
, ((control_w
& CW_RC
) != RC_DOWN
)
289 ? SIGN_POS
: SIGN_NEG
);
291 setsign(dest
, signa
); /* signa may differ from the sign of a. */
295 if ((tagb
== TW_Denormal
) && (b
->sigh
& 0x80000000)) {
296 /* A pseudoDenormal, convert it. */
297 addexponent(dest
, 1);
299 } else if (tagb
> TAG_Empty
)
301 setsign(dest
, signb
); /* signb may differ from the sign of b. */
302 FPU_settagi(deststnr
, tagb
);
305 } else if (tagb
== TAG_Zero
) {
307 if ((taga
== TW_Denormal
) && (a
->sigh
& 0x80000000)) {
308 /* A pseudoDenormal */
309 addexponent(dest
, 1);
311 } else if (taga
> TAG_Empty
)
313 setsign(dest
, signa
); /* signa may differ from the sign of a. */
314 FPU_settagi(deststnr
, taga
);
316 } else if (taga
== TW_Infinity
) {
317 if ((tagb
!= TW_Infinity
) || (signa
== signb
)) {
318 FPU_copy_to_regi(a
, TAG_Special
, deststnr
);
319 setsign(dest
, signa
); /* signa may differ from the sign of a. */
322 /* Infinity-Infinity is undefined. */
323 return arith_invalid(deststnr
);
324 } else if (tagb
== TW_Infinity
) {
325 FPU_copy_to_regi(b
, TAG_Special
, deststnr
);
326 setsign(dest
, signb
); /* signb may differ from the sign of b. */
330 EXCEPTION(EX_INTERNAL
| 0x101);
333 return FPU_Exception
;