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 | |
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(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 */ |
170 | int 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 | |
207 | static 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 | |
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(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 | |
301 | static 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 | |
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(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 | |
405 | void fcom_st(void) |
406 | { |
407 | /* fcom st(i) */ |
408 | compare_st_st(FPU_rm); |
409 | } |
410 | |
411 | void fcompst(void) |
412 | { |
413 | /* fcomp st(i) */ |
414 | if (!compare_st_st(FPU_rm)) |
415 | FPU_pop(); |
416 | } |
417 | |
418 | void 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 | |
429 | void fucom_(void) |
430 | { |
431 | /* fucom st(i) */ |
432 | compare_u_st_st(FPU_rm); |
433 | |
434 | } |
435 | |
436 | void fucomp(void) |
437 | { |
438 | /* fucomp st(i) */ |
439 | if (!compare_u_st_st(FPU_rm)) |
440 | FPU_pop(); |
441 | } |
442 | |
443 | void 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 | |
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 | } |
480 | |