382 lines
		
	
	
	
		
			8.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
		
		
			
		
	
	
			382 lines
		
	
	
	
		
			8.5 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(); | ||
|  | } |