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 } 382