Fix scsi sector size confusion (Blue Swirl).
[qemu] / target-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 "fpa11.h"
24 #include "softfloat.h"
25 #include "fpopcode.h"
26 #include "fpa11.inl"
27 //#include "fpmodule.h"
28 //#include "fpmodule.inl"
29
30 extern flag floatx80_is_nan(floatx80);
31 extern flag float64_is_nan( float64);
32 extern flag float32_is_nan( float32);
33
34 void SetRoundingMode(const unsigned int opcode);
35
36 unsigned int PerformFLT(const unsigned int opcode);
37 unsigned int PerformFIX(const unsigned int opcode);
38
39 static unsigned int
40 PerformComparison(const unsigned int opcode);
41
42 unsigned int EmulateCPRT(const unsigned int opcode)
43 {
44   unsigned int nRc = 1;
45
46   //printk("EmulateCPRT(0x%08x)\n",opcode);
47
48   if (opcode & 0x800000)
49   {
50      /* This is some variant of a comparison (PerformComparison will
51         sort out which one).  Since most of the other CPRT
52         instructions are oddball cases of some sort or other it makes
53         sense to pull this out into a fast path.  */
54      return PerformComparison(opcode);
55   }
56
57   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
58   switch ((opcode & 0x700000) >> 20)
59   {
60     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
61     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
62     
63     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
64     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
65
66 #if 0    /* We currently have no use for the FPCR, so there's no point
67             in emulating it. */
68     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
69     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
70 #endif
71
72     default: nRc = 0;
73   }
74   
75   return nRc;
76 }
77
78 unsigned int PerformFLT(const unsigned int opcode)
79 {
80    FPA11 *fpa11 = GET_FPA11();
81    
82    unsigned int nRc = 1;
83    SetRoundingMode(opcode);
84
85    switch (opcode & MASK_ROUNDING_PRECISION)
86    {
87       case ROUND_SINGLE:
88       {
89         fpa11->fType[getFn(opcode)] = typeSingle;
90         fpa11->fpreg[getFn(opcode)].fSingle =
91            int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
92       }
93       break;
94
95       case ROUND_DOUBLE:
96       {
97         fpa11->fType[getFn(opcode)] = typeDouble;
98         fpa11->fpreg[getFn(opcode)].fDouble =
99             int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
100       }
101       break;
102         
103       case ROUND_EXTENDED:
104       {
105         fpa11->fType[getFn(opcode)] = typeExtended;
106         fpa11->fpreg[getFn(opcode)].fExtended =
107            int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
108       }
109       break;
110       
111       default: nRc = 0;
112   }
113   
114   return nRc;
115 }
116
117 unsigned int PerformFIX(const unsigned int opcode)
118 {
119    FPA11 *fpa11 = GET_FPA11();
120    unsigned int nRc = 1;
121    unsigned int Fn = getFm(opcode);
122    
123    SetRoundingMode(opcode);
124
125    switch (fpa11->fType[Fn])
126    {
127       case typeSingle:
128       {
129          writeRegister(getRd(opcode),
130                        float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
131       }
132       break;
133
134       case typeDouble:
135       {
136          //printf("F%d is 0x%llx\n",Fn,fpa11->fpreg[Fn].fDouble);
137          writeRegister(getRd(opcode),
138                        float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
139       }
140       break;
141                        
142       case typeExtended:
143       {
144          writeRegister(getRd(opcode),
145                        floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
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    FPA11 *fpa11 = GET_FPA11();
160    unsigned int flags = 0;
161
162    /* test for less than condition */
163    if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
164    {
165       flags |= CC_NEGATIVE;
166    }
167   
168    /* test for equal condition */
169    if (floatx80_eq(Fn,Fm, &fpa11->fp_status))
170    {
171       flags |= CC_ZERO;
172    }
173
174    /* test for greater than or equal condition */
175    if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
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, &fpa11->fp_status);
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, &fpa11->fp_status);
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, &fpa11->fp_status);
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, &fpa11->fp_status);
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, &fpa11->fp_status);
287
288    writeConditionCodes(flags);
289    return 1;
290 }