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