root/arch/x86/math-emu/reg_compare.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. compare
  2. FPU_compare_st_data
  3. compare_st_st
  4. compare_i_st_st
  5. compare_u_st_st
  6. compare_ui_st_st
  7. fcom_st
  8. fcompst
  9. fcompp
  10. fucom_
  11. fucomp
  12. fucompp
  13. fcomi_
  14. fcomip
  15. fucomi_
  16. fucomip

   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 }

/* [<][>][^][v][top][bottom][index][help] */