Initial import from FreeBSD RELENG_4:
[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  *
60  */
61
62 /*---------------------------------------------------------------------------+
63  | For each function, the destination may be any FPU_REG, including one of   |
64  | the source FPU_REGs.                                                      |
65  +---------------------------------------------------------------------------*/
66
67 #include <gnu/i386/fpemul/reg_constant.h>
68 #include <gnu/i386/fpemul/control_w.h>
69
70
71 void
72 reg_add(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
73 {
74         int     diff;
75
76         if (!(a->tag | b->tag)) {
77                 /* Both registers are valid */
78                 if (!(a->sign ^ b->sign)) {
79                         /* signs are the same */
80                         reg_u_add(a, b, dest, control_w);
81                         dest->sign = a->sign;
82                         return;
83                 }
84                 /* The signs are different, so do a subtraction */
85                 diff = a->exp - b->exp;
86                 if (!diff) {
87                         diff = a->sigh - b->sigh;       /* Works only if ms bits
88                                                          * are identical */
89                         if (!diff) {
90                                 diff = a->sigl > b->sigl;
91                                 if (!diff)
92                                         diff = -(a->sigl < b->sigl);
93                         }
94                 }
95                 if (diff > 0) {
96                         reg_u_sub(a, b, dest, control_w);
97                         dest->sign = a->sign;
98                 } else
99                         if (diff == 0) {
100                                 reg_move(&CONST_Z, dest);
101                                 /* sign depends upon rounding mode */
102                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
103                                     ? SIGN_POS : SIGN_NEG;
104                         } else {
105                                 reg_u_sub(b, a, dest, control_w);
106                                 dest->sign = b->sign;
107                         }
108                 return;
109         } else {
110                 if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
111                         real_2op_NaN(a, b, dest);
112                         return;
113                 } else
114                         if (a->tag == TW_Zero) {
115                                 if (b->tag == TW_Zero) {
116                                         char    different_signs = a->sign ^ b->sign;
117                                         /* Both are zero, result will be zero. */
118                                         reg_move(a, dest);
119                                         if (different_signs) {
120                                                 /* Signs are different. */
121                                                 /* Sign of answer depends upon
122                                                  * rounding mode. */
123                                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
124                                                     ? SIGN_POS : SIGN_NEG;
125                                         }
126                                 } else {
127 #ifdef DENORM_OPERAND
128                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
129                                             denormal_operand())
130                                                 return;
131 #endif                          /* DENORM_OPERAND */
132                                         reg_move(b, dest);
133                                 }
134                                 return;
135                         } else
136                                 if (b->tag == TW_Zero) {
137 #ifdef DENORM_OPERAND
138                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
139                                             denormal_operand())
140                                                 return;
141 #endif                          /* DENORM_OPERAND */
142                                         reg_move(a, dest);
143                                         return;
144                                 } else
145                                         if (a->tag == TW_Infinity) {
146                                                 if (b->tag != TW_Infinity) {
147 #ifdef DENORM_OPERAND
148                                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
149                                                             denormal_operand())
150                                                                 return;
151 #endif                          /* DENORM_OPERAND */
152                                                         reg_move(a, dest);
153                                                         return;
154                                                 }
155                                                 if (a->sign == b->sign) {
156                                                         /* They are both + or
157                                                          * - infinity */
158                                                         reg_move(a, dest);
159                                                         return;
160                                                 }
161                                                 arith_invalid(dest);    /* Infinity-Infinity is
162                                                                          * undefined. */
163                                                 return;
164                                         } else
165                                                 if (b->tag == TW_Infinity) {
166 #ifdef DENORM_OPERAND
167                                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
168                                                             denormal_operand())
169                                                                 return;
170 #endif                          /* DENORM_OPERAND */
171                                                         reg_move(b, dest);
172                                                         return;
173                                                 }
174         }
175 #ifdef PARANOID
176         EXCEPTION(EX_INTERNAL | 0x101);
177 #endif
178 }
179
180
181 /* Subtract b from a.  (a-b) -> dest */
182 void
183 reg_sub(FPU_REG * a, FPU_REG * b, FPU_REG * dest, int control_w)
184 {
185         int     diff;
186
187         if (!(a->tag | b->tag)) {
188                 /* Both registers are valid */
189                 diff = a->exp - b->exp;
190                 if (!diff) {
191                         diff = a->sigh - b->sigh;       /* Works only if ms bits
192                                                          * are identical */
193                         if (!diff) {
194                                 diff = a->sigl > b->sigl;
195                                 if (!diff)
196                                         diff = -(a->sigl < b->sigl);
197                         }
198                 }
199                 switch (a->sign * 2 + b->sign) {
200                 case 0: /* P - P */
201                 case 3: /* N - N */
202                         if (diff > 0) {
203                                 reg_u_sub(a, b, dest, control_w);
204                                 dest->sign = a->sign;
205                         } else
206                                 if (diff == 0) {
207 #ifdef DENORM_OPERAND
208                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
209                                             denormal_operand())
210                                                 return;
211 #endif                          /* DENORM_OPERAND */
212                                         reg_move(&CONST_Z, dest);
213                                         /* sign depends upon rounding mode */
214                                         dest->sign = ((control_w & CW_RC) != RC_DOWN)
215                                             ? SIGN_POS : SIGN_NEG;
216                                 } else {
217                                         reg_u_sub(b, a, dest, control_w);
218                                         dest->sign = a->sign ^ SIGN_POS ^ SIGN_NEG;
219                                 }
220                         return;
221                 case 1: /* P - N */
222                         reg_u_add(a, b, dest, control_w);
223                         dest->sign = SIGN_POS;
224                         return;
225                 case 2: /* N - P */
226                         reg_u_add(a, b, dest, control_w);
227                         dest->sign = SIGN_NEG;
228                         return;
229                 }
230         } else {
231                 if ((a->tag == TW_NaN) || (b->tag == TW_NaN)) {
232                         real_2op_NaN(a, b, dest);
233                         return;
234                 } else
235                         if (b->tag == TW_Zero) {
236                                 if (a->tag == TW_Zero) {
237                                         char    same_signs = !(a->sign ^ b->sign);
238                                         /* Both are zero, result will be zero. */
239                                         reg_move(a, dest);      /* Answer for different
240                                                                  * signs. */
241                                         if (same_signs) {
242                                                 /* Sign depends upon rounding
243                                                  * mode */
244                                                 dest->sign = ((control_w & CW_RC) != RC_DOWN)
245                                                     ? SIGN_POS : SIGN_NEG;
246                                         }
247                                 } else {
248 #ifdef DENORM_OPERAND
249                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
250                                             denormal_operand())
251                                                 return;
252 #endif                          /* DENORM_OPERAND */
253                                         reg_move(a, dest);
254                                 }
255                                 return;
256                         } else
257                                 if (a->tag == TW_Zero) {
258 #ifdef DENORM_OPERAND
259                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
260                                             denormal_operand())
261                                                 return;
262 #endif                          /* DENORM_OPERAND */
263                                         reg_move(b, dest);
264                                         dest->sign ^= SIGN_POS ^ SIGN_NEG;
265                                         return;
266                                 } else
267                                         if (a->tag == TW_Infinity) {
268                                                 if (b->tag != TW_Infinity) {
269 #ifdef DENORM_OPERAND
270                                                         if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) &&
271                                                             denormal_operand())
272                                                                 return;
273 #endif                          /* DENORM_OPERAND */
274                                                         reg_move(a, dest);
275                                                         return;
276                                                 }
277                                                 /* Both args are Infinity */
278                                                 if (a->sign == b->sign) {
279                                                         arith_invalid(dest);    /* Infinity-Infinity is
280                                                                                  * undefined. */
281                                                         return;
282                                                 }
283                                                 reg_move(a, dest);
284                                                 return;
285                                         } else
286                                                 if (b->tag == TW_Infinity) {
287 #ifdef DENORM_OPERAND
288                                                         if ((a->tag == TW_Valid) && (a->exp <= EXP_UNDER) &&
289                                                             denormal_operand())
290                                                                 return;
291 #endif                          /* DENORM_OPERAND */
292                                                         reg_move(b, dest);
293                                                         dest->sign ^= SIGN_POS ^ SIGN_NEG;
294                                                         return;
295                                                 }
296         }
297 #ifdef PARANOID
298         EXCEPTION(EX_INTERNAL | 0x110);
299 #endif
300 }