Initial import from FreeBSD RELENG_4:
[dragonfly.git] / sys / i386 / gnu / fpemul / reg_compare.c
1 /*
2  *  reg_compare.c
3  *
4  * Compare two floating point registers
5  *
6  *
7  * Copyright (C) 1992,1993,1994
8  *                       W. Metzenthen, 22 Parker St, Ormond, Vic 3163,
9  *                       Australia.  E-mail   billm@vaxc.cc.monash.edu.au
10  * All rights reserved.
11  *
12  * This copyright notice covers the redistribution and use of the
13  * FPU emulator developed by W. Metzenthen. It covers only its use
14  * in the 386BSD, FreeBSD and NetBSD operating systems. Any other
15  * use is not permitted under this copyright.
16  *
17  * Redistribution and use in source and binary forms, with or without
18  * modification, are permitted provided that the following conditions
19  * are met:
20  * 1. Redistributions of source code must retain the above copyright
21  *    notice, this list of conditions and the following disclaimer.
22  * 2. Redistributions in binary form must include information specifying
23  *    that source code for the emulator is freely available and include
24  *    either:
25  *      a) an offer to provide the source code for a nominal distribution
26  *         fee, or
27  *      b) list at least two alternative methods whereby the source
28  *         can be obtained, e.g. a publically accessible bulletin board
29  *         and an anonymous ftp site from which the software can be
30  *         downloaded.
31  * 3. All advertising materials specifically mentioning features or use of
32  *    this emulator must acknowledge that it was developed by W. Metzenthen.
33  * 4. The name of W. Metzenthen may not be used to endorse or promote
34  *    products derived from this software without specific prior written
35  *    permission.
36  *
37  * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
38  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
39  * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL
40  * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
41  * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
42  * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
43  * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
44  * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
45  * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
46  * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
47  *
48  *
49  * The purpose of this copyright, based upon the Berkeley copyright, is to
50  * ensure that the covered software remains freely available to everyone.
51  *
52  * The software (with necessary differences) is also available, but under
53  * the terms of the GNU copyleft, for the Linux operating system and for
54  * the djgpp ms-dos extender.
55  *
56  * W. Metzenthen   June 1994.
57  *
58  *
59  * $FreeBSD: src/sys/gnu/i386/fpemul/reg_compare.c,v 1.11 1999/08/28 00:42:55 peter Exp $
60  *
61  */
62
63 /*---------------------------------------------------------------------------+
64  | compare() is the core FPU_REG comparison function                         |
65  +---------------------------------------------------------------------------*/
66 #include <sys/param.h>
67 #include <sys/proc.h>
68 #include <sys/systm.h>
69 #include <machine/pcb.h>
70
71 #include <gnu/i386/fpemul/fpu_emu.h>
72 #include <gnu/i386/fpemul/fpu_system.h>
73 #include <gnu/i386/fpemul/exception.h>
74 #include <gnu/i386/fpemul/control_w.h>
75 #include <gnu/i386/fpemul/status_w.h>
76
77
78 int
79 compare(FPU_REG * b)
80 {
81         int     diff;
82
83         if (FPU_st0_ptr->tag | b->tag) {
84                 if (FPU_st0_ptr->tag == TW_Zero) {
85                         if (b->tag == TW_Zero)
86                                 return COMP_A_eq_B;
87                         if (b->tag == TW_Valid) {
88 #ifdef DENORM_OPERAND
89                                 if ((b->exp <= EXP_UNDER) && (denormal_operand()))
90                                         return COMP_Denormal;
91 #endif                          /* DENORM_OPERAND */
92                                 return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
93                         }
94                 } else
95                         if (b->tag == TW_Zero) {
96                                 if (FPU_st0_ptr->tag == TW_Valid) {
97 #ifdef DENORM_OPERAND
98                                         if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand()))
99                                                 return COMP_Denormal;
100 #endif                          /* DENORM_OPERAND */
101                                         return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
102                                 }
103                         }
104                 if (FPU_st0_ptr->tag == TW_Infinity) {
105                         if ((b->tag == TW_Valid) || (b->tag == TW_Zero)) {
106 #ifdef DENORM_OPERAND
107                                 if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER)
108                                     && (denormal_operand()))
109                                         return COMP_Denormal;
110 #endif                          /* DENORM_OPERAND */
111                                 return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
112                         } else
113                                 if (b->tag == TW_Infinity) {
114                                         /* The 80486 book says that infinities
115                                          * can be equal! */
116                                         return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B :
117                                             ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
118                                 }
119                         /* Fall through to the NaN code */
120                 } else
121                         if (b->tag == TW_Infinity) {
122                                 if ((FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero)) {
123 #ifdef DENORM_OPERAND
124                                         if ((FPU_st0_ptr->tag == TW_Valid)
125                                             && (FPU_st0_ptr->exp <= EXP_UNDER)
126                                             && (denormal_operand()))
127                                                 return COMP_Denormal;
128 #endif                          /* DENORM_OPERAND */
129                                         return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
130                                 }
131                                 /* Fall through to the NaN code */
132                         }
133                 /* The only possibility now should be that one of the
134                  * arguments is a NaN */
135                 if ((FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN)) {
136                         if (((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000))
137                             || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000)))
138                                 /* At least one arg is a signaling NaN */
139                                 return COMP_No_Comp | COMP_SNaN | COMP_NaN;
140                         else
141                                 /* Neither is a signaling NaN */
142                                 return COMP_No_Comp | COMP_NaN;
143                 }
144                 EXCEPTION(EX_Invalid);
145         }
146 #ifdef PARANOID
147         if (!(FPU_st0_ptr->sigh & 0x80000000))
148                 EXCEPTION(EX_Invalid);
149         if (!(b->sigh & 0x80000000))
150                 EXCEPTION(EX_Invalid);
151 #endif                          /* PARANOID */
152
153 #ifdef DENORM_OPERAND
154         if (((FPU_st0_ptr->exp <= EXP_UNDER) ||
155                 (b->exp <= EXP_UNDER)) && (denormal_operand()))
156                 return COMP_Denormal;
157 #endif                          /* DENORM_OPERAND */
158
159         if (FPU_st0_ptr->sign != b->sign)
160                 return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
161
162         diff = FPU_st0_ptr->exp - b->exp;
163         if (diff == 0) {
164                 diff = FPU_st0_ptr->sigh - b->sigh;     /* Works only if ms bits
165                                                          * are identical */
166                 if (diff == 0) {
167                         diff = FPU_st0_ptr->sigl > b->sigl;
168                         if (diff == 0)
169                                 diff = -(FPU_st0_ptr->sigl < b->sigl);
170                 }
171         }
172         if (diff > 0)
173                 return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B;
174         if (diff < 0)
175                 return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B;
176         return COMP_A_eq_B;
177
178 }
179
180
181 /* This function requires that st(0) is not empty */
182 int
183 compare_st_data(void)
184 {
185         int     f = 0, c;
186
187         c = compare(&FPU_loaded_data);
188
189         if (c & (COMP_NaN | COMP_Denormal)) {
190                 if (c & COMP_NaN) {
191                         EXCEPTION(EX_Invalid);
192                         f = SW_C3 | SW_C2 | SW_C0;
193                 } else {
194                         /* One of the operands is a de-normal */
195                         return 0;
196                 }
197         } else
198                 switch (c) {
199                 case COMP_A_lt_B:
200                         f = SW_C0;
201                         break;
202                 case COMP_A_eq_B:
203                         f = SW_C3;
204                         break;
205                 case COMP_A_gt_B:
206                         f = 0;
207                         break;
208                 case COMP_No_Comp:
209                         f = SW_C3 | SW_C2 | SW_C0;
210                         break;
211 #ifdef PARANOID
212                 default:
213                         EXCEPTION(EX_INTERNAL | 0x121);
214                         f = SW_C3 | SW_C2 | SW_C0;
215                         break;
216 #endif                          /* PARANOID */
217                 }
218         setcc(f);
219         return 1;
220 }
221
222
223 static int
224 compare_st_st(int nr)
225 {
226         int     f = 0, c;
227
228         if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
229                 setcc(SW_C3 | SW_C2 | SW_C0);
230                 /* Stack fault */
231                 EXCEPTION(EX_StackUnder);
232                 return control_word & CW_Invalid;
233         }
234         c = compare(&st(nr));
235         if (c & (COMP_NaN | COMP_Denormal)) {
236                 if (c & COMP_NaN) {
237                         setcc(SW_C3 | SW_C2 | SW_C0);
238                         EXCEPTION(EX_Invalid);
239                         return control_word & CW_Invalid;
240                 } else {
241                         /* One of the operands is a de-normal */
242                         return control_word & CW_Denormal;
243                 }
244         } else
245                 switch (c) {
246                 case COMP_A_lt_B:
247                         f = SW_C0;
248                         break;
249                 case COMP_A_eq_B:
250                         f = SW_C3;
251                         break;
252                 case COMP_A_gt_B:
253                         f = 0;
254                         break;
255                 case COMP_No_Comp:
256                         f = SW_C3 | SW_C2 | SW_C0;
257                         break;
258 #ifdef PARANOID
259                 default:
260                         EXCEPTION(EX_INTERNAL | 0x122);
261                         f = SW_C3 | SW_C2 | SW_C0;
262                         break;
263 #endif                          /* PARANOID */
264                 }
265         setcc(f);
266         return 1;
267 }
268
269
270 static int
271 compare_u_st_st(int nr)
272 {
273         int     f = 0, c;
274
275         if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) {
276                 setcc(SW_C3 | SW_C2 | SW_C0);
277                 /* Stack fault */
278                 EXCEPTION(EX_StackUnder);
279                 return control_word & CW_Invalid;
280         }
281         c = compare(&st(nr));
282         if (c & (COMP_NaN | COMP_Denormal)) {
283                 if (c & COMP_NaN) {
284                         setcc(SW_C3 | SW_C2 | SW_C0);
285                         if (c & COMP_SNaN) {    /* This is the only difference
286                                                  * between un-ordered and
287                                                  * ordinary comparisons */
288                                 EXCEPTION(EX_Invalid);
289                                 return control_word & CW_Invalid;
290                         }
291                         return 1;
292                 } else {
293                         /* One of the operands is a de-normal */
294                         return control_word & CW_Denormal;
295                 }
296         } else
297                 switch (c) {
298                 case COMP_A_lt_B:
299                         f = SW_C0;
300                         break;
301                 case COMP_A_eq_B:
302                         f = SW_C3;
303                         break;
304                 case COMP_A_gt_B:
305                         f = 0;
306                         break;
307                 case COMP_No_Comp:
308                         f = SW_C3 | SW_C2 | SW_C0;
309                         break;
310 #ifdef PARANOID
311                 default:
312                         EXCEPTION(EX_INTERNAL | 0x123);
313                         f = SW_C3 | SW_C2 | SW_C0;
314                         break;
315 #endif                          /* PARANOID */
316                 }
317         setcc(f);
318         return 1;
319 }
320 /*---------------------------------------------------------------------------*/
321
322 void
323 fcom_st()
324 {
325         /* fcom st(i) */
326         compare_st_st(FPU_rm);
327 }
328
329
330 void
331 fcompst()
332 {
333         /* fcomp st(i) */
334         if (compare_st_st(FPU_rm))
335                 pop();
336 }
337
338
339 void
340 fcompp()
341 {
342         /* fcompp */
343         if (FPU_rm != 1) {
344                 Un_impl();
345                 return;
346         }
347         if (compare_st_st(1)) {
348                 pop();
349                 FPU_st0_ptr = &st(0);
350                 pop();
351         }
352 }
353
354
355 void
356 fucom_()
357 {
358         /* fucom st(i) */
359         compare_u_st_st(FPU_rm);
360
361 }
362
363
364 void
365 fucomp()
366 {
367         /* fucomp st(i) */
368         if (compare_u_st_st(FPU_rm))
369                 pop();
370 }
371
372
373 void
374 fucompp()
375 {
376         /* fucompp */
377         if (FPU_rm == 1) {
378                 if (compare_u_st_st(1)) {
379                         pop();
380                         FPU_st0_ptr = &st(0);
381                         pop();
382                 }
383         } else
384                 Un_impl();
385 }