Blame view
arch/x86/math-emu/reg_compare.c
8.09 KB
1da177e4c Linux-2.6.12-rc2 |
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
/*---------------------------------------------------------------------------+ | reg_compare.c | | | | Compare two floating point registers | | | | Copyright (C) 1992,1993,1994,1997 | | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia | | E-mail billm@suburbia.net | | | | | +---------------------------------------------------------------------------*/ /*---------------------------------------------------------------------------+ | compare() is the core FPU_REG comparison function | +---------------------------------------------------------------------------*/ #include "fpu_system.h" #include "exception.h" #include "fpu_emu.h" #include "control_w.h" #include "status_w.h" |
1da177e4c Linux-2.6.12-rc2 |
22 23 |
static int compare(FPU_REG const *b, int tagb) { |
3d0d14f98 x86: lindent arch... |
24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 |
int diff, exp0, expb; u_char st0_tag; FPU_REG *st0_ptr; FPU_REG x, y; u_char st0_sign, signb = getsign(b); st0_ptr = &st(0); st0_tag = FPU_gettag0(); st0_sign = getsign(st0_ptr); if (tagb == TAG_Special) tagb = FPU_Special(b); if (st0_tag == TAG_Special) st0_tag = FPU_Special(st0_ptr); if (((st0_tag != TAG_Valid) && (st0_tag != TW_Denormal)) || ((tagb != TAG_Valid) && (tagb != TW_Denormal))) { if (st0_tag == TAG_Zero) { if (tagb == TAG_Zero) return COMP_A_eq_B; if (tagb == TAG_Valid) return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); if (tagb == TW_Denormal) return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | COMP_Denormal; } else if (tagb == TAG_Zero) { if (st0_tag == TAG_Valid) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); if (st0_tag == TW_Denormal) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | COMP_Denormal; } if (st0_tag == TW_Infinity) { if ((tagb == TAG_Valid) || (tagb == TAG_Zero)) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); else if (tagb == TW_Denormal) return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | COMP_Denormal; else if (tagb == TW_Infinity) { /* The 80486 book says that infinities can be equal! */ return (st0_sign == signb) ? COMP_A_eq_B : ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); } /* Fall through to the NaN code */ } else if (tagb == TW_Infinity) { if ((st0_tag == TAG_Valid) || (st0_tag == TAG_Zero)) return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B); if (st0_tag == TW_Denormal) return ((signb == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | COMP_Denormal; /* Fall through to the NaN code */ } /* The only possibility now should be that one of the arguments is a NaN */ if ((st0_tag == TW_NaN) || (tagb == TW_NaN)) { int signalling = 0, unsupported = 0; if (st0_tag == TW_NaN) { signalling = (st0_ptr->sigh & 0xc0000000) == 0x80000000; unsupported = !((exponent(st0_ptr) == EXP_OVER) && (st0_ptr-> sigh & 0x80000000)); } if (tagb == TW_NaN) { signalling |= (b->sigh & 0xc0000000) == 0x80000000; unsupported |= !((exponent(b) == EXP_OVER) && (b->sigh & 0x80000000)); } if (signalling || unsupported) return COMP_No_Comp | COMP_SNaN | COMP_NaN; else /* Neither is a signaling NaN */ return COMP_No_Comp | COMP_NaN; } EXCEPTION(EX_Invalid); |
1da177e4c Linux-2.6.12-rc2 |
112 |
} |
3d0d14f98 x86: lindent arch... |
113 114 115 116 |
if (st0_sign != signb) { return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? COMP_Denormal : 0); |
1da177e4c Linux-2.6.12-rc2 |
117 |
} |
3d0d14f98 x86: lindent arch... |
118 119 120 121 122 123 124 125 126 127 |
if ((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) { FPU_to_exp16(st0_ptr, &x); FPU_to_exp16(b, &y); st0_ptr = &x; b = &y; exp0 = exponent16(st0_ptr); expb = exponent16(b); } else { exp0 = exponent(st0_ptr); expb = exponent(b); |
1da177e4c Linux-2.6.12-rc2 |
128 |
} |
1da177e4c Linux-2.6.12-rc2 |
129 130 |
#ifdef PARANOID |
3d0d14f98 x86: lindent arch... |
131 132 133 134 |
if (!(st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); |
1da177e4c Linux-2.6.12-rc2 |
135 |
#endif /* PARANOID */ |
3d0d14f98 x86: lindent arch... |
136 137 138 139 140 141 142 143 144 |
diff = exp0 - expb; if (diff == 0) { diff = st0_ptr->sigh - b->sigh; /* Works only if ms bits are identical */ if (diff == 0) { diff = st0_ptr->sigl > b->sigl; if (diff == 0) diff = -(st0_ptr->sigl < b->sigl); } |
1da177e4c Linux-2.6.12-rc2 |
145 |
} |
1da177e4c Linux-2.6.12-rc2 |
146 |
|
3d0d14f98 x86: lindent arch... |
147 148 149 150 151 152 153 154 155 156 |
if (diff > 0) { return ((st0_sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B) | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? COMP_Denormal : 0); } if (diff < 0) { return ((st0_sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B) | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? COMP_Denormal : 0); } |
1da177e4c Linux-2.6.12-rc2 |
157 |
|
3d0d14f98 x86: lindent arch... |
158 159 160 161 162 |
return COMP_A_eq_B | (((st0_tag == TW_Denormal) || (tagb == TW_Denormal)) ? COMP_Denormal : 0); } |
1da177e4c Linux-2.6.12-rc2 |
163 164 165 166 |
/* This function requires that st(0) is not empty */ int FPU_compare_st_data(FPU_REG const *loaded_data, u_char loaded_tag) { |
3d0d14f98 x86: lindent arch... |
167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 |
int f = 0, c; c = compare(loaded_data, loaded_tag); if (c & COMP_NaN) { EXCEPTION(EX_Invalid); f = SW_C3 | SW_C2 | SW_C0; } else switch (c & 7) { case COMP_A_lt_B: f = SW_C0; break; case COMP_A_eq_B: f = SW_C3; break; case COMP_A_gt_B: f = 0; break; case COMP_No_Comp: f = SW_C3 | SW_C2 | SW_C0; break; |
1da177e4c Linux-2.6.12-rc2 |
188 |
#ifdef PARANOID |
3d0d14f98 x86: lindent arch... |
189 190 191 192 |
default: EXCEPTION(EX_INTERNAL | 0x121); f = SW_C3 | SW_C2 | SW_C0; break; |
1da177e4c Linux-2.6.12-rc2 |
193 |
#endif /* PARANOID */ |
3d0d14f98 x86: lindent arch... |
194 195 196 197 198 199 |
} setcc(f); if (c & COMP_Denormal) { return denormal_operand() < 0; } return 0; |
1da177e4c Linux-2.6.12-rc2 |
200 |
} |
1da177e4c Linux-2.6.12-rc2 |
201 202 |
static int compare_st_st(int nr) { |
3d0d14f98 x86: lindent arch... |
203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 |
int f = 0, c; FPU_REG *st_ptr; if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return !(control_word & CW_Invalid); } st_ptr = &st(nr); c = compare(st_ptr, FPU_gettagi(nr)); if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); EXCEPTION(EX_Invalid); return !(control_word & CW_Invalid); } else switch (c & 7) { case COMP_A_lt_B: f = SW_C0; break; case COMP_A_eq_B: f = SW_C3; break; case COMP_A_gt_B: f = 0; break; case COMP_No_Comp: f = SW_C3 | SW_C2 | SW_C0; break; |
1da177e4c Linux-2.6.12-rc2 |
233 |
#ifdef PARANOID |
3d0d14f98 x86: lindent arch... |
234 235 236 237 |
default: EXCEPTION(EX_INTERNAL | 0x122); f = SW_C3 | SW_C2 | SW_C0; break; |
1da177e4c Linux-2.6.12-rc2 |
238 |
#endif /* PARANOID */ |
3d0d14f98 x86: lindent arch... |
239 240 241 242 243 244 |
} setcc(f); if (c & COMP_Denormal) { return denormal_operand() < 0; } return 0; |
1da177e4c Linux-2.6.12-rc2 |
245 |
} |
1da177e4c Linux-2.6.12-rc2 |
246 247 |
static int compare_u_st_st(int nr) { |
3d0d14f98 x86: lindent arch... |
248 249 250 251 252 253 254 255 |
int f = 0, c; FPU_REG *st_ptr; if (!NOT_EMPTY(0) || !NOT_EMPTY(nr)) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return !(control_word & CW_Invalid); |
1da177e4c Linux-2.6.12-rc2 |
256 |
} |
3d0d14f98 x86: lindent arch... |
257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 |
st_ptr = &st(nr); c = compare(st_ptr, FPU_gettagi(nr)); if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); if (c & COMP_SNaN) { /* This is the only difference between un-ordered and ordinary comparisons */ EXCEPTION(EX_Invalid); return !(control_word & CW_Invalid); } return 0; } else switch (c & 7) { case COMP_A_lt_B: f = SW_C0; break; case COMP_A_eq_B: f = SW_C3; break; case COMP_A_gt_B: f = 0; break; case COMP_No_Comp: f = SW_C3 | SW_C2 | SW_C0; break; |
1da177e4c Linux-2.6.12-rc2 |
282 |
#ifdef PARANOID |
3d0d14f98 x86: lindent arch... |
283 284 285 286 287 288 289 290 291 292 293 |
default: EXCEPTION(EX_INTERNAL | 0x123); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* PARANOID */ } setcc(f); if (c & COMP_Denormal) { return denormal_operand() < 0; } return 0; |
1da177e4c Linux-2.6.12-rc2 |
294 295 296 297 298 299 |
} /*---------------------------------------------------------------------------*/ void fcom_st(void) { |
3d0d14f98 x86: lindent arch... |
300 301 |
/* fcom st(i) */ compare_st_st(FPU_rm); |
1da177e4c Linux-2.6.12-rc2 |
302 |
} |
1da177e4c Linux-2.6.12-rc2 |
303 304 |
void fcompst(void) { |
3d0d14f98 x86: lindent arch... |
305 306 307 |
/* fcomp st(i) */ if (!compare_st_st(FPU_rm)) FPU_pop(); |
1da177e4c Linux-2.6.12-rc2 |
308 |
} |
1da177e4c Linux-2.6.12-rc2 |
309 310 |
void fcompp(void) { |
3d0d14f98 x86: lindent arch... |
311 312 313 314 315 316 317 |
/* fcompp */ if (FPU_rm != 1) { FPU_illegal(); return; } if (!compare_st_st(1)) poppop(); |
1da177e4c Linux-2.6.12-rc2 |
318 |
} |
1da177e4c Linux-2.6.12-rc2 |
319 320 |
void fucom_(void) { |
3d0d14f98 x86: lindent arch... |
321 322 |
/* fucom st(i) */ compare_u_st_st(FPU_rm); |
1da177e4c Linux-2.6.12-rc2 |
323 324 |
} |
1da177e4c Linux-2.6.12-rc2 |
325 326 |
void fucomp(void) { |
3d0d14f98 x86: lindent arch... |
327 328 329 |
/* fucomp st(i) */ if (!compare_u_st_st(FPU_rm)) FPU_pop(); |
1da177e4c Linux-2.6.12-rc2 |
330 |
} |
1da177e4c Linux-2.6.12-rc2 |
331 332 |
void fucompp(void) { |
3d0d14f98 x86: lindent arch... |
333 334 335 336 337 338 |
/* fucompp */ if (FPU_rm == 1) { if (!compare_u_st_st(1)) poppop(); } else FPU_illegal(); |
1da177e4c Linux-2.6.12-rc2 |
339 |
} |