GNU Linux-libre 4.14.266-gnu1
[releases.git] / arch / x86 / math-emu / reg_divide.c
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   */
28 int 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 }