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