1// SPDX-License-Identifier: GPL-2.0
2/*---------------------------------------------------------------------------+
3 | reg_add_sub.c |
4 | |
5 | Functions to add or subtract two registers and put the result in a third. |
6 | |
7 | Copyright (C) 1992,1993,1997 |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9 | E-mail billm@suburbia.net |
10 | |
11 | |
12 +---------------------------------------------------------------------------*/
13
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 |
19 | internal error. |
20 +---------------------------------------------------------------------------*/
21
22#include "exception.h"
23#include "reg_constant.h"
24#include "fpu_emu.h"
25#include "control_w.h"
26#include "fpu_system.h"
27
28static
29int 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);
32
33/*
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).
36 */
37int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
38{
39 FPU_REG *a = &st(0);
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;
46
47 if (!(taga | tagb)) {
48 expa = exponent(a);
49 expb = exponent(b);
50
51 valid_add:
52 /* Both registers are valid */
53 if (!(signa ^ signb)) {
54 /* signs are the same */
55 tag =
56 FPU_u_add(arg1: a, arg2: b, answ: dest, control_w, sign: signa, expa, expb);
57 } else {
58 /* The signs are different, so do a subtraction */
59 diff = expa - expb;
60 if (!diff) {
61 diff = a->sigh - b->sigh; /* This works only if the ms bits
62 are identical. */
63 if (!diff) {
64 diff = a->sigl > b->sigl;
65 if (!diff)
66 diff = -(a->sigl < b->sigl);
67 }
68 }
69
70 if (diff > 0) {
71 tag =
72 FPU_u_sub(arg1: a, arg2: b, answ: dest, control_w, sign: signa,
73 expa, expb);
74 } else if (diff < 0) {
75 tag =
76 FPU_u_sub(arg1: b, arg2: a, answ: dest, control_w, sign: signb,
77 expa: expb, expb: expa);
78 } else {
79 FPU_copy_to_regi(r: &CONST_Z, TAG_Zero, stnr: deststnr);
80 /* sign depends upon rounding mode */
81 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
82 ? SIGN_POS : SIGN_NEG);
83 return TAG_Zero;
84 }
85 }
86
87 if (tag < 0) {
88 setsign(dest, saved_sign);
89 return tag;
90 }
91 FPU_settagi(stnr: deststnr, tag);
92 return tag;
93 }
94
95 if (taga == TAG_Special)
96 taga = FPU_Special(ptr: a);
97 if (tagb == TAG_Special)
98 tagb = FPU_Special(ptr: b);
99
100 if (((taga == TAG_Valid) && (tagb == TW_Denormal))
101 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
102 || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
103 FPU_REG x, y;
104
105 if (denormal_operand() < 0)
106 return FPU_Exception;
107
108 FPU_to_exp16(a, x: &x);
109 FPU_to_exp16(a: b, x: &y);
110 a = &x;
111 b = &y;
112 expa = exponent16(a);
113 expb = exponent16(b);
114 goto valid_add;
115 }
116
117 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
118 if (deststnr == 0)
119 return real_2op_NaN(b, tagb, deststnr, defaultNaN: a);
120 else
121 return real_2op_NaN(b: a, tagb: taga, deststnr, defaultNaN: a);
122 }
123
124 return add_sub_specials(a, taga, signa, b, tagb, signb,
125 dest, deststnr, control_w);
126}
127
128/* Subtract b from a. (a-b) -> dest */
129int FPU_sub(int flags, int rm, int control_w)
130{
131 FPU_REG const *a, *b;
132 FPU_REG *dest;
133 u_char taga, tagb, signa, signb, saved_sign, sign;
134 int diff, tag = 0, expa, expb, deststnr;
135
136 a = &st(0);
137 taga = FPU_gettag0();
138
139 deststnr = 0;
140 if (flags & LOADED) {
141 b = (FPU_REG *) rm;
142 tagb = flags & 0x0f;
143 } else {
144 b = &st(rm);
145 tagb = FPU_gettagi(stnr: rm);
146
147 if (flags & DEST_RM)
148 deststnr = rm;
149 }
150
151 signa = getsign(a);
152 signb = getsign(b);
153
154 if (flags & REV) {
155 signa ^= SIGN_NEG;
156 signb ^= SIGN_NEG;
157 }
158
159 dest = &st(deststnr);
160 saved_sign = getsign(dest);
161
162 if (!(taga | tagb)) {
163 expa = exponent(a);
164 expb = exponent(b);
165
166 valid_subtract:
167 /* Both registers are valid */
168
169 diff = expa - expb;
170
171 if (!diff) {
172 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
173 if (!diff) {
174 diff = a->sigl > b->sigl;
175 if (!diff)
176 diff = -(a->sigl < b->sigl);
177 }
178 }
179
180 switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
181 case 0: /* P - P */
182 case 3: /* N - N */
183 if (diff > 0) {
184 /* |a| > |b| */
185 tag =
186 FPU_u_sub(arg1: a, arg2: b, answ: dest, control_w, sign: signa,
187 expa, expb);
188 } else if (diff == 0) {
189 FPU_copy_to_regi(r: &CONST_Z, TAG_Zero, stnr: deststnr);
190
191 /* sign depends upon rounding mode */
192 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
193 ? SIGN_POS : SIGN_NEG);
194 return TAG_Zero;
195 } else {
196 sign = signa ^ SIGN_NEG;
197 tag =
198 FPU_u_sub(arg1: b, arg2: a, answ: dest, control_w, sign, expa: expb,
199 expb: expa);
200 }
201 break;
202 case 1: /* P - N */
203 tag =
204 FPU_u_add(arg1: a, arg2: b, answ: dest, control_w, SIGN_POS, expa,
205 expb);
206 break;
207 case 2: /* N - P */
208 tag =
209 FPU_u_add(arg1: a, arg2: b, answ: dest, control_w, SIGN_NEG, expa,
210 expb);
211 break;
212#ifdef PARANOID
213 default:
214 EXCEPTION(EX_INTERNAL | 0x111);
215 return -1;
216#endif
217 }
218 if (tag < 0) {
219 setsign(dest, saved_sign);
220 return tag;
221 }
222 FPU_settagi(stnr: deststnr, tag);
223 return tag;
224 }
225
226 if (taga == TAG_Special)
227 taga = FPU_Special(ptr: a);
228 if (tagb == TAG_Special)
229 tagb = FPU_Special(ptr: b);
230
231 if (((taga == TAG_Valid) && (tagb == TW_Denormal))
232 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
233 || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
234 FPU_REG x, y;
235
236 if (denormal_operand() < 0)
237 return FPU_Exception;
238
239 FPU_to_exp16(a, x: &x);
240 FPU_to_exp16(a: b, x: &y);
241 a = &x;
242 b = &y;
243 expa = exponent16(a);
244 expb = exponent16(b);
245
246 goto valid_subtract;
247 }
248
249 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
250 FPU_REG const *d1, *d2;
251 if (flags & REV) {
252 d1 = b;
253 d2 = a;
254 } else {
255 d1 = a;
256 d2 = b;
257 }
258 if (flags & LOADED)
259 return real_2op_NaN(b, tagb, deststnr, defaultNaN: d1);
260 if (flags & DEST_RM)
261 return real_2op_NaN(b: a, tagb: taga, deststnr, defaultNaN: d2);
262 else
263 return real_2op_NaN(b, tagb, deststnr, defaultNaN: d2);
264 }
265
266 return add_sub_specials(a, taga, signa, b, tagb, signb: signb ^ SIGN_NEG,
267 dest, deststnr, control_w);
268}
269
270static
271int 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)
274{
275 if (((taga == TW_Denormal) || (tagb == TW_Denormal))
276 && (denormal_operand() < 0))
277 return FPU_Exception;
278
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;
283
284 FPU_copy_to_regi(r: a, TAG_Zero, stnr: 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);
290 } else
291 setsign(dest, signa); /* signa may differ from the sign of a. */
292 return TAG_Zero;
293 } else {
294 reg_copy(x: b, y: dest);
295 if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
296 /* A pseudoDenormal, convert it. */
297 addexponent(dest, 1);
298 tagb = TAG_Valid;
299 } else if (tagb > TAG_Empty)
300 tagb = TAG_Special;
301 setsign(dest, signb); /* signb may differ from the sign of b. */
302 FPU_settagi(stnr: deststnr, tag: tagb);
303 return tagb;
304 }
305 } else if (tagb == TAG_Zero) {
306 reg_copy(x: a, y: dest);
307 if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
308 /* A pseudoDenormal */
309 addexponent(dest, 1);
310 taga = TAG_Valid;
311 } else if (taga > TAG_Empty)
312 taga = TAG_Special;
313 setsign(dest, signa); /* signa may differ from the sign of a. */
314 FPU_settagi(stnr: deststnr, tag: taga);
315 return taga;
316 } else if (taga == TW_Infinity) {
317 if ((tagb != TW_Infinity) || (signa == signb)) {
318 FPU_copy_to_regi(r: a, TAG_Special, stnr: deststnr);
319 setsign(dest, signa); /* signa may differ from the sign of a. */
320 return taga;
321 }
322 /* Infinity-Infinity is undefined. */
323 return arith_invalid(deststnr);
324 } else if (tagb == TW_Infinity) {
325 FPU_copy_to_regi(r: b, TAG_Special, stnr: deststnr);
326 setsign(dest, signb); /* signb may differ from the sign of b. */
327 return tagb;
328 }
329#ifdef PARANOID
330 EXCEPTION(EX_INTERNAL | 0x101);
331#endif
332
333 return FPU_Exception;
334}
335

source code of linux/arch/x86/math-emu/reg_add_sub.c