351 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			351 lines
		
	
	
		
			8.1 KiB
		
	
	
	
		
			C
		
	
	
	
	
	
/*---------------------------------------------------------------------------+
 | 
						|
 |  reg_compare.c                                                            |
 | 
						|
 |                                                                           |
 | 
						|
 | Compare two floating point registers                                      |
 | 
						|
 |                                                                           |
 | 
						|
 | Copyright (C) 1992,1993,1994,1997                                         |
 | 
						|
 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
 | 
						|
 |                  E-mail   billm@suburbia.net                              |
 | 
						|
 |                                                                           |
 | 
						|
 |                                                                           |
 | 
						|
 +---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------+
 | 
						|
 | compare() is the core FPU_REG comparison function                         |
 | 
						|
 +---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
#include "fpu_system.h"
 | 
						|
#include "exception.h"
 | 
						|
#include "fpu_emu.h"
 | 
						|
#include "control_w.h"
 | 
						|
#include "status_w.h"
 | 
						|
 | 
						|
static int compare(FPU_REG const *b, int tagb)
 | 
						|
{
 | 
						|
	int diff, exp0, expb;
 | 
						|
	u_char st0_tag;
 | 
						|
	FPU_REG *st0_ptr;
 | 
						|
	FPU_REG x, y;
 | 
						|
	u_char st0_sign, signb = getsign(b);
 | 
						|
 | 
						|
	st0_ptr = &st(0);
 | 
						|
	st0_tag = FPU_gettag0();
 | 
						|
	st0_sign = getsign(st0_ptr);
 | 
						|
 | 
						|
	if (tagb == TAG_Special)
 | 
						|
		tagb = FPU_Special(b);
 | 
						|
	if (st0_tag == TAG_Special)
 | 
						|
		st0_tag = FPU_Special(st0_ptr);
 | 
						|
 | 
						|
	if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal))
 | 
						|
	    || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) {
 | 
						|
		if (st0_tag == TAG_Zero) {
 | 
						|
			if (tagb == TAG_Zero)
 | 
						|
				return COMP_A_eq_B;
 | 
						|
			if (tagb == TAG_Valid)
 | 
						|
				return ((signb ==
 | 
						|
					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
 | 
						|
			if (tagb == TW_Denormal)
 | 
						|
				return ((signb ==
 | 
						|
					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 | 
						|
				    | COMP_Denormal;
 | 
						|
		} else if (tagb == TAG_Zero) {
 | 
						|
			if (st0_tag == TAG_Valid)
 | 
						|
				return ((st0_sign ==
 | 
						|
					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
 | 
						|
			if (st0_tag == TW_Denormal)
 | 
						|
				return ((st0_sign ==
 | 
						|
					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 | 
						|
				    | COMP_Denormal;
 | 
						|
		}
 | 
						|
 | 
						|
		if (st0_tag == TW_Infinity) {
 | 
						|
			if ((tagb == TAG_Valid) || (tagb == TAG_Zero))
 | 
						|
				return ((st0_sign ==
 | 
						|
					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
 | 
						|
			else if (tagb == TW_Denormal)
 | 
						|
				return ((st0_sign ==
 | 
						|
					 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 | 
						|
				    | COMP_Denormal;
 | 
						|
			else if (tagb == TW_Infinity) {
 | 
						|
				/* The 80486 book says that infinities can be equal! */
 | 
						|
				return (st0_sign == signb) ? COMP_A_eq_B :
 | 
						|
				    ((st0_sign ==
 | 
						|
				      SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B);
 | 
						|
			}
 | 
						|
			/* Fall through to the NaN code */
 | 
						|
		} else if (tagb == TW_Infinity) {
 | 
						|
			if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero))
 | 
						|
				return ((signb ==
 | 
						|
					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B);
 | 
						|
			if (st0_tag == TW_Denormal)
 | 
						|
				return ((signb ==
 | 
						|
					 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 | 
						|
				    | COMP_Denormal;
 | 
						|
			/* Fall through to the NaN code */
 | 
						|
		}
 | 
						|
 | 
						|
		/* The only possibility now should be that one of the arguments
 | 
						|
		   is a NaN */
 | 
						|
		if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) {
 | 
						|
			int signalling = 0, unsupported = 0;
 | 
						|
			if (st0_tag == TW_NaN) {
 | 
						|
				signalling =
 | 
						|
				    (st0_ptr->sigh & 0xc0000000) == 0x80000000;
 | 
						|
				unsupported = !((exponent(st0_ptr) == EXP_OVER)
 | 
						|
						&& (st0_ptr->
 | 
						|
						    sigh & 0x80000000));
 | 
						|
			}
 | 
						|
			if (tagb == TW_NaN) {
 | 
						|
				signalling |=
 | 
						|
				    (b->sigh & 0xc0000000) == 0x80000000;
 | 
						|
				unsupported |= !((exponent(b) == EXP_OVER)
 | 
						|
						 && (b->sigh & 0x80000000));
 | 
						|
			}
 | 
						|
			if (signalling || unsupported)
 | 
						|
				return COMP_No_Comp | COMP_SNaN | COMP_NaN;
 | 
						|
			else
 | 
						|
				/* Neither is a signaling NaN */
 | 
						|
				return COMP_No_Comp | COMP_NaN;
 | 
						|
		}
 | 
						|
 | 
						|
		EXCEPTION(EX_Invalid);
 | 
						|
	}
 | 
						|
 | 
						|
	if (st0_sign != signb) {
 | 
						|
		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 | 
						|
		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 | 
						|
		       COMP_Denormal : 0);
 | 
						|
	}
 | 
						|
 | 
						|
	if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) {
 | 
						|
		FPU_to_exp16(st0_ptr, &x);
 | 
						|
		FPU_to_exp16(b, &y);
 | 
						|
		st0_ptr = &x;
 | 
						|
		b = &y;
 | 
						|
		exp0 = exponent16(st0_ptr);
 | 
						|
		expb = exponent16(b);
 | 
						|
	} else {
 | 
						|
		exp0 = exponent(st0_ptr);
 | 
						|
		expb = exponent(b);
 | 
						|
	}
 | 
						|
 | 
						|
#ifdef PARANOID
 | 
						|
	if (!(st0_ptr->sigh & 0x80000000))
 | 
						|
		EXCEPTION(EX_Invalid);
 | 
						|
	if (!(b->sigh & 0x80000000))
 | 
						|
		EXCEPTION(EX_Invalid);
 | 
						|
#endif /* PARANOID */
 | 
						|
 | 
						|
	diff = exp0 - expb;
 | 
						|
	if (diff == 0) {
 | 
						|
		diff = st0_ptr->sigh - b->sigh;	/* Works only if ms bits are
 | 
						|
						   identical */
 | 
						|
		if (diff == 0) {
 | 
						|
			diff = st0_ptr->sigl > b->sigl;
 | 
						|
			if (diff == 0)
 | 
						|
				diff = -(st0_ptr->sigl < b->sigl);
 | 
						|
		}
 | 
						|
	}
 | 
						|
 | 
						|
	if (diff > 0) {
 | 
						|
		return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B)
 | 
						|
		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 | 
						|
		       COMP_Denormal : 0);
 | 
						|
	}
 | 
						|
	if (diff < 0) {
 | 
						|
		return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B)
 | 
						|
		    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 | 
						|
		       COMP_Denormal : 0);
 | 
						|
	}
 | 
						|
 | 
						|
	return COMP_A_eq_B
 | 
						|
	    | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ?
 | 
						|
	       COMP_Denormal : 0);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
/* This function requires that st(0) is not empty */
 | 
						|
int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag)
 | 
						|
{
 | 
						|
	int f = 0, c;
 | 
						|
 | 
						|
	c = compare(loaded_data, loaded_tag);
 | 
						|
 | 
						|
	if (c & COMP_NaN) {
 | 
						|
		EXCEPTION(EX_Invalid);
 | 
						|
		f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
	} else
 | 
						|
		switch (c & 7) {
 | 
						|
		case COMP_A_lt_B:
 | 
						|
			f = SW_C0;
 | 
						|
			break;
 | 
						|
		case COMP_A_eq_B:
 | 
						|
			f = SW_C3;
 | 
						|
			break;
 | 
						|
		case COMP_A_gt_B:
 | 
						|
			f = 0;
 | 
						|
			break;
 | 
						|
		case COMP_No_Comp:
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#ifdef PARANOID
 | 
						|
		default:
 | 
						|
			EXCEPTION(EX_INTERNAL | 0x121);
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#endif /* PARANOID */
 | 
						|
		}
 | 
						|
	setcc(f);
 | 
						|
	if (c & COMP_Denormal) {
 | 
						|
		return denormal_operand() < 0;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int compare_st_st(int nr)
 | 
						|
{
 | 
						|
	int f = 0, c;
 | 
						|
	FPU_REG *st_ptr;
 | 
						|
 | 
						|
	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 | 
						|
		setcc(SW_C3 | SW_C2 | SW_C0);
 | 
						|
		/* Stack fault */
 | 
						|
		EXCEPTION(EX_StackUnder);
 | 
						|
		return !(control_word & CW_Invalid);
 | 
						|
	}
 | 
						|
 | 
						|
	st_ptr = &st(nr);
 | 
						|
	c = compare(st_ptr, FPU_gettagi(nr));
 | 
						|
	if (c & COMP_NaN) {
 | 
						|
		setcc(SW_C3 | SW_C2 | SW_C0);
 | 
						|
		EXCEPTION(EX_Invalid);
 | 
						|
		return !(control_word & CW_Invalid);
 | 
						|
	} else
 | 
						|
		switch (c & 7) {
 | 
						|
		case COMP_A_lt_B:
 | 
						|
			f = SW_C0;
 | 
						|
			break;
 | 
						|
		case COMP_A_eq_B:
 | 
						|
			f = SW_C3;
 | 
						|
			break;
 | 
						|
		case COMP_A_gt_B:
 | 
						|
			f = 0;
 | 
						|
			break;
 | 
						|
		case COMP_No_Comp:
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#ifdef PARANOID
 | 
						|
		default:
 | 
						|
			EXCEPTION(EX_INTERNAL | 0x122);
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#endif /* PARANOID */
 | 
						|
		}
 | 
						|
	setcc(f);
 | 
						|
	if (c & COMP_Denormal) {
 | 
						|
		return denormal_operand() < 0;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
static int compare_u_st_st(int nr)
 | 
						|
{
 | 
						|
	int f = 0, c;
 | 
						|
	FPU_REG *st_ptr;
 | 
						|
 | 
						|
	if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) {
 | 
						|
		setcc(SW_C3 | SW_C2 | SW_C0);
 | 
						|
		/* Stack fault */
 | 
						|
		EXCEPTION(EX_StackUnder);
 | 
						|
		return !(control_word & CW_Invalid);
 | 
						|
	}
 | 
						|
 | 
						|
	st_ptr = &st(nr);
 | 
						|
	c = compare(st_ptr, FPU_gettagi(nr));
 | 
						|
	if (c & COMP_NaN) {
 | 
						|
		setcc(SW_C3 | SW_C2 | SW_C0);
 | 
						|
		if (c & COMP_SNaN) {	/* This is the only difference between
 | 
						|
					   un-ordered and ordinary comparisons */
 | 
						|
			EXCEPTION(EX_Invalid);
 | 
						|
			return !(control_word & CW_Invalid);
 | 
						|
		}
 | 
						|
		return 0;
 | 
						|
	} else
 | 
						|
		switch (c & 7) {
 | 
						|
		case COMP_A_lt_B:
 | 
						|
			f = SW_C0;
 | 
						|
			break;
 | 
						|
		case COMP_A_eq_B:
 | 
						|
			f = SW_C3;
 | 
						|
			break;
 | 
						|
		case COMP_A_gt_B:
 | 
						|
			f = 0;
 | 
						|
			break;
 | 
						|
		case COMP_No_Comp:
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#ifdef PARANOID
 | 
						|
		default:
 | 
						|
			EXCEPTION(EX_INTERNAL | 0x123);
 | 
						|
			f = SW_C3 | SW_C2 | SW_C0;
 | 
						|
			break;
 | 
						|
#endif /* PARANOID */
 | 
						|
		}
 | 
						|
	setcc(f);
 | 
						|
	if (c & COMP_Denormal) {
 | 
						|
		return denormal_operand() < 0;
 | 
						|
	}
 | 
						|
	return 0;
 | 
						|
}
 | 
						|
 | 
						|
/*---------------------------------------------------------------------------*/
 | 
						|
 | 
						|
void fcom_st(void)
 | 
						|
{
 | 
						|
	/* fcom st(i) */
 | 
						|
	compare_st_st(FPU_rm);
 | 
						|
}
 | 
						|
 | 
						|
void fcompst(void)
 | 
						|
{
 | 
						|
	/* fcomp st(i) */
 | 
						|
	if (!compare_st_st(FPU_rm))
 | 
						|
		FPU_pop();
 | 
						|
}
 | 
						|
 | 
						|
void fcompp(void)
 | 
						|
{
 | 
						|
	/* fcompp */
 | 
						|
	if (FPU_rm != 1) {
 | 
						|
		FPU_illegal();
 | 
						|
		return;
 | 
						|
	}
 | 
						|
	if (!compare_st_st(1))
 | 
						|
		poppop();
 | 
						|
}
 | 
						|
 | 
						|
void fucom_(void)
 | 
						|
{
 | 
						|
	/* fucom st(i) */
 | 
						|
	compare_u_st_st(FPU_rm);
 | 
						|
 | 
						|
}
 | 
						|
 | 
						|
void fucomp(void)
 | 
						|
{
 | 
						|
	/* fucomp st(i) */
 | 
						|
	if (!compare_u_st_st(FPU_rm))
 | 
						|
		FPU_pop();
 | 
						|
}
 | 
						|
 | 
						|
void fucompp(void)
 | 
						|
{
 | 
						|
	/* fucompp */
 | 
						|
	if (FPU_rm == 1) {
 | 
						|
		if (!compare_u_st_st(1))
 | 
						|
			poppop();
 | 
						|
	} else
 | 
						|
		FPU_illegal();
 | 
						|
}
 |