xref: /openbmc/linux/arch/x86/math-emu/reg_compare.c (revision 74ce1896)
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 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 ==
47 					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
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 
112 		EXCEPTION(EX_Invalid);
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))
135 		EXCEPTION(EX_Invalid);
136 	if (!(b->sigh & 0x80000000))
137 		EXCEPTION(EX_Invalid);
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, c;
172 
173 	c = compare(loaded_data, loaded_tag);
174 
175 	if (c & COMP_NaN) {
176 		EXCEPTION(EX_Invalid);
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 		default:
193 #ifdef PARANOID
194 			EXCEPTION(EX_INTERNAL | 0x121);
195 #endif /* PARANOID */
196 			f = SW_C3 | SW_C2 | SW_C0;
197 			break;
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, 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 */
214 		EXCEPTION(EX_StackUnder);
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);
222 		EXCEPTION(EX_Invalid);
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 		default:
239 #ifdef PARANOID
240 			EXCEPTION(EX_INTERNAL | 0x122);
241 #endif /* PARANOID */
242 			f = SW_C3 | SW_C2 | SW_C0;
243 			break;
244 		}
245 	setcc(f);
246 	if (c & COMP_Denormal) {
247 		return denormal_operand() < 0;
248 	}
249 	return 0;
250 }
251 
252 static int compare_i_st_st(int nr)
253 {
254 	int f, c;
255 	FPU_REG *st_ptr;
256 
257 	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
258 		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
259 		/* Stack fault */
260 		EXCEPTION(EX_StackUnder);
261 		return !(control_word & CW_Invalid);
262 	}
263 
264 	partial_status &= ~SW_C0;
265 	st_ptr = &st(nr);
266 	c = compare(st_ptr, FPU_gettagi(nr));
267 	if (c & COMP_NaN) {
268 		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
269 		EXCEPTION(EX_Invalid);
270 		return !(control_word & CW_Invalid);
271 	}
272 
273 	switch (c & 7) {
274 	case COMP_A_lt_B:
275 		f = X86_EFLAGS_CF;
276 		break;
277 	case COMP_A_eq_B:
278 		f = X86_EFLAGS_ZF;
279 		break;
280 	case COMP_A_gt_B:
281 		f = 0;
282 		break;
283 	case COMP_No_Comp:
284 		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
285 		break;
286 	default:
287 #ifdef PARANOID
288 		EXCEPTION(EX_INTERNAL | 0x122);
289 #endif /* PARANOID */
290 		f = 0;
291 		break;
292 	}
293 	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
294 	if (c & COMP_Denormal) {
295 		return denormal_operand() < 0;
296 	}
297 	return 0;
298 }
299 
300 static int compare_u_st_st(int nr)
301 {
302 	int f = 0, c;
303 	FPU_REG *st_ptr;
304 
305 	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
306 		setcc(SW_C3 | SW_C2 | SW_C0);
307 		/* Stack fault */
308 		EXCEPTION(EX_StackUnder);
309 		return !(control_word & CW_Invalid);
310 	}
311 
312 	st_ptr = &st(nr);
313 	c = compare(st_ptr, FPU_gettagi(nr));
314 	if (c & COMP_NaN) {
315 		setcc(SW_C3 | SW_C2 | SW_C0);
316 		if (c & COMP_SNaN) {	/* This is the only difference between
317 					   un-ordered and ordinary comparisons */
318 			EXCEPTION(EX_Invalid);
319 			return !(control_word & CW_Invalid);
320 		}
321 		return 0;
322 	} else
323 		switch (c & 7) {
324 		case COMP_A_lt_B:
325 			f = SW_C0;
326 			break;
327 		case COMP_A_eq_B:
328 			f = SW_C3;
329 			break;
330 		case COMP_A_gt_B:
331 			f = 0;
332 			break;
333 		case COMP_No_Comp:
334 			f = SW_C3 | SW_C2 | SW_C0;
335 			break;
336 #ifdef PARANOID
337 		default:
338 			EXCEPTION(EX_INTERNAL | 0x123);
339 			f = SW_C3 | SW_C2 | SW_C0;
340 			break;
341 #endif /* PARANOID */
342 		}
343 	setcc(f);
344 	if (c & COMP_Denormal) {
345 		return denormal_operand() < 0;
346 	}
347 	return 0;
348 }
349 
350 static int compare_ui_st_st(int nr)
351 {
352 	int f = 0, c;
353 	FPU_REG *st_ptr;
354 
355 	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
356 		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
357 		/* Stack fault */
358 		EXCEPTION(EX_StackUnder);
359 		return !(control_word & CW_Invalid);
360 	}
361 
362 	partial_status &= ~SW_C0;
363 	st_ptr = &st(nr);
364 	c = compare(st_ptr, FPU_gettagi(nr));
365 	if (c & COMP_NaN) {
366 		FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
367 		if (c & COMP_SNaN) {	/* This is the only difference between
368 					   un-ordered and ordinary comparisons */
369 			EXCEPTION(EX_Invalid);
370 			return !(control_word & CW_Invalid);
371 		}
372 		return 0;
373 	}
374 
375 	switch (c & 7) {
376 	case COMP_A_lt_B:
377 		f = X86_EFLAGS_CF;
378 		break;
379 	case COMP_A_eq_B:
380 		f = X86_EFLAGS_ZF;
381 		break;
382 	case COMP_A_gt_B:
383 		f = 0;
384 		break;
385 	case COMP_No_Comp:
386 		f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
387 		break;
388 #ifdef PARANOID
389 	default:
390 		EXCEPTION(EX_INTERNAL | 0x123);
391 		f = 0;
392 		break;
393 #endif /* PARANOID */
394 	}
395 	FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
396 	if (c & COMP_Denormal) {
397 		return denormal_operand() < 0;
398 	}
399 	return 0;
400 }
401 
402 /*---------------------------------------------------------------------------*/
403 
404 void fcom_st(void)
405 {
406 	/* fcom st(i) */
407 	compare_st_st(FPU_rm);
408 }
409 
410 void fcompst(void)
411 {
412 	/* fcomp st(i) */
413 	if (!compare_st_st(FPU_rm))
414 		FPU_pop();
415 }
416 
417 void fcompp(void)
418 {
419 	/* fcompp */
420 	if (FPU_rm != 1) {
421 		FPU_illegal();
422 		return;
423 	}
424 	if (!compare_st_st(1))
425 		poppop();
426 }
427 
428 void fucom_(void)
429 {
430 	/* fucom st(i) */
431 	compare_u_st_st(FPU_rm);
432 
433 }
434 
435 void fucomp(void)
436 {
437 	/* fucomp st(i) */
438 	if (!compare_u_st_st(FPU_rm))
439 		FPU_pop();
440 }
441 
442 void fucompp(void)
443 {
444 	/* fucompp */
445 	if (FPU_rm == 1) {
446 		if (!compare_u_st_st(1))
447 			poppop();
448 	} else
449 		FPU_illegal();
450 }
451 
452 /* P6+ compare-to-EFLAGS ops */
453 
454 void fcomi_(void)
455 {
456 	/* fcomi st(i) */
457 	compare_i_st_st(FPU_rm);
458 }
459 
460 void fcomip(void)
461 {
462 	/* fcomip st(i) */
463 	if (!compare_i_st_st(FPU_rm))
464 		FPU_pop();
465 }
466 
467 void fucomi_(void)
468 {
469 	/* fucomi st(i) */
470 	compare_ui_st_st(FPU_rm);
471 }
472 
473 void fucomip(void)
474 {
475 	/* fucomip st(i) */
476 	if (!compare_ui_st_st(FPU_rm))
477 		FPU_pop();
478 }
479