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;