Linux kernel & device driver programming

Cross-Referenced Linux and Device Driver Code

[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ]
Version: [ 2.6.11.8 ] [ 2.6.25 ] [ 2.6.25.8 ] [ 2.6.31.13 ] Architecture: [ i386 ]
  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   */
 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 */
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
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 
  This page was automatically generated by the LXR engine.