/* $OpenBSD: reg_compare.c,v 1.1 1996/08/27 10:32:57 downsj Exp $ */ /* * reg_compare.c * * Compare two floating point registers * * * Copyright (C) 1992,1993,1994 * W. Metzenthen, 22 Parker St, Ormond, Vic 3163, * Australia. E-mail billm@vaxc.cc.monash.edu.au * All rights reserved. * * This copyright notice covers the redistribution and use of the * FPU emulator developed by W. Metzenthen. It covers only its use * in the 386BSD, FreeBSD and NetBSD operating systems. Any other * use is not permitted under this copyright. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must include information specifying * that source code for the emulator is freely available and include * either: * a) an offer to provide the source code for a nominal distribution * fee, or * b) list at least two alternative methods whereby the source * can be obtained, e.g. a publically accessible bulletin board * and an anonymous ftp site from which the software can be * downloaded. * 3. All advertising materials specifically mentioning features or use of * this emulator must acknowledge that it was developed by W. Metzenthen. * 4. The name of W. Metzenthen may not be used to endorse or promote * products derived from this software without specific prior written * permission. * * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL * W. METZENTHEN BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * * The purpose of this copyright, based upon the Berkeley copyright, is to * ensure that the covered software remains freely available to everyone. * * The software (with necessary differences) is also available, but under * the terms of the GNU copyleft, for the Linux operating system and for * the djgpp ms-dos extender. * * W. Metzenthen June 1994. * * * $FreeBSD: reg_compare.c,v 1.5 1995/05/30 07:57:54 rgrimes Exp $ * */ /*---------------------------------------------------------------------------+ | compare() is the core FPU_REG comparison function | +---------------------------------------------------------------------------*/ #include #include #include #include #include #include #include #include #include #include int compare(FPU_REG * b) { int diff; if (FPU_st0_ptr->tag | b->tag) { if (FPU_st0_ptr->tag == TW_Zero) { if (b->tag == TW_Zero) return COMP_A_eq_B; if (b->tag == TW_Valid) { #ifdef DENORM_OPERAND if ((b->exp <= EXP_UNDER) && (denormal_operand())) return COMP_Denormal; #endif /* DENORM_OPERAND */ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; } } else if (b->tag == TW_Zero) { if (FPU_st0_ptr->tag == TW_Valid) { #ifdef DENORM_OPERAND if ((FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return COMP_Denormal; #endif /* DENORM_OPERAND */ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; } } if (FPU_st0_ptr->tag == TW_Infinity) { if ((b->tag == TW_Valid) || (b->tag == TW_Zero)) { #ifdef DENORM_OPERAND if ((b->tag == TW_Valid) && (b->exp <= EXP_UNDER) && (denormal_operand())) return COMP_Denormal; #endif /* DENORM_OPERAND */ return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; } else if (b->tag == TW_Infinity) { /* The 80486 book says that infinities * can be equal! */ return (FPU_st0_ptr->sign == b->sign) ? COMP_A_eq_B : ((FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B); } /* Fall through to the NaN code */ } else if (b->tag == TW_Infinity) { if ((FPU_st0_ptr->tag == TW_Valid) || (FPU_st0_ptr->tag == TW_Zero)) { #ifdef DENORM_OPERAND if ((FPU_st0_ptr->tag == TW_Valid) && (FPU_st0_ptr->exp <= EXP_UNDER) && (denormal_operand())) return COMP_Denormal; #endif /* DENORM_OPERAND */ return (b->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; } /* Fall through to the NaN code */ } /* The only possibility now should be that one of the * arguments is a NaN */ if ((FPU_st0_ptr->tag == TW_NaN) || (b->tag == TW_NaN)) { if (((FPU_st0_ptr->tag == TW_NaN) && !(FPU_st0_ptr->sigh & 0x40000000)) || ((b->tag == TW_NaN) && !(b->sigh & 0x40000000))) /* At least one arg is a signaling NaN */ return COMP_No_Comp | COMP_SNaN | COMP_NaN; else /* Neither is a signaling NaN */ return COMP_No_Comp | COMP_NaN; } EXCEPTION(EX_Invalid); } #ifdef PARANOID if (!(FPU_st0_ptr->sigh & 0x80000000)) EXCEPTION(EX_Invalid); if (!(b->sigh & 0x80000000)) EXCEPTION(EX_Invalid); #endif /* PARANOID */ #ifdef DENORM_OPERAND if (((FPU_st0_ptr->exp <= EXP_UNDER) || (b->exp <= EXP_UNDER)) && (denormal_operand())) return COMP_Denormal; #endif /* DENORM_OPERAND */ if (FPU_st0_ptr->sign != b->sign) return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; diff = FPU_st0_ptr->exp - b->exp; if (diff == 0) { diff = FPU_st0_ptr->sigh - b->sigh; /* Works only if ms bits * are identical */ if (diff == 0) { diff = FPU_st0_ptr->sigl > b->sigl; if (diff == 0) diff = -(FPU_st0_ptr->sigl < b->sigl); } } if (diff > 0) return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_gt_B : COMP_A_lt_B; if (diff < 0) return (FPU_st0_ptr->sign == SIGN_POS) ? COMP_A_lt_B : COMP_A_gt_B; return COMP_A_eq_B; } /* This function requires that st(0) is not empty */ int compare_st_data(void) { int f = 0, c; c = compare(&FPU_loaded_data); if (c & (COMP_NaN | COMP_Denormal)) { if (c & COMP_NaN) { EXCEPTION(EX_Invalid); f = SW_C3 | SW_C2 | SW_C0; } else { /* One of the operands is a de-normal */ return 0; } } else switch (c) { 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; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x121); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* PARANOID */ } setcc(f); return 1; } static int compare_st_st(int nr) { int f = 0, c; if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return control_word & CW_Invalid; } c = compare(&st(nr)); if (c & (COMP_NaN | COMP_Denormal)) { if (c & COMP_NaN) { setcc(SW_C3 | SW_C2 | SW_C0); EXCEPTION(EX_Invalid); return control_word & CW_Invalid; } else { /* One of the operands is a de-normal */ return control_word & CW_Denormal; } } else switch (c) { 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; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x122); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* PARANOID */ } setcc(f); return 1; } static int compare_u_st_st(int nr) { int f = 0, c; if (!NOT_EMPTY_0 || !NOT_EMPTY(nr)) { setcc(SW_C3 | SW_C2 | SW_C0); /* Stack fault */ EXCEPTION(EX_StackUnder); return control_word & CW_Invalid; } c = compare(&st(nr)); if (c & (COMP_NaN | COMP_Denormal)) { 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 1; } else { /* One of the operands is a de-normal */ return control_word & CW_Denormal; } } else switch (c) { 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; #ifdef PARANOID default: EXCEPTION(EX_INTERNAL | 0x123); f = SW_C3 | SW_C2 | SW_C0; break; #endif /* PARANOID */ } setcc(f); return 1; } /*---------------------------------------------------------------------------*/ void fcom_st() { /* fcom st(i) */ compare_st_st(FPU_rm); } void fcompst() { /* fcomp st(i) */ if (compare_st_st(FPU_rm)) pop(); } void fcompp() { /* fcompp */ if (FPU_rm != 1) return Un_impl(); if (compare_st_st(1)) { pop(); FPU_st0_ptr = &st(0); pop(); } } void fucom_() { /* fucom st(i) */ compare_u_st_st(FPU_rm); } void fucomp() { /* fucomp st(i) */ if (compare_u_st_st(FPU_rm)) pop(); } void fucompp() { /* fucompp */ if (FPU_rm == 1) { if (compare_u_st_st(1)) { pop(); FPU_st0_ptr = &st(0); pop(); } } else Un_impl(); }