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(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);
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(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);
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(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;
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(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;
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(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;
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(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;
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(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
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(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(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