1 | // SPDX-License-Identifier: GPL-2.0-or-later |
2 | /* |
3 | NetWinder Floating Point Emulator |
4 | (c) Rebel.com, 1998-1999 |
5 | (c) Philip Blundell, 1998, 2001 |
6 | |
7 | Direct questions, comments to Scott Bambrough <scottb@netwinder.org> |
8 | |
9 | */ |
10 | |
11 | #include "fpa11.h" |
12 | #include "softfloat.h" |
13 | #include "fpopcode.h" |
14 | #include "fpmodule.h" |
15 | #include "fpmodule.inl" |
16 | |
17 | #include <linux/uaccess.h> |
18 | |
19 | static inline void loadSingle(const unsigned int Fn, const unsigned int __user *pMem) |
20 | { |
21 | FPA11 *fpa11 = GET_FPA11(); |
22 | fpa11->fType[Fn] = typeSingle; |
23 | get_user(fpa11->fpreg[Fn].fSingle, pMem); |
24 | } |
25 | |
26 | static inline void loadDouble(const unsigned int Fn, const unsigned int __user *pMem) |
27 | { |
28 | FPA11 *fpa11 = GET_FPA11(); |
29 | unsigned int *p; |
30 | p = (unsigned int *) &fpa11->fpreg[Fn].fDouble; |
31 | fpa11->fType[Fn] = typeDouble; |
32 | #ifdef __ARMEB__ |
33 | get_user(p[0], &pMem[0]); /* sign & exponent */ |
34 | get_user(p[1], &pMem[1]); |
35 | #else |
36 | get_user(p[0], &pMem[1]); |
37 | get_user(p[1], &pMem[0]); /* sign & exponent */ |
38 | #endif |
39 | } |
40 | |
41 | #ifdef CONFIG_FPE_NWFPE_XP |
42 | static inline void loadExtended(const unsigned int Fn, const unsigned int __user *pMem) |
43 | { |
44 | FPA11 *fpa11 = GET_FPA11(); |
45 | unsigned int *p; |
46 | p = (unsigned int *) &fpa11->fpreg[Fn].fExtended; |
47 | fpa11->fType[Fn] = typeExtended; |
48 | get_user(p[0], &pMem[0]); /* sign & exponent */ |
49 | #ifdef __ARMEB__ |
50 | get_user(p[1], &pMem[1]); /* ms bits */ |
51 | get_user(p[2], &pMem[2]); /* ls bits */ |
52 | #else |
53 | get_user(p[1], &pMem[2]); /* ls bits */ |
54 | get_user(p[2], &pMem[1]); /* ms bits */ |
55 | #endif |
56 | } |
57 | #endif |
58 | |
59 | static inline void loadMultiple(const unsigned int Fn, const unsigned int __user *pMem) |
60 | { |
61 | FPA11 *fpa11 = GET_FPA11(); |
62 | register unsigned int *p; |
63 | unsigned long x; |
64 | |
65 | p = (unsigned int *) &(fpa11->fpreg[Fn]); |
66 | get_user(x, &pMem[0]); |
67 | fpa11->fType[Fn] = (x >> 14) & 0x00000003; |
68 | |
69 | switch (fpa11->fType[Fn]) { |
70 | case typeSingle: |
71 | case typeDouble: |
72 | { |
73 | get_user(p[0], &pMem[2]); /* Single */ |
74 | get_user(p[1], &pMem[1]); /* double msw */ |
75 | p[2] = 0; /* empty */ |
76 | } |
77 | break; |
78 | |
79 | #ifdef CONFIG_FPE_NWFPE_XP |
80 | case typeExtended: |
81 | { |
82 | get_user(p[1], &pMem[2]); |
83 | get_user(p[2], &pMem[1]); /* msw */ |
84 | p[0] = (x & 0x80003fff); |
85 | } |
86 | break; |
87 | #endif |
88 | } |
89 | } |
90 | |
91 | static inline void storeSingle(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) |
92 | { |
93 | FPA11 *fpa11 = GET_FPA11(); |
94 | union { |
95 | float32 f; |
96 | unsigned int i[1]; |
97 | } val; |
98 | |
99 | switch (fpa11->fType[Fn]) { |
100 | case typeDouble: |
101 | val.f = float64_to_float32(roundData, fpa11->fpreg[Fn].fDouble); |
102 | break; |
103 | |
104 | #ifdef CONFIG_FPE_NWFPE_XP |
105 | case typeExtended: |
106 | val.f = floatx80_to_float32(roundData, fpa11->fpreg[Fn].fExtended); |
107 | break; |
108 | #endif |
109 | |
110 | default: |
111 | val.f = fpa11->fpreg[Fn].fSingle; |
112 | } |
113 | |
114 | put_user(val.i[0], pMem); |
115 | } |
116 | |
117 | static inline void storeDouble(struct roundingData *roundData, const unsigned int Fn, unsigned int __user *pMem) |
118 | { |
119 | FPA11 *fpa11 = GET_FPA11(); |
120 | union { |
121 | float64 f; |
122 | unsigned int i[2]; |
123 | } val; |
124 | |
125 | switch (fpa11->fType[Fn]) { |
126 | case typeSingle: |
127 | val.f = float32_to_float64(fpa11->fpreg[Fn].fSingle); |
128 | break; |
129 | |
130 | #ifdef CONFIG_FPE_NWFPE_XP |
131 | case typeExtended: |
132 | val.f = floatx80_to_float64(roundData, fpa11->fpreg[Fn].fExtended); |
133 | break; |
134 | #endif |
135 | |
136 | default: |
137 | val.f = fpa11->fpreg[Fn].fDouble; |
138 | } |
139 | |
140 | #ifdef __ARMEB__ |
141 | put_user(val.i[0], &pMem[0]); /* msw */ |
142 | put_user(val.i[1], &pMem[1]); /* lsw */ |
143 | #else |
144 | put_user(val.i[1], &pMem[0]); /* msw */ |
145 | put_user(val.i[0], &pMem[1]); /* lsw */ |
146 | #endif |
147 | } |
148 | |
149 | #ifdef CONFIG_FPE_NWFPE_XP |
150 | static inline void storeExtended(const unsigned int Fn, unsigned int __user *pMem) |
151 | { |
152 | FPA11 *fpa11 = GET_FPA11(); |
153 | union { |
154 | floatx80 f; |
155 | unsigned int i[3]; |
156 | } val; |
157 | |
158 | switch (fpa11->fType[Fn]) { |
159 | case typeSingle: |
160 | val.f = float32_to_floatx80(fpa11->fpreg[Fn].fSingle); |
161 | break; |
162 | |
163 | case typeDouble: |
164 | val.f = float64_to_floatx80(fpa11->fpreg[Fn].fDouble); |
165 | break; |
166 | |
167 | default: |
168 | val.f = fpa11->fpreg[Fn].fExtended; |
169 | } |
170 | |
171 | put_user(val.i[0], &pMem[0]); /* sign & exp */ |
172 | #ifdef __ARMEB__ |
173 | put_user(val.i[1], &pMem[1]); /* msw */ |
174 | put_user(val.i[2], &pMem[2]); |
175 | #else |
176 | put_user(val.i[1], &pMem[2]); |
177 | put_user(val.i[2], &pMem[1]); /* msw */ |
178 | #endif |
179 | } |
180 | #endif |
181 | |
182 | static inline void storeMultiple(const unsigned int Fn, unsigned int __user *pMem) |
183 | { |
184 | FPA11 *fpa11 = GET_FPA11(); |
185 | register unsigned int nType, *p; |
186 | |
187 | p = (unsigned int *) &(fpa11->fpreg[Fn]); |
188 | nType = fpa11->fType[Fn]; |
189 | |
190 | switch (nType) { |
191 | case typeSingle: |
192 | case typeDouble: |
193 | { |
194 | put_user(p[0], &pMem[2]); /* single */ |
195 | put_user(p[1], &pMem[1]); /* double msw */ |
196 | put_user(nType << 14, &pMem[0]); |
197 | } |
198 | break; |
199 | |
200 | #ifdef CONFIG_FPE_NWFPE_XP |
201 | case typeExtended: |
202 | { |
203 | put_user(p[2], &pMem[1]); /* msw */ |
204 | put_user(p[1], &pMem[2]); |
205 | put_user((p[0] & 0x80003fff) | (nType << 14), &pMem[0]); |
206 | } |
207 | break; |
208 | #endif |
209 | } |
210 | } |
211 | |
212 | unsigned int PerformLDF(const unsigned int opcode) |
213 | { |
214 | unsigned int __user *pBase, *pAddress, *pFinal; |
215 | unsigned int nRc = 1, write_back = WRITE_BACK(opcode); |
216 | |
217 | pBase = (unsigned int __user *) readRegister(getRn(opcode)); |
218 | if (REG_PC == getRn(opcode)) { |
219 | pBase += 2; |
220 | write_back = 0; |
221 | } |
222 | |
223 | pFinal = pBase; |
224 | if (BIT_UP_SET(opcode)) |
225 | pFinal += getOffset(opcode); |
226 | else |
227 | pFinal -= getOffset(opcode); |
228 | |
229 | if (PREINDEXED(opcode)) |
230 | pAddress = pFinal; |
231 | else |
232 | pAddress = pBase; |
233 | |
234 | switch (opcode & MASK_TRANSFER_LENGTH) { |
235 | case TRANSFER_SINGLE: |
236 | loadSingle(getFd(opcode), pMem: pAddress); |
237 | break; |
238 | case TRANSFER_DOUBLE: |
239 | loadDouble(getFd(opcode), pMem: pAddress); |
240 | break; |
241 | #ifdef CONFIG_FPE_NWFPE_XP |
242 | case TRANSFER_EXTENDED: |
243 | loadExtended(getFd(opcode), pAddress); |
244 | break; |
245 | #endif |
246 | default: |
247 | nRc = 0; |
248 | } |
249 | |
250 | if (write_back) |
251 | writeRegister(getRn(opcode), val: (unsigned long) pFinal); |
252 | return nRc; |
253 | } |
254 | |
255 | unsigned int PerformSTF(const unsigned int opcode) |
256 | { |
257 | unsigned int __user *pBase, *pAddress, *pFinal; |
258 | unsigned int nRc = 1, write_back = WRITE_BACK(opcode); |
259 | struct roundingData roundData; |
260 | |
261 | roundData.mode = SetRoundingMode(opcode); |
262 | roundData.precision = SetRoundingPrecision(opcode); |
263 | roundData.exception = 0; |
264 | |
265 | pBase = (unsigned int __user *) readRegister(getRn(opcode)); |
266 | if (REG_PC == getRn(opcode)) { |
267 | pBase += 2; |
268 | write_back = 0; |
269 | } |
270 | |
271 | pFinal = pBase; |
272 | if (BIT_UP_SET(opcode)) |
273 | pFinal += getOffset(opcode); |
274 | else |
275 | pFinal -= getOffset(opcode); |
276 | |
277 | if (PREINDEXED(opcode)) |
278 | pAddress = pFinal; |
279 | else |
280 | pAddress = pBase; |
281 | |
282 | switch (opcode & MASK_TRANSFER_LENGTH) { |
283 | case TRANSFER_SINGLE: |
284 | storeSingle(roundData: &roundData, getFd(opcode), pMem: pAddress); |
285 | break; |
286 | case TRANSFER_DOUBLE: |
287 | storeDouble(roundData: &roundData, getFd(opcode), pMem: pAddress); |
288 | break; |
289 | #ifdef CONFIG_FPE_NWFPE_XP |
290 | case TRANSFER_EXTENDED: |
291 | storeExtended(getFd(opcode), pAddress); |
292 | break; |
293 | #endif |
294 | default: |
295 | nRc = 0; |
296 | } |
297 | |
298 | if (roundData.exception) |
299 | float_raise(roundData.exception); |
300 | |
301 | if (write_back) |
302 | writeRegister(getRn(opcode), val: (unsigned long) pFinal); |
303 | return nRc; |
304 | } |
305 | |
306 | unsigned int PerformLFM(const unsigned int opcode) |
307 | { |
308 | unsigned int __user *pBase, *pAddress, *pFinal; |
309 | unsigned int i, Fd, write_back = WRITE_BACK(opcode); |
310 | |
311 | pBase = (unsigned int __user *) readRegister(getRn(opcode)); |
312 | if (REG_PC == getRn(opcode)) { |
313 | pBase += 2; |
314 | write_back = 0; |
315 | } |
316 | |
317 | pFinal = pBase; |
318 | if (BIT_UP_SET(opcode)) |
319 | pFinal += getOffset(opcode); |
320 | else |
321 | pFinal -= getOffset(opcode); |
322 | |
323 | if (PREINDEXED(opcode)) |
324 | pAddress = pFinal; |
325 | else |
326 | pAddress = pBase; |
327 | |
328 | Fd = getFd(opcode); |
329 | for (i = getRegisterCount(opcode); i > 0; i--) { |
330 | loadMultiple(Fn: Fd, pMem: pAddress); |
331 | pAddress += 3; |
332 | Fd++; |
333 | if (Fd == 8) |
334 | Fd = 0; |
335 | } |
336 | |
337 | if (write_back) |
338 | writeRegister(getRn(opcode), val: (unsigned long) pFinal); |
339 | return 1; |
340 | } |
341 | |
342 | unsigned int PerformSFM(const unsigned int opcode) |
343 | { |
344 | unsigned int __user *pBase, *pAddress, *pFinal; |
345 | unsigned int i, Fd, write_back = WRITE_BACK(opcode); |
346 | |
347 | pBase = (unsigned int __user *) readRegister(getRn(opcode)); |
348 | if (REG_PC == getRn(opcode)) { |
349 | pBase += 2; |
350 | write_back = 0; |
351 | } |
352 | |
353 | pFinal = pBase; |
354 | if (BIT_UP_SET(opcode)) |
355 | pFinal += getOffset(opcode); |
356 | else |
357 | pFinal -= getOffset(opcode); |
358 | |
359 | if (PREINDEXED(opcode)) |
360 | pAddress = pFinal; |
361 | else |
362 | pAddress = pBase; |
363 | |
364 | Fd = getFd(opcode); |
365 | for (i = getRegisterCount(opcode); i > 0; i--) { |
366 | storeMultiple(Fn: Fd, pMem: pAddress); |
367 | pAddress += 3; |
368 | Fd++; |
369 | if (Fd == 8) |
370 | Fd = 0; |
371 | } |
372 | |
373 | if (write_back) |
374 | writeRegister(getRn(opcode), val: (unsigned long) pFinal); |
375 | return 1; |
376 | } |
377 | |
378 | unsigned int EmulateCPDT(const unsigned int opcode) |
379 | { |
380 | unsigned int nRc = 0; |
381 | |
382 | if (LDF_OP(opcode)) { |
383 | nRc = PerformLDF(opcode); |
384 | } else if (LFM_OP(opcode)) { |
385 | nRc = PerformLFM(opcode); |
386 | } else if (STF_OP(opcode)) { |
387 | nRc = PerformSTF(opcode); |
388 | } else if (SFM_OP(opcode)) { |
389 | nRc = PerformSFM(opcode); |
390 | } else { |
391 | nRc = 0; |
392 | } |
393 | |
394 | return nRc; |
395 | } |
396 | |