v2.4.8 -> v2.4.8.1
[opensuse:kernel.git] / arch / arm / 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 "milieu.h"
24 #include "softfloat.h"
25 #include "fpopcode.h"
26 #include "fpa11.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    SetRoundingPrecision(opcode);
86    
87    switch (opcode & MASK_ROUNDING_PRECISION)
88    {
89       case ROUND_SINGLE:
90       {
91         fpa11->fType[getFn(opcode)] = typeSingle;
92         fpa11->fpreg[getFn(opcode)].fSingle =
93            int32_to_float32(readRegister(getRd(opcode)));
94       }
95       break;
96
97       case ROUND_DOUBLE:
98       {
99         fpa11->fType[getFn(opcode)] = typeDouble;
100         fpa11->fpreg[getFn(opcode)].fDouble =
101             int32_to_float64(readRegister(getRd(opcode)));
102       }
103       break;
104         
105       case ROUND_EXTENDED:
106       {
107         fpa11->fType[getFn(opcode)] = typeExtended;
108         fpa11->fpreg[getFn(opcode)].fExtended =
109            int32_to_floatx80(readRegister(getRd(opcode)));
110       }
111       break;
112       
113       default: nRc = 0;
114   }
115   
116   return nRc;
117 }
118
119 unsigned int PerformFIX(const unsigned int opcode)
120 {
121    FPA11 *fpa11 = GET_FPA11();
122    unsigned int nRc = 1;
123    unsigned int Fn = getFm(opcode);
124    
125    SetRoundingMode(opcode);
126
127    switch (fpa11->fType[Fn])
128    {
129       case typeSingle:
130       {
131          writeRegister(getRd(opcode),
132                        float32_to_int32(fpa11->fpreg[Fn].fSingle));
133       }
134       break;
135
136       case typeDouble:
137       {
138          writeRegister(getRd(opcode),
139                        float64_to_int32(fpa11->fpreg[Fn].fDouble));
140       }
141       break;
142                        
143       case typeExtended:
144       {
145          writeRegister(getRd(opcode),
146                        floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
147       }
148       break;
149       
150       default: nRc = 0;
151   }
152   
153   return nRc;
154 }
155
156    
157 static unsigned int __inline__
158 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
159 {
160    unsigned int flags = 0;
161
162    /* test for less than condition */
163    if (floatx80_lt(Fn,Fm))
164    {
165       flags |= CC_NEGATIVE;
166    }
167   
168    /* test for equal condition */
169    if (floatx80_eq(Fn,Fm))
170    {
171       flags |= CC_ZERO;
172    }
173
174    /* test for greater than or equal condition */
175    if (floatx80_lt(Fm,Fn))
176    {
177       flags |= CC_CARRY;
178    }
179    
180    writeConditionCodes(flags);
181    return 1;
182 }
183
184 /* This instruction sets the flags N, Z, C, V in the FPSR. */
185    
186 static unsigned int PerformComparison(const unsigned int opcode)
187 {
188    FPA11 *fpa11 = GET_FPA11();
189    unsigned int Fn, Fm;
190    floatx80 rFn, rFm;
191    int e_flag = opcode & 0x400000;      /* 1 if CxFE */
192    int n_flag = opcode & 0x200000;      /* 1 if CNxx */
193    unsigned int flags = 0;
194
195    //printk("PerformComparison(0x%08x)\n",opcode);
196
197    Fn = getFn(opcode);
198    Fm = getFm(opcode);
199
200    /* Check for unordered condition and convert all operands to 80-bit
201       format.
202       ?? Might be some mileage in avoiding this conversion if possible.
203       Eg, if both operands are 32-bit, detect this and do a 32-bit
204       comparison (cheaper than an 80-bit one).  */
205    switch (fpa11->fType[Fn])
206    {
207       case typeSingle: 
208         //printk("single.\n");
209         if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
210            goto unordered;
211         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
212       break;
213
214       case typeDouble: 
215         //printk("double.\n");
216         if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
217            goto unordered;
218         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
219       break;
220       
221       case typeExtended: 
222         //printk("extended.\n");
223         if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
224            goto unordered;
225         rFn = fpa11->fpreg[Fn].fExtended;
226       break;
227       
228       default: return 0;
229    }
230
231    if (CONSTANT_FM(opcode))
232    {
233      //printk("Fm is a constant: #%d.\n",Fm);
234      rFm = getExtendedConstant(Fm);
235      if (floatx80_is_nan(rFm))
236         goto unordered;
237    }
238    else
239    {
240      //printk("Fm = r%d which contains a ",Fm);
241       switch (fpa11->fType[Fm])
242       {
243          case typeSingle: 
244            //printk("single.\n");
245            if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
246               goto unordered;
247            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
248          break;
249
250          case typeDouble: 
251            //printk("double.\n");
252            if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
253               goto unordered;
254            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
255          break;
256       
257          case typeExtended: 
258            //printk("extended.\n");
259            if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
260               goto unordered;
261            rFm = fpa11->fpreg[Fm].fExtended;
262          break;
263       
264          default: return 0;
265       }
266    }
267
268    if (n_flag)
269    {
270       rFm.high ^= 0x8000;
271    }
272
273    return PerformComparisonOperation(rFn,rFm);
274
275  unordered:
276    /* ?? The FPA data sheet is pretty vague about this, in particular
277       about whether the non-E comparisons can ever raise exceptions.
278       This implementation is based on a combination of what it says in
279       the data sheet, observation of how the Acorn emulator actually
280       behaves (and how programs expect it to) and guesswork.  */
281    flags |= CC_OVERFLOW;
282    flags &= ~(CC_ZERO | CC_NEGATIVE);
283
284    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
285
286    if (e_flag) float_raise(float_flag_invalid);
287
288    writeConditionCodes(flags);
289    return 1;
290 }