Linux-2.6.12-rc2
[linux-2.6] / arch / arm26 / nwfpe / fpa11_cprt.c
1 /*
2     NetWinder Floating Point Emulator
3     (c) Rebel.COM, 1998,1999
4     (c) Philip Blundell, 1999
5
6     Direct questions, comments to Scott Bambrough <scottb@netwinder.org>
7
8     This program is free software; you can redistribute it and/or modify
9     it under the terms of the GNU General Public License as published by
10     the Free Software Foundation; either version 2 of the License, or
11     (at your option) any later version.
12
13     This program is distributed in the hope that it will be useful,
14     but WITHOUT ANY WARRANTY; without even the implied warranty of
15     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16     GNU General Public License for more details.
17
18     You should have received a copy of the GNU General Public License
19     along with this program; if not, write to the Free Software
20     Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
21 */
22
23 #include "fpa11.h"
24 #include "milieu.h"
25 #include "softfloat.h"
26 #include "fpopcode.h"
27 #include "fpa11.inl"
28 #include "fpmodule.h"
29 #include "fpmodule.inl"
30
31 extern flag floatx80_is_nan(floatx80);
32 extern flag float64_is_nan( float64);
33 extern flag float32_is_nan( float32);
34
35 void SetRoundingMode(const unsigned int opcode);
36
37 unsigned int PerformFLT(const unsigned int opcode);
38 unsigned int PerformFIX(const unsigned int opcode);
39
40 static unsigned int
41 PerformComparison(const unsigned int opcode);
42
43 unsigned int EmulateCPRT(const unsigned int opcode)
44 {
45   unsigned int nRc = 1;
46
47   //printk("EmulateCPRT(0x%08x)\n",opcode);
48
49   if (opcode & 0x800000)
50   {
51      /* This is some variant of a comparison (PerformComparison will
52         sort out which one).  Since most of the other CPRT
53         instructions are oddball cases of some sort or other it makes
54         sense to pull this out into a fast path.  */
55      return PerformComparison(opcode);
56   }
57
58   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
59   switch ((opcode & 0x700000) >> 20)
60   {
61     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
62     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
63     
64     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
65     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
66
67 #if 0    /* We currently have no use for the FPCR, so there's no point
68             in emulating it. */
69     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
70     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
71 #endif
72
73     default: nRc = 0;
74   }
75   
76   return nRc;
77 }
78
79 unsigned int PerformFLT(const unsigned int opcode)
80 {
81    FPA11 *fpa11 = GET_FPA11();
82    
83    unsigned int nRc = 1;
84    SetRoundingMode(opcode);
85
86    switch (opcode & MASK_ROUNDING_PRECISION)
87    {
88       case ROUND_SINGLE:
89       {
90         fpa11->fType[getFn(opcode)] = typeSingle;
91         fpa11->fpreg[getFn(opcode)].fSingle =
92            int32_to_float32(readRegister(getRd(opcode)));
93       }
94       break;
95
96       case ROUND_DOUBLE:
97       {
98         fpa11->fType[getFn(opcode)] = typeDouble;
99         fpa11->fpreg[getFn(opcode)].fDouble =
100             int32_to_float64(readRegister(getRd(opcode)));
101       }
102       break;
103         
104       case ROUND_EXTENDED:
105       {
106         fpa11->fType[getFn(opcode)] = typeExtended;
107         fpa11->fpreg[getFn(opcode)].fExtended =
108            int32_to_floatx80(readRegister(getRd(opcode)));
109       }
110       break;
111       
112       default: nRc = 0;
113   }
114   
115   return nRc;
116 }
117
118 unsigned int PerformFIX(const unsigned int opcode)
119 {
120    FPA11 *fpa11 = GET_FPA11();
121    unsigned int nRc = 1;
122    unsigned int Fn = getFm(opcode);
123    
124    SetRoundingMode(opcode);
125
126    switch (fpa11->fType[Fn])
127    {
128       case typeSingle:
129       {
130          writeRegister(getRd(opcode),
131                        float32_to_int32(fpa11->fpreg[Fn].fSingle));
132       }
133       break;
134
135       case typeDouble:
136       {
137          writeRegister(getRd(opcode),
138                        float64_to_int32(fpa11->fpreg[Fn].fDouble));
139       }
140       break;
141                        
142       case typeExtended:
143       {
144          writeRegister(getRd(opcode),
145                        floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
146       }
147       break;
148       
149       default: nRc = 0;
150   }
151   
152   return nRc;
153 }
154
155    
156 static unsigned int __inline__
157 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
158 {
159    unsigned int flags = 0;
160
161    /* test for less than condition */
162    if (floatx80_lt(Fn,Fm))
163    {
164       flags |= CC_NEGATIVE;
165    }
166   
167    /* test for equal condition */
168    if (floatx80_eq(Fn,Fm))
169    {
170       flags |= CC_ZERO;
171    }
172
173    /* test for greater than or equal condition */
174    if (floatx80_lt(Fm,Fn))
175    {
176       flags |= CC_CARRY;
177    }
178    
179    writeConditionCodes(flags);
180    return 1;
181 }
182
183 /* This instruction sets the flags N, Z, C, V in the FPSR. */
184    
185 static unsigned int PerformComparison(const unsigned int opcode)
186 {
187    FPA11 *fpa11 = GET_FPA11();
188    unsigned int Fn, Fm;
189    floatx80 rFn, rFm;
190    int e_flag = opcode & 0x400000;      /* 1 if CxFE */
191    int n_flag = opcode & 0x200000;      /* 1 if CNxx */
192    unsigned int flags = 0;
193
194    //printk("PerformComparison(0x%08x)\n",opcode);
195
196    Fn = getFn(opcode);
197    Fm = getFm(opcode);
198
199    /* Check for unordered condition and convert all operands to 80-bit
200       format.
201       ?? Might be some mileage in avoiding this conversion if possible.
202       Eg, if both operands are 32-bit, detect this and do a 32-bit
203       comparison (cheaper than an 80-bit one).  */
204    switch (fpa11->fType[Fn])
205    {
206       case typeSingle: 
207         //printk("single.\n");
208         if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
209            goto unordered;
210         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
211       break;
212
213       case typeDouble: 
214         //printk("double.\n");
215         if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
216            goto unordered;
217         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
218       break;
219       
220       case typeExtended: 
221         //printk("extended.\n");
222         if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
223            goto unordered;
224         rFn = fpa11->fpreg[Fn].fExtended;
225       break;
226       
227       default: return 0;
228    }
229
230    if (CONSTANT_FM(opcode))
231    {
232      //printk("Fm is a constant: #%d.\n",Fm);
233      rFm = getExtendedConstant(Fm);
234      if (floatx80_is_nan(rFm))
235         goto unordered;
236    }
237    else
238    {
239      //printk("Fm = r%d which contains a ",Fm);
240       switch (fpa11->fType[Fm])
241       {
242          case typeSingle: 
243            //printk("single.\n");
244            if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
245               goto unordered;
246            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
247          break;
248
249          case typeDouble: 
250            //printk("double.\n");
251            if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
252               goto unordered;
253            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
254          break;
255       
256          case typeExtended: 
257            //printk("extended.\n");
258            if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
259               goto unordered;
260            rFm = fpa11->fpreg[Fm].fExtended;
261          break;
262       
263          default: return 0;
264       }
265    }
266
267    if (n_flag)
268    {
269       rFm.high ^= 0x8000;
270    }
271
272    return PerformComparisonOperation(rFn,rFm);
273
274  unordered:
275    /* ?? The FPA data sheet is pretty vague about this, in particular
276       about whether the non-E comparisons can ever raise exceptions.
277       This implementation is based on a combination of what it says in
278       the data sheet, observation of how the Acorn emulator actually
279       behaves (and how programs expect it to) and guesswork.  */
280    flags |= CC_OVERFLOW;
281    flags &= ~(CC_ZERO | CC_NEGATIVE);
282
283    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
284
285    if (e_flag) float_raise(float_flag_invalid);
286
287    writeConditionCodes(flags);
288    return 1;
289 }