File: /usr/src/linux/arch/i386/math-emu/reg_compare.c

1     /*---------------------------------------------------------------------------+
2      |  reg_compare.c                                                            |
3      |                                                                           |
4      | Compare two floating point registers                                      |
5      |                                                                           |
6      | Copyright (C) 1992,1993,1994,1997                                         |
7      |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8      |                  E-mail   billm@suburbia.net                              |
9      |                                                                           |
10      |                                                                           |
11      +---------------------------------------------------------------------------*/
12     
13     /*---------------------------------------------------------------------------+
14      | compare() is the core FPU_REG comparison function                         |
15      +---------------------------------------------------------------------------*/
16     
17     #include "fpu_system.h"
18     #include "exception.h"
19     #include "fpu_emu.h"
20     #include "control_w.h"
21     #include "status_w.h"
22     
23     
24     static int compare(FPU_REG const *b, int tagb)
25     {
26       int diff, exp0, expb;
27       u_char	  	st0_tag;
28       FPU_REG  	*st0_ptr;
29       FPU_REG	x, y;
30       u_char		st0_sign, signb = getsign(b);
31     
32       st0_ptr = &st(0);
33       st0_tag = FPU_gettag0();
34       st0_sign = getsign(st0_ptr);
35     
36       if ( tagb == TAG_Special )
37         tagb = FPU_Special(b);
38       if ( st0_tag == TAG_Special )
39         st0_tag = FPU_Special(st0_ptr);
40     
41       if ( ((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
42            || ((tagb != TAG_Valid) && (tagb != TW_Denormal)) )
43         {
44           if ( st0_tag == TAG_Zero )
45     	{
46     	  if ( tagb == TAG_Zero ) return COMP_A_eq_B;
47     	  if ( tagb == TAG_Valid )
48     	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
49     	  if ( tagb == TW_Denormal )
50     	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
51     	    | COMP_Denormal;
52     	}
53           else if ( tagb == TAG_Zero )
54     	{
55     	  if ( st0_tag == TAG_Valid )
56     	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
57     	  if ( st0_tag == TW_Denormal )
58     	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
59     	    | COMP_Denormal;
60     	}
61     
62           if ( st0_tag == TW_Infinity )
63     	{
64     	  if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) )
65     	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
66     	  else if ( tagb == TW_Denormal )
67     	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
68     	      | COMP_Denormal;
69     	  else if ( tagb == TW_Infinity )
70     	    {
71     	      /* The 80486 book says that infinities can be equal! */
72     	      return (st0_sign == signb) ? COMP_A_eq_B :
73     		((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
74     	    }
75     	  /* Fall through to the NaN code */
76     	}
77           else if ( tagb == TW_Infinity )
78     	{
79     	  if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) )
80     	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
81     	  if ( st0_tag == TW_Denormal )
82     	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
83     		| COMP_Denormal;
84     	  /* Fall through to the NaN code */
85     	}
86     
87           /* The only possibility now should be that one of the arguments
88     	 is a NaN */
89           if ( (st0_tag == TW_NaN) || (tagb == TW_NaN) )
90     	{
91     	  int signalling = 0, unsupported = 0;
92     	  if ( st0_tag == TW_NaN )
93     	    {
94     	      signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000;
95     	      unsupported = !((exponent(st0_ptr) == EXP_OVER)
96     			      && (st0_ptr->sigh & 0x80000000));
97     	    }
98     	  if ( tagb == TW_NaN )
99     	    {
100     	      signalling |= (b->sigh & 0xc0000000) == 0x80000000;
101     	      unsupported |= !((exponent(b) == EXP_OVER)
102     			       && (b->sigh & 0x80000000));
103     	    }
104     	  if ( signalling || unsupported )
105     	    return COMP_No_Comp | COMP_SNaN | COMP_NaN;
106     	  else
107     	    /* Neither is a signaling NaN */
108     	    return COMP_No_Comp | COMP_NaN;
109     	}
110           
111           EXCEPTION(EX_Invalid);
112         }
113       
114       if (st0_sign != signb)
115         {
116           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
117     	| ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
118     	    COMP_Denormal : 0);
119         }
120     
121       if ( (st0_tag == TW_Denormal) || (tagb == TW_Denormal) )
122         {
123           FPU_to_exp16(st0_ptr, &x);
124           FPU_to_exp16(b, &y);
125           st0_ptr = &x;
126           b = &y;
127           exp0 = exponent16(st0_ptr);
128           expb = exponent16(b);
129         }
130       else
131         {
132           exp0 = exponent(st0_ptr);
133           expb = exponent(b);
134         }
135     
136     #ifdef PARANOID
137       if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
138       if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid);
139     #endif /* PARANOID */
140     
141       diff = exp0 - expb;
142       if ( diff == 0 )
143         {
144           diff = st0_ptr->sigh - b->sigh;  /* Works only if ms bits are
145     					      identical */
146           if ( diff == 0 )
147     	{
148     	diff = st0_ptr->sigl > b->sigl;
149     	if ( diff == 0 )
150     	  diff = -(st0_ptr->sigl < b->sigl);
151     	}
152         }
153     
154       if ( diff > 0 )
155         {
156           return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
157     	| ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
158     	    COMP_Denormal : 0);
159         }
160       if ( diff < 0 )
161         {
162           return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
163     	| ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
164     	    COMP_Denormal : 0);
165         }
166     
167       return COMP_A_eq_B
168         | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
169     	COMP_Denormal : 0);
170     
171     }
172     
173     
174     /* This function requires that st(0) is not empty */
175     int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
176     {
177       int f, c;
178     
179       c = compare(loaded_data, loaded_tag);
180     
181       if (c & COMP_NaN)
182         {
183           EXCEPTION(EX_Invalid);
184           f = SW_C3 | SW_C2 | SW_C0;
185         }
186       else
187         switch (c & 7)
188           {
189           case COMP_A_lt_B:
190     	f = SW_C0;
191     	break;
192           case COMP_A_eq_B:
193     	f = SW_C3;
194     	break;
195           case COMP_A_gt_B:
196     	f = 0;
197     	break;
198           case COMP_No_Comp:
199     	f = SW_C3 | SW_C2 | SW_C0;
200     	break;
201     #ifdef PARANOID
202           default:
203     	EXCEPTION(EX_INTERNAL|0x121);
204     	f = SW_C3 | SW_C2 | SW_C0;
205     	break;
206     #endif /* PARANOID */
207           }
208       setcc(f);
209       if (c & COMP_Denormal)
210         {
211           return denormal_operand() < 0;
212         }
213       return 0;
214     }
215     
216     
217     static int compare_st_st(int nr)
218     {
219       int f, c;
220       FPU_REG *st_ptr;
221     
222       if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
223         {
224           setcc(SW_C3 | SW_C2 | SW_C0);
225           /* Stack fault */
226           EXCEPTION(EX_StackUnder);
227           return !(control_word & CW_Invalid);
228         }
229     
230       st_ptr = &st(nr);
231       c = compare(st_ptr, FPU_gettagi(nr));
232       if (c & COMP_NaN)
233         {
234           setcc(SW_C3 | SW_C2 | SW_C0);
235           EXCEPTION(EX_Invalid);
236           return !(control_word & CW_Invalid);
237         }
238       else
239         switch (c & 7)
240           {
241           case COMP_A_lt_B:
242     	f = SW_C0;
243     	break;
244           case COMP_A_eq_B:
245     	f = SW_C3;
246     	break;
247           case COMP_A_gt_B:
248     	f = 0;
249     	break;
250           case COMP_No_Comp:
251     	f = SW_C3 | SW_C2 | SW_C0;
252     	break;
253     #ifdef PARANOID
254           default:
255     	EXCEPTION(EX_INTERNAL|0x122);
256     	f = SW_C3 | SW_C2 | SW_C0;
257     	break;
258     #endif /* PARANOID */
259           }
260       setcc(f);
261       if (c & COMP_Denormal)
262         {
263           return denormal_operand() < 0;
264         }
265       return 0;
266     }
267     
268     
269     static int compare_u_st_st(int nr)
270     {
271       int f, c;
272       FPU_REG *st_ptr;
273     
274       if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) )
275         {
276           setcc(SW_C3 | SW_C2 | SW_C0);
277           /* Stack fault */
278           EXCEPTION(EX_StackUnder);
279           return !(control_word & CW_Invalid);
280         }
281     
282       st_ptr = &st(nr);
283       c = compare(st_ptr, FPU_gettagi(nr));
284       if (c & COMP_NaN)
285         {
286           setcc(SW_C3 | SW_C2 | SW_C0);
287           if (c & COMP_SNaN)       /* This is the only difference between
288     				  un-ordered and ordinary comparisons */
289     	{
290     	  EXCEPTION(EX_Invalid);
291     	  return !(control_word & CW_Invalid);
292     	}
293           return 0;
294         }
295       else
296         switch (c & 7)
297           {
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       if (c & COMP_Denormal)
319         {
320           return denormal_operand() < 0;
321         }
322       return 0;
323     }
324     
325     /*---------------------------------------------------------------------------*/
326     
327     void fcom_st()
328     {
329       /* fcom st(i) */
330       compare_st_st(FPU_rm);
331     }
332     
333     
334     void fcompst()
335     {
336       /* fcomp st(i) */
337       if ( !compare_st_st(FPU_rm) )
338         FPU_pop();
339     }
340     
341     
342     void fcompp()
343     {
344       /* fcompp */
345       if (FPU_rm != 1)
346         {
347           FPU_illegal();
348           return;
349         }
350       if ( !compare_st_st(1) )
351           poppop();
352     }
353     
354     
355     void fucom_()
356     {
357       /* fucom st(i) */
358       compare_u_st_st(FPU_rm);
359     
360     }
361     
362     
363     void fucomp()
364     {
365       /* fucomp st(i) */
366       if ( !compare_u_st_st(FPU_rm) )
367         FPU_pop();
368     }
369     
370     
371     void fucompp()
372     {
373       /* fucompp */
374       if (FPU_rm == 1)
375         {
376           if ( !compare_u_st_st(1) )
377     	poppop();
378         }
379       else
380         FPU_illegal();
381     }
382