PageRenderTime 59ms CodeModel.GetById 17ms app.highlight 38ms RepoModel.GetById 1ms app.codeStats 0ms

/arch/x86/math-emu/reg_compare.c

https://bitbucket.org/thekraven/iscream_thunderc-2.6.35
C | 350 lines | 285 code | 33 blank | 32 comment | 119 complexity | 03c337606d3043cce946379bc54336b7 MD5 | raw file
Possible License(s): GPL-2.0, LGPL-2.0, AGPL-1.0
  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
 23static 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 */
169int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
170{
171	int f = 0, 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#ifdef PARANOID
193		default:
194			EXCEPTION(EX_INTERNAL | 0x121);
195			f = SW_C3 | SW_C2 | SW_C0;
196			break;
197#endif /* PARANOID */
198		}
199	setcc(f);
200	if (c & COMP_Denormal) {
201		return denormal_operand() < 0;
202	}
203	return 0;
204}
205
206static int compare_st_st(int nr)
207{
208	int f = 0, 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#ifdef PARANOID
239		default:
240			EXCEPTION(EX_INTERNAL | 0x122);
241			f = SW_C3 | SW_C2 | SW_C0;
242			break;
243#endif /* PARANOID */
244		}
245	setcc(f);
246	if (c & COMP_Denormal) {
247		return denormal_operand() < 0;
248	}
249	return 0;
250}
251
252static int compare_u_st_st(int nr)
253{
254	int f = 0, c;
255	FPU_REG *st_ptr;
256
257	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
258		setcc(SW_C3 | SW_C2 | SW_C0);
259		/* Stack fault */
260		EXCEPTION(EX_StackUnder);
261		return !(control_word & CW_Invalid);
262	}
263
264	st_ptr = &st(nr);
265	c = compare(st_ptr, FPU_gettagi(nr));
266	if (c & COMP_NaN) {
267		setcc(SW_C3 | SW_C2 | SW_C0);
268		if (c & COMP_SNaN) {	/* This is the only difference between
269					   un-ordered and ordinary comparisons */
270			EXCEPTION(EX_Invalid);
271			return !(control_word & CW_Invalid);
272		}
273		return 0;
274	} else
275		switch (c & 7) {
276		case COMP_A_lt_B:
277			f = SW_C0;
278			break;
279		case COMP_A_eq_B:
280			f = SW_C3;
281			break;
282		case COMP_A_gt_B:
283			f = 0;
284			break;
285		case COMP_No_Comp:
286			f = SW_C3 | SW_C2 | SW_C0;
287			break;
288#ifdef PARANOID
289		default:
290			EXCEPTION(EX_INTERNAL | 0x123);
291			f = SW_C3 | SW_C2 | SW_C0;
292			break;
293#endif /* PARANOID */
294		}
295	setcc(f);
296	if (c & COMP_Denormal) {
297		return denormal_operand() < 0;
298	}
299	return 0;
300}
301
302/*---------------------------------------------------------------------------*/
303
304void fcom_st(void)
305{
306	/* fcom st(i) */
307	compare_st_st(FPU_rm);
308}
309
310void fcompst(void)
311{
312	/* fcomp st(i) */
313	if (!compare_st_st(FPU_rm))
314		FPU_pop();
315}
316
317void fcompp(void)
318{
319	/* fcompp */
320	if (FPU_rm != 1) {
321		FPU_illegal();
322		return;
323	}
324	if (!compare_st_st(1))
325		poppop();
326}
327
328void fucom_(void)
329{
330	/* fucom st(i) */
331	compare_u_st_st(FPU_rm);
332
333}
334
335void fucomp(void)
336{
337	/* fucomp st(i) */
338	if (!compare_u_st_st(FPU_rm))
339		FPU_pop();
340}
341
342void fucompp(void)
343{
344	/* fucompp */
345	if (FPU_rm == 1) {
346		if (!compare_u_st_st(1))
347			poppop();
348	} else
349		FPU_illegal();
350}