/* * Copyright (C) 2002-2019 The DOSBox Team * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation; either version 2 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License along * with this program; if not, write to the Free Software Foundation, Inc., * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ #include "dosbox.h" #if (C_DYNAMIC_X86) #include #include #include #include #include #include #if defined (WIN32) #include #include #endif #if (C_HAVE_MPROTECT) #include #include #ifndef PAGESIZE #define PAGESIZE 4096 #endif #endif /* C_HAVE_MPROTECT */ #include "callback.h" #include "regs.h" #include "mem.h" #include "cpu.h" #include "debug.h" #include "paging.h" #include "inout.h" #include "fpu.h" #define CACHE_MAXSIZE (4096*3) #define CACHE_TOTAL (1024*1024*8) #define CACHE_PAGES (512) #define CACHE_BLOCKS (64*1024) #define CACHE_ALIGN (16) #define DYN_HASH_SHIFT (4) #define DYN_PAGE_HASH (4096>>DYN_HASH_SHIFT) #define DYN_LINKS (16) //#define DYN_LOG 1 //Turn logging on #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions #define X86_DYNFPU_DH_ENABLED #endif enum { G_EAX,G_ECX,G_EDX,G_EBX, G_ESP,G_EBP,G_ESI,G_EDI, G_ES,G_CS,G_SS,G_DS,G_FS,G_GS, G_FLAGS,G_NEWESP,G_EIP, G_EA,G_STACK,G_CYCLES, G_TMPB,G_TMPW,G_SHIFT, G_EXIT, G_MAX, }; enum SingleOps { SOP_INC,SOP_DEC, SOP_NOT,SOP_NEG, }; enum DualOps { DOP_ADD,DOP_ADC, DOP_SUB,DOP_SBB, DOP_CMP,DOP_XOR, DOP_AND,DOP_OR, DOP_TEST, DOP_MOV, DOP_XCHG, }; enum ShiftOps { SHIFT_ROL,SHIFT_ROR, SHIFT_RCL,SHIFT_RCR, SHIFT_SHL,SHIFT_SHR, SHIFT_SAL,SHIFT_SAR, }; enum BranchTypes { BR_O,BR_NO,BR_B,BR_NB, BR_Z,BR_NZ,BR_BE,BR_NBE, BR_S,BR_NS,BR_P,BR_NP, BR_L,BR_NL,BR_LE,BR_NLE }; enum BlockReturn { BR_Normal=0, BR_Cycles, BR_Link1,BR_Link2, BR_Opcode, #if (C_DEBUG) BR_OpcodeFull, #endif BR_Iret, BR_CallBack, BR_SMCBlock }; #define SMC_CURRENT_BLOCK 0xffff #define DYNFLG_HAS16 0x1 //Would like 8-bit host reg support #define DYNFLG_HAS8 0x2 //Would like 16-bit host reg support #define DYNFLG_LOAD 0x4 //Load value when accessed #define DYNFLG_SAVE 0x8 //Needs to be saved back at the end of block #define DYNFLG_CHANGED 0x10 //Value is in a register and changed from load #define DYNFLG_ACTIVE 0x20 //Register has an active value class GenReg; class CodePageHandler; struct DynReg { Bitu flags; GenReg * genreg; void * data; }; enum DynAccess { DA_d,DA_w, DA_bh,DA_bl }; enum ByteCombo { BC_ll,BC_lh, BC_hl,BC_hh, }; static DynReg DynRegs[G_MAX]; #define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ] static struct { Bit32u ea,tmpb,tmpd,stack,shift,newesp; } extra_regs; #define IllegalOption(msg) E_Exit("DYNX86: illegal option in " msg) #include "core_dyn_x86/cache.h" static struct { Bitu callback; Bit32u readdata; } core_dyn; #if defined(X86_DYNFPU_DH_ENABLED) static struct dyn_dh_fpu { Bit16u cw,host_cw; bool state_used; // some fields expanded here for alignment purposes struct { Bit32u cw; Bit32u sw; Bit32u tag; Bit32u ip; Bit32u cs; Bit32u ea; Bit32u ds; Bit8u st_reg[8][10]; } state; FPU_P_Reg temp,temp2; Bit32u dh_fpu_enabled; Bit8u temp_state[128]; } dyn_dh_fpu; #endif #define X86 0x01 #define X86_64 0x02 #if C_TARGETCPU == X86_64 #include "core_dyn_x86/risc_x64.h" #elif C_TARGETCPU == X86 #include "core_dyn_x86/risc_x86.h" #else #error DYN_X86 core not supported for this CPU target. #endif struct DynState { DynReg regs[G_MAX]; }; static void dyn_flags_host_to_gen(void) { gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS)); gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST); gen_load_flags(DREG(EXIT)); gen_releasereg(DREG(EXIT)); gen_releasereg(DREG(FLAGS)); } static void dyn_flags_gen_to_host(void) { gen_save_flags(DREG(EXIT)); gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST); gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST); gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save gen_releasereg(DREG(EXIT)); gen_releasereg(DREG(FLAGS)); } static void dyn_savestate(DynState * state) { for (Bitu i=0;iregs[i].flags=DynRegs[i].flags; state->regs[i].genreg=DynRegs[i].genreg; } } static void dyn_loadstate(DynState * state) { for (Bitu i=0;iregs[i]); } } static void dyn_synchstate(DynState * state) { for (Bitu i=0;iregs[i]); } } static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) { dst_reg->flags=src_reg->flags; dst_reg->genreg=src_reg->genreg; } static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) { dst_reg->flags=src_reg->flags; dst_reg->genreg=src_reg->genreg; dst_reg->genreg->dynreg=dst_reg; // necessary when register has been released } #include "core_dyn_x86/decoder.h" Bits CPU_Core_Dyn_X86_Run(void) { // helper class to auto-save DH_FPU state on function exit class auto_dh_fpu { public: ~auto_dh_fpu(void) { #if defined(X86_DYNFPU_DH_ENABLED) if (dyn_dh_fpu.state_used) gen_dh_fpu_save(); #endif }; }; auto_dh_fpu fpu_saver; /* Determine the linear address of CS:EIP */ restart_core: PhysPt ip_point=SegPhys(cs)+reg_eip; #if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif #endif CodePageHandler * chandler=0; if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { CPU_Exception(cpu.exception.which,cpu.exception.error); goto restart_core; } if (!chandler) { return CPU_Core_Normal_Run(); } /* Find correct Dynamic Block to run */ CacheBlock * block=chandler->FindCacheBlock(ip_point&4095); if (!block) { if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) { block=CreateCacheBlock(chandler,ip_point,32); } else { Bit32s old_cycles=CPU_Cycles; CPU_Cycles=1; // manually save fpu_saver = auto_dh_fpu(); Bits nc_retcode=CPU_Core_Normal_Run(); if (!nc_retcode) { CPU_Cycles=old_cycles-1; goto restart_core; } CPU_CycleLeft+=old_cycles; return nc_retcode; } } run_block: cache.block.running=0; BlockReturn ret=gen_runcode(block->cache.start); #if C_DEBUG cycle_count += 32; #endif switch (ret) { case BR_Iret: #if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) { return debugCallback; } #endif #endif if (!GETFLAG(TF)) { if (GETFLAG(IF) && PIC_IRQCheck) { return CBRET_NONE; } goto restart_core; } cpudecoder=CPU_Core_Dyn_X86_Trap_Run; return CBRET_NONE; case BR_Normal: /* Maybe check if we staying in the same page? */ #if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif #endif goto restart_core; case BR_Cycles: #if C_DEBUG #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif #endif return CBRET_NONE; case BR_CallBack: return core_dyn.callback; case BR_SMCBlock: // LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip); cpu.exception.which=0; // fallthrough, let the normal core handle the block-modifying instruction case BR_Opcode: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Normal_Run(); #if (C_DEBUG) case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; return CPU_Core_Full_Run(); #endif case BR_Link1: case BR_Link2: { Bit32u temp_ip=SegPhys(cs)+reg_eip; CodePageHandler * temp_handler=(CodePageHandler *)get_tlb_readhandler(temp_ip); if (temp_handler->flags & (cpu.code.big ? PFLAG_HASCODE32:PFLAG_HASCODE16)) { block=temp_handler->FindCacheBlock(temp_ip & 4095); if (!block) goto restart_core; cache.block.running->LinkTo(ret==BR_Link2,block); goto run_block; } } goto restart_core; } return CBRET_NONE; } Bits CPU_Core_Dyn_X86_Trap_Run(void) { Bit32s oldCycles = CPU_Cycles; CPU_Cycles = 1; cpu.trap_skip = false; Bits ret=CPU_Core_Normal_Run(); if (!cpu.trap_skip) CPU_HW_Interrupt(1); CPU_Cycles = oldCycles-1; cpudecoder = &CPU_Core_Dyn_X86_Run; return ret; } void CPU_Core_Dyn_X86_Init(void) { Bits i; /* Setup the global registers and their flags */ for (i=0;i