xref: /qemu/linux-user/arm/nwfpe/fpa11_cprt.c (revision 7a4e543d)
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, see <http://www.gnu.org/licenses/>.
20 */
21 
22 #include "qemu/osdep.h"
23 #include "fpa11.h"
24 #include "fpu/softfloat.h"
25 #include "fpopcode.h"
26 #include "fpa11.inl"
27 //#include "fpmodule.h"
28 //#include "fpmodule.inl"
29 
30 unsigned int PerformFLT(const unsigned int opcode);
31 unsigned int PerformFIX(const unsigned int opcode);
32 
33 static unsigned int
34 PerformComparison(const unsigned int opcode);
35 
36 unsigned int EmulateCPRT(const unsigned int opcode)
37 {
38   unsigned int nRc = 1;
39 
40   //printk("EmulateCPRT(0x%08x)\n",opcode);
41 
42   if (opcode & 0x800000)
43   {
44      /* This is some variant of a comparison (PerformComparison will
45 	sort out which one).  Since most of the other CPRT
46 	instructions are oddball cases of some sort or other it makes
47 	sense to pull this out into a fast path.  */
48      return PerformComparison(opcode);
49   }
50 
51   /* Hint to GCC that we'd like a jump table rather than a load of CMPs */
52   switch ((opcode & 0x700000) >> 20)
53   {
54     case  FLT_CODE >> 20: nRc = PerformFLT(opcode); break;
55     case  FIX_CODE >> 20: nRc = PerformFIX(opcode); break;
56 
57     case  WFS_CODE >> 20: writeFPSR(readRegister(getRd(opcode))); break;
58     case  RFS_CODE >> 20: writeRegister(getRd(opcode),readFPSR()); break;
59 
60 #if 0    /* We currently have no use for the FPCR, so there's no point
61 	    in emulating it. */
62     case  WFC_CODE >> 20: writeFPCR(readRegister(getRd(opcode)));
63     case  RFC_CODE >> 20: writeRegister(getRd(opcode),readFPCR()); break;
64 #endif
65 
66     default: nRc = 0;
67   }
68 
69   return nRc;
70 }
71 
72 unsigned int PerformFLT(const unsigned int opcode)
73 {
74    FPA11 *fpa11 = GET_FPA11();
75 
76    unsigned int nRc = 1;
77    SetRoundingMode(opcode);
78 
79    switch (opcode & MASK_ROUNDING_PRECISION)
80    {
81       case ROUND_SINGLE:
82       {
83         fpa11->fType[getFn(opcode)] = typeSingle;
84         fpa11->fpreg[getFn(opcode)].fSingle =
85 	   int32_to_float32(readRegister(getRd(opcode)), &fpa11->fp_status);
86       }
87       break;
88 
89       case ROUND_DOUBLE:
90       {
91         fpa11->fType[getFn(opcode)] = typeDouble;
92         fpa11->fpreg[getFn(opcode)].fDouble =
93             int32_to_float64(readRegister(getRd(opcode)), &fpa11->fp_status);
94       }
95       break;
96 
97       case ROUND_EXTENDED:
98       {
99         fpa11->fType[getFn(opcode)] = typeExtended;
100         fpa11->fpreg[getFn(opcode)].fExtended =
101 	   int32_to_floatx80(readRegister(getRd(opcode)), &fpa11->fp_status);
102       }
103       break;
104 
105       default: nRc = 0;
106   }
107 
108   return nRc;
109 }
110 
111 unsigned int PerformFIX(const unsigned int opcode)
112 {
113    FPA11 *fpa11 = GET_FPA11();
114    unsigned int nRc = 1;
115    unsigned int Fn = getFm(opcode);
116 
117    SetRoundingMode(opcode);
118 
119    switch (fpa11->fType[Fn])
120    {
121       case typeSingle:
122       {
123          writeRegister(getRd(opcode),
124 	               float32_to_int32(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status));
125       }
126       break;
127 
128       case typeDouble:
129       {
130          //printf("F%d is 0x%" PRIx64 "\n",Fn,fpa11->fpreg[Fn].fDouble);
131          writeRegister(getRd(opcode),
132 	               float64_to_int32(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status));
133       }
134       break;
135 
136       case typeExtended:
137       {
138          writeRegister(getRd(opcode),
139 	               floatx80_to_int32(fpa11->fpreg[Fn].fExtended, &fpa11->fp_status));
140       }
141       break;
142 
143       default: nRc = 0;
144   }
145 
146   return nRc;
147 }
148 
149 
150 static __inline unsigned int
151 PerformComparisonOperation(floatx80 Fn, floatx80 Fm)
152 {
153    FPA11 *fpa11 = GET_FPA11();
154    unsigned int flags = 0;
155 
156    /* test for less than condition */
157    if (floatx80_lt(Fn,Fm, &fpa11->fp_status))
158    {
159       flags |= CC_NEGATIVE;
160    }
161 
162    /* test for equal condition */
163    if (floatx80_eq_quiet(Fn,Fm, &fpa11->fp_status))
164    {
165       flags |= CC_ZERO;
166    }
167 
168    /* test for greater than or equal condition */
169    if (floatx80_lt(Fm,Fn, &fpa11->fp_status))
170    {
171       flags |= CC_CARRY;
172    }
173 
174    writeConditionCodes(flags);
175    return 1;
176 }
177 
178 /* This instruction sets the flags N, Z, C, V in the FPSR. */
179 
180 static unsigned int PerformComparison(const unsigned int opcode)
181 {
182    FPA11 *fpa11 = GET_FPA11();
183    unsigned int Fn, Fm;
184    floatx80 rFn, rFm;
185    int e_flag = opcode & 0x400000;	/* 1 if CxFE */
186    int n_flag = opcode & 0x200000;	/* 1 if CNxx */
187    unsigned int flags = 0;
188 
189    //printk("PerformComparison(0x%08x)\n",opcode);
190 
191    Fn = getFn(opcode);
192    Fm = getFm(opcode);
193 
194    /* Check for unordered condition and convert all operands to 80-bit
195       format.
196       ?? Might be some mileage in avoiding this conversion if possible.
197       Eg, if both operands are 32-bit, detect this and do a 32-bit
198       comparison (cheaper than an 80-bit one).  */
199    switch (fpa11->fType[Fn])
200    {
201       case typeSingle:
202         //printk("single.\n");
203 	if (float32_is_any_nan(fpa11->fpreg[Fn].fSingle))
204 	   goto unordered;
205         rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle, &fpa11->fp_status);
206       break;
207 
208       case typeDouble:
209         //printk("double.\n");
210 	if (float64_is_any_nan(fpa11->fpreg[Fn].fDouble))
211 	   goto unordered;
212         rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble, &fpa11->fp_status);
213       break;
214 
215       case typeExtended:
216         //printk("extended.\n");
217 	if (floatx80_is_any_nan(fpa11->fpreg[Fn].fExtended))
218 	   goto unordered;
219         rFn = fpa11->fpreg[Fn].fExtended;
220       break;
221 
222       default: return 0;
223    }
224 
225    if (CONSTANT_FM(opcode))
226    {
227      //printk("Fm is a constant: #%d.\n",Fm);
228      rFm = getExtendedConstant(Fm);
229      if (floatx80_is_any_nan(rFm))
230         goto unordered;
231    }
232    else
233    {
234      //printk("Fm = r%d which contains a ",Fm);
235       switch (fpa11->fType[Fm])
236       {
237          case typeSingle:
238            //printk("single.\n");
239 	   if (float32_is_any_nan(fpa11->fpreg[Fm].fSingle))
240 	      goto unordered;
241            rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle, &fpa11->fp_status);
242          break;
243 
244          case typeDouble:
245            //printk("double.\n");
246 	   if (float64_is_any_nan(fpa11->fpreg[Fm].fDouble))
247 	      goto unordered;
248            rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble, &fpa11->fp_status);
249          break;
250 
251          case typeExtended:
252            //printk("extended.\n");
253 	   if (floatx80_is_any_nan(fpa11->fpreg[Fm].fExtended))
254 	      goto unordered;
255            rFm = fpa11->fpreg[Fm].fExtended;
256          break;
257 
258          default: return 0;
259       }
260    }
261 
262    if (n_flag)
263    {
264       rFm.high ^= 0x8000;
265    }
266 
267    return PerformComparisonOperation(rFn,rFm);
268 
269  unordered:
270    /* ?? The FPA data sheet is pretty vague about this, in particular
271       about whether the non-E comparisons can ever raise exceptions.
272       This implementation is based on a combination of what it says in
273       the data sheet, observation of how the Acorn emulator actually
274       behaves (and how programs expect it to) and guesswork.  */
275    flags |= CC_OVERFLOW;
276    flags &= ~(CC_ZERO | CC_NEGATIVE);
277 
278    if (BIT_AC & readFPSR()) flags |= CC_CARRY;
279 
280    if (e_flag) float_raise(float_flag_invalid, &fpa11->fp_status);
281 
282    writeConditionCodes(flags);
283    return 1;
284 }
285