1 /*
2 * Copyright (C) 2002-2021 The DOSBox Team
3 *
4 * This program is free software; you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation; either version 2 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License along
15 * with this program; if not, write to the Free Software Foundation, Inc.,
16 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
17 */
18
19 static void gen_init(void);
20
21 /* End of needed */
22
23 #define X86_REGS 7
24 #define X86_REG_EAX 0x00
25 #define X86_REG_ECX 0x01
26 #define X86_REG_EDX 0x02
27 #define X86_REG_EBX 0x03
28 #define X86_REG_EBP 0x04
29 #define X86_REG_ESI 0x05
30 #define X86_REG_EDI 0x06
31
32 #define X86_REG_MASK(_REG_) (1 << X86_REG_ ## _REG_)
33
34 static struct {
35 bool flagsactive;
36 Bitu last_used;
37 GenReg * regs[X86_REGS];
38 } x86gen;
39
40 class GenReg {
41 public:
42 Bit8u index = 0;
43 DynReg * dynreg = nullptr;
44 Bitu last_used = 0; //Keeps track of last assigned regs
45 bool notusable = false;
46
GenReg(Bit8u _index)47 GenReg(Bit8u _index) : index(_index) {}
48 void Load(DynReg * _dynreg,bool stale=false) {
49 if (!_dynreg) return;
50 if (GCC_UNLIKELY((Bitu)dynreg)) Clear();
51 dynreg=_dynreg;
52 last_used=x86gen.last_used;
53 dynreg->flags&=~DYNFLG_CHANGED;
54 dynreg->genreg=this;
55 if ((!stale) && (dynreg->flags & (DYNFLG_LOAD|DYNFLG_ACTIVE))) {
56 cache_addw(0x058b+(index << (8+3))); //Mov reg,[data]
57 cache_addd((Bit32u)dynreg->data);
58 }
59 dynreg->flags|=DYNFLG_ACTIVE;
60 }
Save(void)61 void Save(void) {
62 if (GCC_UNLIKELY(!((Bitu)dynreg))) IllegalOption("GenReg->Save");
63 dynreg->flags&=~DYNFLG_CHANGED;
64 cache_addw(0x0589+(index << (8+3))); //Mov [data],reg
65 cache_addd((Bit32u)dynreg->data);
66 }
Release(void)67 void Release(void) {
68 if (GCC_UNLIKELY(!((Bitu)dynreg))) return;
69 if (dynreg->flags&DYNFLG_CHANGED && dynreg->flags&DYNFLG_SAVE) {
70 Save();
71 }
72 dynreg->flags&=~(DYNFLG_CHANGED|DYNFLG_ACTIVE);
73 dynreg->genreg=0;dynreg=0;
74 }
Clear(void)75 void Clear(void) {
76 if (!dynreg) return;
77 if (dynreg->flags&DYNFLG_CHANGED) {
78 Save();
79 }
80 dynreg->genreg=0;dynreg=0;
81 }
82 };
83
gen_runcode(const Bit8u * code)84 static BlockReturn gen_runcode(const Bit8u * code) {
85 BlockReturn retval;
86 #if defined (_MSC_VER)
87 __asm {
88 /* Prepare the flags */
89 mov eax,[code]
90 push ebx
91 push ebp
92 push esi
93 push edi
94 mov ebx,[reg_flags]
95 and ebx,FMASK_TEST
96 push offset(return_address)
97 push ebx
98 jmp eax
99 /* Restore the flags */
100 return_address:
101 /* return here with flags in ecx */
102 and dword ptr [reg_flags],~FMASK_TEST
103 and ecx,FMASK_TEST
104 or [reg_flags],ecx
105 pop edi
106 pop esi
107 pop ebp
108 pop ebx
109 mov [retval],eax
110 }
111 #elif defined (MACOSX)
112 Bit32u tempflags = reg_flags & FMASK_TEST;
113 __asm__ volatile (
114 "pushl %%ebx \n"
115 "pushl %%ebp \n"
116 "pushl $(run_return_adress) \n"
117 "pushl %2 \n"
118 "jmp *%3 \n"
119 "run_return_adress: \n"
120 "popl %%ebp \n"
121 "popl %%ebx \n"
122 :"=a" (retval), "=c" (tempflags)
123 :"r" (tempflags),"r" (code)
124 :"%edx","%edi","%esi","cc","memory"
125 );
126 reg_flags=(reg_flags & ~FMASK_TEST) | (tempflags & FMASK_TEST);
127 #else
128 Bit32u tempflags = reg_flags & FMASK_TEST;
129 __asm__ volatile (
130 "pushl %%ebp \n"
131 "pushl $(run_return_adress) \n"
132 "pushl %2 \n"
133 "jmp *%3 \n"
134 "run_return_adress: \n"
135 "popl %%ebp \n"
136 :"=a" (retval), "=c" (tempflags)
137 :"r" (tempflags),"r" (code)
138 :"%edx","%ebx","%edi","%esi","cc","memory"
139 );
140 reg_flags=(reg_flags & ~FMASK_TEST) | (tempflags & FMASK_TEST);
141 #endif
142 return retval;
143 }
144
145 static GenReg * FindDynReg(DynReg * dynreg,bool stale=false) {
146 x86gen.last_used++;
147 if (dynreg->genreg) {
148 dynreg->genreg->last_used=x86gen.last_used;
149 return dynreg->genreg;
150 }
151 /* Find best match for selected global reg */
152 Bits i;
153 Bits first_used,first_index;
154 first_used=-1;
155 if (dynreg->flags & DYNFLG_HAS8) {
156 /* Has to be eax,ebx,ecx,edx */
157 for (i=first_index=0;i<=X86_REG_EBX;i++) {
158 GenReg * genreg=x86gen.regs[i];
159 if (genreg->notusable) continue;
160 if (!(genreg->dynreg)) {
161 genreg->Load(dynreg,stale);
162 return genreg;
163 }
164 if (genreg->last_used<(Bitu)first_used) {
165 first_used=genreg->last_used;
166 first_index=i;
167 }
168 }
169 } else {
170 for (i=first_index=X86_REGS-1;i>=0;i--) {
171 GenReg * genreg=x86gen.regs[i];
172 if (genreg->notusable) continue;
173 if (!(genreg->dynreg)) {
174 genreg->Load(dynreg,stale);
175 return genreg;
176 }
177 if (genreg->last_used<(Bitu)first_used) {
178 first_used=genreg->last_used;
179 first_index=i;
180 }
181 }
182 }
183 /* No free register found use earliest assigned one */
184 GenReg * newreg=x86gen.regs[first_index];
185 newreg->Load(dynreg,stale);
186 return newreg;
187 }
188
ForceDynReg(GenReg * genreg,DynReg * dynreg)189 static GenReg * ForceDynReg(GenReg * genreg,DynReg * dynreg) {
190 genreg->last_used=++x86gen.last_used;
191 if (dynreg->genreg==genreg) return genreg;
192 if (genreg->dynreg) genreg->Clear();
193 if (dynreg->genreg) dynreg->genreg->Clear();
194 genreg->Load(dynreg);
195 return genreg;
196 }
197
gen_preloadreg(DynReg * dynreg)198 static void gen_preloadreg(DynReg * dynreg) {
199 FindDynReg(dynreg);
200 }
201
gen_releasereg(DynReg * dynreg)202 static void gen_releasereg(DynReg * dynreg) {
203 GenReg * genreg=dynreg->genreg;
204 if (genreg) genreg->Release();
205 else dynreg->flags&=~(DYNFLG_ACTIVE|DYNFLG_CHANGED);
206 }
207
gen_setupreg(DynReg * dnew,DynReg * dsetup)208 static void gen_setupreg(DynReg * dnew,DynReg * dsetup) {
209 dnew->flags=dsetup->flags;
210 if (dnew->genreg==dsetup->genreg) return;
211 /* Not the same genreg must be wrong */
212 if (dnew->genreg) {
213 /* Check if the genreg i'm changing is actually linked to me */
214 if (dnew->genreg->dynreg==dnew) dnew->genreg->dynreg=0;
215 }
216 dnew->genreg=dsetup->genreg;
217 if (dnew->genreg) dnew->genreg->dynreg=dnew;
218 }
219
gen_synchreg(DynReg * dnew,DynReg * dsynch)220 static void gen_synchreg(DynReg * dnew,DynReg * dsynch) {
221 /* First make sure the registers match */
222 if (dnew->genreg!=dsynch->genreg) {
223 if (dnew->genreg) dnew->genreg->Clear();
224 if (dsynch->genreg) {
225 dsynch->genreg->Load(dnew);
226 }
227 }
228 /* Always use the loadonce flag from either state */
229 dnew->flags|=(dsynch->flags & dnew->flags&DYNFLG_ACTIVE);
230 if ((dnew->flags ^ dsynch->flags) & DYNFLG_CHANGED) {
231 /* Ensure the changed value gets saved */
232 if (dnew->flags & DYNFLG_CHANGED) {
233 if (GCC_LIKELY(dnew->genreg != NULL))
234 dnew->genreg->Save();
235 } else dnew->flags|=DYNFLG_CHANGED;
236 }
237 }
238
gen_needflags(void)239 static void gen_needflags(void) {
240 if (!x86gen.flagsactive) {
241 x86gen.flagsactive=true;
242 cache_addb(0x9d); //POPFD
243 }
244 }
245
gen_protectflags(void)246 static void gen_protectflags(void) {
247 if (x86gen.flagsactive) {
248 x86gen.flagsactive=false;
249 cache_addb(0x9c); //PUSHFD
250 }
251 }
252
gen_discardflags(void)253 static void gen_discardflags(void) {
254 if (!x86gen.flagsactive) {
255 x86gen.flagsactive=true;
256 cache_addw(0xc483); //ADD ESP,4
257 cache_addb(0x4);
258 }
259 }
260
gen_needcarry(void)261 static void gen_needcarry(void) {
262 if (!x86gen.flagsactive) {
263 x86gen.flagsactive=true;
264 cache_addw(0x2cd1); //SHR DWORD [ESP],1
265 cache_addb(0x24);
266 cache_addd(0x0424648d); //LEA ESP,[ESP+4]
267 }
268 }
269
270 #if 0
271 static void gen_setzeroflag(void) {
272 if (x86gen.flagsactive) IllegalOption("gen_setzeroflag");
273 cache_addw(0x0c83); //OR DWORD [ESP],0x40
274 cache_addw(0x4024);
275 }
276
277 static void gen_clearzeroflag(void) {
278 if (x86gen.flagsactive) IllegalOption("gen_clearzeroflag");
279 cache_addw(0x2483); //AND DWORD [ESP],~0x40
280 cache_addw(0xbf24);
281 }
282 #endif
283
284 static bool skip_flags=false;
285
set_skipflags(bool state)286 static void set_skipflags(bool state) {
287 if (!state) gen_discardflags();
288 skip_flags=state;
289 }
290
gen_reinit(void)291 static void gen_reinit(void) {
292 x86gen.last_used=0;
293 x86gen.flagsactive=false;
294 for (Bitu i=0;i<X86_REGS;i++) {
295 x86gen.regs[i]->dynreg=0;
296 }
297 }
298
299
gen_load_host(void * data,DynReg * dr1,Bitu size)300 static void gen_load_host(void * data,DynReg * dr1,Bitu size) {
301 GenReg * gr1=FindDynReg(dr1,true);
302 switch (size) {
303 case 1:cache_addw(0xb60f);break; //movzx byte
304 case 2:cache_addw(0xb70f);break; //movzx word
305 case 4:cache_addb(0x8b);break; //mov
306 default:
307 IllegalOption("gen_load_host");
308 }
309 cache_addb(0x5+(gr1->index<<3));
310 cache_addd((Bit32u)data);
311 dr1->flags|=DYNFLG_CHANGED;
312 }
313
314 static void gen_mov_host(void * data,DynReg * dr1,Bitu size,Bitu di1=0) {
315 GenReg * gr1=FindDynReg(dr1,(size==4));
316 switch (size) {
317 case 1:cache_addb(0x8a);break; //mov byte
318 case 2:cache_addb(0x66); //mov word
319 [[fallthrough]];
320 case 4:cache_addb(0x8b);break; //mov
321 default:
322 IllegalOption("gen_mov_host");
323 }
324 cache_addb(0x5+((gr1->index+di1)<<3));
325 cache_addd((Bit32u)data);
326 dr1->flags|=DYNFLG_CHANGED;
327 }
328
329
gen_dop_byte(DualOps op,DynReg * dr1,Bit8u di1,DynReg * dr2,Bit8u di2)330 static void gen_dop_byte(DualOps op,DynReg * dr1,Bit8u di1,DynReg * dr2,Bit8u di2) {
331 GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
332 Bit8u tmp = 0x00;
333 switch (op) {
334 case DOP_ADD: tmp=0x02; break;
335 case DOP_ADC: tmp=0x12; break;
336 case DOP_SUB: tmp=0x2a; break;
337 case DOP_SBB: tmp=0x1a; break;
338 case DOP_CMP: tmp=0x3a; goto nochange;
339 case DOP_XOR: tmp=0x32; break;
340 case DOP_AND: tmp=0x22; if ((dr1==dr2) && (di1==di2)) goto nochange; break;
341 case DOP_OR: tmp=0x0a; if ((dr1==dr2) && (di1==di2)) goto nochange; break;
342 case DOP_TEST: tmp=0x84; goto nochange;
343 case DOP_MOV: if ((dr1==dr2) && (di1==di2)) return; tmp=0x8a; break;
344 case DOP_XCHG: if ((dr1==dr2) && (di1==di2)) return;
345 tmp=0x86; dr2->flags|=DYNFLG_CHANGED; break;
346 default:
347 IllegalOption("gen_dop_byte");
348 }
349 dr1->flags|=DYNFLG_CHANGED;
350 nochange:
351 cache_addw(tmp|(0xc0+((gr1->index+di1)<<3)+gr2->index+di2)<<8);
352 }
353
gen_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1,Bitu imm)354 static void gen_dop_byte_imm(DualOps op,DynReg * dr1,Bit8u di1,Bitu imm) {
355 GenReg * gr1=FindDynReg(dr1);
356 Bit16u tmp;
357 switch (op) {
358 case DOP_ADD: tmp=0xc080; break;
359 case DOP_ADC: tmp=0xd080; break;
360 case DOP_SUB: tmp=0xe880; break;
361 case DOP_SBB: tmp=0xd880; break;
362 case DOP_CMP: tmp=0xf880; goto nochange; //Doesn't change
363 case DOP_XOR: tmp=0xf080; break;
364 case DOP_AND: tmp=0xe080; break;
365 case DOP_OR: tmp=0xc880; break;
366 case DOP_TEST: tmp=0xc0f6; goto nochange; //Doesn't change
367 case DOP_MOV: cache_addb(0xb0+gr1->index+di1);
368 dr1->flags|=DYNFLG_CHANGED;
369 goto finish;
370 default:
371 IllegalOption("gen_dop_byte_imm");
372 }
373 dr1->flags|=DYNFLG_CHANGED;
374 nochange:
375 cache_addw(tmp+((gr1->index+di1)<<8));
376 finish:
377 cache_addb(imm);
378 }
379
gen_dop_byte_imm_mem(DualOps op,DynReg * dr1,Bit8u di1,void * data)380 static void gen_dop_byte_imm_mem(DualOps op,DynReg * dr1,Bit8u di1,void* data) {
381 GenReg * gr1=FindDynReg(dr1);
382 Bit16u tmp;
383 switch (op) {
384 case DOP_ADD: tmp=0x0502; break;
385 case DOP_ADC: tmp=0x0512; break;
386 case DOP_SUB: tmp=0x052a; break;
387 case DOP_SBB: tmp=0x051a; break;
388 case DOP_CMP: tmp=0x053a; goto nochange; //Doesn't change
389 case DOP_XOR: tmp=0x0532; break;
390 case DOP_AND: tmp=0x0522; break;
391 case DOP_OR: tmp=0x050a; break;
392 case DOP_TEST: tmp=0x0584; goto nochange; //Doesn't change
393 case DOP_MOV: tmp=0x058A; break;
394 default:
395 IllegalOption("gen_dop_byte_imm_mem");
396 }
397 dr1->flags|=DYNFLG_CHANGED;
398 nochange:
399 cache_addw(tmp+((gr1->index+di1)<<11));
400 cache_addd((Bit32u)data);
401 }
402
gen_sop_byte(SingleOps op,DynReg * dr1,Bit8u di1)403 static void gen_sop_byte(SingleOps op,DynReg * dr1,Bit8u di1) {
404 GenReg * gr1=FindDynReg(dr1);
405 Bit16u tmp;
406 switch (op) {
407 case SOP_INC: tmp=0xc0FE; break;
408 case SOP_DEC: tmp=0xc8FE; break;
409 case SOP_NOT: tmp=0xd0f6; break;
410 case SOP_NEG: tmp=0xd8f6; break;
411 default:
412 IllegalOption("gen_sop_byte");
413 }
414 cache_addw(tmp + ((gr1->index+di1)<<8));
415 dr1->flags|=DYNFLG_CHANGED;
416 }
417
418
gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr)419 static void gen_extend_word(bool sign,DynReg * ddr,DynReg * dsr) {
420 GenReg * gsr=FindDynReg(dsr);
421 GenReg * gdr=FindDynReg(ddr,true);
422 if (sign) cache_addw(0xbf0f);
423 else cache_addw(0xb70f);
424 cache_addb(0xc0+(gdr->index<<3)+(gsr->index));
425 ddr->flags|=DYNFLG_CHANGED;
426 }
427
gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bit8u dsi)428 static void gen_extend_byte(bool sign,bool dword,DynReg * ddr,DynReg * dsr,Bit8u dsi) {
429 GenReg * gsr=FindDynReg(dsr);
430 GenReg * gdr=FindDynReg(ddr,dword);
431 if (!dword) cache_addb(0x66);
432 if (sign) cache_addw(0xbe0f);
433 else cache_addw(0xb60f);
434 cache_addb(0xc0+(gdr->index<<3)+(gsr->index+dsi));
435 ddr->flags|=DYNFLG_CHANGED;
436 }
437
gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm)438 static void gen_lea(DynReg * ddr,DynReg * dsr1,DynReg * dsr2,Bitu scale,Bits imm) {
439 GenReg * gdr=FindDynReg(ddr);
440 Bitu imm_size;
441 Bit8u rm_base=(gdr->index << 3);
442 if (dsr1) {
443 GenReg * gsr1=FindDynReg(dsr1);
444 if (!imm && (gsr1->index!=0x5)) {
445 imm_size=0; rm_base+=0x0; //no imm
446 } else if ((imm>=-128 && imm<=127)) {
447 imm_size=1;rm_base+=0x40; //Signed byte imm
448 } else {
449 imm_size=4;rm_base+=0x80; //Signed dword imm
450 }
451 if (dsr2) {
452 GenReg * gsr2=FindDynReg(dsr2);
453 cache_addb(0x8d); //LEA
454 cache_addb(rm_base+0x4); //The sib indicator
455 Bit8u sib=(gsr1->index)+(gsr2->index<<3)+(scale<<6);
456 cache_addb(sib);
457 } else {
458 if ((ddr==dsr1) && !imm_size) return;
459 cache_addb(0x8d); //LEA
460 cache_addb(rm_base+gsr1->index);
461 }
462 } else {
463 if (dsr2) {
464 GenReg * gsr2=FindDynReg(dsr2);
465 cache_addb(0x8d); //LEA
466 cache_addb(rm_base+0x4); //The sib indicator
467 Bit8u sib=(5+(gsr2->index<<3)+(scale<<6));
468 cache_addb(sib);
469 imm_size=4;
470 } else {
471 cache_addb(0x8d); //LEA
472 cache_addb(rm_base+0x05); //dword imm
473 imm_size=4;
474 }
475 }
476 switch (imm_size) {
477 case 0: break;
478 case 1:cache_addb(imm);break;
479 case 4:cache_addd(imm);break;
480 }
481 ddr->flags|=DYNFLG_CHANGED;
482 }
483
gen_lea_imm_mem(DynReg * ddr,DynReg * dsr,void * data)484 static void gen_lea_imm_mem(DynReg * ddr,DynReg * dsr,void* data) {
485 GenReg * gdr=FindDynReg(ddr);
486 Bit8u rm_base=(gdr->index << 3);
487 cache_addw(0x058b+(rm_base<<8));
488 cache_addd((Bit32u)data);
489 GenReg * gsr=FindDynReg(dsr);
490 cache_addb(0x8d); //LEA
491 cache_addb(rm_base+0x44);
492 cache_addb(rm_base+gsr->index);
493 cache_addb(0x00);
494 ddr->flags|=DYNFLG_CHANGED;
495 }
496
gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2)497 static void gen_dop_word(DualOps op,bool dword,DynReg * dr1,DynReg * dr2) {
498 GenReg * gr2=FindDynReg(dr2);
499 GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
500 Bit8u tmp;
501 switch (op) {
502 case DOP_ADD: tmp=0x03; break;
503 case DOP_ADC: tmp=0x13; break;
504 case DOP_SUB: tmp=0x2b; break;
505 case DOP_SBB: tmp=0x1b; break;
506 case DOP_CMP: tmp=0x3b; goto nochange;
507 case DOP_XOR: tmp=0x33; break;
508 case DOP_AND: tmp=0x23; if (dr1==dr2) goto nochange; break;
509 case DOP_OR: tmp=0x0b; if (dr1==dr2) goto nochange; break;
510 case DOP_TEST: tmp=0x85; goto nochange;
511 case DOP_MOV: if (dr1==dr2) return; tmp=0x8b; break;
512 case DOP_XCHG: if (dr1==dr2) return;
513 dr2->flags|=DYNFLG_CHANGED;
514 if (dword && !((dr1->flags&DYNFLG_HAS8) ^ (dr2->flags&DYNFLG_HAS8))) {
515 dr1->genreg=gr2;dr1->genreg->dynreg=dr1;
516 dr2->genreg=gr1;dr2->genreg->dynreg=dr2;
517 dr1->flags|=DYNFLG_CHANGED;
518 return;
519 }
520 tmp=0x87;
521 break;
522 default:
523 IllegalOption("gen_dop_word");
524 }
525 dr1->flags|=DYNFLG_CHANGED;
526 nochange:
527 if (!dword) cache_addb(0x66);
528 cache_addw(tmp|(0xc0+(gr1->index<<3)+gr2->index)<<8);
529 }
530
gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm)531 static void gen_dop_word_imm(DualOps op,bool dword,DynReg * dr1,Bits imm) {
532 GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
533 Bit16u tmp;
534 if (!dword) cache_addb(0x66);
535 switch (op) {
536 case DOP_ADD: tmp=0xc081; break;
537 case DOP_ADC: tmp=0xd081; break;
538 case DOP_SUB: tmp=0xe881; break;
539 case DOP_SBB: tmp=0xd881; break;
540 case DOP_CMP: tmp=0xf881; goto nochange; //Doesn't change
541 case DOP_XOR: tmp=0xf081; break;
542 case DOP_AND: tmp=0xe081; break;
543 case DOP_OR: tmp=0xc881; break;
544 case DOP_TEST: tmp=0xc0f7; goto nochange; //Doesn't change
545 case DOP_MOV: cache_addb(0xb8+(gr1->index)); dr1->flags|=DYNFLG_CHANGED; goto finish;
546 default:
547 IllegalOption("gen_dop_word_imm");
548 }
549 dr1->flags|=DYNFLG_CHANGED;
550 nochange:
551 cache_addw(tmp+(gr1->index<<8));
552 finish:
553 if (dword) cache_addd(imm);
554 else cache_addw(imm);
555 }
556
gen_dop_word_imm_mem(DualOps op,bool dword,DynReg * dr1,void * data)557 static void gen_dop_word_imm_mem(DualOps op,bool dword,DynReg * dr1,void* data) {
558 GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
559 Bit16u tmp;
560 switch (op) {
561 case DOP_ADD: tmp=0x0503; break;
562 case DOP_ADC: tmp=0x0513; break;
563 case DOP_SUB: tmp=0x052b; break;
564 case DOP_SBB: tmp=0x051b; break;
565 case DOP_CMP: tmp=0x053b; goto nochange; //Doesn't change
566 case DOP_XOR: tmp=0x0533; break;
567 case DOP_AND: tmp=0x0523; break;
568 case DOP_OR: tmp=0x050b; break;
569 case DOP_TEST: tmp=0x0585; goto nochange; //Doesn't change
570 case DOP_MOV:
571 gen_mov_host(data,dr1,dword?4:2);
572 dr1->flags|=DYNFLG_CHANGED;
573 return;
574 default:
575 IllegalOption("gen_dop_word_imm_mem");
576 }
577 dr1->flags|=DYNFLG_CHANGED;
578 nochange:
579 if (!dword) cache_addb(0x66);
580 cache_addw(tmp+(gr1->index<<11));
581 cache_addd((Bit32u)data);
582 }
583
gen_dop_word_var(DualOps op,bool dword,DynReg * dr1,void * drd)584 static void gen_dop_word_var(DualOps op,bool dword,DynReg * dr1,void* drd) {
585 GenReg * gr1=FindDynReg(dr1,dword && op==DOP_MOV);
586 Bit8u tmp;
587 switch (op) {
588 case DOP_ADD: tmp=0x03; break;
589 case DOP_ADC: tmp=0x13; break;
590 case DOP_SUB: tmp=0x2b; break;
591 case DOP_SBB: tmp=0x1b; break;
592 case DOP_CMP: tmp=0x3b; break;
593 case DOP_XOR: tmp=0x33; break;
594 case DOP_AND: tmp=0x23; break;
595 case DOP_OR: tmp=0x0b; break;
596 case DOP_TEST: tmp=0x85; break;
597 case DOP_MOV: tmp=0x8b; break;
598 case DOP_XCHG: tmp=0x87; break;
599 default:
600 IllegalOption("gen_dop_word_var");
601 }
602 if (!dword) cache_addb(0x66);
603 cache_addw(tmp|(0x05+((gr1->index)<<3))<<8);
604 cache_addd((Bit32u)drd);
605 }
606
gen_imul_word(bool dword,DynReg * dr1,DynReg * dr2)607 static void gen_imul_word(bool dword,DynReg * dr1,DynReg * dr2) {
608 GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
609 dr1->flags|=DYNFLG_CHANGED;
610 if (!dword) {
611 cache_addd(0xaf0f66|(0xc0+(gr1->index<<3)+gr2->index)<<24);
612 } else {
613 cache_addw(0xaf0f);
614 cache_addb(0xc0+(gr1->index<<3)+gr2->index);
615 }
616 }
617
gen_imul_word_imm(bool dword,DynReg * dr1,DynReg * dr2,Bits imm)618 static void gen_imul_word_imm(bool dword,DynReg * dr1,DynReg * dr2,Bits imm) {
619 GenReg * gr1=FindDynReg(dr1);GenReg * gr2=FindDynReg(dr2);
620 if (!dword) cache_addb(0x66);
621 if ((imm>=-128 && imm<=127)) {
622 cache_addb(0x6b);
623 cache_addb(0xc0+(gr1->index<<3)+gr2->index);
624 cache_addb(imm);
625 } else {
626 cache_addb(0x69);
627 cache_addb(0xc0+(gr1->index<<3)+gr2->index);
628 if (dword) cache_addd(imm);
629 else cache_addw(imm);
630 }
631 dr1->flags|=DYNFLG_CHANGED;
632 }
633
634
gen_sop_word(SingleOps op,bool dword,DynReg * dr1)635 static void gen_sop_word(SingleOps op,bool dword,DynReg * dr1) {
636 GenReg * gr1=FindDynReg(dr1);
637 if (!dword) cache_addb(0x66);
638 switch (op) {
639 case SOP_INC:cache_addb(0x40+gr1->index);break;
640 case SOP_DEC:cache_addb(0x48+gr1->index);break;
641 case SOP_NOT:cache_addw(0xd0f7+(gr1->index<<8));break;
642 case SOP_NEG:cache_addw(0xd8f7+(gr1->index<<8));break;
643 default:
644 IllegalOption("gen_sop_word");
645 }
646 dr1->flags|=DYNFLG_CHANGED;
647 }
648
gen_shift_byte_cl(Bitu op,DynReg * dr1,Bit8u di1,DynReg * drecx)649 static void gen_shift_byte_cl(Bitu op,DynReg * dr1,Bit8u di1,DynReg * drecx) {
650 ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
651 GenReg * gr1=FindDynReg(dr1);
652 cache_addw(0xc0d2+(((Bit16u)op) << 11)+ ((gr1->index+di1)<<8));
653 dr1->flags|=DYNFLG_CHANGED;
654 }
655
gen_shift_byte_imm(Bitu op,DynReg * dr1,Bit8u di1,Bit8u imm)656 static void gen_shift_byte_imm(Bitu op,DynReg * dr1,Bit8u di1,Bit8u imm) {
657 GenReg * gr1=FindDynReg(dr1);
658 cache_addw(0xc0c0+(((Bit16u)op) << 11) + ((gr1->index+di1)<<8));
659 cache_addb(imm);
660 dr1->flags|=DYNFLG_CHANGED;
661 }
662
gen_shift_word_cl(Bitu op,bool dword,DynReg * dr1,DynReg * drecx)663 static void gen_shift_word_cl(Bitu op,bool dword,DynReg * dr1,DynReg * drecx) {
664 ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
665 GenReg * gr1=FindDynReg(dr1);
666 if (!dword) cache_addb(0x66);
667 cache_addw(0xc0d3+(((Bit16u)op) << 11) + ((gr1->index)<<8));
668 dr1->flags|=DYNFLG_CHANGED;
669 }
670
gen_shift_word_imm(Bitu op,bool dword,DynReg * dr1,Bit8u imm)671 static void gen_shift_word_imm(Bitu op,bool dword,DynReg * dr1,Bit8u imm) {
672 GenReg * gr1=FindDynReg(dr1);
673 dr1->flags|=DYNFLG_CHANGED;
674 if (!dword) {
675 cache_addd(0x66|((0xc0c1+((Bit16u)op << 11) + (gr1->index<<8))|imm<<16)<<8);
676 } else {
677 cache_addw(0xc0c1+((Bit16u)op << 11) + (gr1->index<<8));
678 cache_addb(imm);
679 }
680 }
681
gen_cbw(bool dword,DynReg * dyn_ax)682 static void gen_cbw(bool dword,DynReg * dyn_ax) {
683 ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
684 if (!dword) cache_addb(0x66);
685 cache_addb(0x98);
686 dyn_ax->flags|=DYNFLG_CHANGED;
687 }
688
gen_cwd(bool dword,DynReg * dyn_ax,DynReg * dyn_dx)689 static void gen_cwd(bool dword,DynReg * dyn_ax,DynReg * dyn_dx) {
690 ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
691 ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx);
692 dyn_ax->flags|=DYNFLG_CHANGED;
693 dyn_dx->flags|=DYNFLG_CHANGED;
694 if (!dword) cache_addw(0x9966);
695 else cache_addb(0x99);
696 }
697
gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bit8u di1)698 static void gen_mul_byte(bool imul,DynReg * dyn_ax,DynReg * dr1,Bit8u di1) {
699 ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
700 GenReg * gr1=FindDynReg(dr1);
701 if (imul) cache_addw(0xe8f6+((gr1->index+di1)<<8));
702 else cache_addw(0xe0f6+((gr1->index+di1)<<8));
703 dyn_ax->flags|=DYNFLG_CHANGED;
704 }
705
gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1)706 static void gen_mul_word(bool imul,DynReg * dyn_ax,DynReg * dyn_dx,bool dword,DynReg * dr1) {
707 ForceDynReg(x86gen.regs[X86_REG_EAX],dyn_ax);
708 ForceDynReg(x86gen.regs[X86_REG_EDX],dyn_dx);
709 GenReg * gr1=FindDynReg(dr1);
710 if (!dword) cache_addb(0x66);
711 if (imul) cache_addw(0xe8f7+(gr1->index<<8));
712 else cache_addw(0xe0f7+(gr1->index<<8));
713 dyn_ax->flags|=DYNFLG_CHANGED;
714 dyn_dx->flags|=DYNFLG_CHANGED;
715 }
716
gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm)717 static void gen_dshift_imm(bool dword,bool left,DynReg * dr1,DynReg * dr2,Bitu imm) {
718 GenReg * gr1=FindDynReg(dr1);
719 GenReg * gr2=FindDynReg(dr2);
720 if (!dword) cache_addb(0x66);
721 if (left) cache_addw(0xa40f); //SHLD IMM
722 else cache_addw(0xac0f); //SHRD IMM
723 cache_addb(0xc0+gr1->index+(gr2->index<<3));
724 cache_addb(imm);
725 dr1->flags|=DYNFLG_CHANGED;
726 }
727
gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx)728 static void gen_dshift_cl(bool dword,bool left,DynReg * dr1,DynReg * dr2,DynReg * drecx) {
729 ForceDynReg(x86gen.regs[X86_REG_ECX],drecx);
730 GenReg * gr1=FindDynReg(dr1);
731 GenReg * gr2=FindDynReg(dr2);
732 if (!dword) cache_addb(0x66);
733 if (left) cache_addw(0xa50f); //SHLD CL
734 else cache_addw(0xad0f); //SHRD CL
735 cache_addb(0xc0+gr1->index+(gr2->index<<3));
736 dr1->flags|=DYNFLG_CHANGED;
737 }
738
gen_call_function(void * func,char const * ops,...)739 static void gen_call_function(void * func,char const* ops,...) {
740 Bits paramcount=0;
741 bool release_flags=false;
742 struct ParamInfo {
743 const char * line;
744 Bitu value;
745 } pinfo[32];
746 ParamInfo * retparam=0;
747 /* Clear the EAX Genreg for usage */
748 x86gen.regs[X86_REG_EAX]->Clear();
749 x86gen.regs[X86_REG_EAX]->notusable=true;
750 /* Save the flags */
751 if (GCC_UNLIKELY(!skip_flags)) gen_protectflags();
752 /* Scan for the amount of params */
753 if (ops) {
754 va_list params;
755 va_start(params,ops);
756 #if defined (MACOSX)
757 Bitu stack_used=0;
758 bool free_flags=false;
759 #endif
760 Bits pindex=0;
761 while (*ops) {
762 if (*ops=='%') {
763 pinfo[pindex].line=ops+1;
764 pinfo[pindex].value=va_arg(params,Bitu);
765 #if defined (MACOSX)
766 const char * scan=pinfo[pindex].line;
767 if ((*scan=='I') || (*scan=='D')) stack_used+=4;
768 else if (*scan=='F') free_flags=true;
769 #endif
770 pindex++;
771 }
772 ops++;
773 }
774 va_end(params);
775
776 #if defined (MACOSX)
777 /* align stack */
778 stack_used+=4; // saving esp on stack as well
779
780 cache_addw(0xc48b); // mov eax,esp
781 cache_addb(0x2d); // sub eax,stack_used
782 cache_addd(stack_used);
783 cache_addw(0xe083); // and eax,0xfffffff0
784 cache_addb(0xf0);
785 cache_addb(0x05); // sub eax,stack_used
786 cache_addd(stack_used);
787 cache_addb(0x94); // xchg eax,esp
788 if (free_flags) {
789 cache_addw(0xc083); // add eax,4
790 cache_addb(0x04);
791 }
792 cache_addb(0x50); // push eax (==old esp)
793 #endif
794
795 paramcount=0;
796 while (pindex) {
797 pindex--;
798 const char * scan=pinfo[pindex].line;
799 switch (*scan++) {
800 case 'I': /* immediate value */
801 paramcount++;
802 cache_addb(0x68); //Push immediate
803 cache_addd(pinfo[pindex].value); //Push value
804 break;
805 case 'D': /* Dynamic register */
806 {
807 bool release=false;
808 paramcount++;
809 DynReg * dynreg=(DynReg *)pinfo[pindex].value;
810 GenReg * genreg=FindDynReg(dynreg);
811 scanagain:
812 switch (*scan++) {
813 case 'd':
814 cache_addb(0x50+genreg->index); //Push reg
815 break;
816 case 'w':
817 cache_addw(0xb70f); //MOVZX EAX,reg
818 cache_addb(0xc0+genreg->index);
819 cache_addb(0x50); //Push EAX
820 break;
821 case 'l':
822 cache_addw(0xb60f); //MOVZX EAX,reg[0]
823 cache_addb(0xc0+genreg->index);
824 cache_addb(0x50); //Push EAX
825 break;
826 case 'h':
827 cache_addw(0xb60f); //MOVZX EAX,reg[1]
828 cache_addb(0xc4+genreg->index);
829 cache_addb(0x50); //Push EAX
830 break;
831 case 'r': /* release the reg afterwards */
832 release=true;
833 goto scanagain;
834 default:
835 IllegalOption("gen_call_function param:DREG");
836 }
837 if (release) gen_releasereg(dynreg);
838 }
839 break;
840 case 'R': /* Dynamic register to get the return value */
841 retparam =&pinfo[pindex];
842 pinfo[pindex].line=scan;
843 break;
844 case 'F': /* Release flags from stack */
845 release_flags=true;
846 break;
847 default:
848 IllegalOption("gen_call_function unknown param");
849 }
850 }
851 #if defined (MACOSX)
852 if (free_flags) release_flags=false;
853 } else {
854 /* align stack */
855 Bit32u stack_used=8; // saving esp and return address on the stack
856
857 cache_addw(0xc48b); // mov eax,esp
858 cache_addb(0x2d); // sub eax,stack_used
859 cache_addd(stack_used);
860 cache_addw(0xe083); // and eax,0xfffffff0
861 cache_addb(0xf0);
862 cache_addb(0x05); // sub eax,stack_used
863 cache_addd(stack_used);
864 cache_addb(0x94); // xchg eax,esp
865 cache_addb(0x50); // push esp (==old esp)
866 #endif
867 }
868
869 /* Clear some unprotected registers */
870 x86gen.regs[X86_REG_ECX]->Clear();
871 x86gen.regs[X86_REG_EDX]->Clear();
872 /* Make sure reg_esp is current */
873 if (DynRegs[G_ESP].flags & DYNFLG_CHANGED)
874 DynRegs[G_ESP].genreg->Save();
875 /* Do the actual call to the procedure */
876 cache_addb(0xe8);
877 cache_addd((Bit32u)func - (Bit32u)cache.pos-4);
878 /* Restore the params of the stack */
879 if (paramcount) {
880 cache_addw(0xc483); //add ESP,imm byte
881 cache_addb(paramcount*4+(release_flags?4:0));
882 } else if (release_flags) {
883 cache_addw(0xc483); //add ESP,imm byte
884 cache_addb(4);
885 }
886 /* Save the return value in correct register */
887 if (retparam) {
888 DynReg * dynreg=(DynReg *)retparam->value;
889 GenReg * genreg=FindDynReg(dynreg);
890 if (genreg->index) // test for (e)ax/al
891 switch (*retparam->line) {
892 case 'd':
893 cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
894 break;
895 case 'w':
896 cache_addb(0x66);
897 cache_addw(0xc08b+(genreg->index <<(8+3))); //mov reg,eax
898 break;
899 case 'l':
900 cache_addw(0xc08a+(genreg->index <<(8+3))); //mov reg,eax
901 break;
902 case 'h':
903 cache_addw(0xc08a+((genreg->index+4) <<(8+3))); //mov reg,eax
904 break;
905 }
906 dynreg->flags|=DYNFLG_CHANGED;
907 }
908 /* Restore EAX registers to be used again */
909 x86gen.regs[X86_REG_EAX]->notusable=false;
910
911 #if defined (MACOSX)
912 /* restore stack */
913 cache_addb(0x5c); // pop esp
914 #endif
915 }
916
gen_call_write(DynReg * dr,Bit32u val,Bitu write_size)917 static void gen_call_write(DynReg * dr,Bit32u val,Bitu write_size) {
918 /* Clear the EAX Genreg for usage */
919 x86gen.regs[X86_REG_EAX]->Clear();
920 x86gen.regs[X86_REG_EAX]->notusable=true;
921 gen_protectflags();
922
923 #if defined (MACOSX)
924 /* align stack */
925 Bitu stack_used=12;
926
927 cache_addw(0xc48b); // mov eax,esp
928 cache_addb(0x2d); // sub eax,stack_used
929 cache_addd(stack_used);
930 cache_addw(0xe083); // and eax,0xfffffff0
931 cache_addb(0xf0);
932 cache_addb(0x05); // sub eax,stack_used
933 cache_addd(stack_used);
934 cache_addb(0x94); // xchg eax,esp
935 cache_addb(0x50); // push eax (==old esp)
936 #endif
937
938 cache_addb(0x68); //PUSH val
939 cache_addd(val);
940 GenReg * genreg=FindDynReg(dr);
941 cache_addb(0x50+genreg->index); //PUSH reg
942
943 /* Clear some unprotected registers */
944 x86gen.regs[X86_REG_ECX]->Clear();
945 x86gen.regs[X86_REG_EDX]->Clear();
946 /* Make sure reg_esp is current */
947 if (DynRegs[G_ESP].flags & DYNFLG_CHANGED)
948 DynRegs[G_ESP].genreg->Save();
949 /* Do the actual call to the procedure */
950 cache_addb(0xe8);
951 switch (write_size) {
952 case 1: cache_addd((Bit32u)mem_writeb_checked - (Bit32u)cache.pos-4); break;
953 case 2: cache_addd((Bit32u)mem_writew_checked - (Bit32u)cache.pos-4); break;
954 case 4: cache_addd((Bit32u)mem_writed_checked - (Bit32u)cache.pos-4); break;
955 default: IllegalOption("gen_call_write");
956 }
957
958 cache_addw(0xc483); //ADD ESP,8
959 cache_addb(2*4);
960 x86gen.regs[X86_REG_EAX]->notusable=false;
961 gen_releasereg(dr);
962
963 #if defined (MACOSX)
964 /* restore stack */
965 cache_addb(0x5c); // pop esp
966 #endif
967 }
968
gen_create_branch(BranchTypes type)969 static const Bit8u * gen_create_branch(BranchTypes type) {
970 /* First free all registers */
971 cache_addw(0x70+type);
972 return (cache.pos-1);
973 }
974
975 static void gen_fill_branch(const Bit8u * data,const Bit8u * from=cache.pos) {
976 #if C_DEBUG
977 Bits len=from-data;
978 if (len<0) len=-len;
979 if (len>126) LOG_MSG("Big jump %d",len);
980 #endif
981 cache_addb((Bit8u)(from-data-1),data);
982 }
983
gen_create_branch_long(BranchTypes type)984 static const Bit8u * gen_create_branch_long(BranchTypes type) {
985 cache_addw(0x800f+(type<<8));
986 cache_addd(0);
987 return (cache.pos-4);
988 }
989
990 static void gen_fill_branch_long(const Bit8u * data,const Bit8u * from=cache.pos) {
991 cache_addd((Bit32u)(from-data-4),data);
992 }
993
994 static const Bit8u * gen_create_jump(const Bit8u * to=0) {
995 /* First free all registers */
996 cache_addb(0xe9);
997 cache_addd(to-(cache.pos+4));
998 return (cache.pos-4);
999 }
1000
1001 static void gen_fill_jump(const Bit8u * data,const Bit8u * to=cache.pos) {
1002 gen_fill_branch_long(data,to);
1003 }
1004
1005
1006 static void gen_jmp_ptr(void * ptr,Bits imm=0) {
1007 cache_addb(0xa1);
1008 cache_addd((Bit32u)ptr);
1009 cache_addb(0xff); //JMP EA
1010 if (!imm) { //NO EBP
1011 cache_addb(0x20);
1012 } else if ((imm>=-128 && imm<=127)) {
1013 cache_addb(0x60);
1014 cache_addb(imm);
1015 } else {
1016 cache_addb(0xa0);
1017 cache_addd(imm);
1018 }
1019 }
1020
gen_save_flags(DynReg * dynreg)1021 static void gen_save_flags(DynReg * dynreg) {
1022 if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_save_flags");
1023 GenReg * genreg=FindDynReg(dynreg);
1024 cache_addb(0x8b); //MOV REG,[esp]
1025 cache_addw(0x2404+(genreg->index << 3));
1026 dynreg->flags|=DYNFLG_CHANGED;
1027 }
1028
gen_load_flags(DynReg * dynreg)1029 static void gen_load_flags(DynReg * dynreg) {
1030 if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_load_flags");
1031 cache_addw(0xc483); //ADD ESP,4
1032 cache_addb(0x4);
1033 GenReg * genreg=FindDynReg(dynreg);
1034 cache_addb(0x50+genreg->index); //PUSH 32
1035 }
1036
gen_save_host_direct(void * data,Bits imm)1037 static void gen_save_host_direct(void * data,Bits imm) {
1038 cache_addw(0x05c7); //MOV [],dword
1039 cache_addd((Bit32u)data);
1040 cache_addd(imm);
1041 }
1042
gen_return(BlockReturn retcode)1043 static void gen_return(BlockReturn retcode) {
1044 gen_protectflags();
1045 cache_addb(0x59); //POP ECX, the flags
1046 if (retcode==0) cache_addw(0xc033); //MOV EAX, 0
1047 else {
1048 cache_addb(0xb8); //MOV EAX, retcode
1049 cache_addd(retcode);
1050 }
1051 cache_addb(0xc3); //RET
1052 }
1053
1054 static void gen_return_fast(BlockReturn retcode,bool ret_exception=false) {
1055 if (GCC_UNLIKELY(x86gen.flagsactive)) IllegalOption("gen_return_fast");
1056 cache_addw(0x0d8b); //MOV ECX, the flags
1057 cache_addd((Bit32u)&cpu_regs.flags);
1058 if (!ret_exception) {
1059 cache_addw(0xc483); //ADD ESP,4
1060 cache_addb(0x4);
1061 if (retcode==0) cache_addw(0xc033); //MOV EAX, 0
1062 else {
1063 cache_addb(0xb8); //MOV EAX, retcode
1064 cache_addd(retcode);
1065 }
1066 }
1067 cache_addb(0xc3); //RET
1068 }
1069
gen_init(void)1070 static void gen_init(void) {
1071 x86gen.regs[X86_REG_EAX]=new GenReg(0);
1072 x86gen.regs[X86_REG_ECX]=new GenReg(1);
1073 x86gen.regs[X86_REG_EDX]=new GenReg(2);
1074 x86gen.regs[X86_REG_EBX]=new GenReg(3);
1075 x86gen.regs[X86_REG_EBP]=new GenReg(5);
1076 x86gen.regs[X86_REG_ESI]=new GenReg(6);
1077 x86gen.regs[X86_REG_EDI]=new GenReg(7);
1078 }
1079
1080 #if defined(X86_DYNFPU_DH_ENABLED)
gen_dh_fpu_save(void)1081 static void gen_dh_fpu_save(void)
1082 #if defined (_MSC_VER)
1083 {
1084 __asm {
1085 __asm fnsave dyn_dh_fpu.state
1086 __asm fldcw dyn_dh_fpu.host_cw
1087 }
1088 dyn_dh_fpu.state_used=false;
1089 dyn_dh_fpu.state.cw|=0x3f;
1090 }
1091 #else
1092 {
1093 __asm__ volatile (
1094 "fnsave %0 \n"
1095 "fldcw %1 \n"
1096 : "=m" (dyn_dh_fpu.state)
1097 : "m" (dyn_dh_fpu.host_cw)
1098 : "memory"
1099 );
1100 dyn_dh_fpu.state_used=false;
1101 dyn_dh_fpu.state.cw|=0x3f;
1102 }
1103 #endif
1104 #endif
1105