1  /*
2   * UAE - The Un*x Amiga Emulator
3   *
4   * MC68881 emulation
5   *
6   * Conversion routines for hosts knowing floating point format.
7   *
8   * Copyright 1996 Herman ten Brugge
9   * Modified 2005 Peter Keunecke
10   */
11 
12 #include <math.h>
13 
14 #define	FPCR_ROUNDING_MODE	0x00000030
15 #define	FPCR_ROUND_NEAR		0x00000000
16 #define	FPCR_ROUND_ZERO		0x00000010
17 #define	FPCR_ROUND_MINF		0x00000020
18 #define	FPCR_ROUND_PINF		0x00000030
19 
20 #define	FPCR_ROUNDING_PRECISION	0x000000c0
21 #define	FPCR_PRECISION_SINGLE	0x00000040
22 #define	FPCR_PRECISION_DOUBLE	0x00000080
23 #define FPCR_PRECISION_EXTENDED	0x00000000
24 
25 extern void to_single(fpdata *fpd, uae_u32 value);
26 extern void to_double(fpdata *fpd, uae_u32 wrd1, uae_u32 wrd2);
27 extern void to_exten(fpdata *fpd, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3);
28 extern const TCHAR *fp_print(fpdata *fpd);
29 
30 #if 0
31 STATIC_INLINE void exten_zeronormalize(uae_u32 *pwrd1, uae_u32 *pwrd2, uae_u32 *pwrd3)
32 {
33 	uae_u32 wrd1 = *pwrd1;
34 	uae_u32 wrd2 = *pwrd2;
35 	uae_u32 wrd3 = *pwrd3;
36 	int exp = (wrd1 >> 16) & 0x7fff;
37 	// Force zero if mantissa is zero but exponent is non-zero
38 	// M68k FPU automatically convert them to plain zeros.
39 	// x86 FPU considers them invalid values
40 	if (exp != 0 && exp != 0x7fff && !wrd2 && !wrd3) {
41 		*pwrd1 = (wrd1 & 0x80000000);
42 	}
43 }
44 
45 #if USE_LONG_DOUBLE
46 STATIC_INLINE void to_exten_x(fptype *fp, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3)
47 {
48 	// force correct long double alignment
49 	union
50 	{
51 		long double lf;
52 		uae_u32 longarray[3];
53 	} uld;
54 	exten_zeronormalize(&wrd1, &wrd2, &wrd3);
55 	// little endian order
56 	uld.longarray[0] = wrd3;
57 	uld.longarray[1] = wrd2;
58 	uld.longarray[2] = wrd1 >> 16;
59 	long double *longdoublewords = (long double *)uld.longarray;
60 	*fp = *longdoublewords;
61 }
62 #define HAVE_to_exten
63 
64 STATIC_INLINE void from_exten_x(fptype fp, uae_u32 * wrd1, uae_u32 * wrd2, uae_u32 * wrd3)
65 {
66 	uae_u32 *longarray = (uae_u32 *)&fp;
67 	uae_u16 *finalword = (uae_u16 *)(((uae_u8*)&fp) + 8);
68 
69 	*wrd1 = finalword[0] << 16;
70 	*wrd2 = longarray[1];
71 	*wrd3 = longarray[0]; // little endian
72 }
73 #define HAVE_from_exten
74 #endif /* USE_LONG_DOUBLE */
75 
76 #if defined(X86_MSVC_ASSEMBLY_FPU)
77 #ifndef HAVE_to_single
78 #define HAVE_to_single
79 STATIC_INLINE double to_single_x (uae_u32 longvalue)
80 {
81 	double floatfake;
82 
83 	__asm {
84 		fld dword ptr longvalue;
85 		fstp qword ptr floatfake;
86 	}
87 	return floatfake;
88 }
89 #endif
90 
91 #ifndef HAVE_from_single
92 #define HAVE_from_single
93 STATIC_INLINE uae_u32 from_single_x (double floatfake)
94 {
95 	uae_u32 longvalue;
96 
97 	__asm {
98 		fld qword ptr floatfake;
99 		fstp dword ptr longvalue;
100 	}
101 	return longvalue;
102 }
103 #endif
104 
105 #ifndef HAVE_to_exten
106 #define HAVE_to_exten
107 STATIC_INLINE void to_exten_x(fptype *fp, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3)
108 {
109 	uae_u32 longarray[3];
110 	double  extenfake;
111 
112 	exten_normalize(&wrd1, &wrd2, &wrd3);
113 	longarray[0] = wrd3; // littlen endian
114 	longarray[1] = wrd2;
115 	longarray[2] = wrd2 >> 16;
116 
117 	__asm {
118 		fld tbyte ptr longarray;
119 		fstp qword ptr extenfake;
120 	}
121 	*fp = extenfake;
122 }
123 #endif
124 
125 #ifndef HAVE_from_exten
126 #define HAVE_from_exten
127 STATIC_INLINE void from_exten_x(fptype fp, uae_u32 * wrd1, uae_u32 * wrd2, uae_u32 * wrd3)
128 {
129 	fptype src = fp;
130 	uae_u32 longarray[3], *srcarray = (uae_u32 *)&src;
131 	__asm {
132 		fld qword ptr src;
133 		fstp tbyte ptr longarray;
134 	}
135 	*wrd1 = (longarray[2] & 0xffff) <<16;
136 	*wrd2 =  longarray[1];
137 	*wrd3 =  longarray[0]; // little endian
138 	if (!srcarray[0] && (srcarray[1] == 0x7ff00000 || srcarray[1] == 0xfff00000))
139 		*wrd2 = 0; // The MSB of the mantissa was set wrongly for infinity, causing a NaN
140 }
141 #endif
142 #endif /* X86_MSVC_ASSEMBLY */
143 
144 #ifndef HAVE_to_single
145 #define HAVE_to_single
146 STATIC_INLINE double to_single_x (uae_u32 value)
147 {
148 	union {
149 		float f;
150 		uae_u32 u;
151 	} val;
152 
153 	val.u = value;
154 	return val.f;
155 }
156 #endif
157 
158 #ifndef HAVE_from_single
159 #define HAVE_from_single
160 STATIC_INLINE uae_u32 from_single_x (double src)
161 {
162 	union {
163 		float f;
164 		uae_u32 u;
165 	} val;
166 
167 	val.f = (float) src;
168 	return val.u;
169 }
170 #endif
171 
172 #ifndef HAVE_to_double
173 #define HAVE_to_double
174 STATIC_INLINE double to_double_x(uae_u32 wrd1, uae_u32 wrd2)
175 {
176 	union {
177 		double d;
178 		uae_u32 u[2];
179 	} val;
180 
181 #ifdef WORDS_BIGENDIAN
182 	val.u[0] = wrd1;
183 	val.u[1] = wrd2;
184 #else
185 	val.u[1] = wrd1;
186 	val.u[0] = wrd2;
187 #endif
188 	return val.d;
189 }
190 #endif
191 
192 #ifndef HAVE_from_double
193 #define HAVE_from_double
194 STATIC_INLINE void from_double_x(double src, uae_u32 * wrd1, uae_u32 * wrd2)
195 {
196 	uae_u32 *longarray = (uae_u32 *)&src;
197 
198 	*wrd1 = longarray[1]; // little endian
199 	*wrd2 = longarray[0];
200 }
201 #endif
202 
203 #ifndef HAVE_to_exten
204 #define HAVE_to_exten
205 STATIC_INLINE void to_exten_x(fptype *fp, uae_u32 wrd1, uae_u32 wrd2, uae_u32 wrd3)
206 {
207 	double frac;
208 	exten_zeronormalize(&wrd1, &wrd2, &wrd3);
209 	if ((wrd1 & 0x7fff0000) == 0 && wrd2 == 0 && wrd3 == 0) {
210 		*fp = (wrd1 & 0x80000000) ? -0.0 : +0.0;
211 		return;
212 	}
213 	frac = ((double)wrd2 + ((double)wrd3 / twoto32)) / 2147483648.0;
214 	if (wrd1 & 0x80000000)
215 		frac = -frac;
216 	*fp = ldexp (frac, ((wrd1 >> 16) & 0x7fff) - 16383);
217 }
218 #endif
219 
220 #ifndef HAVE_from_exten
221 #define HAVE_from_exten
222 STATIC_INLINE void from_exten_x(fptype fp, uae_u32 * wrd1, uae_u32 * wrd2, uae_u32 * wrd3)
223 {
224 	int expon;
225 	double frac;
226 	fptype v;
227 
228 	v = fp;
229 	if (v == 0.0) {
230 		*wrd1 = signbit(v) ? 0x80000000 : 0;
231 		*wrd2 = 0;
232 		*wrd3 = 0;
233 		return;
234 	}
235 	if (v < 0) {
236 		*wrd1 = 0x80000000;
237 		v = -v;
238 	} else {
239 		*wrd1 = 0;
240 	}
241 	frac = frexp (v, &expon);
242 	frac += 0.5 / (twoto32 * twoto32);
243 	if (frac >= 1.0) {
244 		frac /= 2.0;
245 		expon++;
246 	}
247 	*wrd1 |= (((expon + 16383 - 1) & 0x7fff) << 16);
248 	*wrd2 = (uae_u32) (frac * twoto32);
249 	*wrd3 = (uae_u32) ((frac * twoto32 - *wrd2) * twoto32);
250 }
251 #endif
252 #endif
253