1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21#include "exception.h"
22#include "reg_constant.h"
23#include "fpu_emu.h"
24#include "control_w.h"
25#include "fpu_system.h"
26
27static
28int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
29 FPU_REG const *b, u_char tagb, u_char signb,
30 FPU_REG * dest, int deststnr, int control_w);
31
32
33
34
35
36int FPU_add(FPU_REG const *b, u_char tagb, int deststnr, int control_w)
37{
38 FPU_REG *a = &st(0);
39 FPU_REG *dest = &st(deststnr);
40 u_char signb = getsign(b);
41 u_char taga = FPU_gettag0();
42 u_char signa = getsign(a);
43 u_char saved_sign = getsign(dest);
44 int diff, tag, expa, expb;
45
46 if (!(taga | tagb)) {
47 expa = exponent(a);
48 expb = exponent(b);
49
50 valid_add:
51
52 if (!(signa ^ signb)) {
53
54 tag =
55 FPU_u_add(a, b, dest, control_w, signa, expa, expb);
56 } else {
57
58 diff = expa - expb;
59 if (!diff) {
60 diff = a->sigh - b->sigh;
61
62 if (!diff) {
63 diff = a->sigl > b->sigl;
64 if (!diff)
65 diff = -(a->sigl < b->sigl);
66 }
67 }
68
69 if (diff > 0) {
70 tag =
71 FPU_u_sub(a, b, dest, control_w, signa,
72 expa, expb);
73 } else if (diff < 0) {
74 tag =
75 FPU_u_sub(b, a, dest, control_w, signb,
76 expb, expa);
77 } else {
78 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
79
80 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
81 ? SIGN_POS : SIGN_NEG);
82 return TAG_Zero;
83 }
84 }
85
86 if (tag < 0) {
87 setsign(dest, saved_sign);
88 return tag;
89 }
90 FPU_settagi(deststnr, tag);
91 return tag;
92 }
93
94 if (taga == TAG_Special)
95 taga = FPU_Special(a);
96 if (tagb == TAG_Special)
97 tagb = FPU_Special(b);
98
99 if (((taga == TAG_Valid) && (tagb == TW_Denormal))
100 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
101 || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
102 FPU_REG x, y;
103
104 if (denormal_operand() < 0)
105 return FPU_Exception;
106
107 FPU_to_exp16(a, &x);
108 FPU_to_exp16(b, &y);
109 a = &x;
110 b = &y;
111 expa = exponent16(a);
112 expb = exponent16(b);
113 goto valid_add;
114 }
115
116 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
117 if (deststnr == 0)
118 return real_2op_NaN(b, tagb, deststnr, a);
119 else
120 return real_2op_NaN(a, taga, deststnr, a);
121 }
122
123 return add_sub_specials(a, taga, signa, b, tagb, signb,
124 dest, deststnr, control_w);
125}
126
127
128int FPU_sub(int flags, int rm, int control_w)
129{
130 FPU_REG const *a, *b;
131 FPU_REG *dest;
132 u_char taga, tagb, signa, signb, saved_sign, sign;
133 int diff, tag = 0, expa, expb, deststnr;
134
135 a = &st(0);
136 taga = FPU_gettag0();
137
138 deststnr = 0;
139 if (flags & LOADED) {
140 b = (FPU_REG *) rm;
141 tagb = flags & 0x0f;
142 } else {
143 b = &st(rm);
144 tagb = FPU_gettagi(rm);
145
146 if (flags & DEST_RM)
147 deststnr = rm;
148 }
149
150 signa = getsign(a);
151 signb = getsign(b);
152
153 if (flags & REV) {
154 signa ^= SIGN_NEG;
155 signb ^= SIGN_NEG;
156 }
157
158 dest = &st(deststnr);
159 saved_sign = getsign(dest);
160
161 if (!(taga | tagb)) {
162 expa = exponent(a);
163 expb = exponent(b);
164
165 valid_subtract:
166
167
168 diff = expa - expb;
169
170 if (!diff) {
171 diff = a->sigh - b->sigh;
172 if (!diff) {
173 diff = a->sigl > b->sigl;
174 if (!diff)
175 diff = -(a->sigl < b->sigl);
176 }
177 }
178
179 switch ((((int)signa) * 2 + signb) / SIGN_NEG) {
180 case 0:
181 case 3:
182 if (diff > 0) {
183
184 tag =
185 FPU_u_sub(a, b, dest, control_w, signa,
186 expa, expb);
187 } else if (diff == 0) {
188 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
189
190
191 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
192 ? SIGN_POS : SIGN_NEG);
193 return TAG_Zero;
194 } else {
195 sign = signa ^ SIGN_NEG;
196 tag =
197 FPU_u_sub(b, a, dest, control_w, sign, expb,
198 expa);
199 }
200 break;
201 case 1:
202 tag =
203 FPU_u_add(a, b, dest, control_w, SIGN_POS, expa,
204 expb);
205 break;
206 case 2:
207 tag =
208 FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa,
209 expb);
210 break;
211#ifdef PARANOID
212 default:
213 EXCEPTION(EX_INTERNAL | 0x111);
214 return -1;
215#endif
216 }
217 if (tag < 0) {
218 setsign(dest, saved_sign);
219 return tag;
220 }
221 FPU_settagi(deststnr, tag);
222 return tag;
223 }
224
225 if (taga == TAG_Special)
226 taga = FPU_Special(a);
227 if (tagb == TAG_Special)
228 tagb = FPU_Special(b);
229
230 if (((taga == TAG_Valid) && (tagb == TW_Denormal))
231 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
232 || ((taga == TW_Denormal) && (tagb == TW_Denormal))) {
233 FPU_REG x, y;
234
235 if (denormal_operand() < 0)
236 return FPU_Exception;
237
238 FPU_to_exp16(a, &x);
239 FPU_to_exp16(b, &y);
240 a = &x;
241 b = &y;
242 expa = exponent16(a);
243 expb = exponent16(b);
244
245 goto valid_subtract;
246 }
247
248 if ((taga == TW_NaN) || (tagb == TW_NaN)) {
249 FPU_REG const *d1, *d2;
250 if (flags & REV) {
251 d1 = b;
252 d2 = a;
253 } else {
254 d1 = a;
255 d2 = b;
256 }
257 if (flags & LOADED)
258 return real_2op_NaN(b, tagb, deststnr, d1);
259 if (flags & DEST_RM)
260 return real_2op_NaN(a, taga, deststnr, d2);
261 else
262 return real_2op_NaN(b, tagb, deststnr, d2);
263 }
264
265 return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
266 dest, deststnr, control_w);
267}
268
269static
270int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
271 FPU_REG const *b, u_char tagb, u_char signb,
272 FPU_REG * dest, int deststnr, int control_w)
273{
274 if (((taga == TW_Denormal) || (tagb == TW_Denormal))
275 && (denormal_operand() < 0))
276 return FPU_Exception;
277
278 if (taga == TAG_Zero) {
279 if (tagb == TAG_Zero) {
280
281 u_char different_signs = signa ^ signb;
282
283 FPU_copy_to_regi(a, TAG_Zero, deststnr);
284 if (different_signs) {
285
286
287 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
288 ? SIGN_POS : SIGN_NEG);
289 } else
290 setsign(dest, signa);
291 return TAG_Zero;
292 } else {
293 reg_copy(b, dest);
294 if ((tagb == TW_Denormal) && (b->sigh & 0x80000000)) {
295
296 addexponent(dest, 1);
297 tagb = TAG_Valid;
298 } else if (tagb > TAG_Empty)
299 tagb = TAG_Special;
300 setsign(dest, signb);
301 FPU_settagi(deststnr, tagb);
302 return tagb;
303 }
304 } else if (tagb == TAG_Zero) {
305 reg_copy(a, dest);
306 if ((taga == TW_Denormal) && (a->sigh & 0x80000000)) {
307
308 addexponent(dest, 1);
309 taga = TAG_Valid;
310 } else if (taga > TAG_Empty)
311 taga = TAG_Special;
312 setsign(dest, signa);
313 FPU_settagi(deststnr, taga);
314 return taga;
315 } else if (taga == TW_Infinity) {
316 if ((tagb != TW_Infinity) || (signa == signb)) {
317 FPU_copy_to_regi(a, TAG_Special, deststnr);
318 setsign(dest, signa);
319 return taga;
320 }
321
322 return arith_invalid(deststnr);
323 } else if (tagb == TW_Infinity) {
324 FPU_copy_to_regi(b, TAG_Special, deststnr);
325 setsign(dest, signb);
326 return tagb;
327 }
328#ifdef PARANOID
329 EXCEPTION(EX_INTERNAL | 0x101);
330#endif
331
332 return FPU_Exception;
333}
334