1// SPDX-License-Identifier: GPL-2.0
2/*---------------------------------------------------------------------------+
3 | reg_compare.c |
4 | |
5 | Compare two floating point registers |
6 | |
7 | Copyright (C) 1992,1993,1994,1997 |
8 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
9 | E-mail billm@suburbia.net |
10 | |
11 | |
12 +---------------------------------------------------------------------------*/
13
14/*---------------------------------------------------------------------------+
15 | compare() is the core FPU_REG comparison function |
16 +---------------------------------------------------------------------------*/
17
18#include "fpu_system.h"
19#include "exception.h"
20#include "fpu_emu.h"
21#include "control_w.h"
22#include "status_w.h"
23
24static 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(ptr: b);
38 if (st0_tag == TAG_Special)
39 st0_tag = FPU_Special(ptr: st0_ptr);
40
41 if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
42 || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
43 if (st0_tag == TAG_Zero) {
44 if (tagb == TAG_Zero)
45 return COMP_A_eq_B;
46 if (tagb == TAG_Valid)
47 return ((signb ==
48 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
49 if (tagb == TW_Denormal)
50 return ((signb ==
51 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
52 | COMP_Denormal;
53 } else if (tagb == TAG_Zero) {
54 if (st0_tag == TAG_Valid)
55 return ((st0_sign ==
56 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
57 if (st0_tag == TW_Denormal)
58 return ((st0_sign ==
59 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
60 | COMP_Denormal;
61 }
62
63 if (st0_tag == TW_Infinity) {
64 if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
65 return ((st0_sign ==
66 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
67 else if (tagb == TW_Denormal)
68 return ((st0_sign ==
69 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
70 | COMP_Denormal;
71 else if (tagb == TW_Infinity) {
72 /* The 80486 book says that infinities can be equal! */
73 return (st0_sign == signb) ? COMP_A_eq_B :
74 ((st0_sign ==
75 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
76 }
77 /* Fall through to the NaN code */
78 } else if (tagb == TW_Infinity) {
79 if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
80 return ((signb ==
81 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
82 if (st0_tag == TW_Denormal)
83 return ((signb ==
84 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
85 | COMP_Denormal;
86 /* Fall through to the NaN code */
87 }
88
89 /* The only possibility now should be that one of the arguments
90 is a NaN */
91 if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
92 int signalling = 0, unsupported = 0;
93 if (st0_tag == TW_NaN) {
94 signalling =
95 (st0_ptr->sigh & 0xc0000000) == 0x80000000;
96 unsupported = !((exponent(st0_ptr) == EXP_OVER)
97 && (st0_ptr->
98 sigh & 0x80000000));
99 }
100 if (tagb == TW_NaN) {
101 signalling |=
102 (b->sigh & 0xc0000000) == 0x80000000;
103 unsupported |= !((exponent(b) == EXP_OVER)
104 && (b->sigh & 0x80000000));
105 }
106 if (signalling || unsupported)
107 return COMP_No_Comp | COMP_SNaN | COMP_NaN;
108 else
109 /* Neither is a signaling NaN */
110 return COMP_No_Comp | COMP_NaN;
111 }
112
113 EXCEPTION(EX_Invalid);
114 }
115
116 if (st0_sign != signb) {
117 return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
118 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
119 COMP_Denormal : 0);
120 }
121
122 if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
123 FPU_to_exp16(a: st0_ptr, x: &x);
124 FPU_to_exp16(a: b, x: &y);
125 st0_ptr = &x;
126 b = &y;
127 exp0 = exponent16(st0_ptr);
128 expb = exponent16(b);
129 } else {
130 exp0 = exponent(st0_ptr);
131 expb = exponent(b);
132 }
133
134#ifdef PARANOID
135 if (!(st0_ptr->sigh & 0x80000000))
136 EXCEPTION(EX_Invalid);
137 if (!(b->sigh & 0x80000000))
138 EXCEPTION(EX_Invalid);
139#endif /* PARANOID */
140
141 diff = exp0 - expb;
142 if (diff == 0) {
143 diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are
144 identical */
145 if (diff == 0) {
146 diff = st0_ptr->sigl > b->sigl;
147 if (diff == 0)
148 diff = -(st0_ptr->sigl < b->sigl);
149 }
150 }
151
152 if (diff > 0) {
153 return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
154 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
155 COMP_Denormal : 0);
156 }
157 if (diff < 0) {
158 return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
159 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
160 COMP_Denormal : 0);
161 }
162
163 return COMP_A_eq_B
164 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
165 COMP_Denormal : 0);
166
167}
168
169/* This function requires that st(0) is not empty */
170int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
171{
172 int f, c;
173
174 c = compare(b: loaded_data, tagb: loaded_tag);
175
176 if (c & COMP_NaN) {
177 EXCEPTION(EX_Invalid);
178 f = SW_C3 | SW_C2 | SW_C0;
179 } else
180 switch (c & 7) {
181 case COMP_A_lt_B:
182 f = SW_C0;
183 break;
184 case COMP_A_eq_B:
185 f = SW_C3;
186 break;
187 case COMP_A_gt_B:
188 f = 0;
189 break;
190 case COMP_No_Comp:
191 f = SW_C3 | SW_C2 | SW_C0;
192 break;
193 default:
194#ifdef PARANOID
195 EXCEPTION(EX_INTERNAL | 0x121);
196#endif /* PARANOID */
197 f = SW_C3 | SW_C2 | SW_C0;
198 break;
199 }
200 setcc(f);
201 if (c & COMP_Denormal) {
202 return denormal_operand() < 0;
203 }
204 return 0;
205}
206
207static int compare_st_st(int nr)
208{
209 int f, c;
210 FPU_REG *st_ptr;
211
212 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
213 setcc(SW_C3 | SW_C2 | SW_C0);
214 /* Stack fault */
215 EXCEPTION(EX_StackUnder);
216 return !(control_word & CW_Invalid);
217 }
218
219 st_ptr = &st(nr);
220 c = compare(b: st_ptr, tagb: FPU_gettagi(stnr: nr));
221 if (c & COMP_NaN) {
222 setcc(SW_C3 | SW_C2 | SW_C0);
223 EXCEPTION(EX_Invalid);
224 return !(control_word & CW_Invalid);
225 } else
226 switch (c & 7) {
227 case COMP_A_lt_B:
228 f = SW_C0;
229 break;
230 case COMP_A_eq_B:
231 f = SW_C3;
232 break;
233 case COMP_A_gt_B:
234 f = 0;
235 break;
236 case COMP_No_Comp:
237 f = SW_C3 | SW_C2 | SW_C0;
238 break;
239 default:
240#ifdef PARANOID
241 EXCEPTION(EX_INTERNAL | 0x122);
242#endif /* PARANOID */
243 f = SW_C3 | SW_C2 | SW_C0;
244 break;
245 }
246 setcc(f);
247 if (c & COMP_Denormal) {
248 return denormal_operand() < 0;
249 }
250 return 0;
251}
252
253static int compare_i_st_st(int nr)
254{
255 int f, c;
256 FPU_REG *st_ptr;
257
258 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
259 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
260 /* Stack fault */
261 EXCEPTION(EX_StackUnder);
262 return !(control_word & CW_Invalid);
263 }
264
265 partial_status &= ~SW_C0;
266 st_ptr = &st(nr);
267 c = compare(b: st_ptr, tagb: FPU_gettagi(stnr: nr));
268 if (c & COMP_NaN) {
269 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
270 EXCEPTION(EX_Invalid);
271 return !(control_word & CW_Invalid);
272 }
273
274 switch (c & 7) {
275 case COMP_A_lt_B:
276 f = X86_EFLAGS_CF;
277 break;
278 case COMP_A_eq_B:
279 f = X86_EFLAGS_ZF;
280 break;
281 case COMP_A_gt_B:
282 f = 0;
283 break;
284 case COMP_No_Comp:
285 f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
286 break;
287 default:
288#ifdef PARANOID
289 EXCEPTION(EX_INTERNAL | 0x122);
290#endif /* PARANOID */
291 f = 0;
292 break;
293 }
294 FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
295 if (c & COMP_Denormal) {
296 return denormal_operand() < 0;
297 }
298 return 0;
299}
300
301static int compare_u_st_st(int nr)
302{
303 int f = 0, c;
304 FPU_REG *st_ptr;
305
306 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
307 setcc(SW_C3 | SW_C2 | SW_C0);
308 /* Stack fault */
309 EXCEPTION(EX_StackUnder);
310 return !(control_word & CW_Invalid);
311 }
312
313 st_ptr = &st(nr);
314 c = compare(b: st_ptr, tagb: FPU_gettagi(stnr: nr));
315 if (c & COMP_NaN) {
316 setcc(SW_C3 | SW_C2 | SW_C0);
317 if (c & COMP_SNaN) { /* This is the only difference between
318 un-ordered and ordinary comparisons */
319 EXCEPTION(EX_Invalid);
320 return !(control_word & CW_Invalid);
321 }
322 return 0;
323 } else
324 switch (c & 7) {
325 case COMP_A_lt_B:
326 f = SW_C0;
327 break;
328 case COMP_A_eq_B:
329 f = SW_C3;
330 break;
331 case COMP_A_gt_B:
332 f = 0;
333 break;
334 case COMP_No_Comp:
335 f = SW_C3 | SW_C2 | SW_C0;
336 break;
337#ifdef PARANOID
338 default:
339 EXCEPTION(EX_INTERNAL | 0x123);
340 f = SW_C3 | SW_C2 | SW_C0;
341 break;
342#endif /* PARANOID */
343 }
344 setcc(f);
345 if (c & COMP_Denormal) {
346 return denormal_operand() < 0;
347 }
348 return 0;
349}
350
351static int compare_ui_st_st(int nr)
352{
353 int f = 0, c;
354 FPU_REG *st_ptr;
355
356 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
357 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
358 /* Stack fault */
359 EXCEPTION(EX_StackUnder);
360 return !(control_word & CW_Invalid);
361 }
362
363 partial_status &= ~SW_C0;
364 st_ptr = &st(nr);
365 c = compare(b: st_ptr, tagb: FPU_gettagi(stnr: nr));
366 if (c & COMP_NaN) {
367 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF);
368 if (c & COMP_SNaN) { /* This is the only difference between
369 un-ordered and ordinary comparisons */
370 EXCEPTION(EX_Invalid);
371 return !(control_word & CW_Invalid);
372 }
373 return 0;
374 }
375
376 switch (c & 7) {
377 case COMP_A_lt_B:
378 f = X86_EFLAGS_CF;
379 break;
380 case COMP_A_eq_B:
381 f = X86_EFLAGS_ZF;
382 break;
383 case COMP_A_gt_B:
384 f = 0;
385 break;
386 case COMP_No_Comp:
387 f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF;
388 break;
389#ifdef PARANOID
390 default:
391 EXCEPTION(EX_INTERNAL | 0x123);
392 f = 0;
393 break;
394#endif /* PARANOID */
395 }
396 FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f;
397 if (c & COMP_Denormal) {
398 return denormal_operand() < 0;
399 }
400 return 0;
401}
402
403/*---------------------------------------------------------------------------*/
404
405void fcom_st(void)
406{
407 /* fcom st(i) */
408 compare_st_st(FPU_rm);
409}
410
411void fcompst(void)
412{
413 /* fcomp st(i) */
414 if (!compare_st_st(FPU_rm))
415 FPU_pop();
416}
417
418void fcompp(void)
419{
420 /* fcompp */
421 if (FPU_rm != 1) {
422 FPU_illegal();
423 return;
424 }
425 if (!compare_st_st(nr: 1))
426 poppop();
427}
428
429void fucom_(void)
430{
431 /* fucom st(i) */
432 compare_u_st_st(FPU_rm);
433
434}
435
436void fucomp(void)
437{
438 /* fucomp st(i) */
439 if (!compare_u_st_st(FPU_rm))
440 FPU_pop();
441}
442
443void fucompp(void)
444{
445 /* fucompp */
446 if (FPU_rm == 1) {
447 if (!compare_u_st_st(nr: 1))
448 poppop();
449 } else
450 FPU_illegal();
451}
452
453/* P6+ compare-to-EFLAGS ops */
454
455void fcomi_(void)
456{
457 /* fcomi st(i) */
458 compare_i_st_st(FPU_rm);
459}
460
461void fcomip(void)
462{
463 /* fcomip st(i) */
464 if (!compare_i_st_st(FPU_rm))
465 FPU_pop();
466}
467
468void fucomi_(void)
469{
470 /* fucomi st(i) */
471 compare_ui_st_st(FPU_rm);
472}
473
474void fucomip(void)
475{
476 /* fucomip st(i) */
477 if (!compare_ui_st_st(FPU_rm))
478 FPU_pop();
479}
480

source code of linux/arch/x86/math-emu/reg_compare.c