diff options
Diffstat (limited to 'sys/gnu/fpemul/reg_compare.c')
| -rw-r--r-- | sys/gnu/fpemul/reg_compare.c | 384 |
1 files changed, 384 insertions, 0 deletions
diff --git a/sys/gnu/fpemul/reg_compare.c b/sys/gnu/fpemul/reg_compare.c new file mode 100644 index 000000000000..692cdc3910f8 --- /dev/null +++ b/sys/gnu/fpemul/reg_compare.c @@ -0,0 +1,384 @@ +/* + * 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. + * + * + * $Id: reg_compare.c,v 1.3 1994/06/10 07:44:47 rich Exp $ + * + */ + +/*---------------------------------------------------------------------------+ + | compare() is the core FPU_REG comparison function | + +---------------------------------------------------------------------------*/ +#include "param.h" +#include "proc.h" +#include "systm.h" +#include "machine/cpu.h" +#include "machine/pcb.h" + +#include "fpu_emu.h" +#include "fpu_system.h" +#include "exception.h" +#include "control_w.h" +#include "status_w.h" + + +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, 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, 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, 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(); +} |
