 1da177e4c3
			
		
	
	
	1da177e4c3
	
	
	
		
			
			Initial git repository build. I'm not bothering with the full history, even though we have it. We can create a separate "historical" git archive of that later if we want to, and in the meantime it's about 3.2GB when imported into git - space that would just make the early git days unnecessarily complicated, when we don't have a lot of good infrastructure for it. Let it rip!
		
			
				
	
	
		
			381 lines
		
	
	
	
		
			8.5 KiB
			
		
	
	
	
		
			C
		
	
	
	
	
	
			
		
		
	
	
			381 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();
 | |
| }
 |