]>
Commit | Line | Data |
---|---|---|
b2441318 | 1 | // SPDX-License-Identifier: GPL-2.0 |
1da177e4 LT |
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 | ||
1da177e4 LT |
24 | static int compare(FPU_REG const *b, int tagb) |
25 | { | |
3d0d14f9 IM |
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 | 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); | |
1da177e4 LT |
114 | } |
115 | ||
3d0d14f9 IM |
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); | |
1da177e4 LT |
120 | } |
121 | ||
3d0d14f9 IM |
122 | if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) { |
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 | } else { | |
130 | exp0 = exponent(st0_ptr); | |
131 | expb = exponent(b); | |
1da177e4 | 132 | } |
1da177e4 LT |
133 | |
134 | #ifdef PARANOID | |
3d0d14f9 IM |
135 | if (!(st0_ptr->sigh & 0x80000000)) |
136 | EXCEPTION(EX_Invalid); | |
137 | if (!(b->sigh & 0x80000000)) | |
138 | EXCEPTION(EX_Invalid); | |
1da177e4 LT |
139 | #endif /* PARANOID */ |
140 | ||
3d0d14f9 IM |
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 | } | |
1da177e4 | 150 | } |
1da177e4 | 151 | |
3d0d14f9 IM |
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 | } | |
1da177e4 | 162 | |
3d0d14f9 IM |
163 | return COMP_A_eq_B |
164 | | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | |
165 | COMP_Denormal : 0); | |
166 | ||
167 | } | |
1da177e4 LT |
168 | |
169 | /* This function requires that st(0) is not empty */ | |
170 | int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) | |
171 | { | |
75e2f0a6 | 172 | int f, c; |
3d0d14f9 IM |
173 | |
174 | c = compare(loaded_data, 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; | |
3d0d14f9 | 193 | default: |
75e2f0a6 | 194 | #ifdef PARANOID |
3d0d14f9 | 195 | EXCEPTION(EX_INTERNAL | 0x121); |
75e2f0a6 | 196 | #endif /* PARANOID */ |
3d0d14f9 IM |
197 | f = SW_C3 | SW_C2 | SW_C0; |
198 | break; | |
3d0d14f9 IM |
199 | } |
200 | setcc(f); | |
201 | if (c & COMP_Denormal) { | |
202 | return denormal_operand() < 0; | |
203 | } | |
204 | return 0; | |
1da177e4 LT |
205 | } |
206 | ||
1da177e4 LT |
207 | static int compare_st_st(int nr) |
208 | { | |
75e2f0a6 | 209 | int f, c; |
3d0d14f9 IM |
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(st_ptr, FPU_gettagi(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; | |
3d0d14f9 | 239 | default: |
75e2f0a6 | 240 | #ifdef PARANOID |
3d0d14f9 | 241 | EXCEPTION(EX_INTERNAL | 0x122); |
75e2f0a6 | 242 | #endif /* PARANOID */ |
3d0d14f9 IM |
243 | f = SW_C3 | SW_C2 | SW_C0; |
244 | break; | |
3d0d14f9 IM |
245 | } |
246 | setcc(f); | |
247 | if (c & COMP_Denormal) { | |
248 | return denormal_operand() < 0; | |
249 | } | |
250 | return 0; | |
1da177e4 LT |
251 | } |
252 | ||
b8e4a910 DV |
253 | static 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(st_ptr, FPU_gettagi(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; | |
b8e4a910 | 287 | default: |
75e2f0a6 | 288 | #ifdef PARANOID |
b8e4a910 | 289 | EXCEPTION(EX_INTERNAL | 0x122); |
75e2f0a6 | 290 | #endif /* PARANOID */ |
b8e4a910 DV |
291 | f = 0; |
292 | break; | |
b8e4a910 DV |
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 | ||
1da177e4 LT |
301 | static int compare_u_st_st(int nr) |
302 | { | |
3d0d14f9 IM |
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); | |
1da177e4 | 311 | } |
3d0d14f9 IM |
312 | |
313 | st_ptr = &st(nr); | |
314 | c = compare(st_ptr, FPU_gettagi(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; | |
1da177e4 | 337 | #ifdef PARANOID |
3d0d14f9 IM |
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; | |
1da177e4 LT |
349 | } |
350 | ||
b8e4a910 DV |
351 | static 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(st_ptr, FPU_gettagi(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 | ||
1da177e4 LT |
403 | /*---------------------------------------------------------------------------*/ |
404 | ||
405 | void fcom_st(void) | |
406 | { | |
3d0d14f9 IM |
407 | /* fcom st(i) */ |
408 | compare_st_st(FPU_rm); | |
1da177e4 LT |
409 | } |
410 | ||
1da177e4 LT |
411 | void fcompst(void) |
412 | { | |
3d0d14f9 IM |
413 | /* fcomp st(i) */ |
414 | if (!compare_st_st(FPU_rm)) | |
415 | FPU_pop(); | |
1da177e4 LT |
416 | } |
417 | ||
1da177e4 LT |
418 | void fcompp(void) |
419 | { | |
3d0d14f9 IM |
420 | /* fcompp */ |
421 | if (FPU_rm != 1) { | |
422 | FPU_illegal(); | |
423 | return; | |
424 | } | |
425 | if (!compare_st_st(1)) | |
426 | poppop(); | |
1da177e4 LT |
427 | } |
428 | ||
1da177e4 LT |
429 | void fucom_(void) |
430 | { | |
3d0d14f9 IM |
431 | /* fucom st(i) */ |
432 | compare_u_st_st(FPU_rm); | |
1da177e4 LT |
433 | |
434 | } | |
435 | ||
1da177e4 LT |
436 | void fucomp(void) |
437 | { | |
3d0d14f9 IM |
438 | /* fucomp st(i) */ |
439 | if (!compare_u_st_st(FPU_rm)) | |
440 | FPU_pop(); | |
1da177e4 LT |
441 | } |
442 | ||
1da177e4 LT |
443 | void fucompp(void) |
444 | { | |
3d0d14f9 IM |
445 | /* fucompp */ |
446 | if (FPU_rm == 1) { | |
447 | if (!compare_u_st_st(1)) | |
448 | poppop(); | |
449 | } else | |
450 | FPU_illegal(); | |
1da177e4 | 451 | } |
b8e4a910 DV |
452 | |
453 | /* P6+ compare-to-EFLAGS ops */ | |
454 | ||
455 | void fcomi_(void) | |
456 | { | |
457 | /* fcomi st(i) */ | |
458 | compare_i_st_st(FPU_rm); | |
459 | } | |
460 | ||
461 | void fcomip(void) | |
462 | { | |
463 | /* fcomip st(i) */ | |
464 | if (!compare_i_st_st(FPU_rm)) | |
465 | FPU_pop(); | |
466 | } | |
467 | ||
468 | void fucomi_(void) | |
469 | { | |
470 | /* fucomi st(i) */ | |
471 | compare_ui_st_st(FPU_rm); | |
472 | } | |
473 | ||
474 | void fucomip(void) | |
475 | { | |
476 | /* fucomip st(i) */ | |
477 | if (!compare_ui_st_st(FPU_rm)) | |
478 | FPU_pop(); | |
479 | } |