Merge from vendor branch DIFFUTILS:
[dragonfly.git] / sys / i386 / gnu / fpemul / reg_add_sub.c
1 /*
2  *  reg_add_sub.c
3  *
4  * Functions to add or subtract two registers and put the result in a third.
5  *
6  * Copyright (C) 1992,1993,1994
7  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
8  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
9  * All rights reserved.
10  *
11  * This copyright notice covers the redistribution and use of the
12  * FPU emulator developed by W. Metzenthen. It covers only its use
13  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
14  * use is not permitted under this copyright.
15  *
16  * Redistribution and use in source and binary forms, with or without
17  * modification, are permitted provided that the following conditions
18  * are met:
19  * 1. Redistributions of source code must retain the above copyright
20  *    notice, this list of conditions and the following disclaimer.
21  * 2. Redistributions in binary form must include information specifying
22  *    that source code for the emulator is freely available and include
23  *    either:
24  *      a) an offer to provide the source code for a nominal distribution
25  *         fee, or
26  *      b) list at least two alternative methods whereby the source
27  *         can be obtained, e.g. a publically accessible bulletin board
28  *         and an anonymous ftp site from which the software can be
29  *         downloaded.
30  * 3. All advertising materials specifically mentioning features or use of
31  *    this emulator must acknowledge that it was developed by W. Metzenthen.
32  * 4. The name of W. Metzenthen may not be used to endorse or promote
33  *    products derived from this software without specific prior written
34  *    permission.
35  *
36  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
37  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
38  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
39  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
40  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
41  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
42  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
43  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
44  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
45  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
46  *
47  *
48  * The purpose of this copyright, based upon the Berkeley copyright, is to
49  * ensure that the covered software remains freely available to everyone.
50  *
51  * The software (with necessary differences) is also available, but under
52  * the terms of the GNU copyleft, for the Linux operating system and for
53  * the djgpp ms-dos extender.
54  *
55  * W. Metzenthen   June 1994.
56  *
57  *
58  * $FreeBSD: src/sys/gnu/i386/fpemul/reg_add_sub.c,v 1.8 1999/08/28 00:42:55 peter Exp $
59  * $DragonFly: src/sys/i386/gnu/fpemul/Attic/reg_add_sub.c,v 1.3 2003/08/07 21:17:20 dillon Exp $
60  *
61  */
62
63 /*---------------------------------------------------------------------------+
64  | For each function, the destination may be any FPU_REG, including one of   |
65  | the source FPU_REGs.                                                      |
66  +---------------------------------------------------------------------------*/
67
68 #include "reg_constant.h"
69 #include "control_w.h"
70
71
72 void
73 reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
74 {
75         int     diff;
76
77         if (!(a->tag | b->tag)) {
78                 /* Both registers are valid */
79                 if (!(a->sign ^ b->sign)) {
80                         /* signs are the same */
81                         reg_u_add(a, b, dest, control_w);
82                         dest->sign = a->sign;
83                         return;
84                 }
85                 /* The signs are different, so do a subtraction */
86                 diff = a->exp - b->exp;
87                 if (!diff) {
88                         diff = a->sigh - b->sigh;       /* Works only if ms bits
89                                                          * are identical */
90                         if (!diff) {
91                                 diff = a->sigl > b->sigl;
92                                 if (!diff)
93                                         diff = -(a->sigl < b->sigl);
94                         }
95                 }
96                 if (diff > 0) {
97                         reg_u_sub(a, b, dest, control_w);
98                         dest->sign = a->sign;
99                 } else
100                         if (diff == 0) {
101                                 reg_move(&CONST_Z, dest);
102                                 /* sign depends upon rounding mode */
103                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
104                                     ? SIGN_POS : SIGN_NEG;
105                         } else {
106                                 reg_u_sub(b, a, dest, control_w);
107                                 dest->sign = b->sign;
108                         }
109                 return;
110         } else {
111                 if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
112                         real_2op_NaN(a, b, dest);
113                         return;
114                 } else
115                         if (a->tag == TW_Zero) {
116                                 if (b->tag == TW_Zero) {
117                                         char    different_signs = a->sign ^ b->sign;
118                                         /* Both are zero, result will be zero. */
119                                         reg_move(a, dest);
120                                         if (different_signs) {
121                                                 /* Signs are different. */
122                                                 /* Sign of answer depends upon
123                                                  * rounding mode. */
124                                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
125                                                     ? SIGN_POS : SIGN_NEG;
126                                         }
127                                 } else {
128 #ifdef DENORM_OPERAND
129                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
130                                             denormal_operand())
131                                                 return;
132 #endif                          /* DENORM_OPERAND */
133                                         reg_move(b, dest);
134                                 }
135                                 return;
136                         } else
137                                 if (b->tag == TW_Zero) {
138 #ifdef DENORM_OPERAND
139                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
140                                             denormal_operand())
141                                                 return;
142 #endif                          /* DENORM_OPERAND */
143                                         reg_move(a, dest);
144                                         return;
145                                 } else
146                                         if (a->tag == TW_Infinity) {
147                                                 if (b->tag != TW_Infinity) {
148 #ifdef DENORM_OPERAND
149                                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
150                                                             denormal_operand())
151                                                                 return;
152 #endif                          /* DENORM_OPERAND */
153                                                         reg_move(a, dest);
154                                                         return;
155                                                 }
156                                                 if (a->sign == b->sign) {
157                                                         /* They are both + or
158                                                          * - infinity */
159                                                         reg_move(a, dest);
160                                                         return;
161                                                 }
162                                                 arith_invalid(dest);    /* Infinity-Infinity is
163                                                                          * undefined. */
164                                                 return;
165                                         } else
166                                                 if (b->tag == TW_Infinity) {
167 #ifdef DENORM_OPERAND
168                                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
169                                                             denormal_operand())
170                                                                 return;
171 #endif                          /* DENORM_OPERAND */
172                                                         reg_move(b, dest);
173                                                         return;
174                                                 }
175         }
176 #ifdef PARANOID
177         EXCEPTION(EX_INTERNAL | 0x101);
178 #endif
179 }
180
181
182 /* Subtract b from a.  (a-b) -> dest */
183 void
184 reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
185 {
186         int     diff;
187
188         if (!(a->tag | b->tag)) {
189                 /* Both registers are valid */
190                 diff = a->exp - b->exp;
191                 if (!diff) {
192                         diff = a->sigh - b->sigh;       /* Works only if ms bits
193                                                          * are identical */
194                         if (!diff) {
195                                 diff = a->sigl > b->sigl;
196                                 if (!diff)
197                                         diff = -(a->sigl < b->sigl);
198                         }
199                 }
200                 switch (a->sign * 2 + b->sign) {
201                 case 0: /* P - P */
202                 case 3: /* N - N */
203                         if (diff > 0) {
204                                 reg_u_sub(a, b, dest, control_w);
205                                 dest->sign = a->sign;
206                         } else
207                                 if (diff == 0) {
208 #ifdef DENORM_OPERAND
209                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
210                                             denormal_operand())
211                                                 return;
212 #endif                          /* DENORM_OPERAND */
213                                         reg_move(&CONST_Z, dest);
214                                         /* sign depends upon rounding mode */
215                                         dest->sign = ((control_w & CW_RC) != RC_DOWN)
216                                             ? SIGN_POS : SIGN_NEG;
217                                 } else {
218                                         reg_u_sub(b, a, dest, control_w);
219                                         dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG;
220                                 }
221                         return;
222                 case 1: /* P - N */
223                         reg_u_add(a, b, dest, control_w);
224                         dest->sign = SIGN_POS;
225                         return;
226                 case 2: /* N - P */
227                         reg_u_add(a, b, dest, control_w);
228                         dest->sign = SIGN_NEG;
229                         return;
230                 }
231         } else {
232                 if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
233                         real_2op_NaN(a, b, dest);
234                         return;
235                 } else
236                         if (b->tag == TW_Zero) {
237                                 if (a->tag == TW_Zero) {
238                                         char    same_signs = !(a->sign ^ b->sign);
239                                         /* Both are zero, result will be zero. */
240                                         reg_move(a, dest);      /* Answer for different
241                                                                  * signs. */
242                                         if (same_signs) {
243                                                 /* Sign depends upon rounding
244                                                  * mode */
245                                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
246                                                     ? SIGN_POS : SIGN_NEG;
247                                         }
248                                 } else {
249 #ifdef DENORM_OPERAND
250                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
251                                             denormal_operand())
252                                                 return;
253 #endif                          /* DENORM_OPERAND */
254                                         reg_move(a, dest);
255                                 }
256                                 return;
257                         } else
258                                 if (a->tag == TW_Zero) {
259 #ifdef DENORM_OPERAND
260                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
261                                             denormal_operand())
262                                                 return;
263 #endif                          /* DENORM_OPERAND */
264                                         reg_move(b, dest);
265                                         dest->sign ^= SIGN_POS ^ SIGN_NEG;
266                                         return;
267                                 } else
268                                         if (a->tag == TW_Infinity) {
269                                                 if (b->tag != TW_Infinity) {
270 #ifdef DENORM_OPERAND
271                                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
272                                                             denormal_operand())
273                                                                 return;
274 #endif                          /* DENORM_OPERAND */
275                                                         reg_move(a, dest);
276                                                         return;
277                                                 }
278                                                 /* Both args are Infinity */
279                                                 if (a->sign == b->sign) {
280                                                         arith_invalid(dest);    /* Infinity-Infinity is
281                                                                                  * undefined. */
282                                                         return;
283                                                 }
284                                                 reg_move(a, dest);
285                                                 return;
286                                         } else
287                                                 if (b->tag == TW_Infinity) {
288 #ifdef DENORM_OPERAND
289                                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
290                                                             denormal_operand())
291                                                                 return;
292 #endif                          /* DENORM_OPERAND */
293                                                         reg_move(b, dest);
294                                                         dest->sign ^= SIGN_POS ^ SIGN_NEG;
295                                                         return;
296                                                 }
297         }
298 #ifdef PARANOID
299         EXCEPTION(EX_INTERNAL | 0x110);
300 #endif
301 }