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 static int compare(FPU_REG const *b, int tagb) 24 { 25 int diff, exp0, expb; 26 u_char st0_tag; 27 FPU_REG *st0_ptr; 28 FPU_REG x, y; 29 u_char st0_sign, signb = getsign(b); 30 31 st0_ptr = &st(0); 32 st0_tag = FPU_gettag0(); 33 st0_sign = getsign(st0_ptr); 34 35 if (tagb == TAG_Special) 36 tagb = FPU_Special(b); 37 if (st0_tag == TAG_Special) 38 st0_tag = FPU_Special(st0_ptr); 39 40 if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal)) 41 || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) { 42 if (st0_tag == TAG_Zero) { 43 if (tagb == TAG_Zero) 44 return COMP_A_eq_B; 45 if (tagb == TAG_Valid) 46 return ((signb == 47 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); 48 if (tagb == TW_Denormal) 49 return ((signb == 50 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) 51 | COMP_Denormal; 52 } else if (tagb == TAG_Zero) { 53 if (st0_tag == TAG_Valid) 54 return ((st0_sign == 55 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); 56 if (st0_tag == TW_Denormal) 57 return ((st0_sign == 58 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) 59 | COMP_Denormal; 60 } 61 62 if (st0_tag == TW_Infinity) { 63 if ((tagb == TAG_Valid) || (tagb == TAG_Zero)) 64 return ((st0_sign == 65 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); 66 else if (tagb == TW_Denormal) 67 return ((st0_sign == 68 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) 69 | COMP_Denormal; 70 else if (tagb == TW_Infinity) { 71 /* The 80486 book says that infinities can be equal! */ 72 return (st0_sign == signb) ? COMP_A_eq_B : 73 ((st0_sign == 74 SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); 75 } 76 /* Fall through to the NaN code */ 77 } else if (tagb == TW_Infinity) { 78 if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero)) 79 return ((signb == 80 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); 81 if (st0_tag == TW_Denormal) 82 return ((signb == 83 SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) 84 | COMP_Denormal; 85 /* Fall through to the NaN code */ 86 } 87 88 /* The only possibility now should be that one of the arguments 89 is a NaN */ 90 if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) { 91 int signalling = 0, unsupported = 0; 92 if (st0_tag == TW_NaN) { 93 signalling = 94 (st0_ptr->sigh & 0xc0000000) == 0x80000000; 95 unsupported = !((exponent(st0_ptr) == EXP_OVER) 96 && (st0_ptr-> 97 sigh & 0x80000000)); 98 } 99 if (tagb == TW_NaN) { 100 signalling |= 101 (b->sigh & 0xc0000000) == 0x80000000; 102 unsupported |= !((exponent(b) == EXP_OVER) 103 && (b->sigh & 0x80000000)); 104 } 105 if (signalling || unsupported) 106 return COMP_No_Comp | COMP_SNaN | COMP_NaN; 107 else 108 /* Neither is a signaling NaN */ 109 return COMP_No_Comp | COMP_NaN; 110 } 111 112 EXCEPTION(EX_Invalid); 113 } 114 115 if (st0_sign != signb) { 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 FPU_to_exp16(st0_ptr, &x); 123 FPU_to_exp16(b, &y); 124 st0_ptr = &x; 125 b = &y; 126 exp0 = exponent16(st0_ptr); 127 expb = exponent16(b); 128 } else { 129 exp0 = exponent(st0_ptr); 130 expb = exponent(b); 131 } 132 133 #ifdef PARANOID 134 if (!(st0_ptr->sigh & 0x80000000)) 135 EXCEPTION(EX_Invalid); 136 if (!(b->sigh & 0x80000000)) 137 EXCEPTION(EX_Invalid); 138 #endif /* PARANOID */ 139 140 diff = exp0 - expb; 141 if (diff == 0) { 142 diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are 143 identical */ 144 if (diff == 0) { 145 diff = st0_ptr->sigl > b->sigl; 146 if (diff == 0) 147 diff = -(st0_ptr->sigl < b->sigl); 148 } 149 } 150 151 if (diff > 0) { 152 return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) 153 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? 154 COMP_Denormal : 0); 155 } 156 if (diff < 0) { 157 return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) 158 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? 159 COMP_Denormal : 0); 160 } 161 162 return COMP_A_eq_B 163 | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? 164 COMP_Denormal : 0); 165 166 } 167 168 /* This function requires that st(0) is not empty */ 169 int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) 170 { 171 int f = 0, c; 172 173 c = compare(loaded_data, loaded_tag); 174 175 if (c & COMP_NaN) { 176 EXCEPTION(EX_Invalid); 177 f = SW_C3 | SW_C2 | SW_C0; 178 } else 179 switch (c & 7) { 180 case COMP_A_lt_B: 181 f = SW_C0; 182 break; 183 case COMP_A_eq_B: 184 f = SW_C3; 185 break; 186 case COMP_A_gt_B: 187 f = 0; 188 break; 189 case COMP_No_Comp: 190 f = SW_C3 | SW_C2 | SW_C0; 191 break; 192 #ifdef PARANOID 193 default: 194 EXCEPTION(EX_INTERNAL | 0x121); 195 f = SW_C3 | SW_C2 | SW_C0; 196 break; 197 #endif /* PARANOID */ 198 } 199 setcc(f); 200 if (c & COMP_Denormal) { 201 return denormal_operand() < 0; 202 } 203 return 0; 204 } 205 206 static int compare_st_st(int nr) 207 { 208 int f = 0, c; 209 FPU_REG *st_ptr; 210 211 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { 212 setcc(SW_C3 | SW_C2 | SW_C0); 213 /* Stack fault */ 214 EXCEPTION(EX_StackUnder); 215 return !(control_word & CW_Invalid); 216 } 217 218 st_ptr = &st(nr); 219 c = compare(st_ptr, FPU_gettagi(nr)); 220 if (c & COMP_NaN) { 221 setcc(SW_C3 | SW_C2 | SW_C0); 222 EXCEPTION(EX_Invalid); 223 return !(control_word & CW_Invalid); 224 } else 225 switch (c & 7) { 226 case COMP_A_lt_B: 227 f = SW_C0; 228 break; 229 case COMP_A_eq_B: 230 f = SW_C3; 231 break; 232 case COMP_A_gt_B: 233 f = 0; 234 break; 235 case COMP_No_Comp: 236 f = SW_C3 | SW_C2 | SW_C0; 237 break; 238 #ifdef PARANOID 239 default: 240 EXCEPTION(EX_INTERNAL | 0x122); 241 f = SW_C3 | SW_C2 | SW_C0; 242 break; 243 #endif /* PARANOID */ 244 } 245 setcc(f); 246 if (c & COMP_Denormal) { 247 return denormal_operand() < 0; 248 } 249 return 0; 250 } 251 252 static int compare_i_st_st(int nr) 253 { 254 int f, c; 255 FPU_REG *st_ptr; 256 257 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { 258 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 259 /* Stack fault */ 260 EXCEPTION(EX_StackUnder); 261 return !(control_word & CW_Invalid); 262 } 263 264 partial_status &= ~SW_C0; 265 st_ptr = &st(nr); 266 c = compare(st_ptr, FPU_gettagi(nr)); 267 if (c & COMP_NaN) { 268 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 269 EXCEPTION(EX_Invalid); 270 return !(control_word & CW_Invalid); 271 } 272 273 switch (c & 7) { 274 case COMP_A_lt_B: 275 f = X86_EFLAGS_CF; 276 break; 277 case COMP_A_eq_B: 278 f = X86_EFLAGS_ZF; 279 break; 280 case COMP_A_gt_B: 281 f = 0; 282 break; 283 case COMP_No_Comp: 284 f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF; 285 break; 286 #ifdef PARANOID 287 default: 288 EXCEPTION(EX_INTERNAL | 0x122); 289 f = 0; 290 break; 291 #endif /* PARANOID */ 292 } 293 FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f; 294 if (c & COMP_Denormal) { 295 return denormal_operand() < 0; 296 } 297 return 0; 298 } 299 300 static int compare_u_st_st(int nr) 301 { 302 int f = 0, c; 303 FPU_REG *st_ptr; 304 305 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { 306 setcc(SW_C3 | SW_C2 | SW_C0); 307 /* Stack fault */ 308 EXCEPTION(EX_StackUnder); 309 return !(control_word & CW_Invalid); 310 } 311 312 st_ptr = &st(nr); 313 c = compare(st_ptr, FPU_gettagi(nr)); 314 if (c & COMP_NaN) { 315 setcc(SW_C3 | SW_C2 | SW_C0); 316 if (c & COMP_SNaN) { /* This is the only difference between 317 un-ordered and ordinary comparisons */ 318 EXCEPTION(EX_Invalid); 319 return !(control_word & CW_Invalid); 320 } 321 return 0; 322 } else 323 switch (c & 7) { 324 case COMP_A_lt_B: 325 f = SW_C0; 326 break; 327 case COMP_A_eq_B: 328 f = SW_C3; 329 break; 330 case COMP_A_gt_B: 331 f = 0; 332 break; 333 case COMP_No_Comp: 334 f = SW_C3 | SW_C2 | SW_C0; 335 break; 336 #ifdef PARANOID 337 default: 338 EXCEPTION(EX_INTERNAL | 0x123); 339 f = SW_C3 | SW_C2 | SW_C0; 340 break; 341 #endif /* PARANOID */ 342 } 343 setcc(f); 344 if (c & COMP_Denormal) { 345 return denormal_operand() < 0; 346 } 347 return 0; 348 } 349 350 static int compare_ui_st_st(int nr) 351 { 352 int f = 0, c; 353 FPU_REG *st_ptr; 354 355 if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { 356 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 357 /* Stack fault */ 358 EXCEPTION(EX_StackUnder); 359 return !(control_word & CW_Invalid); 360 } 361 362 partial_status &= ~SW_C0; 363 st_ptr = &st(nr); 364 c = compare(st_ptr, FPU_gettagi(nr)); 365 if (c & COMP_NaN) { 366 FPU_EFLAGS |= (X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF); 367 if (c & COMP_SNaN) { /* This is the only difference between 368 un-ordered and ordinary comparisons */ 369 EXCEPTION(EX_Invalid); 370 return !(control_word & CW_Invalid); 371 } 372 return 0; 373 } 374 375 switch (c & 7) { 376 case COMP_A_lt_B: 377 f = X86_EFLAGS_CF; 378 break; 379 case COMP_A_eq_B: 380 f = X86_EFLAGS_ZF; 381 break; 382 case COMP_A_gt_B: 383 f = 0; 384 break; 385 case COMP_No_Comp: 386 f = X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF; 387 break; 388 #ifdef PARANOID 389 default: 390 EXCEPTION(EX_INTERNAL | 0x123); 391 f = 0; 392 break; 393 #endif /* PARANOID */ 394 } 395 FPU_EFLAGS = (FPU_EFLAGS & ~(X86_EFLAGS_ZF | X86_EFLAGS_PF | X86_EFLAGS_CF)) | f; 396 if (c & COMP_Denormal) { 397 return denormal_operand() < 0; 398 } 399 return 0; 400 } 401 402 /*---------------------------------------------------------------------------*/ 403 404 void fcom_st(void) 405 { 406 /* fcom st(i) */ 407 compare_st_st(FPU_rm); 408 } 409 410 void fcompst(void) 411 { 412 /* fcomp st(i) */ 413 if (!compare_st_st(FPU_rm)) 414 FPU_pop(); 415 } 416 417 void fcompp(void) 418 { 419 /* fcompp */ 420 if (FPU_rm != 1) { 421 FPU_illegal(); 422 return; 423 } 424 if (!compare_st_st(1)) 425 poppop(); 426 } 427 428 void fucom_(void) 429 { 430 /* fucom st(i) */ 431 compare_u_st_st(FPU_rm); 432 433 } 434 435 void fucomp(void) 436 { 437 /* fucomp st(i) */ 438 if (!compare_u_st_st(FPU_rm)) 439 FPU_pop(); 440 } 441 442 void fucompp(void) 443 { 444 /* fucompp */ 445 if (FPU_rm == 1) { 446 if (!compare_u_st_st(1)) 447 poppop(); 448 } else 449 FPU_illegal(); 450 } 451 452 /* P6+ compare-to-EFLAGS ops */ 453 454 void fcomi_(void) 455 { 456 /* fcomi st(i) */ 457 compare_i_st_st(FPU_rm); 458 } 459 460 void fcomip(void) 461 { 462 /* fcomip st(i) */ 463 if (!compare_i_st_st(FPU_rm)) 464 FPU_pop(); 465 } 466 467 void fucomi_(void) 468 { 469 /* fucomi st(i) */ 470 compare_ui_st_st(FPU_rm); 471 } 472 473 void fucomip(void) 474 { 475 /* fucomip st(i) */ 476 if (!compare_ui_st_st(FPU_rm)) 477 FPU_pop(); 478 } 479