1*b2441318SGreg Kroah-Hartman // SPDX-License-Identifier: GPL-2.0
2da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
3da957e11SThomas Gleixner | reg_add_sub.c |
4da957e11SThomas Gleixner | |
5da957e11SThomas Gleixner | Functions to add or subtract two registers and put the result in a third. |
6da957e11SThomas Gleixner | |
7da957e11SThomas Gleixner | Copyright (C) 1992,1993,1997 |
8da957e11SThomas Gleixner | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9da957e11SThomas Gleixner | E-mail billm@suburbia.net |
10da957e11SThomas Gleixner | |
11da957e11SThomas Gleixner | |
12da957e11SThomas Gleixner +---------------------------------------------------------------------------*/
13da957e11SThomas Gleixner
14da957e11SThomas Gleixner /*---------------------------------------------------------------------------+
15da957e11SThomas Gleixner | For each function, the destination may be any FPU_REG, including one of |
16da957e11SThomas Gleixner | the source FPU_REGs. |
17da957e11SThomas Gleixner | Each function returns 0 if the answer is o.k., otherwise a non-zero |
18da957e11SThomas Gleixner | value is returned, indicating either an exception condition or an |
19da957e11SThomas Gleixner | internal error. |
20da957e11SThomas Gleixner +---------------------------------------------------------------------------*/
21da957e11SThomas Gleixner
22da957e11SThomas Gleixner #include "exception.h"
23da957e11SThomas Gleixner #include "reg_constant.h"
24da957e11SThomas Gleixner #include "fpu_emu.h"
25da957e11SThomas Gleixner #include "control_w.h"
26da957e11SThomas Gleixner #include "fpu_system.h"
27da957e11SThomas Gleixner
28da957e11SThomas Gleixner static
29da957e11SThomas Gleixner int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
30da957e11SThomas Gleixner FPU_REG const *b, u_char tagb, u_char signb,
31da957e11SThomas Gleixner FPU_REG * dest, int deststnr, int control_w);
32da957e11SThomas Gleixner
33da957e11SThomas Gleixner /*
34da957e11SThomas Gleixner Operates on st(0) and st(n), or on st(0) and temporary data.
35da957e11SThomas Gleixner The destination must be one of the source st(x).
36da957e11SThomas Gleixner */
FPU_add(FPU_REG const * b,u_char tagb,int deststnr,int control_w)37da957e11SThomas Gleixner int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
38da957e11SThomas Gleixner {
39da957e11SThomas Gleixner FPU_REG *a = &st(0);
40da957e11SThomas Gleixner FPU_REG *dest = &st(deststnr);
41da957e11SThomas Gleixner u_char signb = getsign(b);
42da957e11SThomas Gleixner u_char taga = FPU_gettag0();
43da957e11SThomas Gleixner u_char signa = getsign(a);
44da957e11SThomas Gleixner u_char saved_sign = getsign(dest);
45da957e11SThomas Gleixner int diff, tag, expa, expb;
46da957e11SThomas Gleixner
473d0d14f9SIngo Molnar if (!(taga | tagb)) {
48da957e11SThomas Gleixner expa = exponent(a);
49da957e11SThomas Gleixner expb = exponent(b);
50da957e11SThomas Gleixner
51da957e11SThomas Gleixner valid_add:
52da957e11SThomas Gleixner /* Both registers are valid */
533d0d14f9SIngo Molnar if (!(signa ^ signb)) {
54da957e11SThomas Gleixner /* signs are the same */
553d0d14f9SIngo Molnar tag =
563d0d14f9SIngo Molnar FPU_u_add(a, b, dest, control_w, signa, expa, expb);
573d0d14f9SIngo Molnar } else {
58da957e11SThomas Gleixner /* The signs are different, so do a subtraction */
59da957e11SThomas Gleixner diff = expa - expb;
603d0d14f9SIngo Molnar if (!diff) {
61da957e11SThomas Gleixner diff = a->sigh - b->sigh; /* This works only if the ms bits
62da957e11SThomas Gleixner are identical. */
633d0d14f9SIngo Molnar if (!diff) {
64da957e11SThomas Gleixner diff = a->sigl > b->sigl;
65da957e11SThomas Gleixner if (!diff)
66da957e11SThomas Gleixner diff = -(a->sigl < b->sigl);
67da957e11SThomas Gleixner }
68da957e11SThomas Gleixner }
69da957e11SThomas Gleixner
703d0d14f9SIngo Molnar if (diff > 0) {
713d0d14f9SIngo Molnar tag =
723d0d14f9SIngo Molnar FPU_u_sub(a, b, dest, control_w, signa,
733d0d14f9SIngo Molnar expa, expb);
743d0d14f9SIngo Molnar } else if (diff < 0) {
753d0d14f9SIngo Molnar tag =
763d0d14f9SIngo Molnar FPU_u_sub(b, a, dest, control_w, signb,
773d0d14f9SIngo Molnar expb, expa);
783d0d14f9SIngo Molnar } else {
79da957e11SThomas Gleixner FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
80da957e11SThomas Gleixner /* sign depends upon rounding mode */
81da957e11SThomas Gleixner setsign(dest, ((control_w & CW_RC) != RC_DOWN)
82da957e11SThomas Gleixner ? SIGN_POS : SIGN_NEG);
83da957e11SThomas Gleixner return TAG_Zero;
84da957e11SThomas Gleixner }
85da957e11SThomas Gleixner }
86da957e11SThomas Gleixner
873d0d14f9SIngo Molnar if (tag < 0) {
88da957e11SThomas Gleixner setsign(dest, saved_sign);
89da957e11SThomas Gleixner return tag;
90da957e11SThomas Gleixner }
91da957e11SThomas Gleixner FPU_settagi(deststnr, tag);
92da957e11SThomas Gleixner return tag;
93da957e11SThomas Gleixner }
94da957e11SThomas Gleixner
95da957e11SThomas Gleixner if (taga == TAG_Special)
96da957e11SThomas Gleixner taga = FPU_Special(a);
97da957e11SThomas Gleixner if (tagb == TAG_Special)
98da957e11SThomas Gleixner tagb = FPU_Special(b);
99da957e11SThomas Gleixner
100da957e11SThomas Gleixner if (((taga == TAG_Valid) && (tagb == TW_Denormal))
101da957e11SThomas Gleixner || ((taga == TW_Denormal) && (tagb == TAG_Valid))
1023d0d14f9SIngo Molnar || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
103da957e11SThomas Gleixner FPU_REG x, y;
104da957e11SThomas Gleixner
105da957e11SThomas Gleixner if (denormal_operand() < 0)
106da957e11SThomas Gleixner return FPU_Exception;
107da957e11SThomas Gleixner
108da957e11SThomas Gleixner FPU_to_exp16(a, &x);
109da957e11SThomas Gleixner FPU_to_exp16(b, &y);
110da957e11SThomas Gleixner a = &x;
111da957e11SThomas Gleixner b = &y;
112da957e11SThomas Gleixner expa = exponent16(a);
113da957e11SThomas Gleixner expb = exponent16(b);
114da957e11SThomas Gleixner goto valid_add;
115da957e11SThomas Gleixner }
116da957e11SThomas Gleixner
1173d0d14f9SIngo Molnar if ((taga == TW_NaN) || (tagb == TW_NaN)) {
118da957e11SThomas Gleixner if (deststnr == 0)
119da957e11SThomas Gleixner return real_2op_NaN(b, tagb, deststnr, a);
120da957e11SThomas Gleixner else
121da957e11SThomas Gleixner return real_2op_NaN(a, taga, deststnr, a);
122da957e11SThomas Gleixner }
123da957e11SThomas Gleixner
124da957e11SThomas Gleixner return add_sub_specials(a, taga, signa, b, tagb, signb,
125da957e11SThomas Gleixner dest, deststnr, control_w);
126da957e11SThomas Gleixner }
127da957e11SThomas Gleixner
128da957e11SThomas Gleixner /* Subtract b from a. (a-b) -> dest */
FPU_sub(int flags,int rm,int control_w)129da957e11SThomas Gleixner int FPU_sub(int flags, int rm, int control_w)
130da957e11SThomas Gleixner {
131da957e11SThomas Gleixner FPU_REG const *a, *b;
132da957e11SThomas Gleixner FPU_REG *dest;
133da957e11SThomas Gleixner u_char taga, tagb, signa, signb, saved_sign, sign;
134da957e11SThomas Gleixner int diff, tag = 0, expa, expb, deststnr;
135da957e11SThomas Gleixner
136da957e11SThomas Gleixner a = &st(0);
137da957e11SThomas Gleixner taga = FPU_gettag0();
138da957e11SThomas Gleixner
139da957e11SThomas Gleixner deststnr = 0;
1403d0d14f9SIngo Molnar if (flags & LOADED) {
141da957e11SThomas Gleixner b = (FPU_REG *) rm;
142da957e11SThomas Gleixner tagb = flags & 0x0f;
1433d0d14f9SIngo Molnar } else {
144da957e11SThomas Gleixner b = &st(rm);
145da957e11SThomas Gleixner tagb = FPU_gettagi(rm);
146da957e11SThomas Gleixner
147da957e11SThomas Gleixner if (flags & DEST_RM)
148da957e11SThomas Gleixner deststnr = rm;
149da957e11SThomas Gleixner }
150da957e11SThomas Gleixner
151da957e11SThomas Gleixner signa = getsign(a);
152da957e11SThomas Gleixner signb = getsign(b);
153da957e11SThomas Gleixner
1543d0d14f9SIngo Molnar if (flags & REV) {
155da957e11SThomas Gleixner signa ^= SIGN_NEG;
156da957e11SThomas Gleixner signb ^= SIGN_NEG;
157da957e11SThomas Gleixner }
158da957e11SThomas Gleixner
159da957e11SThomas Gleixner dest = &st(deststnr);
160da957e11SThomas Gleixner saved_sign = getsign(dest);
161da957e11SThomas Gleixner
1623d0d14f9SIngo Molnar if (!(taga | tagb)) {
163da957e11SThomas Gleixner expa = exponent(a);
164da957e11SThomas Gleixner expb = exponent(b);
165da957e11SThomas Gleixner
166da957e11SThomas Gleixner valid_subtract:
167da957e11SThomas Gleixner /* Both registers are valid */
168da957e11SThomas Gleixner
169da957e11SThomas Gleixner diff = expa - expb;
170da957e11SThomas Gleixner
1713d0d14f9SIngo Molnar if (!diff) {
172da957e11SThomas Gleixner diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
1733d0d14f9SIngo Molnar if (!diff) {
174da957e11SThomas Gleixner diff = a->sigl > b->sigl;
175da957e11SThomas Gleixner if (!diff)
176da957e11SThomas Gleixner diff = -(a->sigl < b->sigl);
177da957e11SThomas Gleixner }
178da957e11SThomas Gleixner }
179da957e11SThomas Gleixner
1803d0d14f9SIngo Molnar switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
181da957e11SThomas Gleixner case 0: /* P - P */
182da957e11SThomas Gleixner case 3: /* N - N */
1833d0d14f9SIngo Molnar if (diff > 0) {
184da957e11SThomas Gleixner /* |a| > |b| */
1853d0d14f9SIngo Molnar tag =
1863d0d14f9SIngo Molnar FPU_u_sub(a, b, dest, control_w, signa,
1873d0d14f9SIngo Molnar expa, expb);
1883d0d14f9SIngo Molnar } else if (diff == 0) {
189da957e11SThomas Gleixner FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
190da957e11SThomas Gleixner
191da957e11SThomas Gleixner /* sign depends upon rounding mode */
192da957e11SThomas Gleixner setsign(dest, ((control_w & CW_RC) != RC_DOWN)
193da957e11SThomas Gleixner ? SIGN_POS : SIGN_NEG);
194da957e11SThomas Gleixner return TAG_Zero;
1953d0d14f9SIngo Molnar } else {
196da957e11SThomas Gleixner sign = signa ^ SIGN_NEG;
1973d0d14f9SIngo Molnar tag =
1983d0d14f9SIngo Molnar FPU_u_sub(b, a, dest, control_w, sign, expb,
1993d0d14f9SIngo Molnar expa);
200da957e11SThomas Gleixner }
201da957e11SThomas Gleixner break;
202da957e11SThomas Gleixner case 1: /* P - N */
2033d0d14f9SIngo Molnar tag =
2043d0d14f9SIngo Molnar FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
2053d0d14f9SIngo Molnar expb);
206da957e11SThomas Gleixner break;
207da957e11SThomas Gleixner case 2: /* N - P */
2083d0d14f9SIngo Molnar tag =
2093d0d14f9SIngo Molnar FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
2103d0d14f9SIngo Molnar expb);
211da957e11SThomas Gleixner break;
212da957e11SThomas Gleixner #ifdef PARANOID
213da957e11SThomas Gleixner default:
214da957e11SThomas Gleixner EXCEPTION(EX_INTERNAL | 0x111);
215da957e11SThomas Gleixner return -1;
216da957e11SThomas Gleixner #endif
217da957e11SThomas Gleixner }
2183d0d14f9SIngo Molnar if (tag < 0) {
219da957e11SThomas Gleixner setsign(dest, saved_sign);
220da957e11SThomas Gleixner return tag;
221da957e11SThomas Gleixner }
222da957e11SThomas Gleixner FPU_settagi(deststnr, tag);
223da957e11SThomas Gleixner return tag;
224da957e11SThomas Gleixner }
225da957e11SThomas Gleixner
226da957e11SThomas Gleixner if (taga == TAG_Special)
227da957e11SThomas Gleixner taga = FPU_Special(a);
228da957e11SThomas Gleixner if (tagb == TAG_Special)
229da957e11SThomas Gleixner tagb = FPU_Special(b);
230da957e11SThomas Gleixner
231da957e11SThomas Gleixner if (((taga == TAG_Valid) && (tagb == TW_Denormal))
232da957e11SThomas Gleixner || ((taga == TW_Denormal) && (tagb == TAG_Valid))
2333d0d14f9SIngo Molnar || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
234da957e11SThomas Gleixner FPU_REG x, y;
235da957e11SThomas Gleixner
236da957e11SThomas Gleixner if (denormal_operand() < 0)
237da957e11SThomas Gleixner return FPU_Exception;
238da957e11SThomas Gleixner
239da957e11SThomas Gleixner FPU_to_exp16(a, &x);
240da957e11SThomas Gleixner FPU_to_exp16(b, &y);
241da957e11SThomas Gleixner a = &x;
242da957e11SThomas Gleixner b = &y;
243da957e11SThomas Gleixner expa = exponent16(a);
244da957e11SThomas Gleixner expb = exponent16(b);
245da957e11SThomas Gleixner
246da957e11SThomas Gleixner goto valid_subtract;
247da957e11SThomas Gleixner }
248da957e11SThomas Gleixner
2493d0d14f9SIngo Molnar if ((taga == TW_NaN) || (tagb == TW_NaN)) {
250da957e11SThomas Gleixner FPU_REG const *d1, *d2;
2513d0d14f9SIngo Molnar if (flags & REV) {
252da957e11SThomas Gleixner d1 = b;
253da957e11SThomas Gleixner d2 = a;
2543d0d14f9SIngo Molnar } else {
255da957e11SThomas Gleixner d1 = a;
256da957e11SThomas Gleixner d2 = b;
257da957e11SThomas Gleixner }
258da957e11SThomas Gleixner if (flags & LOADED)
259da957e11SThomas Gleixner return real_2op_NaN(b, tagb, deststnr, d1);
260da957e11SThomas Gleixner if (flags & DEST_RM)
261da957e11SThomas Gleixner return real_2op_NaN(a, taga, deststnr, d2);
262da957e11SThomas Gleixner else
263da957e11SThomas Gleixner return real_2op_NaN(b, tagb, deststnr, d2);
264da957e11SThomas Gleixner }
265da957e11SThomas Gleixner
266da957e11SThomas Gleixner return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
267da957e11SThomas Gleixner dest, deststnr, control_w);
268da957e11SThomas Gleixner }
269da957e11SThomas Gleixner
270da957e11SThomas Gleixner static
add_sub_specials(FPU_REG const * a,u_char taga,u_char signa,FPU_REG const * b,u_char tagb,u_char signb,FPU_REG * dest,int deststnr,int control_w)271da957e11SThomas Gleixner int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
272da957e11SThomas Gleixner FPU_REG const *b, u_char tagb, u_char signb,
273da957e11SThomas Gleixner FPU_REG * dest, int deststnr, int control_w)
274da957e11SThomas Gleixner {
275da957e11SThomas Gleixner if (((taga == TW_Denormal) || (tagb == TW_Denormal))
276da957e11SThomas Gleixner && (denormal_operand() < 0))
277da957e11SThomas Gleixner return FPU_Exception;
278da957e11SThomas Gleixner
2793d0d14f9SIngo Molnar if (taga == TAG_Zero) {
2803d0d14f9SIngo Molnar if (tagb == TAG_Zero) {
281da957e11SThomas Gleixner /* Both are zero, result will be zero. */
282da957e11SThomas Gleixner u_char different_signs = signa ^ signb;
283da957e11SThomas Gleixner
284da957e11SThomas Gleixner FPU_copy_to_regi(a, TAG_Zero, deststnr);
2853d0d14f9SIngo Molnar if (different_signs) {
286da957e11SThomas Gleixner /* Signs are different. */
287da957e11SThomas Gleixner /* Sign of answer depends upon rounding mode. */
288da957e11SThomas Gleixner setsign(dest, ((control_w & CW_RC) != RC_DOWN)
289da957e11SThomas Gleixner ? SIGN_POS : SIGN_NEG);
2903d0d14f9SIngo Molnar } else
291da957e11SThomas Gleixner setsign(dest, signa); /* signa may differ from the sign of a. */
292da957e11SThomas Gleixner return TAG_Zero;
2933d0d14f9SIngo Molnar } else {
294da957e11SThomas Gleixner reg_copy(b, dest);
2953d0d14f9SIngo Molnar if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
296da957e11SThomas Gleixner /* A pseudoDenormal, convert it. */
297da957e11SThomas Gleixner addexponent(dest, 1);
298da957e11SThomas Gleixner tagb = TAG_Valid;
2993d0d14f9SIngo Molnar } else if (tagb > TAG_Empty)
300da957e11SThomas Gleixner tagb = TAG_Special;
301da957e11SThomas Gleixner setsign(dest, signb); /* signb may differ from the sign of b. */
302da957e11SThomas Gleixner FPU_settagi(deststnr, tagb);
303da957e11SThomas Gleixner return tagb;
304da957e11SThomas Gleixner }
3053d0d14f9SIngo Molnar } else if (tagb == TAG_Zero) {
306da957e11SThomas Gleixner reg_copy(a, dest);
3073d0d14f9SIngo Molnar if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
308da957e11SThomas Gleixner /* A pseudoDenormal */
309da957e11SThomas Gleixner addexponent(dest, 1);
310da957e11SThomas Gleixner taga = TAG_Valid;
3113d0d14f9SIngo Molnar } else if (taga > TAG_Empty)
312da957e11SThomas Gleixner taga = TAG_Special;
313da957e11SThomas Gleixner setsign(dest, signa); /* signa may differ from the sign of a. */
314da957e11SThomas Gleixner FPU_settagi(deststnr, taga);
315da957e11SThomas Gleixner return taga;
3163d0d14f9SIngo Molnar } else if (taga == TW_Infinity) {
3173d0d14f9SIngo Molnar if ((tagb != TW_Infinity) || (signa == signb)) {
318da957e11SThomas Gleixner FPU_copy_to_regi(a, TAG_Special, deststnr);
319da957e11SThomas Gleixner setsign(dest, signa); /* signa may differ from the sign of a. */
320da957e11SThomas Gleixner return taga;
321da957e11SThomas Gleixner }
322da957e11SThomas Gleixner /* Infinity-Infinity is undefined. */
323da957e11SThomas Gleixner return arith_invalid(deststnr);
3243d0d14f9SIngo Molnar } else if (tagb == TW_Infinity) {
325da957e11SThomas Gleixner FPU_copy_to_regi(b, TAG_Special, deststnr);
326da957e11SThomas Gleixner setsign(dest, signb); /* signb may differ from the sign of b. */
327da957e11SThomas Gleixner return tagb;
328da957e11SThomas Gleixner }
329da957e11SThomas Gleixner #ifdef PARANOID
330da957e11SThomas Gleixner EXCEPTION(EX_INTERNAL | 0x101);
331da957e11SThomas Gleixner #endif
332da957e11SThomas Gleixner
333da957e11SThomas Gleixner return FPU_Exception;
334da957e11SThomas Gleixner }
335