1 /*---------------------------------------------------------------------------+
2 | reg_add_sub.c |
3 | |
4 | Functions to add or subtract two registers and put the result in a third. |
5 | |
6 | Copyright (C) 1992,1993,1997 |
7 | W. Metzenthen, 22 Parker St, Ormond, Vic 3163, Australia |
8 | E-mail billm@suburbia.net |
9 | |
10 | |
11 +---------------------------------------------------------------------------*/
12
13 /*---------------------------------------------------------------------------+
14 | For each function, the destination may be any FPU_REG, including one of |
15 | the source FPU_REGs. |
16 | Each function returns 0 if the answer is o.k., otherwise a non-zero |
17 | value is returned, indicating either an exception condition or an |
18 | internal error. |
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
27 static
28 int 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 Operates on st(0) and st(n), or on st(0) and temporary data.
34 The destination must be one of the source st(x).
35 */
FPU_add(FPU_REG const * b,u_char tagb,int deststnr,int control_w)36 int 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 {
48 expa = exponent(a);
49 expb = exponent(b);
50
51 valid_add:
52 /* Both registers are valid */
53 if (!(signa ^ signb))
54 {
55 /* signs are the same */
56 tag = FPU_u_add(a, b, dest, control_w, signa, expa, expb);
57 }
58 else
59 {
60 /* The signs are different, so do a subtraction */
61 diff = expa - expb;
62 if (!diff)
63 {
64 diff = a->sigh - b->sigh; /* This works only if the ms bits
65 are identical. */
66 if (!diff)
67 {
68 diff = a->sigl > b->sigl;
69 if (!diff)
70 diff = -(a->sigl < b->sigl);
71 }
72 }
73
74 if (diff > 0)
75 {
76 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
77 }
78 else if ( diff < 0 )
79 {
80 tag = FPU_u_sub(b, a, dest, control_w, signb, expb, expa);
81 }
82 else
83 {
84 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
85 /* sign depends upon rounding mode */
86 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
87 ? SIGN_POS : SIGN_NEG);
88 return TAG_Zero;
89 }
90 }
91
92 if ( tag < 0 )
93 {
94 setsign(dest, saved_sign);
95 return tag;
96 }
97 FPU_settagi(deststnr, tag);
98 return tag;
99 }
100
101 if ( taga == TAG_Special )
102 taga = FPU_Special(a);
103 if ( tagb == TAG_Special )
104 tagb = FPU_Special(b);
105
106 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
107 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
108 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
109 {
110 FPU_REG x, y;
111
112 if ( denormal_operand() < 0 )
113 return FPU_Exception;
114
115 FPU_to_exp16(a, &x);
116 FPU_to_exp16(b, &y);
117 a = &x;
118 b = &y;
119 expa = exponent16(a);
120 expb = exponent16(b);
121 goto valid_add;
122 }
123
124 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
125 {
126 if ( deststnr == 0 )
127 return real_2op_NaN(b, tagb, deststnr, a);
128 else
129 return real_2op_NaN(a, taga, deststnr, a);
130 }
131
132 return add_sub_specials(a, taga, signa, b, tagb, signb,
133 dest, deststnr, control_w);
134 }
135
136
137 /* Subtract b from a. (a-b) -> dest */
FPU_sub(int flags,int rm,int control_w)138 int FPU_sub(int flags, int rm, int control_w)
139 {
140 FPU_REG const *a, *b;
141 FPU_REG *dest;
142 u_char taga, tagb, signa, signb, saved_sign, sign;
143 int diff, tag = 0, expa, expb, deststnr;
144
145 a = &st(0);
146 taga = FPU_gettag0();
147
148 deststnr = 0;
149 if ( flags & LOADED )
150 {
151 b = (FPU_REG *)rm;
152 tagb = flags & 0x0f;
153 }
154 else
155 {
156 b = &st(rm);
157 tagb = FPU_gettagi(rm);
158
159 if ( flags & DEST_RM )
160 deststnr = rm;
161 }
162
163 signa = getsign(a);
164 signb = getsign(b);
165
166 if ( flags & REV )
167 {
168 signa ^= SIGN_NEG;
169 signb ^= SIGN_NEG;
170 }
171
172 dest = &st(deststnr);
173 saved_sign = getsign(dest);
174
175 if ( !(taga | tagb) )
176 {
177 expa = exponent(a);
178 expb = exponent(b);
179
180 valid_subtract:
181 /* Both registers are valid */
182
183 diff = expa - expb;
184
185 if (!diff)
186 {
187 diff = a->sigh - b->sigh; /* Works only if ms bits are identical */
188 if (!diff)
189 {
190 diff = a->sigl > b->sigl;
191 if (!diff)
192 diff = -(a->sigl < b->sigl);
193 }
194 }
195
196 switch ( (((int)signa)*2 + signb) / SIGN_NEG )
197 {
198 case 0: /* P - P */
199 case 3: /* N - N */
200 if (diff > 0)
201 {
202 /* |a| > |b| */
203 tag = FPU_u_sub(a, b, dest, control_w, signa, expa, expb);
204 }
205 else if ( diff == 0 )
206 {
207 FPU_copy_to_regi(&CONST_Z, TAG_Zero, deststnr);
208
209 /* sign depends upon rounding mode */
210 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
211 ? SIGN_POS : SIGN_NEG);
212 return TAG_Zero;
213 }
214 else
215 {
216 sign = signa ^ SIGN_NEG;
217 tag = FPU_u_sub(b, a, dest, control_w, sign, expb, expa);
218 }
219 break;
220 case 1: /* P - N */
221 tag = FPU_u_add(a, b, dest, control_w, SIGN_POS, expa, expb);
222 break;
223 case 2: /* N - P */
224 tag = FPU_u_add(a, b, dest, control_w, SIGN_NEG, expa, expb);
225 break;
226 #ifdef PARANOID
227 default:
228 EXCEPTION(EX_INTERNAL|0x111);
229 return -1;
230 #endif
231 }
232 if ( tag < 0 )
233 {
234 setsign(dest, saved_sign);
235 return tag;
236 }
237 FPU_settagi(deststnr, tag);
238 return tag;
239 }
240
241 if ( taga == TAG_Special )
242 taga = FPU_Special(a);
243 if ( tagb == TAG_Special )
244 tagb = FPU_Special(b);
245
246 if ( ((taga == TAG_Valid) && (tagb == TW_Denormal))
247 || ((taga == TW_Denormal) && (tagb == TAG_Valid))
248 || ((taga == TW_Denormal) && (tagb == TW_Denormal)) )
249 {
250 FPU_REG x, y;
251
252 if ( denormal_operand() < 0 )
253 return FPU_Exception;
254
255 FPU_to_exp16(a, &x);
256 FPU_to_exp16(b, &y);
257 a = &x;
258 b = &y;
259 expa = exponent16(a);
260 expb = exponent16(b);
261
262 goto valid_subtract;
263 }
264
265 if ( (taga == TW_NaN) || (tagb == TW_NaN) )
266 {
267 FPU_REG const *d1, *d2;
268 if ( flags & REV )
269 {
270 d1 = b;
271 d2 = a;
272 }
273 else
274 {
275 d1 = a;
276 d2 = b;
277 }
278 if ( flags & LOADED )
279 return real_2op_NaN(b, tagb, deststnr, d1);
280 if ( flags & DEST_RM )
281 return real_2op_NaN(a, taga, deststnr, d2);
282 else
283 return real_2op_NaN(b, tagb, deststnr, d2);
284 }
285
286 return add_sub_specials(a, taga, signa, b, tagb, signb ^ SIGN_NEG,
287 dest, deststnr, control_w);
288 }
289
290
291 static
add_sub_specials(FPU_REG const * a,u_char taga,u_char signa,FPU_REG const * b,u_char tagb,u_char signb,FPU_REG * dest,int deststnr,int control_w)292 int add_sub_specials(FPU_REG const *a, u_char taga, u_char signa,
293 FPU_REG const *b, u_char tagb, u_char signb,
294 FPU_REG *dest, int deststnr, int control_w)
295 {
296 if ( ((taga == TW_Denormal) || (tagb == TW_Denormal))
297 && (denormal_operand() < 0) )
298 return FPU_Exception;
299
300 if (taga == TAG_Zero)
301 {
302 if (tagb == TAG_Zero)
303 {
304 /* Both are zero, result will be zero. */
305 u_char different_signs = signa ^ signb;
306
307 FPU_copy_to_regi(a, TAG_Zero, deststnr);
308 if ( different_signs )
309 {
310 /* Signs are different. */
311 /* Sign of answer depends upon rounding mode. */
312 setsign(dest, ((control_w & CW_RC) != RC_DOWN)
313 ? SIGN_POS : SIGN_NEG);
314 }
315 else
316 setsign(dest, signa); /* signa may differ from the sign of a. */
317 return TAG_Zero;
318 }
319 else
320 {
321 reg_copy(b, dest);
322 if ( (tagb == TW_Denormal) && (b->sigh & 0x80000000) )
323 {
324 /* A pseudoDenormal, convert it. */
325 addexponent(dest, 1);
326 tagb = TAG_Valid;
327 }
328 else if ( tagb > TAG_Empty )
329 tagb = TAG_Special;
330 setsign(dest, signb); /* signb may differ from the sign of b. */
331 FPU_settagi(deststnr, tagb);
332 return tagb;
333 }
334 }
335 else if (tagb == TAG_Zero)
336 {
337 reg_copy(a, dest);
338 if ( (taga == TW_Denormal) && (a->sigh & 0x80000000) )
339 {
340 /* A pseudoDenormal */
341 addexponent(dest, 1);
342 taga = TAG_Valid;
343 }
344 else if ( taga > TAG_Empty )
345 taga = TAG_Special;
346 setsign(dest, signa); /* signa may differ from the sign of a. */
347 FPU_settagi(deststnr, taga);
348 return taga;
349 }
350 else if (taga == TW_Infinity)
351 {
352 if ( (tagb != TW_Infinity) || (signa == signb) )
353 {
354 FPU_copy_to_regi(a, TAG_Special, deststnr);
355 setsign(dest, signa); /* signa may differ from the sign of a. */
356 return taga;
357 }
358 /* Infinity-Infinity is undefined. */
359 return arith_invalid(deststnr);
360 }
361 else if (tagb == TW_Infinity)
362 {
363 FPU_copy_to_regi(b, TAG_Special, deststnr);
364 setsign(dest, signb); /* signb may differ from the sign of b. */
365 return tagb;
366 }
367
368 #ifdef PARANOID
369 EXCEPTION(EX_INTERNAL|0x101);
370 #endif
371
372 return FPU_Exception;
373 }
374
375