/* * Copyright (C) 2002-2009 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., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ /* $Id: core_dyn_x86.cpp,v 1.35 2009/05/27 09:15:41 qbix79 Exp $ */ #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) #if 0 #define DYN_LOG LOG_MSG #else #define DYN_LOG #endif #if C_FPU #define CPU_FPU 1 //Enable FPU escape instructions #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 { Bitu ea,tmpb,tmpd,stack,shift,newesp; } extra_regs; static void IllegalOption(const char* msg) { E_Exit("DynCore: illegal option in %s",msg); } #include "core_dyn_x86/cache.h" static struct { Bitu callback; Bit32u readdata; } core_dyn; static struct { Bit32u state[32]; FPU_P_Reg temp,temp2; Bit32u dh_fpu_enabled; Bit32u state_used; Bit32u cw,host_cw; Bit8u temp_state[128]; } dyn_dh_fpu; #include "core_dyn_x86/risc_x86.h" 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" #if defined (_MSC_VER) #define DH_FPU_SAVE_REINIT \ { \ __asm { \ __asm fnsave dyn_dh_fpu.state[0] \ } \ dyn_dh_fpu.state_used=false; \ dyn_dh_fpu.state[0]|=0x3f; \ } #else #define DH_FPU_SAVE_REINIT \ { \ __asm__ volatile ( \ "fnsave %0 \n" \ : \ : "m" (dyn_dh_fpu.state[0]) \ : "memory" \ ); \ dyn_dh_fpu.state_used=false; \ dyn_dh_fpu.state[0]|=0x3f; \ } #endif Bits CPU_Core_Dyn_X86_Run(void) { /* Determine the linear address of CS:EIP */ restart_core: PhysPt ip_point=SegPhys(cs)+reg_eip; #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif CodePageHandler * chandler=0; if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) { CPU_Exception(cpu.exception.which,cpu.exception.error); goto restart_core; } if (!chandler) { if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT 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 { Bitu old_cycles=CPU_Cycles; CPU_Cycles=1; Bits nc_retcode=CPU_Core_Normal_Run(); if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT 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); switch (ret) { case BR_Iret: #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) { if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return debugCallback; } #endif if (!GETFLAG(TF)) goto restart_core; cpudecoder=CPU_Core_Dyn_X86_Trap_Run; if (!dyn_dh_fpu.state_used) return CBRET_NONE; DH_FPU_SAVE_REINIT return CBRET_NONE; case BR_Normal: /* Maybe check if we staying in the same page? */ #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif goto restart_core; case BR_Cycles: #if C_HEAVY_DEBUG if (DEBUG_HeavyIsBreakpoint()) return debugCallback; #endif if (!dyn_dh_fpu.state_used) return CBRET_NONE; DH_FPU_SAVE_REINIT return CBRET_NONE; case BR_CallBack: if (!dyn_dh_fpu.state_used) return core_dyn.callback; DH_FPU_SAVE_REINIT 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; if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Normal_Run(); #if (C_DEBUG) case BR_OpcodeFull: CPU_CycleLeft+=CPU_Cycles; CPU_Cycles=1; if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CPU_Core_Full_Run(); #endif case BR_Link1: case BR_Link2: { Bitu temp_ip=SegPhys(cs)+reg_eip; CodePageHandler * temp_handler=(CodePageHandler *)get_tlb_readhandler(temp_ip); if (temp_handler->flags & PFLAG_HASCODE) { 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; } if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT return CBRET_NONE; } Bits CPU_Core_Dyn_X86_Trap_Run(void) { Bits 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