1 /*
2     NetWinder Floating Point Emulator
3     (c) Rebel.COM, 1998,1999
4     (c) Philip Blundell, 1999, 2001
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 "fpopcode.h"
25 #include "fpa11.inl"
26 #include "fpmodule.h"
27 #include "fpmodule.inl"
28 
29 #ifdef CONFIG_FPE_NWFPE_XP
30 extern flag floatx80_is_nan(floatx80);
31 #endif
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 PerformComparison(const unsigned int opcode);
41 
EmulateCPRT(const unsigned int opcode)42 unsigned int EmulateCPRT(const unsigned int opcode)
43 {
44 
45 	if (opcode & 0x800000) {
46 		/* This is some variant of a comparison (PerformComparison
47 		   will sort out which one).  Since most of the other CPRT
48 		   instructions are oddball cases of some sort or other it
49 		   makes sense to pull this out into a fast path.  */
50 		return PerformComparison(opcode);
51 	}
52 
53 	/* Hint to GCC that we'd like a jump table rather than a load of CMPs */
54 	switch ((opcode & 0x700000) >> 20) {
55 	case FLT_CODE >> 20:
56 		return PerformFLT(opcode);
57 		break;
58 	case FIX_CODE >> 20:
59 		return PerformFIX(opcode);
60 		break;
61 
62 	case WFS_CODE >> 20:
63 		writeFPSR(readRegister(getRd(opcode)));
64 		break;
65 	case RFS_CODE >> 20:
66 		writeRegister(getRd(opcode), readFPSR());
67 		break;
68 
69 	default:
70 		return 0;
71 	}
72 
73 	return 1;
74 }
75 
PerformFLT(const unsigned int opcode)76 unsigned int PerformFLT(const unsigned int opcode)
77 {
78 	FPA11 *fpa11 = GET_FPA11();
79 	SetRoundingMode(opcode);
80 	SetRoundingPrecision(opcode);
81 
82 	switch (opcode & MASK_ROUNDING_PRECISION) {
83 	case ROUND_SINGLE:
84 		{
85 			fpa11->fType[getFn(opcode)] = typeSingle;
86 			fpa11->fpreg[getFn(opcode)].fSingle = int32_to_float32(readRegister(getRd(opcode)));
87 		}
88 		break;
89 
90 	case ROUND_DOUBLE:
91 		{
92 			fpa11->fType[getFn(opcode)] = typeDouble;
93 			fpa11->fpreg[getFn(opcode)].fDouble = int32_to_float64(readRegister(getRd(opcode)));
94 		}
95 		break;
96 
97 #ifdef CONFIG_FPE_NWFPE_XP
98 	case ROUND_EXTENDED:
99 		{
100 			fpa11->fType[getFn(opcode)] = typeExtended;
101 			fpa11->fpreg[getFn(opcode)].fExtended = int32_to_floatx80(readRegister(getRd(opcode)));
102 		}
103 		break;
104 #endif
105 
106 	default:
107 		return 0;
108 	}
109 
110 	return 1;
111 }
112 
PerformFIX(const unsigned int opcode)113 unsigned int PerformFIX(const unsigned int opcode)
114 {
115 	FPA11 *fpa11 = GET_FPA11();
116 	unsigned int Fn = getFm(opcode);
117 
118 	SetRoundingMode(opcode);
119 
120 	switch (fpa11->fType[Fn]) {
121 	case typeSingle:
122 		{
123 			writeRegister(getRd(opcode), float32_to_int32(fpa11->fpreg[Fn].fSingle));
124 		}
125 		break;
126 
127 	case typeDouble:
128 		{
129 			writeRegister(getRd(opcode), float64_to_int32(fpa11->fpreg[Fn].fDouble));
130 		}
131 		break;
132 
133 #ifdef CONFIG_FPE_NWFPE_XP
134 	case typeExtended:
135 		{
136 			writeRegister(getRd(opcode), floatx80_to_int32(fpa11->fpreg[Fn].fExtended));
137 		}
138 		break;
139 #endif
140 
141 	default:
142 		return 0;
143 	}
144 
145 	return 1;
146 }
147 
148 /* This instruction sets the flags N, Z, C, V in the FPSR. */
PerformComparison(const unsigned int opcode)149 static unsigned int PerformComparison(const unsigned int opcode)
150 {
151 	FPA11 *fpa11 = GET_FPA11();
152 	unsigned int Fn = getFn(opcode), Fm = getFm(opcode);
153 	int e_flag = opcode & 0x400000;	/* 1 if CxFE */
154 	int n_flag = opcode & 0x200000;	/* 1 if CNxx */
155 	unsigned int flags = 0;
156 
157 #ifdef CONFIG_FPE_NWFPE_XP
158 	floatx80 rFn, rFm;
159 
160 	/* Check for unordered condition and convert all operands to 80-bit
161 	   format.
162 	   ?? Might be some mileage in avoiding this conversion if possible.
163 	   Eg, if both operands are 32-bit, detect this and do a 32-bit
164 	   comparison (cheaper than an 80-bit one).  */
165 	switch (fpa11->fType[Fn]) {
166 	case typeSingle:
167 		//printk("single.\n");
168 		if (float32_is_nan(fpa11->fpreg[Fn].fSingle))
169 			goto unordered;
170 		rFn = float32_to_floatx80(fpa11->fpreg[Fn].fSingle);
171 		break;
172 
173 	case typeDouble:
174 		//printk("double.\n");
175 		if (float64_is_nan(fpa11->fpreg[Fn].fDouble))
176 			goto unordered;
177 		rFn = float64_to_floatx80(fpa11->fpreg[Fn].fDouble);
178 		break;
179 
180 	case typeExtended:
181 		//printk("extended.\n");
182 		if (floatx80_is_nan(fpa11->fpreg[Fn].fExtended))
183 			goto unordered;
184 		rFn = fpa11->fpreg[Fn].fExtended;
185 		break;
186 
187 	default:
188 		return 0;
189 	}
190 
191 	if (CONSTANT_FM(opcode)) {
192 		//printk("Fm is a constant: #%d.\n",Fm);
193 		rFm = getExtendedConstant(Fm);
194 		if (floatx80_is_nan(rFm))
195 			goto unordered;
196 	} else {
197 		//printk("Fm = r%d which contains a ",Fm);
198 		switch (fpa11->fType[Fm]) {
199 		case typeSingle:
200 			//printk("single.\n");
201 			if (float32_is_nan(fpa11->fpreg[Fm].fSingle))
202 				goto unordered;
203 			rFm = float32_to_floatx80(fpa11->fpreg[Fm].fSingle);
204 			break;
205 
206 		case typeDouble:
207 			//printk("double.\n");
208 			if (float64_is_nan(fpa11->fpreg[Fm].fDouble))
209 				goto unordered;
210 			rFm = float64_to_floatx80(fpa11->fpreg[Fm].fDouble);
211 			break;
212 
213 		case typeExtended:
214 			//printk("extended.\n");
215 			if (floatx80_is_nan(fpa11->fpreg[Fm].fExtended))
216 				goto unordered;
217 			rFm = fpa11->fpreg[Fm].fExtended;
218 			break;
219 
220 		default:
221 			return 0;
222 		}
223 	}
224 
225 	if (n_flag)
226 		rFm.high ^= 0x8000;
227 
228 	/* test for less than condition */
229 	if (floatx80_lt(rFn, rFm))
230 		flags |= CC_NEGATIVE;
231 
232 	/* test for equal condition */
233 	if (floatx80_eq(rFn, rFm))
234 		flags |= CC_ZERO;
235 
236 	/* test for greater than or equal condition */
237 	if (floatx80_lt(rFm, rFn))
238 		flags |= CC_CARRY;
239 
240 #else
241 	if (CONSTANT_FM(opcode)) {
242 		/* Fm is a constant.  Do the comparison in whatever precision
243 		   Fn happens to be stored in.  */
244 		if (fpa11->fType[Fn] == typeSingle) {
245 			float32 rFm = getSingleConstant(Fm);
246 			float32 rFn = fpa11->fpreg[Fn].fSingle;
247 
248 			if (float32_is_nan(rFn))
249 				goto unordered;
250 
251 			if (n_flag)
252 				rFm ^= 0x80000000;
253 
254 			/* test for less than condition */
255 			if (float32_lt_nocheck(rFn, rFm))
256 				flags |= CC_NEGATIVE;
257 
258 			/* test for equal condition */
259 			if (float32_eq_nocheck(rFn, rFm))
260 				flags |= CC_ZERO;
261 
262 			/* test for greater than or equal condition */
263 			if (float32_lt_nocheck(rFm, rFn))
264 				flags |= CC_CARRY;
265 		} else {
266 			float64 rFm = getDoubleConstant(Fm);
267 			float64 rFn = fpa11->fpreg[Fn].fDouble;
268 
269 			if (float64_is_nan(rFn))
270 				goto unordered;
271 
272 			if (n_flag)
273 				rFm ^= 0x8000000000000000ULL;
274 
275 			/* test for less than condition */
276 			if (float64_lt_nocheck(rFn, rFm))
277 				flags |= CC_NEGATIVE;
278 
279 			/* test for equal condition */
280 			if (float64_eq_nocheck(rFn, rFm))
281 				flags |= CC_ZERO;
282 
283 			/* test for greater than or equal condition */
284 			if (float64_lt_nocheck(rFm, rFn))
285 				flags |= CC_CARRY;
286 		}
287 	} else {
288 		/* Both operands are in registers.  */
289 		if (fpa11->fType[Fn] == typeSingle
290 		    && fpa11->fType[Fm] == typeSingle) {
291 			float32 rFm = fpa11->fpreg[Fm].fSingle;
292 			float32 rFn = fpa11->fpreg[Fn].fSingle;
293 
294 			if (float32_is_nan(rFn)
295 			    || float32_is_nan(rFm))
296 				goto unordered;
297 
298 			if (n_flag)
299 				rFm ^= 0x80000000;
300 
301 			/* test for less than condition */
302 			if (float32_lt_nocheck(rFn, rFm))
303 				flags |= CC_NEGATIVE;
304 
305 			/* test for equal condition */
306 			if (float32_eq_nocheck(rFn, rFm))
307 				flags |= CC_ZERO;
308 
309 			/* test for greater than or equal condition */
310 			if (float32_lt_nocheck(rFm, rFn))
311 				flags |= CC_CARRY;
312 		} else {
313 			/* Promote 32-bit operand to 64 bits.  */
314 			float64 rFm, rFn;
315 
316 			rFm = (fpa11->fType[Fm] == typeSingle) ?
317 			    float32_to_float64(fpa11->fpreg[Fm].fSingle)
318 			    : fpa11->fpreg[Fm].fDouble;
319 
320 			rFn = (fpa11->fType[Fn] == typeSingle) ?
321 			    float32_to_float64(fpa11->fpreg[Fn].fSingle)
322 			    : fpa11->fpreg[Fn].fDouble;
323 
324 			if (float64_is_nan(rFn)
325 			    || float64_is_nan(rFm))
326 				goto unordered;
327 
328 			if (n_flag)
329 				rFm ^= 0x8000000000000000ULL;
330 
331 			/* test for less than condition */
332 			if (float64_lt_nocheck(rFn, rFm))
333 				flags |= CC_NEGATIVE;
334 
335 			/* test for equal condition */
336 			if (float64_eq_nocheck(rFn, rFm))
337 				flags |= CC_ZERO;
338 
339 			/* test for greater than or equal condition */
340 			if (float64_lt_nocheck(rFm, rFn))
341 				flags |= CC_CARRY;
342 		}
343 	}
344 
345 #endif
346 
347 	writeConditionCodes(flags);
348 
349 	return 1;
350 
351       unordered:
352 	/* ?? The FPA data sheet is pretty vague about this, in particular
353 	   about whether the non-E comparisons can ever raise exceptions.
354 	   This implementation is based on a combination of what it says in
355 	   the data sheet, observation of how the Acorn emulator actually
356 	   behaves (and how programs expect it to) and guesswork.  */
357 	flags |= CC_OVERFLOW;
358 	flags &= ~(CC_ZERO | CC_NEGATIVE);
359 
360 	if (BIT_AC & readFPSR())
361 		flags |= CC_CARRY;
362 
363 	if (e_flag)
364 		float_raise(float_flag_invalid);
365 
366 	writeConditionCodes(flags);
367 	return 1;
368 }
369