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