| Linus Torvalds | 1da177e | 2005-04-16 15:20:36 -0700 | [diff] [blame] | 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 |  | 
 | 23 |  | 
 | 24 | static 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 |     { | 
 | 44 |       if ( st0_tag == TAG_Zero ) | 
 | 45 | 	{ | 
 | 46 | 	  if ( tagb == TAG_Zero ) return COMP_A_eq_B; | 
 | 47 | 	  if ( tagb == TAG_Valid ) | 
 | 48 | 	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); | 
 | 49 | 	  if ( tagb == TW_Denormal ) | 
 | 50 | 	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | 
 | 51 | 	    | COMP_Denormal; | 
 | 52 | 	} | 
 | 53 |       else if ( tagb == TAG_Zero ) | 
 | 54 | 	{ | 
 | 55 | 	  if ( st0_tag == TAG_Valid ) | 
 | 56 | 	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | 
 | 57 | 	  if ( st0_tag == TW_Denormal ) | 
 | 58 | 	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | 
 | 59 | 	    | COMP_Denormal; | 
 | 60 | 	} | 
 | 61 |  | 
 | 62 |       if ( st0_tag == TW_Infinity ) | 
 | 63 | 	{ | 
 | 64 | 	  if ( (tagb == TAG_Valid) || (tagb == TAG_Zero) ) | 
 | 65 | 	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | 
 | 66 | 	  else if ( tagb == TW_Denormal ) | 
 | 67 | 	    return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | 
 | 68 | 	      | COMP_Denormal; | 
 | 69 | 	  else if ( tagb == TW_Infinity ) | 
 | 70 | 	    { | 
 | 71 | 	      /* The 80486 book says that infinities can be equal! */ | 
 | 72 | 	      return (st0_sign == signb) ? COMP_A_eq_B : | 
 | 73 | 		((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); | 
 | 74 | 	    } | 
 | 75 | 	  /* Fall through to the NaN code */ | 
 | 76 | 	} | 
 | 77 |       else if ( tagb == TW_Infinity ) | 
 | 78 | 	{ | 
 | 79 | 	  if ( (st0_tag == TAG_Valid) || (st0_tag == TAG_Zero) ) | 
 | 80 | 	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); | 
 | 81 | 	  if ( st0_tag == TW_Denormal ) | 
 | 82 | 	    return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | 
 | 83 | 		| COMP_Denormal; | 
 | 84 | 	  /* Fall through to the NaN code */ | 
 | 85 | 	} | 
 | 86 |  | 
 | 87 |       /* The only possibility now should be that one of the arguments | 
 | 88 | 	 is a NaN */ | 
 | 89 |       if ( (st0_tag == TW_NaN) || (tagb == TW_NaN) ) | 
 | 90 | 	{ | 
 | 91 | 	  int signalling = 0, unsupported = 0; | 
 | 92 | 	  if ( st0_tag == TW_NaN ) | 
 | 93 | 	    { | 
 | 94 | 	      signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000; | 
 | 95 | 	      unsupported = !((exponent(st0_ptr) == EXP_OVER) | 
 | 96 | 			      && (st0_ptr->sigh & 0x80000000)); | 
 | 97 | 	    } | 
 | 98 | 	  if ( tagb == TW_NaN ) | 
 | 99 | 	    { | 
 | 100 | 	      signalling |= (b->sigh & 0xc0000000) == 0x80000000; | 
 | 101 | 	      unsupported |= !((exponent(b) == EXP_OVER) | 
 | 102 | 			       && (b->sigh & 0x80000000)); | 
 | 103 | 	    } | 
 | 104 | 	  if ( signalling || unsupported ) | 
 | 105 | 	    return COMP_No_Comp | COMP_SNaN | COMP_NaN; | 
 | 106 | 	  else | 
 | 107 | 	    /* Neither is a signaling NaN */ | 
 | 108 | 	    return COMP_No_Comp | COMP_NaN; | 
 | 109 | 	} | 
 | 110 |        | 
 | 111 |       EXCEPTION(EX_Invalid); | 
 | 112 |     } | 
 | 113 |    | 
 | 114 |   if (st0_sign != signb) | 
 | 115 |     { | 
 | 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 |     { | 
 | 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 |     } | 
 | 130 |   else | 
 | 131 |     { | 
 | 132 |       exp0 = exponent(st0_ptr); | 
 | 133 |       expb = exponent(b); | 
 | 134 |     } | 
 | 135 |  | 
 | 136 | #ifdef PARANOID | 
 | 137 |   if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); | 
 | 138 |   if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); | 
 | 139 | #endif /* PARANOID */ | 
 | 140 |  | 
 | 141 |   diff = exp0 - expb; | 
 | 142 |   if ( diff == 0 ) | 
 | 143 |     { | 
 | 144 |       diff = st0_ptr->sigh - b->sigh;  /* Works only if ms bits are | 
 | 145 | 					      identical */ | 
 | 146 |       if ( diff == 0 ) | 
 | 147 | 	{ | 
 | 148 | 	diff = st0_ptr->sigl > b->sigl; | 
 | 149 | 	if ( diff == 0 ) | 
 | 150 | 	  diff = -(st0_ptr->sigl < b->sigl); | 
 | 151 | 	} | 
 | 152 |     } | 
 | 153 |  | 
 | 154 |   if ( diff > 0 ) | 
 | 155 |     { | 
 | 156 |       return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | 
 | 157 | 	| ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | 
 | 158 | 	    COMP_Denormal : 0); | 
 | 159 |     } | 
 | 160 |   if ( diff < 0 ) | 
 | 161 |     { | 
 | 162 |       return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | 
 | 163 | 	| ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | 
 | 164 | 	    COMP_Denormal : 0); | 
 | 165 |     } | 
 | 166 |  | 
 | 167 |   return COMP_A_eq_B | 
 | 168 |     | ( ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? | 
 | 169 | 	COMP_Denormal : 0); | 
 | 170 |  | 
 | 171 | } | 
 | 172 |  | 
 | 173 |  | 
 | 174 | /* This function requires that st(0) is not empty */ | 
 | 175 | int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) | 
 | 176 | { | 
 | 177 |   int f = 0, c; | 
 | 178 |  | 
 | 179 |   c = compare(loaded_data, loaded_tag); | 
 | 180 |  | 
 | 181 |   if (c & COMP_NaN) | 
 | 182 |     { | 
 | 183 |       EXCEPTION(EX_Invalid); | 
 | 184 |       f = SW_C3 | SW_C2 | SW_C0; | 
 | 185 |     } | 
 | 186 |   else | 
 | 187 |     switch (c & 7) | 
 | 188 |       { | 
 | 189 |       case COMP_A_lt_B: | 
 | 190 | 	f = SW_C0; | 
 | 191 | 	break; | 
 | 192 |       case COMP_A_eq_B: | 
 | 193 | 	f = SW_C3; | 
 | 194 | 	break; | 
 | 195 |       case COMP_A_gt_B: | 
 | 196 | 	f = 0; | 
 | 197 | 	break; | 
 | 198 |       case COMP_No_Comp: | 
 | 199 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 200 | 	break; | 
 | 201 | #ifdef PARANOID | 
 | 202 |       default: | 
 | 203 | 	EXCEPTION(EX_INTERNAL|0x121); | 
 | 204 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 205 | 	break; | 
 | 206 | #endif /* PARANOID */ | 
 | 207 |       } | 
 | 208 |   setcc(f); | 
 | 209 |   if (c & COMP_Denormal) | 
 | 210 |     { | 
 | 211 |       return denormal_operand() < 0; | 
 | 212 |     } | 
 | 213 |   return 0; | 
 | 214 | } | 
 | 215 |  | 
 | 216 |  | 
 | 217 | static int compare_st_st(int nr) | 
 | 218 | { | 
 | 219 |   int f = 0, c; | 
 | 220 |   FPU_REG *st_ptr; | 
 | 221 |  | 
 | 222 |   if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) | 
 | 223 |     { | 
 | 224 |       setcc(SW_C3 | SW_C2 | SW_C0); | 
 | 225 |       /* Stack fault */ | 
 | 226 |       EXCEPTION(EX_StackUnder); | 
 | 227 |       return !(control_word & CW_Invalid); | 
 | 228 |     } | 
 | 229 |  | 
 | 230 |   st_ptr = &st(nr); | 
 | 231 |   c = compare(st_ptr, FPU_gettagi(nr)); | 
 | 232 |   if (c & COMP_NaN) | 
 | 233 |     { | 
 | 234 |       setcc(SW_C3 | SW_C2 | SW_C0); | 
 | 235 |       EXCEPTION(EX_Invalid); | 
 | 236 |       return !(control_word & CW_Invalid); | 
 | 237 |     } | 
 | 238 |   else | 
 | 239 |     switch (c & 7) | 
 | 240 |       { | 
 | 241 |       case COMP_A_lt_B: | 
 | 242 | 	f = SW_C0; | 
 | 243 | 	break; | 
 | 244 |       case COMP_A_eq_B: | 
 | 245 | 	f = SW_C3; | 
 | 246 | 	break; | 
 | 247 |       case COMP_A_gt_B: | 
 | 248 | 	f = 0; | 
 | 249 | 	break; | 
 | 250 |       case COMP_No_Comp: | 
 | 251 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 252 | 	break; | 
 | 253 | #ifdef PARANOID | 
 | 254 |       default: | 
 | 255 | 	EXCEPTION(EX_INTERNAL|0x122); | 
 | 256 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 257 | 	break; | 
 | 258 | #endif /* PARANOID */ | 
 | 259 |       } | 
 | 260 |   setcc(f); | 
 | 261 |   if (c & COMP_Denormal) | 
 | 262 |     { | 
 | 263 |       return denormal_operand() < 0; | 
 | 264 |     } | 
 | 265 |   return 0; | 
 | 266 | } | 
 | 267 |  | 
 | 268 |  | 
 | 269 | static int compare_u_st_st(int nr) | 
 | 270 | { | 
 | 271 |   int f = 0, c; | 
 | 272 |   FPU_REG *st_ptr; | 
 | 273 |  | 
 | 274 |   if ( !NOT_EMPTY(0) || !NOT_EMPTY(nr) ) | 
 | 275 |     { | 
 | 276 |       setcc(SW_C3 | SW_C2 | SW_C0); | 
 | 277 |       /* Stack fault */ | 
 | 278 |       EXCEPTION(EX_StackUnder); | 
 | 279 |       return !(control_word & CW_Invalid); | 
 | 280 |     } | 
 | 281 |  | 
 | 282 |   st_ptr = &st(nr); | 
 | 283 |   c = compare(st_ptr, FPU_gettagi(nr)); | 
 | 284 |   if (c & COMP_NaN) | 
 | 285 |     { | 
 | 286 |       setcc(SW_C3 | SW_C2 | SW_C0); | 
 | 287 |       if (c & COMP_SNaN)       /* This is the only difference between | 
 | 288 | 				  un-ordered and ordinary comparisons */ | 
 | 289 | 	{ | 
 | 290 | 	  EXCEPTION(EX_Invalid); | 
 | 291 | 	  return !(control_word & CW_Invalid); | 
 | 292 | 	} | 
 | 293 |       return 0; | 
 | 294 |     } | 
 | 295 |   else | 
 | 296 |     switch (c & 7) | 
 | 297 |       { | 
 | 298 |       case COMP_A_lt_B: | 
 | 299 | 	f = SW_C0; | 
 | 300 | 	break; | 
 | 301 |       case COMP_A_eq_B: | 
 | 302 | 	f = SW_C3; | 
 | 303 | 	break; | 
 | 304 |       case COMP_A_gt_B: | 
 | 305 | 	f = 0; | 
 | 306 | 	break; | 
 | 307 |       case COMP_No_Comp: | 
 | 308 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 309 | 	break; | 
 | 310 | #ifdef PARANOID | 
 | 311 |       default: | 
 | 312 | 	EXCEPTION(EX_INTERNAL|0x123); | 
 | 313 | 	f = SW_C3 | SW_C2 | SW_C0; | 
 | 314 | 	break; | 
 | 315 | #endif /* PARANOID */  | 
 | 316 |       } | 
 | 317 |   setcc(f); | 
 | 318 |   if (c & COMP_Denormal) | 
 | 319 |     { | 
 | 320 |       return denormal_operand() < 0; | 
 | 321 |     } | 
 | 322 |   return 0; | 
 | 323 | } | 
 | 324 |  | 
 | 325 | /*---------------------------------------------------------------------------*/ | 
 | 326 |  | 
 | 327 | void fcom_st(void) | 
 | 328 | { | 
 | 329 |   /* fcom st(i) */ | 
 | 330 |   compare_st_st(FPU_rm); | 
 | 331 | } | 
 | 332 |  | 
 | 333 |  | 
 | 334 | void fcompst(void) | 
 | 335 | { | 
 | 336 |   /* fcomp st(i) */ | 
 | 337 |   if ( !compare_st_st(FPU_rm) ) | 
 | 338 |     FPU_pop(); | 
 | 339 | } | 
 | 340 |  | 
 | 341 |  | 
 | 342 | void fcompp(void) | 
 | 343 | { | 
 | 344 |   /* fcompp */ | 
 | 345 |   if (FPU_rm != 1) | 
 | 346 |     { | 
 | 347 |       FPU_illegal(); | 
 | 348 |       return; | 
 | 349 |     } | 
 | 350 |   if ( !compare_st_st(1) ) | 
 | 351 |       poppop(); | 
 | 352 | } | 
 | 353 |  | 
 | 354 |  | 
 | 355 | void fucom_(void) | 
 | 356 | { | 
 | 357 |   /* fucom st(i) */ | 
 | 358 |   compare_u_st_st(FPU_rm); | 
 | 359 |  | 
 | 360 | } | 
 | 361 |  | 
 | 362 |  | 
 | 363 | void fucomp(void) | 
 | 364 | { | 
 | 365 |   /* fucomp st(i) */ | 
 | 366 |   if ( !compare_u_st_st(FPU_rm) ) | 
 | 367 |     FPU_pop(); | 
 | 368 | } | 
 | 369 |  | 
 | 370 |  | 
 | 371 | void fucompp(void) | 
 | 372 | { | 
 | 373 |   /* fucompp */ | 
 | 374 |   if (FPU_rm == 1) | 
 | 375 |     { | 
 | 376 |       if ( !compare_u_st_st(1) ) | 
 | 377 | 	poppop(); | 
 | 378 |     } | 
 | 379 |   else | 
 | 380 |     FPU_illegal(); | 
 | 381 | } |