linux/arch/x86/math-emu/reg_divide.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0
   2/*---------------------------------------------------------------------------+
   3 |  reg_divide.c                                                             |
   4 |                                                                           |
   5 | Divide one FPU_REG by another and put the result in a destination FPU_REG.|
   6 |                                                                           |
   7 | Copyright (C) 1996                                                        |
   8 |                  W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
   9 |                  E-mail   billm@jacobi.maths.monash.edu.au                |
  10 |                                                                           |
  11 |    Return value is the tag of the answer, or-ed with FPU_Exception if     |
  12 |    one was raised, or -1 on internal error.                               |
  13 |                                                                           |
  14 +---------------------------------------------------------------------------*/
  15
  16/*---------------------------------------------------------------------------+
  17 | The destination may be any FPU_REG, including one of the source FPU_REGs. |
  18 +---------------------------------------------------------------------------*/
  19
  20#include "exception.h"
  21#include "reg_constant.h"
  22#include "fpu_emu.h"
  23#include "fpu_system.h"
  24
  25/*
  26  Divide one register by another and put the result into a third register.
  27  */
  28int FPU_div(int flags, int rm, int control_w)
  29{
  30        FPU_REG x, y;
  31        FPU_REG const *a, *b, *st0_ptr, *st_ptr;
  32        FPU_REG *dest;
  33        u_char taga, tagb, signa, signb, sign, saved_sign;
  34        int tag, deststnr;
  35
  36        if (flags & DEST_RM)
  37                deststnr = rm;
  38        else
  39                deststnr = 0;
  40
  41        if (flags & REV) {
  42                b = &st(0);
  43                st0_ptr = b;
  44                tagb = FPU_gettag0();
  45                if (flags & LOADED) {
  46                        a = (FPU_REG *) rm;
  47                        taga = flags & 0x0f;
  48                } else {
  49                        a = &st(rm);
  50                        st_ptr = a;
  51                        taga = FPU_gettagi(rm);
  52                }
  53        } else {
  54                a = &st(0);
  55                st0_ptr = a;
  56                taga = FPU_gettag0();
  57                if (flags & LOADED) {
  58                        b = (FPU_REG *) rm;
  59                        tagb = flags & 0x0f;
  60                } else {
  61                        b = &st(rm);
  62                        st_ptr = b;
  63                        tagb = FPU_gettagi(rm);
  64                }
  65        }
  66
  67        signa = getsign(a);
  68        signb = getsign(b);
  69
  70        sign = signa ^ signb;
  71
  72        dest = &st(deststnr);
  73        saved_sign = getsign(dest);
  74
  75        if (!(taga | tagb)) {
  76                /* Both regs Valid, this should be the most common case. */
  77                reg_copy(a, &x);
  78                reg_copy(b, &y);
  79                setpositive(&x);
  80                setpositive(&y);
  81                tag = FPU_u_div(&x, &y, dest, control_w, sign);
  82
  83                if (tag < 0)
  84                        return tag;
  85
  86                FPU_settagi(deststnr, tag);
  87                return tag;
  88        }
  89
  90        if (taga == TAG_Special)
  91                taga = FPU_Special(a);
  92        if (tagb == TAG_Special)
  93                tagb = FPU_Special(b);
  94
  95        if (((taga == TAG_Valid) && (tagb == TW_Denormal))
  96            || ((taga == TW_Denormal) && (tagb == TAG_Valid))
  97            || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
  98                if (denormal_operand() < 0)
  99                        return FPU_Exception;
 100
 101                FPU_to_exp16(a, &x);
 102                FPU_to_exp16(b, &y);
 103                tag = FPU_u_div(&x, &y, dest, control_w, sign);
 104                if (tag < 0)
 105                        return tag;
 106
 107                FPU_settagi(deststnr, tag);
 108                return tag;
 109        } else if ((taga <= TW_Denormal) && (tagb <= TW_Denormal)) {
 110                if (tagb != TAG_Zero) {
 111                        /* Want to find Zero/Valid */
 112                        if (tagb == TW_Denormal) {
 113                                if (denormal_operand() < 0)
 114                                        return FPU_Exception;
 115                        }
 116
 117                        /* The result is zero. */
 118                        FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
 119                        setsign(dest, sign);
 120                        return TAG_Zero;
 121                }
 122                /* We have an exception condition, either 0/0 or Valid/Zero. */
 123                if (taga == TAG_Zero) {
 124                        /* 0/0 */
 125                        return arith_invalid(deststnr);
 126                }
 127                /* Valid/Zero */
 128                return FPU_divide_by_zero(deststnr, sign);
 129        }
 130        /* Must have infinities, NaNs, etc */
 131        else if ((taga == TW_NaN) || (tagb == TW_NaN)) {
 132                if (flags & LOADED)
 133                        return real_2op_NaN((FPU_REG *) rm, flags & 0x0f, 0,
 134                                            st0_ptr);
 135
 136                if (flags & DEST_RM) {
 137                        int tag;
 138                        tag = FPU_gettag0();
 139                        if (tag == TAG_Special)
 140                                tag = FPU_Special(st0_ptr);
 141                        return real_2op_NaN(st0_ptr, tag, rm,
 142                                            (flags & REV) ? st0_ptr : &st(rm));
 143                } else {
 144                        int tag;
 145                        tag = FPU_gettagi(rm);
 146                        if (tag == TAG_Special)
 147                                tag = FPU_Special(&st(rm));
 148                        return real_2op_NaN(&st(rm), tag, 0,
 149                                            (flags & REV) ? st0_ptr : &st(rm));
 150                }
 151        } else if (taga == TW_Infinity) {
 152                if (tagb == TW_Infinity) {
 153                        /* infinity/infinity */
 154                        return arith_invalid(deststnr);
 155                } else {
 156                        /* tagb must be Valid or Zero */
 157                        if ((tagb == TW_Denormal) && (denormal_operand() < 0))
 158                                return FPU_Exception;
 159
 160                        /* Infinity divided by Zero or Valid does
 161                           not raise and exception, but returns Infinity */
 162                        FPU_copy_to_regi(a, TAG_Special, deststnr);
 163                        setsign(dest, sign);
 164                        return taga;
 165                }
 166        } else if (tagb == TW_Infinity) {
 167                if ((taga == TW_Denormal) && (denormal_operand() < 0))
 168                        return FPU_Exception;
 169
 170                /* The result is zero. */
 171                FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
 172                setsign(dest, sign);
 173                return TAG_Zero;
 174        }
 175#ifdef PARANOID
 176        else {
 177                EXCEPTION(EX_INTERNAL | 0x102);
 178                return FPU_Exception;
 179        }
 180#endif /* PARANOID */
 181
 182        return 0;
 183}
 184