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