Linux Kernel  3.7.1
 All Data Structures Namespaces Files Functions Variables Typedefs Enumerations Enumerator Macros Groups Pages
reg_compare.c
Go to the documentation of this file.
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 [email protected] |
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 static int compare(FPU_REG const *b, int tagb)
24 {
25  int diff, exp0, expb;
26  u_char st0_tag;
27  FPU_REG *st0_ptr;
28  FPU_REG x, y;
29  u_char st0_sign, signb = getsign(b);
30 
31  st0_ptr = &st(0);
32  st0_tag = FPU_gettag0();
33  st0_sign = getsign(st0_ptr);
34 
35  if (tagb == TAG_Special)
36  tagb = FPU_Special(b);
37  if (st0_tag == TAG_Special)
38  st0_tag = FPU_Special(st0_ptr);
39 
40  if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
41  || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
42  if (st0_tag == TAG_Zero) {
43  if (tagb == TAG_Zero)
44  return COMP_A_eq_B;
45  if (tagb == TAG_Valid)
46  return ((signb ==
48  if (tagb == TW_Denormal)
49  return ((signb ==
50  SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
51  | COMP_Denormal;
52  } else if (tagb == TAG_Zero) {
53  if (st0_tag == TAG_Valid)
54  return ((st0_sign ==
55  SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
56  if (st0_tag == TW_Denormal)
57  return ((st0_sign ==
58  SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
59  | COMP_Denormal;
60  }
61 
62  if (st0_tag == TW_Infinity) {
63  if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
64  return ((st0_sign ==
65  SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
66  else if (tagb == TW_Denormal)
67  return ((st0_sign ==
68  SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
69  | COMP_Denormal;
70  else if (tagb == TW_Infinity) {
71  /* The 80486 book says that infinities can be equal! */
72  return (st0_sign == signb) ? COMP_A_eq_B :
73  ((st0_sign ==
74  SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
75  }
76  /* Fall through to the NaN code */
77  } else if (tagb == TW_Infinity) {
78  if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
79  return ((signb ==
80  SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
81  if (st0_tag == TW_Denormal)
82  return ((signb ==
83  SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
84  | COMP_Denormal;
85  /* Fall through to the NaN code */
86  }
87 
88  /* The only possibility now should be that one of the arguments
89  is a NaN */
90  if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
91  int signalling = 0, unsupported = 0;
92  if (st0_tag == TW_NaN) {
93  signalling =
94  (st0_ptr->sigh & 0xc0000000) == 0x80000000;
95  unsupported = !((exponent(st0_ptr) == EXP_OVER)
96  && (st0_ptr->
97  sigh & 0x80000000));
98  }
99  if (tagb == TW_NaN) {
100  signalling |=
101  (b->sigh & 0xc0000000) == 0x80000000;
102  unsupported |= !((exponent(b) == EXP_OVER)
103  && (b->sigh & 0x80000000));
104  }
105  if (signalling || unsupported)
106  return COMP_No_Comp | COMP_SNaN | COMP_NaN;
107  else
108  /* Neither is a signaling NaN */
109  return COMP_No_Comp | COMP_NaN;
110  }
111 
113  }
114 
115  if (st0_sign != signb) {
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  FPU_to_exp16(st0_ptr, &x);
123  FPU_to_exp16(b, &y);
124  st0_ptr = &x;
125  b = &y;
126  exp0 = exponent16(st0_ptr);
127  expb = exponent16(b);
128  } else {
129  exp0 = exponent(st0_ptr);
130  expb = exponent(b);
131  }
132 
133 #ifdef PARANOID
134  if (!(st0_ptr->sigh & 0x80000000))
136  if (!(b->sigh & 0x80000000))
138 #endif /* PARANOID */
139 
140  diff = exp0 - expb;
141  if (diff == 0) {
142  diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
143  identical */
144  if (diff == 0) {
145  diff = st0_ptr->sigl > b->sigl;
146  if (diff == 0)
147  diff = -(st0_ptr->sigl < b->sigl);
148  }
149  }
150 
151  if (diff > 0) {
152  return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
153  | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
154  COMP_Denormal : 0);
155  }
156  if (diff < 0) {
157  return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
158  | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
159  COMP_Denormal : 0);
160  }
161 
162  return COMP_A_eq_B
163  | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
164  COMP_Denormal : 0);
165 
166 }
167 
168 /* This function requires that st(0) is not empty */
169 int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
170 {
171  int f = 0, c;
172 
173  c = compare(loaded_data, loaded_tag);
174 
175  if (c & COMP_NaN) {
177  f = SW_C3 | SW_C2 | SW_C0;
178  } else
179  switch (c & 7) {
180  case COMP_A_lt_B:
181  f = SW_C0;
182  break;
183  case COMP_A_eq_B:
184  f = SW_C3;
185  break;
186  case COMP_A_gt_B:
187  f = 0;
188  break;
189  case COMP_No_Comp:
190  f = SW_C3 | SW_C2 | SW_C0;
191  break;
192 #ifdef PARANOID
193  default:
194  EXCEPTION(EX_INTERNAL | 0x121);
195  f = SW_C3 | SW_C2 | SW_C0;
196  break;
197 #endif /* PARANOID */
198  }
199  setcc(f);
200  if (c & COMP_Denormal) {
201  return denormal_operand() < 0;
202  }
203  return 0;
204 }
205 
206 static int compare_st_st(int nr)
207 {
208  int f = 0, c;
209  FPU_REG *st_ptr;
210 
211  if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
212  setcc(SW_C3 | SW_C2 | SW_C0);
213  /* Stack fault */
215  return !(control_word & CW_Invalid);
216  }
217 
218  st_ptr = &st(nr);
219  c = compare(st_ptr, FPU_gettagi(nr));
220  if (c & COMP_NaN) {
221  setcc(SW_C3 | SW_C2 | SW_C0);
223  return !(control_word & CW_Invalid);
224  } else
225  switch (c & 7) {
226  case COMP_A_lt_B:
227  f = SW_C0;
228  break;
229  case COMP_A_eq_B:
230  f = SW_C3;
231  break;
232  case COMP_A_gt_B:
233  f = 0;
234  break;
235  case COMP_No_Comp:
236  f = SW_C3 | SW_C2 | SW_C0;
237  break;
238 #ifdef PARANOID
239  default:
240  EXCEPTION(EX_INTERNAL | 0x122);
241  f = SW_C3 | SW_C2 | SW_C0;
242  break;
243 #endif /* PARANOID */
244  }
245  setcc(f);
246  if (c & COMP_Denormal) {
247  return denormal_operand() < 0;
248  }
249  return 0;
250 }
251 
252 static int compare_u_st_st(int nr)
253 {
254  int f = 0, c;
255  FPU_REG *st_ptr;
256 
257  if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
258  setcc(SW_C3 | SW_C2 | SW_C0);
259  /* Stack fault */
261  return !(control_word & CW_Invalid);
262  }
263 
264  st_ptr = &st(nr);
265  c = compare(st_ptr, FPU_gettagi(nr));
266  if (c & COMP_NaN) {
267  setcc(SW_C3 | SW_C2 | SW_C0);
268  if (c & COMP_SNaN) { /* This is the only difference between
269  un-ordered and ordinary comparisons */
271  return !(control_word & CW_Invalid);
272  }
273  return 0;
274  } else
275  switch (c & 7) {
276  case COMP_A_lt_B:
277  f = SW_C0;
278  break;
279  case COMP_A_eq_B:
280  f = SW_C3;
281  break;
282  case COMP_A_gt_B:
283  f = 0;
284  break;
285  case COMP_No_Comp:
286  f = SW_C3 | SW_C2 | SW_C0;
287  break;
288 #ifdef PARANOID
289  default:
290  EXCEPTION(EX_INTERNAL | 0x123);
291  f = SW_C3 | SW_C2 | SW_C0;
292  break;
293 #endif /* PARANOID */
294  }
295  setcc(f);
296  if (c & COMP_Denormal) {
297  return denormal_operand() < 0;
298  }
299  return 0;
300 }
301 
302 /*---------------------------------------------------------------------------*/
303 
304 void fcom_st(void)
305 {
306  /* fcom st(i) */
307  compare_st_st(FPU_rm);
308 }
309 
310 void fcompst(void)
311 {
312  /* fcomp st(i) */
313  if (!compare_st_st(FPU_rm))
314  FPU_pop();
315 }
316 
317 void fcompp(void)
318 {
319  /* fcompp */
320  if (FPU_rm != 1) {
321  FPU_illegal();
322  return;
323  }
324  if (!compare_st_st(1))
325  poppop();
326 }
327 
328 void fucom_(void)
329 {
330  /* fucom st(i) */
331  compare_u_st_st(FPU_rm);
332 
333 }
334 
335 void fucomp(void)
336 {
337  /* fucomp st(i) */
338  if (!compare_u_st_st(FPU_rm))
339  FPU_pop();
340 }
341 
342 void fucompp(void)
343 {
344  /* fucompp */
345  if (FPU_rm == 1) {
346  if (!compare_u_st_st(1))
347  poppop();
348  } else
349  FPU_illegal();
350 }