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
20 #include "dosbox.h"
21
22 #if (C_DYNAMIC_X86)
23
24 #include <cassert>
25 #include <cstdarg>
26 #include <cstdio>
27 #include <cstring>
28 #include <cstddef>
29 #include <cstdlib>
30
31 #if defined (WIN32)
32 #include <windows.h>
33 #include <winbase.h>
34 #endif
35
36 #if defined(HAVE_MPROTECT) || defined(HAVE_MMAP)
37 #include <sys/mman.h>
38
39 #include <limits.h>
40
41 #ifndef PAGESIZE
42 #define PAGESIZE 4096
43 #endif
44
45 #endif // HAVE_MPROTECT
46
47 #include "callback.h"
48 #include "regs.h"
49 #include "mem.h"
50 #include "cpu.h"
51 #include "debug.h"
52 #include "paging.h"
53 #include "inout.h"
54 #include "fpu.h"
55
56 #define CACHE_MAXSIZE (4096*3)
57 #define CACHE_TOTAL (1024*1024*8)
58 #define CACHE_PAGES (512)
59 #define CACHE_BLOCKS (64*1024)
60 #define CACHE_ALIGN (16)
61 #define DYN_HASH_SHIFT (4)
62 #define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT)
63 #define DYN_LINKS (16)
64
65 //#define DYN_LOG 1 //Turn logging on
66
67
68 #if C_FPU
69 #define CPU_FPU 1 //Enable FPU escape instructions
70 #define X86_DYNFPU_DH_ENABLED
71 #endif
72
73 enum {
74 G_EAX,G_ECX,G_EDX,G_EBX,
75 G_ESP,G_EBP,G_ESI,G_EDI,
76 G_ES,G_CS,G_SS,G_DS,G_FS,G_GS,
77 G_FLAGS,G_NEWESP,G_EIP,
78 G_EA,G_STACK,G_CYCLES,
79 G_TMPB,G_TMPW,G_SHIFT,
80 G_EXIT,
81 G_MAX,
82 };
83
84 enum SingleOps {
85 SOP_INC,SOP_DEC,
86 SOP_NOT,SOP_NEG,
87 };
88
89 enum DualOps {
90 DOP_ADD,DOP_ADC,
91 DOP_SUB,DOP_SBB,
92 DOP_CMP,DOP_XOR,
93 DOP_AND,DOP_OR,
94 DOP_TEST,
95 DOP_MOV,
96 DOP_XCHG,
97 };
98
99 enum ShiftOps {
100 SHIFT_ROL,SHIFT_ROR,
101 SHIFT_RCL,SHIFT_RCR,
102 SHIFT_SHL,SHIFT_SHR,
103 SHIFT_SAL,SHIFT_SAR,
104 };
105
106 enum BranchTypes {
107 BR_O,BR_NO,BR_B,BR_NB,
108 BR_Z,BR_NZ,BR_BE,BR_NBE,
109 BR_S,BR_NS,BR_P,BR_NP,
110 BR_L,BR_NL,BR_LE,BR_NLE
111 };
112
113
114 enum BlockReturn {
115 BR_Normal=0,
116 BR_Cycles,
117 BR_Link1,BR_Link2,
118 BR_Opcode,
119 BR_Iret,
120 BR_CallBack,
121 BR_SMCBlock
122 };
123
124 #define SMC_CURRENT_BLOCK 0xffff
125
126
127 #define DYNFLG_HAS16 0x1 //Would like 8-bit host reg support
128 #define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support
129 #define DYNFLG_LOAD 0x4 //Load value when accessed
130 #define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block
131 #define DYNFLG_CHANGED 0x10 //Value is in a register and changed from load
132 #define DYNFLG_ACTIVE 0x20 //Register has an active value
133
134 class GenReg;
135
136 struct DynReg {
137 Bitu flags;
138 GenReg * genreg;
139 void * data;
140 };
141
142 enum DynAccess {
143 DA_d,DA_w,
144 DA_bh,DA_bl
145 };
146
147 enum ByteCombo {
148 BC_ll,BC_lh,
149 BC_hl,BC_hh,
150 };
151
152 static DynReg DynRegs[G_MAX];
153 #define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ]
154
155 static struct {
156 Bit32u ea,tmpb,tmpd,stack,shift,newesp;
157 } extra_regs;
158
159 #define IllegalOption(msg) E_Exit("DYNX86: illegal option in " msg)
160
161 #define dyn_return(a,b) gen_return(a)
162 #include "dyn_cache.h"
163
164 static struct {
165 Bitu callback;
166 uint32_t readdata;
167 } core_dyn;
168
169 #if defined(X86_DYNFPU_DH_ENABLED)
170 static struct dyn_dh_fpu {
171 Bit16u cw,host_cw;
172 bool state_used;
173 // some fields expanded here for alignment purposes
174 struct {
175 Bit32u cw;
176 Bit32u sw;
177 Bit32u tag;
178 Bit32u ip;
179 Bit32u cs;
180 Bit32u ea;
181 Bit32u ds;
182 Bit8u st_reg[8][10];
183 } state;
184 FPU_P_Reg temp,temp2;
185 Bit32u dh_fpu_enabled;
186 Bit8u temp_state[128];
187 } dyn_dh_fpu;
188 #endif
189
190 #define X86 0x01
191 #define X86_64 0x02
192
193 #if C_TARGETCPU == X86_64
194 #include "core_dyn_x86/risc_x64.h"
195 #elif C_TARGETCPU == X86
196 #include "core_dyn_x86/risc_x86.h"
197 #else
198 #error DYN_X86 core not supported for this CPU target.
199 #endif
200
201 struct DynState {
202 DynReg regs[G_MAX];
203 };
204
dyn_flags_host_to_gen(void)205 static void dyn_flags_host_to_gen(void) {
206 gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS));
207 gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
208 gen_load_flags(DREG(EXIT));
209 gen_releasereg(DREG(EXIT));
210 gen_releasereg(DREG(FLAGS));
211 }
212
dyn_flags_gen_to_host(void)213 static void dyn_flags_gen_to_host(void) {
214 gen_save_flags(DREG(EXIT));
215 gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
216 gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST);
217 gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save
218 gen_releasereg(DREG(EXIT));
219 gen_releasereg(DREG(FLAGS));
220 }
221
dyn_savestate(DynState * state)222 static void dyn_savestate(DynState * state) {
223 for (Bitu i=0;i<G_MAX;i++) {
224 state->regs[i].flags=DynRegs[i].flags;
225 state->regs[i].genreg=DynRegs[i].genreg;
226 }
227 }
228
dyn_loadstate(DynState * state)229 static void dyn_loadstate(DynState * state) {
230 for (Bitu i=0;i<G_MAX;i++) {
231 gen_setupreg(&DynRegs[i],&state->regs[i]);
232 }
233 }
234
dyn_synchstate(DynState * state)235 static void dyn_synchstate(DynState * state) {
236 for (Bitu i=0;i<G_MAX;i++) {
237 gen_synchreg(&DynRegs[i],&state->regs[i]);
238 }
239 }
240
dyn_saveregister(DynReg * src_reg,DynReg * dst_reg)241 static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) {
242 dst_reg->flags=src_reg->flags;
243 dst_reg->genreg=src_reg->genreg;
244 }
245
dyn_restoreregister(DynReg * src_reg,DynReg * dst_reg)246 static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) {
247 dst_reg->flags=src_reg->flags;
248 dst_reg->genreg=src_reg->genreg;
249 dst_reg->genreg->dynreg=dst_reg; // necessary when register has been released
250 }
251
252 #include "core_dyn_x86/decoder.h"
253
CPU_Core_Dyn_X86_Run(void)254 Bits CPU_Core_Dyn_X86_Run(void) {
255 // helper class to auto-save DH_FPU state on function exit
256 class auto_dh_fpu {
257 public:
258 ~auto_dh_fpu()
259 {
260 #if defined(X86_DYNFPU_DH_ENABLED)
261 if (dyn_dh_fpu.state_used)
262 gen_dh_fpu_save();
263 #endif
264 }
265 };
266 auto_dh_fpu fpu_saver;
267
268 /* Determine the linear address of CS:EIP */
269 restart_core:
270 PhysPt ip_point=SegPhys(cs)+reg_eip;
271 #if C_DEBUG
272 #if C_HEAVY_DEBUG
273 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
274 #endif
275 #endif
276 CodePageHandler * chandler=0;
277 if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
278 CPU_Exception(cpu.exception.which,cpu.exception.error);
279 goto restart_core;
280 }
281 if (!chandler) {
282 return CPU_Core_Normal_Run();
283 }
284 /* Find correct Dynamic Block to run */
285 CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
286 if (!block) {
287 if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
288 block=CreateCacheBlock(chandler,ip_point,32);
289 } else {
290 Bit32s old_cycles=CPU_Cycles;
291 CPU_Cycles=1;
292 // manually save
293 fpu_saver = auto_dh_fpu();
294 Bits nc_retcode=CPU_Core_Normal_Run();
295 if (!nc_retcode) {
296 CPU_Cycles=old_cycles-1;
297 goto restart_core;
298 }
299 CPU_CycleLeft+=old_cycles;
300 return nc_retcode;
301 }
302 }
303 run_block:
304 cache.block.running=0;
305 BlockReturn ret=gen_runcode(block->cache.start);
306 #if C_DEBUG
307 cycle_count += 32;
308 #endif
309 switch (ret) {
310 case BR_Iret:
311 #if C_DEBUG
312 #if C_HEAVY_DEBUG
313 if (DEBUG_HeavyIsBreakpoint()) {
314 return debugCallback;
315 }
316 #endif
317 #endif
318 if (!GETFLAG(TF)) {
319 if (GETFLAG(IF) && PIC_IRQCheck) {
320 return CBRET_NONE;
321 }
322 goto restart_core;
323 }
324 cpudecoder=CPU_Core_Dyn_X86_Trap_Run;
325 return CBRET_NONE;
326 case BR_Normal:
327 /* Maybe check if we staying in the same page? */
328 #if C_DEBUG
329 #if C_HEAVY_DEBUG
330 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
331 #endif
332 #endif
333 goto restart_core;
334 case BR_Cycles:
335 #if C_DEBUG
336 #if C_HEAVY_DEBUG
337 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
338 #endif
339 #endif
340 return CBRET_NONE;
341 case BR_CallBack:
342 return core_dyn.callback;
343 case BR_SMCBlock:
344 // LOG_MSG("selfmodification of running block at %x:%x",
345 // SegValue(cs), reg_eip);
346 cpu.exception.which=0;
347 [[fallthrough]]; // let the normal core handle the block-modifying
348 // instruction
349 case BR_Opcode:
350 CPU_CycleLeft+=CPU_Cycles;
351 CPU_Cycles=1;
352 return CPU_Core_Normal_Run();
353 case BR_Link1:
354 case BR_Link2:
355 {
356 Bit32u temp_ip=SegPhys(cs)+reg_eip;
357 CodePageHandler* temp_handler = reinterpret_cast<CodePageHandler *>(get_tlb_readhandler(temp_ip));
358 if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) {
359 block=temp_handler->FindCacheBlock(temp_ip & 4095);
360 if (!block || !cache.block.running) goto restart_core;
361 cache.block.running->LinkTo(ret==BR_Link2,block);
362 goto run_block;
363 }
364 }
365 goto restart_core;
366 }
367 return CBRET_NONE;
368 }
369
CPU_Core_Dyn_X86_Trap_Run(void)370 Bits CPU_Core_Dyn_X86_Trap_Run(void) {
371 Bit32s oldCycles = CPU_Cycles;
372 CPU_Cycles = 1;
373 cpu.trap_skip = false;
374
375 Bits ret=CPU_Core_Normal_Run();
376 if (!cpu.trap_skip) CPU_DebugException(DBINT_STEP,reg_eip);
377 CPU_Cycles = oldCycles-1;
378 cpudecoder = &CPU_Core_Dyn_X86_Run;
379
380 return ret;
381 }
382
CPU_Core_Dyn_X86_Init(void)383 void CPU_Core_Dyn_X86_Init(void) {
384 Bits i;
385 /* Setup the global registers and their flags */
386 for (i=0;i<G_MAX;i++) DynRegs[i].genreg=0;
387 DynRegs[G_EAX].data=®_eax;
388 DynRegs[G_EAX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
389 DynRegs[G_ECX].data=®_ecx;
390 DynRegs[G_ECX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
391 DynRegs[G_EDX].data=®_edx;
392 DynRegs[G_EDX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
393 DynRegs[G_EBX].data=®_ebx;
394 DynRegs[G_EBX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
395
396 DynRegs[G_EBP].data=®_ebp;
397 DynRegs[G_EBP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
398 DynRegs[G_ESP].data=®_esp;
399 DynRegs[G_ESP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
400 DynRegs[G_EDI].data=®_edi;
401 DynRegs[G_EDI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
402 DynRegs[G_ESI].data=®_esi;
403 DynRegs[G_ESI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
404
405 DynRegs[G_ES].data=&Segs.phys[es];
406 DynRegs[G_ES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
407 DynRegs[G_CS].data=&Segs.phys[cs];
408 DynRegs[G_CS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
409 DynRegs[G_SS].data=&Segs.phys[ss];
410 DynRegs[G_SS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
411 DynRegs[G_DS].data=&Segs.phys[ds];
412 DynRegs[G_DS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
413 DynRegs[G_FS].data=&Segs.phys[fs];
414 DynRegs[G_FS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
415 DynRegs[G_GS].data=&Segs.phys[gs];
416 DynRegs[G_GS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
417
418 DynRegs[G_FLAGS].data=®_flags;
419 DynRegs[G_FLAGS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
420
421 DynRegs[G_NEWESP].data=&extra_regs.newesp;
422 DynRegs[G_NEWESP].flags=0;
423
424 DynRegs[G_EIP].data=®_eip;
425 DynRegs[G_EIP].flags=DYNFLG_LOAD|DYNFLG_SAVE;
426
427 DynRegs[G_EA].data=&extra_regs.ea;
428 DynRegs[G_EA].flags=0;
429 DynRegs[G_STACK].data=&extra_regs.stack;
430 DynRegs[G_STACK].flags=0;
431 DynRegs[G_CYCLES].data=&CPU_Cycles;
432 DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
433 DynRegs[G_TMPB].data=&extra_regs.tmpb;
434 DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16;
435 DynRegs[G_TMPW].data=&extra_regs.tmpd;
436 DynRegs[G_TMPW].flags=DYNFLG_HAS16;
437 DynRegs[G_SHIFT].data=&extra_regs.shift;
438 DynRegs[G_SHIFT].flags=DYNFLG_HAS8|DYNFLG_HAS16;
439 DynRegs[G_EXIT].data=0;
440 DynRegs[G_EXIT].flags=DYNFLG_HAS16;
441 /* Init the generator */
442 gen_init();
443
444 #if defined(X86_DYNFPU_DH_ENABLED)
445 /* Init the fpu state */
446 dyn_dh_fpu.dh_fpu_enabled=true;
447 dyn_dh_fpu.state_used=false;
448 dyn_dh_fpu.cw=0x37f;
449 // FINIT
450 memset(&dyn_dh_fpu.state, 0, sizeof(dyn_dh_fpu.state));
451 dyn_dh_fpu.state.cw = 0x37F;
452 dyn_dh_fpu.state.tag = 0xFFFF;
453 #endif
454
455 return;
456 }
457
CPU_Core_Dyn_X86_Cache_Init(bool enable_cache)458 void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache) {
459 /* Initialize code cache and dynamic blocks */
460 cache_init(enable_cache);
461 }
462
CPU_Core_Dyn_X86_Cache_Close(void)463 void CPU_Core_Dyn_X86_Cache_Close(void) {
464 cache_close();
465 }
466
CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu)467 void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) {
468 #if defined(X86_DYNFPU_DH_ENABLED)
469 dyn_dh_fpu.dh_fpu_enabled=dh_fpu;
470 #endif
471 }
472
473 #endif
474