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(stnr: 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(stnr: 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(x: a, y: &x);
78 reg_copy(x: b, y: &y);
79 setpositive(&x);
80 setpositive(&y);
81 tag = FPU_u_div(arg1: &x, arg2: &y, answ: dest, control_w, sign);
82
83 if (tag < 0)
84 return tag;
85
86 FPU_settagi(stnr: deststnr, tag);
87 return tag;
88 }
89
90 if (taga == TAG_Special)
91 taga = FPU_Special(ptr: a);
92 if (tagb == TAG_Special)
93 tagb = FPU_Special(ptr: 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: &x);
102 FPU_to_exp16(a: b, x: &y);
103 tag = FPU_u_div(arg1: &x, arg2: &y, answ: dest, control_w, sign);
104 if (tag < 0)
105 return tag;
106
107 FPU_settagi(stnr: 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(r: &CONST_Z, TAG_Zero, stnr: 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(b: (FPU_REG *) rm, tagb: flags & 0x0f, deststnr: 0,
134 defaultNaN: st0_ptr);
135
136 if (flags & DEST_RM) {
137 int tag;
138 tag = FPU_gettag0();
139 if (tag == TAG_Special)
140 tag = FPU_Special(ptr: st0_ptr);
141 return real_2op_NaN(b: st0_ptr, tagb: tag, deststnr: rm,
142 defaultNaN: (flags & REV) ? st0_ptr : &st(rm));
143 } else {
144 int tag;
145 tag = FPU_gettagi(stnr: rm);
146 if (tag == TAG_Special)
147 tag = FPU_Special(ptr: &st(rm));
148 return real_2op_NaN(b: &st(rm), tagb: tag, deststnr: 0,
149 defaultNaN: (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(r: a, TAG_Special, stnr: 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(r: &CONST_Z, TAG_Zero, stnr: 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

source code of linux/arch/x86/math-emu/reg_divide.c