added helper-i386.c - alpha fixes
[qemu] / helper-i386.c
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
22 #if 0
23 /* full interrupt support (only useful for real CPU emulation, not
24    finished) - I won't do it any time soon, finish it if you want ! */
25 void raise_interrupt(int intno, int is_int, int error_code, 
26                      unsigned int next_eip)
27 {
28     SegmentDescriptorTable *dt;
29     uint8_t *ptr;
30     int type, dpl, cpl;
31     uint32_t e1, e2;
32     
33     dt = &env->idt;
34     if (intno * 8 + 7 > dt->limit)
35         raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
36     ptr = dt->base + intno * 8;
37     e1 = ldl(ptr);
38     e2 = ldl(ptr + 4);
39     /* check gate type */
40     type = (e2 >> DESC_TYPE_SHIFT) & 0x1f;
41     switch(type) {
42     case 5: /* task gate */
43     case 6: /* 286 interrupt gate */
44     case 7: /* 286 trap gate */
45     case 14: /* 386 interrupt gate */
46     case 15: /* 386 trap gate */
47         break;
48     default:
49         raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
50         break;
51     }
52     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
53     cpl = env->segs[R_CS] & 3;
54     /* check privledge if software int */
55     if (is_int && dpl < cpl)
56         raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
57     /* check valid bit */
58     if (!(e2 & DESC_P_MASK))
59         raise_exception_err(EXCP0B_NOSEG, intno * 8 + 2);
60 }
61
62 #else
63
64 /*
65  * is_int is TRUE if coming from the int instruction. next_eip is the
66  * EIP value AFTER the interrupt instruction. It is only relevant if
67  * is_int is TRUE.  
68  */
69 void raise_interrupt(int intno, int is_int, int error_code, 
70                      unsigned int next_eip)
71 {
72     SegmentDescriptorTable *dt;
73     uint8_t *ptr;
74     int dpl, cpl;
75     uint32_t e2;
76
77     dt = &env->idt;
78     ptr = dt->base + (intno * 8);
79     e2 = ldl(ptr + 4);
80     
81     dpl = (e2 >> DESC_DPL_SHIFT) & 3;
82     cpl = 3;
83     /* check privledge if software int */
84     if (is_int && dpl < cpl)
85         raise_exception_err(EXCP0D_GPF, intno * 8 + 2);
86
87     /* Since we emulate only user space, we cannot do more than
88        exiting the emulation with the suitable exception and error
89        code */
90     if (is_int)
91         EIP = next_eip;
92     env->exception_index = intno;
93     env->error_code = error_code;
94
95     cpu_loop_exit();
96 }
97
98 #endif
99
100 /* shortcuts to generate exceptions */
101 void raise_exception_err(int exception_index, int error_code)
102 {
103     raise_interrupt(exception_index, 0, error_code, 0);
104 }
105
106 void raise_exception(int exception_index)
107 {
108     raise_interrupt(exception_index, 0, 0, 0);
109 }
110
111 /* We simulate a pre-MMX pentium as in valgrind */
112 #define CPUID_FP87 (1 << 0)
113 #define CPUID_VME  (1 << 1)
114 #define CPUID_DE   (1 << 2)
115 #define CPUID_PSE  (1 << 3)
116 #define CPUID_TSC  (1 << 4)
117 #define CPUID_MSR  (1 << 5)
118 #define CPUID_PAE  (1 << 6)
119 #define CPUID_MCE  (1 << 7)
120 #define CPUID_CX8  (1 << 8)
121 #define CPUID_APIC (1 << 9)
122 #define CPUID_SEP  (1 << 11) /* sysenter/sysexit */
123 #define CPUID_MTRR (1 << 12)
124 #define CPUID_PGE  (1 << 13)
125 #define CPUID_MCA  (1 << 14)
126 #define CPUID_CMOV (1 << 15)
127 /* ... */
128 #define CPUID_MMX  (1 << 23)
129 #define CPUID_FXSR (1 << 24)
130 #define CPUID_SSE  (1 << 25)
131 #define CPUID_SSE2 (1 << 26)
132
133 void helper_cpuid(void)
134 {
135     if (EAX == 0) {
136         EAX = 1; /* max EAX index supported */
137         EBX = 0x756e6547;
138         ECX = 0x6c65746e;
139         EDX = 0x49656e69;
140     } else if (EAX == 1) {
141         /* EAX = 1 info */
142         EAX = 0x52b;
143         EBX = 0;
144         ECX = 0;
145         EDX = CPUID_FP87 | CPUID_DE | CPUID_PSE |
146             CPUID_TSC | CPUID_MSR | CPUID_MCE |
147             CPUID_CX8;
148     }
149 }
150
151 /* only works if protected mode and not VM86 */
152 void load_seg(int seg_reg, int selector, unsigned cur_eip)
153 {
154     SegmentCache *sc;
155     SegmentDescriptorTable *dt;
156     int index;
157     uint32_t e1, e2;
158     uint8_t *ptr;
159
160     sc = &env->seg_cache[seg_reg];
161     if ((selector & 0xfffc) == 0) {
162         /* null selector case */
163         if (seg_reg == R_SS) {
164             EIP = cur_eip;
165             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
166         } else {
167             /* XXX: each access should trigger an exception */
168             sc->base = NULL;
169             sc->limit = 0;
170             sc->seg_32bit = 1;
171         }
172     } else {
173         if (selector & 0x4)
174             dt = &env->ldt;
175         else
176             dt = &env->gdt;
177         index = selector & ~7;
178         if ((index + 7) > dt->limit) {
179             EIP = cur_eip;
180             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
181         }
182         ptr = dt->base + index;
183         e1 = ldl(ptr);
184         e2 = ldl(ptr + 4);
185         if (!(e2 & DESC_S_MASK) ||
186             (e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
187             EIP = cur_eip;
188             raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
189         }
190
191         if (seg_reg == R_SS) {
192             if ((e2 & (DESC_CS_MASK | DESC_W_MASK)) == 0) {
193                 EIP = cur_eip;
194                 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
195             }
196         } else {
197             if ((e2 & (DESC_CS_MASK | DESC_R_MASK)) == DESC_CS_MASK) {
198                 EIP = cur_eip;
199                 raise_exception_err(EXCP0D_GPF, selector & 0xfffc);
200             }
201         }
202
203         if (!(e2 & DESC_P_MASK)) {
204             EIP = cur_eip;
205             if (seg_reg == R_SS)
206                 raise_exception_err(EXCP0C_STACK, selector & 0xfffc);
207             else
208                 raise_exception_err(EXCP0B_NOSEG, selector & 0xfffc);
209         }
210         
211         sc->base = (void *)((e1 >> 16) | ((e2 & 0xff) << 16) | (e2 & 0xff000000));
212         sc->limit = (e1 & 0xffff) | (e2 & 0x000f0000);
213         if (e2 & (1 << 23))
214             sc->limit = (sc->limit << 12) | 0xfff;
215         sc->seg_32bit = (e2 >> 22) & 1;
216 #if 0
217         fprintf(logfile, "load_seg: sel=0x%04x base=0x%08lx limit=0x%08lx seg_32bit=%d\n", 
218                 selector, (unsigned long)sc->base, sc->limit, sc->seg_32bit);
219 #endif
220     }
221     env->segs[seg_reg] = selector;
222 }
223
224 void helper_lsl(void)
225 {
226     unsigned int selector, limit;
227     SegmentDescriptorTable *dt;
228     int index;
229     uint32_t e1, e2;
230     uint8_t *ptr;
231
232     CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
233     selector = T0 & 0xffff;
234     if (selector & 0x4)
235         dt = &env->ldt;
236     else
237         dt = &env->gdt;
238     index = selector & ~7;
239     if ((index + 7) > dt->limit)
240         return;
241     ptr = dt->base + index;
242     e1 = ldl(ptr);
243     e2 = ldl(ptr + 4);
244     limit = (e1 & 0xffff) | (e2 & 0x000f0000);
245     if (e2 & (1 << 23))
246         limit = (limit << 12) | 0xfff;
247     T1 = limit;
248     CC_SRC |= CC_Z;
249 }
250
251 void helper_lar(void)
252 {
253     unsigned int selector;
254     SegmentDescriptorTable *dt;
255     int index;
256     uint32_t e2;
257     uint8_t *ptr;
258
259     CC_SRC = cc_table[CC_OP].compute_all() & ~CC_Z;
260     selector = T0 & 0xffff;
261     if (selector & 0x4)
262         dt = &env->ldt;
263     else
264         dt = &env->gdt;
265     index = selector & ~7;
266     if ((index + 7) > dt->limit)
267         return;
268     ptr = dt->base + index;
269     e2 = ldl(ptr + 4);
270     T1 = e2 & 0x00f0ff00;
271     CC_SRC |= CC_Z;
272 }
273
274 /* FPU helpers */
275
276 #ifndef USE_X86LDOUBLE
277 void helper_fldt_ST0_A0(void)
278 {
279     ST0 = helper_fldt((uint8_t *)A0);
280 }
281
282 void helper_fstt_ST0_A0(void)
283 {
284     helper_fstt(ST0, (uint8_t *)A0);
285 }
286 #endif
287
288 /* BCD ops */
289
290 #define MUL10(iv) ( iv + iv + (iv << 3) )
291
292 void helper_fbld_ST0_A0(void)
293 {
294     uint8_t *seg;
295     CPU86_LDouble fpsrcop;
296     int m32i;
297     unsigned int v;
298
299     /* in this code, seg/m32i will be used as temporary ptr/int */
300     seg = (uint8_t *)A0 + 8;
301     v = ldub(seg--);
302     /* XXX: raise exception */
303     if (v != 0)
304         return;
305     v = ldub(seg--);
306     /* XXX: raise exception */
307     if ((v & 0xf0) != 0)
308         return;
309     m32i = v;  /* <-- d14 */
310     v = ldub(seg--);
311     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d13 */
312     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d12 */
313     v = ldub(seg--);
314     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d11 */
315     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d10 */
316     v = ldub(seg--);
317     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d9 */
318     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d8 */
319     fpsrcop = ((CPU86_LDouble)m32i) * 100000000.0;
320
321     v = ldub(seg--);
322     m32i = (v >> 4);  /* <-- d7 */
323     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d6 */
324     v = ldub(seg--);
325     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d5 */
326     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d4 */
327     v = ldub(seg--);
328     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d3 */
329     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d2 */
330     v = ldub(seg);
331     m32i = MUL10(m32i) + (v >> 4);  /* <-- val * 10 + d1 */
332     m32i = MUL10(m32i) + (v & 0xf); /* <-- val * 10 + d0 */
333     fpsrcop += ((CPU86_LDouble)m32i);
334     if ( ldub(seg+9) & 0x80 )
335         fpsrcop = -fpsrcop;
336     ST0 = fpsrcop;
337 }
338
339 void helper_fbst_ST0_A0(void)
340 {
341     CPU86_LDouble fptemp;
342     CPU86_LDouble fpsrcop;
343     int v;
344     uint8_t *mem_ref, *mem_end;
345
346     fpsrcop = rint(ST0);
347     mem_ref = (uint8_t *)A0;
348     mem_end = mem_ref + 8;
349     if ( fpsrcop < 0.0 ) {
350         stw(mem_end, 0x8000);
351         fpsrcop = -fpsrcop;
352     } else {
353         stw(mem_end, 0x0000);
354     }
355     while (mem_ref < mem_end) {
356         if (fpsrcop == 0.0)
357             break;
358         fptemp = floor(fpsrcop/10.0);
359         v = ((int)(fpsrcop - fptemp*10.0));
360         if  (fptemp == 0.0)  { 
361             stb(mem_ref++, v); 
362             break; 
363         }
364         fpsrcop = fptemp;
365         fptemp = floor(fpsrcop/10.0);
366         v |= (((int)(fpsrcop - fptemp*10.0)) << 4);
367         stb(mem_ref++, v);
368         fpsrcop = fptemp;
369     }
370     while (mem_ref < mem_end) {
371         stb(mem_ref++, 0);
372     }
373 }
374
375 void helper_f2xm1(void)
376 {
377     ST0 = pow(2.0,ST0) - 1.0;
378 }
379
380 void helper_fyl2x(void)
381 {
382     CPU86_LDouble fptemp;
383     
384     fptemp = ST0;
385     if (fptemp>0.0){
386         fptemp = log(fptemp)/log(2.0);   /* log2(ST) */
387         ST1 *= fptemp;
388         fpop();
389     } else { 
390         env->fpus &= (~0x4700);
391         env->fpus |= 0x400;
392     }
393 }
394
395 void helper_fptan(void)
396 {
397     CPU86_LDouble fptemp;
398
399     fptemp = ST0;
400     if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
401         env->fpus |= 0x400;
402     } else {
403         ST0 = tan(fptemp);
404         fpush();
405         ST0 = 1.0;
406         env->fpus &= (~0x400);  /* C2 <-- 0 */
407         /* the above code is for  |arg| < 2**52 only */
408     }
409 }
410
411 void helper_fpatan(void)
412 {
413     CPU86_LDouble fptemp, fpsrcop;
414
415     fpsrcop = ST1;
416     fptemp = ST0;
417     ST1 = atan2(fpsrcop,fptemp);
418     fpop();
419 }
420
421 void helper_fxtract(void)
422 {
423     CPU86_LDoubleU temp;
424     unsigned int expdif;
425
426     temp.d = ST0;
427     expdif = EXPD(temp) - EXPBIAS;
428     /*DP exponent bias*/
429     ST0 = expdif;
430     fpush();
431     BIASEXPONENT(temp);
432     ST0 = temp.d;
433 }
434
435 void helper_fprem1(void)
436 {
437     CPU86_LDouble dblq, fpsrcop, fptemp;
438     CPU86_LDoubleU fpsrcop1, fptemp1;
439     int expdif;
440     int q;
441
442     fpsrcop = ST0;
443     fptemp = ST1;
444     fpsrcop1.d = fpsrcop;
445     fptemp1.d = fptemp;
446     expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
447     if (expdif < 53) {
448         dblq = fpsrcop / fptemp;
449         dblq = (dblq < 0.0)? ceil(dblq): floor(dblq);
450         ST0 = fpsrcop - fptemp*dblq;
451         q = (int)dblq; /* cutting off top bits is assumed here */
452         env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
453                                 /* (C0,C1,C3) <-- (q2,q1,q0) */
454         env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */
455         env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */
456         env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */
457     } else {
458         env->fpus |= 0x400;  /* C2 <-- 1 */
459         fptemp = pow(2.0, expdif-50);
460         fpsrcop = (ST0 / ST1) / fptemp;
461         /* fpsrcop = integer obtained by rounding to the nearest */
462         fpsrcop = (fpsrcop-floor(fpsrcop) < ceil(fpsrcop)-fpsrcop)?
463             floor(fpsrcop): ceil(fpsrcop);
464         ST0 -= (ST1 * fpsrcop * fptemp);
465     }
466 }
467
468 void helper_fprem(void)
469 {
470     CPU86_LDouble dblq, fpsrcop, fptemp;
471     CPU86_LDoubleU fpsrcop1, fptemp1;
472     int expdif;
473     int q;
474     
475     fpsrcop = ST0;
476     fptemp = ST1;
477     fpsrcop1.d = fpsrcop;
478     fptemp1.d = fptemp;
479     expdif = EXPD(fpsrcop1) - EXPD(fptemp1);
480     if ( expdif < 53 ) {
481         dblq = fpsrcop / fptemp;
482         dblq = (dblq < 0.0)? ceil(dblq): floor(dblq);
483         ST0 = fpsrcop - fptemp*dblq;
484         q = (int)dblq; /* cutting off top bits is assumed here */
485         env->fpus &= (~0x4700); /* (C3,C2,C1,C0) <-- 0000 */
486                                 /* (C0,C1,C3) <-- (q2,q1,q0) */
487         env->fpus |= (q&0x4) << 6; /* (C0) <-- q2 */
488         env->fpus |= (q&0x2) << 8; /* (C1) <-- q1 */
489         env->fpus |= (q&0x1) << 14; /* (C3) <-- q0 */
490     } else {
491         env->fpus |= 0x400;  /* C2 <-- 1 */
492         fptemp = pow(2.0, expdif-50);
493         fpsrcop = (ST0 / ST1) / fptemp;
494         /* fpsrcop = integer obtained by chopping */
495         fpsrcop = (fpsrcop < 0.0)?
496             -(floor(fabs(fpsrcop))): floor(fpsrcop);
497         ST0 -= (ST1 * fpsrcop * fptemp);
498     }
499 }
500
501 void helper_fyl2xp1(void)
502 {
503     CPU86_LDouble fptemp;
504
505     fptemp = ST0;
506     if ((fptemp+1.0)>0.0) {
507         fptemp = log(fptemp+1.0) / log(2.0); /* log2(ST+1.0) */
508         ST1 *= fptemp;
509         fpop();
510     } else { 
511         env->fpus &= (~0x4700);
512         env->fpus |= 0x400;
513     }
514 }
515
516 void helper_fsqrt(void)
517 {
518     CPU86_LDouble fptemp;
519
520     fptemp = ST0;
521     if (fptemp<0.0) { 
522         env->fpus &= (~0x4700);  /* (C3,C2,C1,C0) <-- 0000 */
523         env->fpus |= 0x400;
524     }
525     ST0 = sqrt(fptemp);
526 }
527
528 void helper_fsincos(void)
529 {
530     CPU86_LDouble fptemp;
531
532     fptemp = ST0;
533     if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
534         env->fpus |= 0x400;
535     } else {
536         ST0 = sin(fptemp);
537         fpush();
538         ST0 = cos(fptemp);
539         env->fpus &= (~0x400);  /* C2 <-- 0 */
540         /* the above code is for  |arg| < 2**63 only */
541     }
542 }
543
544 void helper_frndint(void)
545 {
546     ST0 = rint(ST0);
547 }
548
549 void helper_fscale(void)
550 {
551     CPU86_LDouble fpsrcop, fptemp;
552
553     fpsrcop = 2.0;
554     fptemp = pow(fpsrcop,ST1);
555     ST0 *= fptemp;
556 }
557
558 void helper_fsin(void)
559 {
560     CPU86_LDouble fptemp;
561
562     fptemp = ST0;
563     if ((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
564         env->fpus |= 0x400;
565     } else {
566         ST0 = sin(fptemp);
567         env->fpus &= (~0x400);  /* C2 <-- 0 */
568         /* the above code is for  |arg| < 2**53 only */
569     }
570 }
571
572 void helper_fcos(void)
573 {
574     CPU86_LDouble fptemp;
575
576     fptemp = ST0;
577     if((fptemp > MAXTAN)||(fptemp < -MAXTAN)) {
578         env->fpus |= 0x400;
579     } else {
580         ST0 = cos(fptemp);
581         env->fpus &= (~0x400);  /* C2 <-- 0 */
582         /* the above code is for  |arg5 < 2**63 only */
583     }
584 }
585
586 void helper_fxam_ST0(void)
587 {
588     CPU86_LDoubleU temp;
589     int expdif;
590
591     temp.d = ST0;
592
593     env->fpus &= (~0x4700);  /* (C3,C2,C1,C0) <-- 0000 */
594     if (SIGND(temp))
595         env->fpus |= 0x200; /* C1 <-- 1 */
596
597     expdif = EXPD(temp);
598     if (expdif == MAXEXPD) {
599         if (MANTD(temp) == 0)
600             env->fpus |=  0x500 /*Infinity*/;
601         else
602             env->fpus |=  0x100 /*NaN*/;
603     } else if (expdif == 0) {
604         if (MANTD(temp) == 0)
605             env->fpus |=  0x4000 /*Zero*/;
606         else
607             env->fpus |= 0x4400 /*Denormal*/;
608     } else {
609         env->fpus |= 0x400;
610     }
611 }
612
613 void helper_fstenv(uint8_t *ptr, int data32)
614 {
615     int fpus, fptag, exp, i;
616     uint64_t mant;
617     CPU86_LDoubleU tmp;
618
619     fpus = (env->fpus & ~0x3800) | (env->fpstt & 0x7) << 11;
620     fptag = 0;
621     for (i=7; i>=0; i--) {
622         fptag <<= 2;
623         if (env->fptags[i]) {
624             fptag |= 3;
625         } else {
626             tmp.d = env->fpregs[i];
627             exp = EXPD(tmp);
628             mant = MANTD(tmp);
629             if (exp == 0 && mant == 0) {
630                 /* zero */
631                 fptag |= 1;
632             } else if (exp == 0 || exp == MAXEXPD
633 #ifdef USE_X86LDOUBLE
634                        || (mant & (1LL << 63)) == 0
635 #endif
636                        ) {
637                 /* NaNs, infinity, denormal */
638                 fptag |= 2;
639             }
640         }
641     }
642     if (data32) {
643         /* 32 bit */
644         stl(ptr, env->fpuc);
645         stl(ptr + 4, fpus);
646         stl(ptr + 8, fptag);
647         stl(ptr + 12, 0);
648         stl(ptr + 16, 0);
649         stl(ptr + 20, 0);
650         stl(ptr + 24, 0);
651     } else {
652         /* 16 bit */
653         stw(ptr, env->fpuc);
654         stw(ptr + 2, fpus);
655         stw(ptr + 4, fptag);
656         stw(ptr + 6, 0);
657         stw(ptr + 8, 0);
658         stw(ptr + 10, 0);
659         stw(ptr + 12, 0);
660     }
661 }
662
663 void helper_fldenv(uint8_t *ptr, int data32)
664 {
665     int i, fpus, fptag;
666
667     if (data32) {
668         env->fpuc = lduw(ptr);
669         fpus = lduw(ptr + 4);
670         fptag = lduw(ptr + 8);
671     }
672     else {
673         env->fpuc = lduw(ptr);
674         fpus = lduw(ptr + 2);
675         fptag = lduw(ptr + 4);
676     }
677     env->fpstt = (fpus >> 11) & 7;
678     env->fpus = fpus & ~0x3800;
679     for(i = 0;i < 7; i++) {
680         env->fptags[i] = ((fptag & 3) == 3);
681         fptag >>= 2;
682     }
683 }
684
685 void helper_fsave(uint8_t *ptr, int data32)
686 {
687     CPU86_LDouble tmp;
688     int i;
689
690     helper_fstenv(ptr, data32);
691
692     ptr += (14 << data32);
693     for(i = 0;i < 8; i++) {
694         tmp = ST(i);
695 #ifdef USE_X86LDOUBLE
696         *(long double *)ptr = tmp;
697 #else
698         helper_fstt(tmp, ptr);
699 #endif        
700         ptr += 10;
701     }
702
703     /* fninit */
704     env->fpus = 0;
705     env->fpstt = 0;
706     env->fpuc = 0x37f;
707     env->fptags[0] = 1;
708     env->fptags[1] = 1;
709     env->fptags[2] = 1;
710     env->fptags[3] = 1;
711     env->fptags[4] = 1;
712     env->fptags[5] = 1;
713     env->fptags[6] = 1;
714     env->fptags[7] = 1;
715 }
716
717 void helper_frstor(uint8_t *ptr, int data32)
718 {
719     CPU86_LDouble tmp;
720     int i;
721
722     helper_fldenv(ptr, data32);
723     ptr += (14 << data32);
724
725     for(i = 0;i < 8; i++) {
726 #ifdef USE_X86LDOUBLE
727         tmp = *(long double *)ptr;
728 #else
729         tmp = helper_fldt(ptr);
730 #endif        
731         ST(i) = tmp;
732         ptr += 10;
733     }
734 }
735