]>
Commit | Line | Data |
---|---|---|
3ec9c4fc FB |
1 | /* |
2 | * i386 helpers | |
3 | * | |
4 | * Copyright (c) 2003 Fabrice Bellard | |
5 | * | |
6 | * This library is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU Lesser General Public | |
8 | * License as published by the Free Software Foundation; either | |
9 | * version 2 of the License, or (at your option) any later version. | |
10 | * | |
11 | * This library is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * Lesser General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU Lesser General Public | |
17 | * License along with this library; if not, write to the Free Software | |
18 | * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
19 | */ | |
20 | #include "exec-i386.h" | |
21 | ||
1e5ffbed FB |
22 | const uint8_t parity_table[256] = { |
23 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
24 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
25 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
26 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
27 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
28 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
29 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
30 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
31 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
32 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
33 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
34 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
35 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
36 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
37 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
38 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
39 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
40 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
41 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
42 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
43 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
44 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
45 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
46 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
47 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
48 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
49 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
50 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
51 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
52 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
53 | CC_P, 0, 0, CC_P, 0, CC_P, CC_P, 0, | |
54 | 0, CC_P, CC_P, 0, CC_P, 0, 0, CC_P, | |
55 | }; | |
56 | ||
57 | /* modulo 17 table */ | |
58 | const uint8_t rclw_table[32] = { | |
59 | 0, 1, 2, 3, 4, 5, 6, 7, | |
60 | 8, 9,10,11,12,13,14,15, | |
61 | 16, 0, 1, 2, 3, 4, 5, 6, | |
62 | 7, 8, 9,10,11,12,13,14, | |
63 | }; | |
64 | ||
65 | /* modulo 9 table */ | |
66 | const uint8_t rclb_table[32] = { | |
67 | 0, 1, 2, 3, 4, 5, 6, 7, | |
68 | 8, 0, 1, 2, 3, 4, 5, 6, | |
69 | 7, 8, 0, 1, 2, 3, 4, 5, | |
70 | 6, 7, 8, 0, 1, 2, 3, 4, | |
71 | }; | |
72 | ||
2d0e9143 FB |
73 | const CPU86_LDouble f15rk[7] = |
74 | { | |
75 | 0.00000000000000000000L, | |
76 | 1.00000000000000000000L, | |
77 | 3.14159265358979323851L, /*pi*/ | |
78 | 0.30102999566398119523L, /*lg2*/ | |
79 | 0.69314718055994530943L, /*ln2*/ | |
80 | 1.44269504088896340739L, /*l2e*/ | |
81 | 3.32192809488736234781L, /*l2t*/ | |
82 | }; | |
83 | ||
84 | /* thread support */ | |
85 | ||
86 | spinlock_t global_cpu_lock = SPIN_LOCK_UNLOCKED; | |
87 | ||
88 | void cpu_lock(void) | |
89 | { | |
90 | spin_lock(&global_cpu_lock); | |
91 | } | |
92 | ||
93 | void cpu_unlock(void) | |
94 | { | |
95 | spin_unlock(&global_cpu_lock); | |
96 | } | |
97 | ||
98 | void cpu_loop_exit(void) | |
99 | { | |
100 | /* NOTE: the register at this point must be saved by hand because | |
101 | longjmp restore them */ | |
102 | #ifdef reg_EAX | |
103 | env->regs[R_EAX] = EAX; | |
104 | #endif | |
105 | #ifdef reg_ECX | |
106 | env->regs[R_ECX] = ECX; | |
107 | #endif | |
108 | #ifdef reg_EDX | |
109 | env->regs[R_EDX] = EDX; | |
110 | #endif | |
111 | #ifdef reg_EBX | |
112 | env->regs[R_EBX] = EBX; | |
113 | #endif | |
114 | #ifdef reg_ESP | |
115 | env->regs[R_ESP] = ESP; | |
116 | #endif | |
117 | #ifdef reg_EBP | |
118 | env->regs[R_EBP] = EBP; | |
119 | #endif | |
120 | #ifdef reg_ESI | |
121 | env->regs[R_ESI] = ESI; | |
122 | #endif | |
123 | #ifdef reg_EDI | |
124 | env->regs[R_EDI] = EDI; | |
125 | #endif | |
126 | longjmp(env->jmp_env, 1); | |
127 | } | |
128 | ||
3ec9c4fc FB |
129 | #if 0 |
130 | /* full interrupt support (only useful for real CPU emulation, not | |
131 | finished) - I won't do it any time soon, finish it if you want ! */ | |
132 | void raise_interrupt(int intno, int is_int, int error_code, | |
133 | unsigned int next_eip) | |
134 | { | |
135 | SegmentDescriptorTable *dt; | |
136 | uint8_t *ptr; | |
137 | int type, dpl, cpl; | |
138 | uint32_t e1, e2; | |
139 | ||
140 | dt = &env->idt; | |
141 | if (intno * 8 + 7 > dt->limit) | |
142 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | |
143 | ptr = dt->base + intno * 8; | |
144 | e1 = ldl(ptr); | |
145 | e2 = ldl(ptr + 4); | |
146 | /* check gate type */ | |
147 | type = (e2 >> DESC_TYPE_SHIFT) & 0x1f; | |
148 | switch(type) { | |
149 | case 5: /* task gate */ | |
150 | case 6: /* 286 interrupt gate */ | |
151 | case 7: /* 286 trap gate */ | |
152 | case 14: /* 386 interrupt gate */ | |
153 | case 15: /* 386 trap gate */ | |
154 | break; | |
155 | default: | |
156 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | |
157 | break; | |
158 | } | |
159 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | |
160 | cpl = env->segs[R_CS] & 3; | |
161 | /* check privledge if software int */ | |
162 | if (is_int && dpl < cpl) | |
163 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | |
164 | /* check valid bit */ | |
165 | if (!(e2 & DESC_P_MASK)) | |
166 | raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2); | |
167 | } | |
168 | ||
169 | #else | |
170 | ||
171 | /* | |
172 | * is_int is TRUE if coming from the int instruction. next_eip is the | |
173 | * EIP value AFTER the interrupt instruction. It is only relevant if | |
174 | * is_int is TRUE. | |
175 | */ | |
176 | void raise_interrupt(int intno, int is_int, int error_code, | |
177 | unsigned int next_eip) | |
178 | { | |
179 | SegmentDescriptorTable *dt; | |
180 | uint8_t *ptr; | |
181 | int dpl, cpl; | |
182 | uint32_t e2; | |
183 | ||
184 | dt = &env->idt; | |
185 | ptr = dt->base + (intno * 8); | |
186 | e2 = ldl(ptr + 4); | |
187 | ||
188 | dpl = (e2 >> DESC_DPL_SHIFT) & 3; | |
189 | cpl = 3; | |
190 | /* check privledge if software int */ | |
191 | if (is_int && dpl < cpl) | |
192 | raise_exception_err(EXCP0D_GPF, intno * 8 + 2); | |
193 | ||
194 | /* Since we emulate only user space, we cannot do more than | |
195 | exiting the emulation with the suitable exception and error | |
196 | code */ | |
197 | if (is_int) | |
198 | EIP = next_eip; | |
199 | env->exception_index = intno; | |
200 | env->error_code = error_code; | |
201 | ||
202 | cpu_loop_exit(); | |
203 | } | |
204 | ||
205 | #endif | |
206 | ||
207 | /* shortcuts to generate exceptions */ | |
208 | void raise_exception_err(int exception_index, int error_code) | |
209 | { | |
210 | raise_interrupt(exception_index, 0, error_code, 0); | |
211 | } | |
212 | ||
213 | void raise_exception(int exception_index) | |
214 | { | |
215 | raise_interrupt(exception_index, 0, 0, 0); | |
216 | } | |
217 | ||
2d0e9143 FB |
218 | #ifdef BUGGY_GCC_DIV64 |
219 | /* gcc 2.95.4 on PowerPC does not seem to like using __udivdi3, so we | |
220 | call it from another function */ | |
221 | uint32_t div64(uint32_t *q_ptr, uint64_t num, uint32_t den) | |
222 | { | |
223 | *q_ptr = num / den; | |
224 | return num % den; | |
225 | } | |
226 | ||
227 | int32_t idiv64(int32_t *q_ptr, int64_t num, int32_t den) | |
228 | { | |
229 | *q_ptr = num / den; | |
230 | return num % den; | |
231 | } | |
232 | #endif | |
233 | ||
234 | void helper_divl_EAX_T0(uint32_t eip) | |
235 | { | |
236 | unsigned int den, q, r; | |
237 | uint64_t num; | |
238 | ||
239 | num = EAX | ((uint64_t)EDX << 32); | |
240 | den = T0; | |
241 | if (den == 0) { | |
242 | EIP = eip; | |
243 | raise_exception(EXCP00_DIVZ); | |
244 | } | |
245 | #ifdef BUGGY_GCC_DIV64 | |
246 | r = div64(&q, num, den); | |
247 | #else | |
248 | q = (num / den); | |
249 | r = (num % den); | |
250 | #endif | |
251 | EAX = q; | |
252 | EDX = r; | |
253 | } | |
254 | ||
255 | void helper_idivl_EAX_T0(uint32_t eip) | |
256 | { | |
257 | int den, q, r; | |
258 | int64_t num; | |
259 | ||
260 | num = EAX | ((uint64_t)EDX << 32); | |
261 | den = T0; | |
262 | if (den == 0) { | |
263 | EIP = eip; | |
264 | raise_exception(EXCP00_DIVZ); | |
265 | } | |
266 | #ifdef BUGGY_GCC_DIV64 | |
267 | r = idiv64(&q, num, den); | |
268 | #else | |
269 | q = (num / den); | |
270 | r = (num % den); | |
271 | #endif | |
272 | EAX = q; | |
273 | EDX = r; | |
274 | } | |
275 | ||
276 | void helper_cmpxchg8b(void) | |
277 | { | |
278 | uint64_t d; | |
279 | int eflags; | |
280 | ||
281 | eflags = cc_table[CC_OP].compute_all(); | |
282 | d = ldq((uint8_t *)A0); | |
283 | if (d == (((uint64_t)EDX << 32) | EAX)) { | |
284 | stq((uint8_t *)A0, ((uint64_t)ECX << 32) | EBX); | |
285 | eflags |= CC_Z; | |
286 | } else { | |
287 | EDX = d >> 32; | |
288 | EAX = d; | |
289 | eflags &= ~CC_Z; | |
290 | } | |
291 | CC_SRC = eflags; | |
292 | } | |
293 | ||
3ec9c4fc FB |
294 | /* We simulate a pre-MMX pentium as in valgrind */ |
295 | #define CPUID_FP87 (1 << 0) | |
296 | #define CPUID_VME (1 << 1) | |
297 | #define CPUID_DE (1 << 2) | |
298 | #define CPUID_PSE (1 << 3) | |
299 | #define CPUID_TSC (1 << 4) | |
300 | #define CPUID_MSR (1 << 5) | |
301 | #define CPUID_PAE (1 << 6) | |
302 | #define CPUID_MCE (1 << 7) | |
303 | #define CPUID_CX8 (1 << 8) | |
304 | #define CPUID_APIC (1 << 9) | |
305 | #define CPUID_SEP (1 << 11) /* sysenter/sysexit */ | |
306 | #define CPUID_MTRR (1 << 12) | |
307 | #define CPUID_PGE (1 << 13) | |
308 | #define CPUID_MCA (1 << 14) | |
309 | #define CPUID_CMOV (1 << 15) | |
310 | /* ... */ | |
311 | #define CPUID_MMX (1 << 23) | |
312 | #define CPUID_FXSR (1 << 24) | |
313 | #define CPUID_SSE (1 << 25) | |
314 | #define CPUID_SSE2 (1 << 26) | |
315 | ||
316 | void helper_cpuid(void) | |
317 | { | |
318 | if (EAX == 0) { | |
319 | EAX = 1; /* max EAX index supported */ | |
320 | EBX = 0x756e6547; | |
321 | ECX = 0x6c65746e; | |
322 | EDX = 0x49656e69; | |
323 | } else if (EAX == 1) { | |
324 | /* EAX = 1 info */ | |
325 | EAX = 0x52b; | |
326 | EBX = 0; | |
327 | ECX = 0; | |
328 | EDX = CPUID_FP87 | CPUID_DE | CPUID_PSE | | |
329 | CPUID_TSC | CPUID_MSR | CPUID_MCE | | |
330 | CPUID_CX8; | |
331 | } | |
332 | } | |
333 | ||
334 | /* only works if protected mode and not VM86 */ | |
335 | void load_seg(int seg_reg, int selector, unsigned cur_eip) | |
336 | { | |
337 | SegmentCache *sc; | |
338 | SegmentDescriptorTable *dt; | |
339 | int index; | |
340 | uint32_t e1, e2; | |
341 | uint8_t *ptr; | |
342 | ||
343 | sc = &env->seg_cache[seg_reg]; | |
344 | if ((selector & 0xfffc) == 0) { | |
345 | /* null selector case */ | |
346 | if (seg_reg == R_SS) { | |
347 | EIP = cur_eip; | |
348 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | |
349 | } else { | |
350 | /* XXX: each access should trigger an exception */ | |
351 | sc->base = NULL; | |
352 | sc->limit = 0; | |
353 | sc->seg_32bit = 1; | |
354 | } | |
355 | } else { | |
356 | if (selector & 0x4) | |
357 | dt = &env->ldt; | |
358 | else | |
359 | dt = &env->gdt; | |
360 | index = selector & ~7; | |
361 | if ((index + 7) > dt->limit) { | |
362 | EIP = cur_eip; | |
363 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | |
364 | } | |
365 | ptr = dt->base + index; | |
366 | e1 = ldl(ptr); | |
367 | e2 = ldl(ptr + 4); | |
368 | if (!(e2 & DESC_S_MASK) || | |
369 | (e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { | |
370 | EIP = cur_eip; | |
371 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | |
372 | } | |
373 | ||
374 | if (seg_reg == R_SS) { | |
375 | if ((e2 & (DESC_CS_MASK | DESC_W_MASK)) == 0) { | |
376 | EIP = cur_eip; | |
377 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | |
378 | } | |
379 | } else { | |
380 | if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) { | |
381 | EIP = cur_eip; | |
382 | raise_exception_err(EXCP0D_GPF, selector & 0xfffc); | |
383 | } | |
384 | } | |
385 | ||
386 | if (!(e2 & DESC_P_MASK)) { | |
387 | EIP = cur_eip; | |
388 | if (seg_reg == R_SS) | |
389 | raise_exception_err(EXCP0C_STACK, selector & 0xfffc); | |
390 | else | |
391 | raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc); | |
392 | } | |
393 | ||
394 | sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000)); | |
395 | sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000); | |
396 | if (e2 & (1 << 23)) | |
397 | sc->limit = (sc->limit << 12) | 0xfff; | |
398 | sc->seg_32bit = (e2 >> 22) & 1; | |
399 | #if 0 | |
400 | fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx seg_32bit=%d\n", | |
401 | selector, (unsigned long)sc->base, sc->limit, sc->seg_32bit); | |
402 | #endif | |
403 | } | |
404 | env->segs[seg_reg] = selector; | |
405 | } | |
406 | ||
2d0e9143 FB |
407 | /* rdtsc */ |
408 | #ifndef __i386__ | |
409 | uint64_t emu_time; | |
410 | #endif | |
411 | ||
412 | void helper_rdtsc(void) | |
413 | { | |
414 | uint64_t val; | |
415 | #ifdef __i386__ | |
416 | asm("rdtsc" : "=A" (val)); | |
417 | #else | |
418 | /* better than nothing: the time increases */ | |
419 | val = emu_time++; | |
420 | #endif | |
421 | EAX = val; | |
422 | EDX = val >> 32; | |
423 | } | |
424 | ||
3ec9c4fc FB |
425 | void helper_lsl(void) |
426 | { | |
427 | unsigned int selector, limit; | |
428 | SegmentDescriptorTable *dt; | |
429 | int index; | |
430 | uint32_t e1, e2; | |
431 | uint8_t *ptr; | |
432 | ||
433 | CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z; | |
434 | selector = T0 & 0xffff; | |
435 | if (selector & 0x4) | |
436 | dt = &env->ldt; | |
437 | else | |
438 | dt = &env->gdt; | |
439 | index = selector & ~7; | |
440 | if ((index + 7) > dt->limit) | |
441 | return; | |
442 | ptr = dt->base + index; | |
443 | e1 = ldl(ptr); | |
444 | e2 = ldl(ptr + 4); | |
445 | limit = (e1 & 0xffff) | (e2 & 0x000f0000); | |
446 | if (e2 & (1 << 23)) | |
447 | limit = (limit << 12) | 0xfff; | |
448 | T1 = limit; | |
449 | CC_SRC |= CC_Z; | |
450 | } | |
451 | ||
452 | void helper_lar(void) | |
453 | { | |
454 | unsigned int selector; | |
455 | SegmentDescriptorTable *dt; | |
456 | int index; | |
457 | uint32_t e2; | |
458 | uint8_t *ptr; | |
459 | ||
460 | CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z; | |
461 | selector = T0 & 0xffff; | |
462 | if (selector & 0x4) | |
463 | dt = &env->ldt; | |
464 | else | |
465 | dt = &env->gdt; | |
466 | index = selector & ~7; | |
467 | if ((index + 7) > dt->limit) | |
468 | return; | |
469 | ptr = dt->base + index; | |
470 | e2 = ldl(ptr + 4); | |
471 | T1 = e2 & 0x00f0ff00; | |
472 | CC_SRC |= CC_Z; | |
473 | } | |
474 | ||
475 | /* FPU helpers */ | |
476 | ||
477 | #ifndef USE_X86LDOUBLE | |
478 | void helper_fldt_ST0_A0(void) | |
479 | { | |
480 | ST0 = helper_fldt((uint8_t *)A0); | |
481 | } | |
482 | ||
483 | void helper_fstt_ST0_A0(void) | |
484 | { | |
485 | helper_fstt(ST0, (uint8_t *)A0); | |
486 | } | |
487 | #endif | |
488 | ||
489 | /* BCD ops */ | |
490 | ||
491 | #define MUL10(iv) ( iv + iv + (iv << 3) ) | |
492 | ||
493 | void helper_fbld_ST0_A0(void) | |
494 | { | |
495 | uint8_t *seg; | |
496 | CPU86_LDouble fpsrcop; | |
497 | int m32i; | |
498 | unsigned int v; | |
499 | ||
500 | /* in this code, seg/m32i will be used as temporary ptr/int */ | |
501 | seg = (uint8_t *)A0 + 8; | |
502 | v = ldub(seg--); | |
503 | /* XXX: raise exception */ | |
504 | if (v != 0) | |
505 | return; | |
506 | v = ldub(seg--); | |
507 | /* XXX: raise exception */ | |
508 | if ((v & 0xf0) != 0) | |
509 | return; | |
510 | m32i = v; /* <-- d14 */ | |
511 | v = ldub(seg--); | |
512 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d13 */ | |
513 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d12 */ | |
514 | v = ldub(seg--); | |
515 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d11 */ | |
516 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d10 */ | |
517 | v = ldub(seg--); | |
518 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d9 */ | |
519 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d8 */ | |
520 | fpsrcop = ((CPU86_LDouble)m32i) * 100000000.0; | |
521 | ||
522 | v = ldub(seg--); | |
523 | m32i = (v >> 4); /* <-- d7 */ | |
524 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d6 */ | |
525 | v = ldub(seg--); | |
526 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d5 */ | |
527 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d4 */ | |
528 | v = ldub(seg--); | |
529 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d3 */ | |
530 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d2 */ | |
531 | v = ldub(seg); | |
532 | m32i = MUL10(m32i) + (v >> 4); /* <-- val * 10 + d1 */ | |
533 | m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d0 */ | |
534 | fpsrcop += ((CPU86_LDouble)m32i); | |
535 | if ( ldub(seg+9) & 0x80 ) | |
536 | fpsrcop = -fpsrcop; | |
537 | ST0 = fpsrcop; | |
538 | } | |
539 | ||
540 | void helper_fbst_ST0_A0(void) | |
541 | { | |
542 | CPU86_LDouble fptemp; | |
543 | CPU86_LDouble fpsrcop; | |
544 | int v; | |
545 | uint8_t *mem_ref, *mem_end; | |
546 | ||
547 | fpsrcop = rint(ST0); | |
548 | mem_ref = (uint8_t *)A0; | |
549 | mem_end = mem_ref + 8; | |
550 | if ( fpsrcop < 0.0 ) { | |
551 | stw(mem_end, 0x8000); | |
552 | fpsrcop = -fpsrcop; | |
553 | } else { | |
554 | stw(mem_end, 0x0000); | |
555 | } | |
556 | while (mem_ref < mem_end) { | |
557 | if (fpsrcop == 0.0) | |
558 | break; | |
559 | fptemp = floor(fpsrcop/10.0); | |
560 | v = ((int)(fpsrcop - fptemp*10.0)); | |
561 | if (fptemp == 0.0) { | |
562 | stb(mem_ref++, v); | |
563 | break; | |
564 | } | |
565 | fpsrcop = fptemp; | |
566 | fptemp = floor(fpsrcop/10.0); | |
567 | v |= (((int)(fpsrcop - fptemp*10.0)) << 4); | |
568 | stb(mem_ref++, v); | |
569 | fpsrcop = fptemp; | |
570 | } | |
571 | while (mem_ref < mem_end) { | |
572 | stb(mem_ref++, 0); | |
573 | } | |
574 | } | |
575 | ||
576 | void helper_f2xm1(void) | |
577 | { | |
578 | ST0 = pow(2.0,ST0) - 1.0; | |
579 | } | |
580 | ||
581 | void helper_fyl2x(void) | |
582 | { | |
583 | CPU86_LDouble fptemp; | |
584 | ||
585 | fptemp = ST0; | |
586 | if (fptemp>0.0){ | |
587 | fptemp = log(fptemp)/log(2.0); /* log2(ST) */ | |
588 | ST1 *= fptemp; | |
589 | fpop(); | |
590 | } else { | |
591 | env->fpus &= (~0x4700); | |
592 | env->fpus |= 0x400; | |
593 | } | |
594 | } | |
595 | ||
596 | void helper_fptan(void) | |
597 | { | |
598 | CPU86_LDouble fptemp; | |
599 | ||
600 | fptemp = ST0; | |
601 | if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { | |
602 | env->fpus |= 0x400; | |
603 | } else { | |
604 | ST0 = tan(fptemp); | |
605 | fpush(); | |
606 | ST0 = 1.0; | |
607 | env->fpus &= (~0x400); /* C2 <-- 0 */ | |
608 | /* the above code is for |arg| < 2**52 only */ | |
609 | } | |
610 | } | |
611 | ||
612 | void helper_fpatan(void) | |
613 | { | |
614 | CPU86_LDouble fptemp, fpsrcop; | |
615 | ||
616 | fpsrcop = ST1; | |
617 | fptemp = ST0; | |
618 | ST1 = atan2(fpsrcop,fptemp); | |
619 | fpop(); | |
620 | } | |
621 | ||
622 | void helper_fxtract(void) | |
623 | { | |
624 | CPU86_LDoubleU temp; | |
625 | unsigned int expdif; | |
626 | ||
627 | temp.d = ST0; | |
628 | expdif = EXPD(temp) - EXPBIAS; | |
629 | /*DP exponent bias*/ | |
630 | ST0 = expdif; | |
631 | fpush(); | |
632 | BIASEXPONENT(temp); | |
633 | ST0 = temp.d; | |
634 | } | |
635 | ||
636 | void helper_fprem1(void) | |
637 | { | |
638 | CPU86_LDouble dblq, fpsrcop, fptemp; | |
639 | CPU86_LDoubleU fpsrcop1, fptemp1; | |
640 | int expdif; | |
641 | int q; | |
642 | ||
643 | fpsrcop = ST0; | |
644 | fptemp = ST1; | |
645 | fpsrcop1.d = fpsrcop; | |
646 | fptemp1.d = fptemp; | |
647 | expdif = EXPD(fpsrcop1) - EXPD(fptemp1); | |
648 | if (expdif < 53) { | |
649 | dblq = fpsrcop / fptemp; | |
650 | dblq = (dblq < 0.0)? ceil(dblq): floor(dblq); | |
651 | ST0 = fpsrcop - fptemp*dblq; | |
652 | q = (int)dblq; /* cutting off top bits is assumed here */ | |
653 | env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ | |
654 | /* (C0,C1,C3) <-- (q2,q1,q0) */ | |
655 | env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */ | |
656 | env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */ | |
657 | env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */ | |
658 | } else { | |
659 | env->fpus |= 0x400; /* C2 <-- 1 */ | |
660 | fptemp = pow(2.0, expdif-50); | |
661 | fpsrcop = (ST0 / ST1) / fptemp; | |
662 | /* fpsrcop = integer obtained by rounding to the nearest */ | |
663 | fpsrcop = (fpsrcop-floor(fpsrcop) < ceil(fpsrcop)-fpsrcop)? | |
664 | floor(fpsrcop): ceil(fpsrcop); | |
665 | ST0 -= (ST1 * fpsrcop * fptemp); | |
666 | } | |
667 | } | |
668 | ||
669 | void helper_fprem(void) | |
670 | { | |
671 | CPU86_LDouble dblq, fpsrcop, fptemp; | |
672 | CPU86_LDoubleU fpsrcop1, fptemp1; | |
673 | int expdif; | |
674 | int q; | |
675 | ||
676 | fpsrcop = ST0; | |
677 | fptemp = ST1; | |
678 | fpsrcop1.d = fpsrcop; | |
679 | fptemp1.d = fptemp; | |
680 | expdif = EXPD(fpsrcop1) - EXPD(fptemp1); | |
681 | if ( expdif < 53 ) { | |
682 | dblq = fpsrcop / fptemp; | |
683 | dblq = (dblq < 0.0)? ceil(dblq): floor(dblq); | |
684 | ST0 = fpsrcop - fptemp*dblq; | |
685 | q = (int)dblq; /* cutting off top bits is assumed here */ | |
686 | env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ | |
687 | /* (C0,C1,C3) <-- (q2,q1,q0) */ | |
688 | env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */ | |
689 | env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */ | |
690 | env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */ | |
691 | } else { | |
692 | env->fpus |= 0x400; /* C2 <-- 1 */ | |
693 | fptemp = pow(2.0, expdif-50); | |
694 | fpsrcop = (ST0 / ST1) / fptemp; | |
695 | /* fpsrcop = integer obtained by chopping */ | |
696 | fpsrcop = (fpsrcop < 0.0)? | |
697 | -(floor(fabs(fpsrcop))): floor(fpsrcop); | |
698 | ST0 -= (ST1 * fpsrcop * fptemp); | |
699 | } | |
700 | } | |
701 | ||
702 | void helper_fyl2xp1(void) | |
703 | { | |
704 | CPU86_LDouble fptemp; | |
705 | ||
706 | fptemp = ST0; | |
707 | if ((fptemp+1.0)>0.0) { | |
708 | fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */ | |
709 | ST1 *= fptemp; | |
710 | fpop(); | |
711 | } else { | |
712 | env->fpus &= (~0x4700); | |
713 | env->fpus |= 0x400; | |
714 | } | |
715 | } | |
716 | ||
717 | void helper_fsqrt(void) | |
718 | { | |
719 | CPU86_LDouble fptemp; | |
720 | ||
721 | fptemp = ST0; | |
722 | if (fptemp<0.0) { | |
723 | env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ | |
724 | env->fpus |= 0x400; | |
725 | } | |
726 | ST0 = sqrt(fptemp); | |
727 | } | |
728 | ||
729 | void helper_fsincos(void) | |
730 | { | |
731 | CPU86_LDouble fptemp; | |
732 | ||
733 | fptemp = ST0; | |
734 | if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { | |
735 | env->fpus |= 0x400; | |
736 | } else { | |
737 | ST0 = sin(fptemp); | |
738 | fpush(); | |
739 | ST0 = cos(fptemp); | |
740 | env->fpus &= (~0x400); /* C2 <-- 0 */ | |
741 | /* the above code is for |arg| < 2**63 only */ | |
742 | } | |
743 | } | |
744 | ||
745 | void helper_frndint(void) | |
746 | { | |
1e5ffbed FB |
747 | CPU86_LDouble a; |
748 | ||
749 | a = ST0; | |
750 | #ifdef __arm__ | |
751 | switch(env->fpuc & RC_MASK) { | |
752 | default: | |
753 | case RC_NEAR: | |
754 | asm("rndd %0, %1" : "=f" (a) : "f"(a)); | |
755 | break; | |
756 | case RC_DOWN: | |
757 | asm("rnddm %0, %1" : "=f" (a) : "f"(a)); | |
758 | break; | |
759 | case RC_UP: | |
760 | asm("rnddp %0, %1" : "=f" (a) : "f"(a)); | |
761 | break; | |
762 | case RC_CHOP: | |
763 | asm("rnddz %0, %1" : "=f" (a) : "f"(a)); | |
764 | break; | |
765 | } | |
766 | #else | |
767 | a = rint(a); | |
768 | #endif | |
769 | ST0 = a; | |
3ec9c4fc FB |
770 | } |
771 | ||
772 | void helper_fscale(void) | |
773 | { | |
774 | CPU86_LDouble fpsrcop, fptemp; | |
775 | ||
776 | fpsrcop = 2.0; | |
777 | fptemp = pow(fpsrcop,ST1); | |
778 | ST0 *= fptemp; | |
779 | } | |
780 | ||
781 | void helper_fsin(void) | |
782 | { | |
783 | CPU86_LDouble fptemp; | |
784 | ||
785 | fptemp = ST0; | |
786 | if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { | |
787 | env->fpus |= 0x400; | |
788 | } else { | |
789 | ST0 = sin(fptemp); | |
790 | env->fpus &= (~0x400); /* C2 <-- 0 */ | |
791 | /* the above code is for |arg| < 2**53 only */ | |
792 | } | |
793 | } | |
794 | ||
795 | void helper_fcos(void) | |
796 | { | |
797 | CPU86_LDouble fptemp; | |
798 | ||
799 | fptemp = ST0; | |
800 | if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) { | |
801 | env->fpus |= 0x400; | |
802 | } else { | |
803 | ST0 = cos(fptemp); | |
804 | env->fpus &= (~0x400); /* C2 <-- 0 */ | |
805 | /* the above code is for |arg5 < 2**63 only */ | |
806 | } | |
807 | } | |
808 | ||
809 | void helper_fxam_ST0(void) | |
810 | { | |
811 | CPU86_LDoubleU temp; | |
812 | int expdif; | |
813 | ||
814 | temp.d = ST0; | |
815 | ||
816 | env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */ | |
817 | if (SIGND(temp)) | |
818 | env->fpus |= 0x200; /* C1 <-- 1 */ | |
819 | ||
820 | expdif = EXPD(temp); | |
821 | if (expdif == MAXEXPD) { | |
822 | if (MANTD(temp) == 0) | |
823 | env->fpus |= 0x500 /*Infinity*/; | |
824 | else | |
825 | env->fpus |= 0x100 /*NaN*/; | |
826 | } else if (expdif == 0) { | |
827 | if (MANTD(temp) == 0) | |
828 | env->fpus |= 0x4000 /*Zero*/; | |
829 | else | |
830 | env->fpus |= 0x4400 /*Denormal*/; | |
831 | } else { | |
832 | env->fpus |= 0x400; | |
833 | } | |
834 | } | |
835 | ||
836 | void helper_fstenv(uint8_t *ptr, int data32) | |
837 | { | |
838 | int fpus, fptag, exp, i; | |
839 | uint64_t mant; | |
840 | CPU86_LDoubleU tmp; | |
841 | ||
842 | fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11; | |
843 | fptag = 0; | |
844 | for (i=7; i>=0; i--) { | |
845 | fptag <<= 2; | |
846 | if (env->fptags[i]) { | |
847 | fptag |= 3; | |
848 | } else { | |
849 | tmp.d = env->fpregs[i]; | |
850 | exp = EXPD(tmp); | |
851 | mant = MANTD(tmp); | |
852 | if (exp == 0 && mant == 0) { | |
853 | /* zero */ | |
854 | fptag |= 1; | |
855 | } else if (exp == 0 || exp == MAXEXPD | |
856 | #ifdef USE_X86LDOUBLE | |
857 | || (mant & (1LL << 63)) == 0 | |
858 | #endif | |
859 | ) { | |
860 | /* NaNs, infinity, denormal */ | |
861 | fptag |= 2; | |
862 | } | |
863 | } | |
864 | } | |
865 | if (data32) { | |
866 | /* 32 bit */ | |
867 | stl(ptr, env->fpuc); | |
868 | stl(ptr + 4, fpus); | |
869 | stl(ptr + 8, fptag); | |
870 | stl(ptr + 12, 0); | |
871 | stl(ptr + 16, 0); | |
872 | stl(ptr + 20, 0); | |
873 | stl(ptr + 24, 0); | |
874 | } else { | |
875 | /* 16 bit */ | |
876 | stw(ptr, env->fpuc); | |
877 | stw(ptr + 2, fpus); | |
878 | stw(ptr + 4, fptag); | |
879 | stw(ptr + 6, 0); | |
880 | stw(ptr + 8, 0); | |
881 | stw(ptr + 10, 0); | |
882 | stw(ptr + 12, 0); | |
883 | } | |
884 | } | |
885 | ||
886 | void helper_fldenv(uint8_t *ptr, int data32) | |
887 | { | |
888 | int i, fpus, fptag; | |
889 | ||
890 | if (data32) { | |
891 | env->fpuc = lduw(ptr); | |
892 | fpus = lduw(ptr + 4); | |
893 | fptag = lduw(ptr + 8); | |
894 | } | |
895 | else { | |
896 | env->fpuc = lduw(ptr); | |
897 | fpus = lduw(ptr + 2); | |
898 | fptag = lduw(ptr + 4); | |
899 | } | |
900 | env->fpstt = (fpus >> 11) & 7; | |
901 | env->fpus = fpus & ~0x3800; | |
902 | for(i = 0;i < 7; i++) { | |
903 | env->fptags[i] = ((fptag & 3) == 3); | |
904 | fptag >>= 2; | |
905 | } | |
906 | } | |
907 | ||
908 | void helper_fsave(uint8_t *ptr, int data32) | |
909 | { | |
910 | CPU86_LDouble tmp; | |
911 | int i; | |
912 | ||
913 | helper_fstenv(ptr, data32); | |
914 | ||
915 | ptr += (14 << data32); | |
916 | for(i = 0;i < 8; i++) { | |
917 | tmp = ST(i); | |
918 | #ifdef USE_X86LDOUBLE | |
919 | *(long double *)ptr = tmp; | |
920 | #else | |
921 | helper_fstt(tmp, ptr); | |
922 | #endif | |
923 | ptr += 10; | |
924 | } | |
925 | ||
926 | /* fninit */ | |
927 | env->fpus = 0; | |
928 | env->fpstt = 0; | |
929 | env->fpuc = 0x37f; | |
930 | env->fptags[0] = 1; | |
931 | env->fptags[1] = 1; | |
932 | env->fptags[2] = 1; | |
933 | env->fptags[3] = 1; | |
934 | env->fptags[4] = 1; | |
935 | env->fptags[5] = 1; | |
936 | env->fptags[6] = 1; | |
937 | env->fptags[7] = 1; | |
938 | } | |
939 | ||
940 | void helper_frstor(uint8_t *ptr, int data32) | |
941 | { | |
942 | CPU86_LDouble tmp; | |
943 | int i; | |
944 | ||
945 | helper_fldenv(ptr, data32); | |
946 | ptr += (14 << data32); | |
947 | ||
948 | for(i = 0;i < 8; i++) { | |
949 | #ifdef USE_X86LDOUBLE | |
950 | tmp = *(long double *)ptr; | |
951 | #else | |
952 | tmp = helper_fldt(ptr); | |
953 | #endif | |
954 | ST(i) = tmp; | |
955 | ptr += 10; | |
956 | } | |
957 | } | |
958 |