DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Defines
src/cpu/core_dyn_x86.cpp
00001 /*
00002  *  Copyright (C) 2002-2015  The DOSBox Team
00003  *
00004  *  This program is free software; you can redistribute it and/or modify
00005  *  it under the terms of the GNU General Public License as published by
00006  *  the Free Software Foundation; either version 2 of the License, or
00007  *  (at your option) any later version.
00008  *
00009  *  This program is distributed in the hope that it will be useful,
00010  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  *  GNU General Public License for more details.
00013  *
00014  *  You should have received a copy of the GNU General Public License
00015  *  along with this program; if not, write to the Free Software
00016  *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
00017  */
00018 
00019 
00020 #include "dosbox.h"
00021 
00022 #if (C_DYNAMIC_X86)
00023 
00024 #include <assert.h>
00025 #include <stdarg.h>
00026 #include <stdio.h>
00027 #include <string.h>
00028 #include <stddef.h>
00029 #include <stdlib.h>
00030 
00031 #if defined (WIN32)
00032 #include <windows.h>
00033 #include <winbase.h>
00034 #endif
00035 
00036 #if (C_HAVE_MPROTECT)
00037 #include <sys/mman.h>
00038 
00039 #include <limits.h>
00040 #ifndef PAGESIZE
00041 #define PAGESIZE 4096
00042 #endif
00043 #endif /* C_HAVE_MPROTECT */
00044 
00045 #include "callback.h"
00046 #include "regs.h"
00047 #include "mem.h"
00048 #include "cpu.h"
00049 #include "debug.h"
00050 #include "paging.h"
00051 #include "inout.h"
00052 #include "fpu.h"
00053 
00054 #define CACHE_MAXSIZE   (4096*3)
00055 #define CACHE_TOTAL             (1024*1024*8)
00056 #define CACHE_PAGES             (512)
00057 #define CACHE_BLOCKS    (64*1024)
00058 #define CACHE_ALIGN             (16)
00059 #define DYN_HASH_SHIFT  (4)
00060 #define DYN_PAGE_HASH   (4096>>DYN_HASH_SHIFT)
00061 #define DYN_LINKS               (16)
00062 
00063 //#define DYN_LOG 1 //Turn logging on
00064 
00065 
00066 #if C_FPU
00067 #define CPU_FPU 1                                               //Enable FPU escape instructions
00068 #endif
00069 
00070 enum {
00071         G_EAX,G_ECX,G_EDX,G_EBX,
00072         G_ESP,G_EBP,G_ESI,G_EDI,
00073         G_ES,G_CS,G_SS,G_DS,G_FS,G_GS,
00074         G_FLAGS,G_NEWESP,G_EIP,
00075         G_EA,G_STACK,G_CYCLES,
00076         G_TMPB,G_TMPW,G_SHIFT,
00077         G_EXIT,
00078         G_MAX,
00079 };
00080 
00081 enum SingleOps {
00082         SOP_INC,SOP_DEC,
00083         SOP_NOT,SOP_NEG,
00084 };
00085 
00086 enum DualOps {
00087         DOP_ADD,DOP_ADC,
00088         DOP_SUB,DOP_SBB,
00089         DOP_CMP,DOP_XOR,
00090         DOP_AND,DOP_OR,
00091         DOP_TEST,
00092         DOP_MOV,
00093         DOP_XCHG,
00094 };
00095 
00096 enum ShiftOps {
00097         SHIFT_ROL,SHIFT_ROR,
00098         SHIFT_RCL,SHIFT_RCR,
00099         SHIFT_SHL,SHIFT_SHR,
00100         SHIFT_SAL,SHIFT_SAR,
00101 };
00102 
00103 enum BranchTypes {
00104         BR_O,BR_NO,BR_B,BR_NB,
00105         BR_Z,BR_NZ,BR_BE,BR_NBE,
00106         BR_S,BR_NS,BR_P,BR_NP,
00107         BR_L,BR_NL,BR_LE,BR_NLE
00108 };
00109 
00110 
00111 enum BlockReturn {
00112         BR_Normal=0,
00113         BR_Cycles,
00114         BR_Link1,BR_Link2,
00115         BR_Opcode,
00116 #if (C_DEBUG)
00117         BR_OpcodeFull,
00118 #endif
00119         BR_Iret,
00120         BR_CallBack,
00121         BR_SMCBlock
00122 };
00123 
00124 #define SMC_CURRENT_BLOCK       0xffff
00125 
00126 
00127 #define DYNFLG_HAS16            0x1             //Would like 8-bit host reg support
00128 #define DYNFLG_HAS8                     0x2             //Would like 16-bit host reg support
00129 #define DYNFLG_LOAD                     0x4             //Load value when accessed
00130 #define DYNFLG_SAVE                     0x8             //Needs to be saved back at the end of block
00131 #define DYNFLG_CHANGED          0x10    //Value is in a register and changed from load
00132 #define DYNFLG_ACTIVE           0x20    //Register has an active value
00133 
00134 class GenReg;
00135 class CodePageHandler;
00136 
00137 struct DynReg {
00138         Bitu flags;
00139         GenReg * genreg;
00140         void * data;
00141 }; 
00142 
00143 enum DynAccess {
00144         DA_d,DA_w,
00145         DA_bh,DA_bl
00146 };
00147 
00148 enum ByteCombo {
00149         BC_ll,BC_lh,
00150         BC_hl,BC_hh,
00151 };
00152 
00153 static DynReg DynRegs[G_MAX];
00154 #define DREG(_WHICH_) &DynRegs[G_ ## _WHICH_ ]
00155 
00156 static struct {
00157         Bitu ea,tmpb,tmpd,stack,shift,newesp;
00158 } extra_regs;
00159 
00160 static void IllegalOption(const char* msg) {
00161         E_Exit("DynCore: illegal option in %s",msg);
00162 }
00163 
00164 #include "core_dyn_x86/cache.h" 
00165 
00166 static struct {
00167         Bitu callback;
00168         Bit32u readdata;
00169 } core_dyn;
00170 
00171 static struct {
00172         Bit32u          state[32];
00173         FPU_P_Reg       temp,temp2;
00174         Bit32u          dh_fpu_enabled;
00175         Bit32u          state_used;
00176         Bit32u          cw,host_cw;
00177         Bit8u           temp_state[128];
00178 } dyn_dh_fpu;
00179 
00180 
00181 #include "core_dyn_x86/risc_x86.h"
00182 
00183 struct DynState {
00184         DynReg regs[G_MAX];
00185 };
00186 
00187 static void dyn_flags_host_to_gen(void) {
00188         gen_dop_word(DOP_MOV,true,DREG(EXIT),DREG(FLAGS));
00189         gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
00190         gen_load_flags(DREG(EXIT));
00191         gen_releasereg(DREG(EXIT));
00192         gen_releasereg(DREG(FLAGS));
00193 }
00194 
00195 static void dyn_flags_gen_to_host(void) {
00196         gen_save_flags(DREG(EXIT));
00197         gen_dop_word_imm(DOP_AND,true,DREG(EXIT),FMASK_TEST);
00198         gen_dop_word_imm(DOP_AND,true,DREG(FLAGS),~FMASK_TEST);
00199         gen_dop_word(DOP_OR,true,DREG(FLAGS),DREG(EXIT)); //flags are marked for save
00200         gen_releasereg(DREG(EXIT));
00201         gen_releasereg(DREG(FLAGS));
00202 }
00203 
00204 static void dyn_savestate(DynState * state) {
00205         for (Bitu i=0;i<G_MAX;i++) {
00206                 state->regs[i].flags=DynRegs[i].flags;
00207                 state->regs[i].genreg=DynRegs[i].genreg;
00208         }
00209 }
00210 
00211 static void dyn_loadstate(DynState * state) {
00212         for (Bitu i=0;i<G_MAX;i++) {
00213                 gen_setupreg(&DynRegs[i],&state->regs[i]);
00214         }
00215 }
00216 
00217 static void dyn_synchstate(DynState * state) {
00218         for (Bitu i=0;i<G_MAX;i++) {
00219                 gen_synchreg(&DynRegs[i],&state->regs[i]);
00220         }
00221 }
00222 
00223 static void dyn_saveregister(DynReg * src_reg, DynReg * dst_reg) {
00224         dst_reg->flags=src_reg->flags;
00225         dst_reg->genreg=src_reg->genreg;
00226 }
00227 
00228 static void dyn_restoreregister(DynReg * src_reg, DynReg * dst_reg) {
00229         dst_reg->flags=src_reg->flags;
00230         dst_reg->genreg=src_reg->genreg;
00231         dst_reg->genreg->dynreg=dst_reg;        // necessary when register has been released
00232 }
00233 
00234 #include "core_dyn_x86/decoder.h"
00235 
00236 #if defined (_MSC_VER)
00237 #define DH_FPU_SAVE_REINIT                              \
00238 {                                                                               \
00239         __asm {                                                         \
00240         __asm   fnsave  dyn_dh_fpu.state[0]     \
00241         }                                                                       \
00242         dyn_dh_fpu.state_used=false;            \
00243         dyn_dh_fpu.state[0]|=0x3f;                      \
00244 }
00245 #else
00246 #define DH_FPU_SAVE_REINIT                              \
00247 {                                                                               \
00248         __asm__ volatile (                                      \
00249                 "fnsave         %0                      \n"             \
00250                 :       "=m" (dyn_dh_fpu.state[0])      \
00251                 :                                                               \
00252                 :       "memory"                                        \
00253         );                                                                      \
00254         dyn_dh_fpu.state_used=false;            \
00255         dyn_dh_fpu.state[0]|=0x3f;                      \
00256 }
00257 #endif
00258 
00259 extern bool use_dynamic_core_with_paging;
00260 extern int dynamic_core_cache_block_size;
00261 
00262 static bool paging_warning = true;
00263 
00264 Bits CPU_Core_Dyn_X86_Run(void) {
00265     /* Dynamic core is NOT compatible with the way page faults
00266      * in the guest are handled in this emulator. Do not use
00267      * dynamic core if paging is enabled. Do not comment this
00268      * out, even if it happens to work for a minute, a half
00269      * hour, a day, because it will turn around and cause
00270      * Windows 95 to crash when you've become most comfortable
00271      * with the idea that it works. This code cannot handle
00272      * the sudden context switch of a page fault and it never
00273      * will. Don't do it. You have been warned. */
00274     if (paging.enabled && !use_dynamic_core_with_paging) {
00275         if (paging_warning) {
00276             LOG_MSG("Dynamic core warning: The guest OS/Application has just switched on 80386 paging, which is not supported by the dynamic core. The normal core will be used until paging is switched off again.");
00277             paging_warning = false;
00278         }
00279 
00280         return CPU_Core_Normal_Run();
00281     }
00282 
00283         /* Determine the linear address of CS:EIP */
00284 restart_core:
00285         PhysPt ip_point=SegPhys(cs)+reg_eip;
00286 #if C_DEBUG
00287 #if C_HEAVY_DEBUG
00288                 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
00289 #endif
00290 #endif
00291         CodePageHandler * chandler=0;
00292         if (GCC_UNLIKELY(MakeCodePage(ip_point,chandler))) {
00293                 CPU_Exception(cpu.exception.which,cpu.exception.error);
00294                 goto restart_core;
00295         }
00296         if (!chandler) {
00297                 if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00298                 return CPU_Core_Normal_Run();
00299         }
00300         /* Find correct Dynamic Block to run */
00301         CacheBlock * block=chandler->FindCacheBlock(ip_point&4095);
00302         if (!block) {
00303                 if (!chandler->invalidation_map || (chandler->invalidation_map[ip_point&4095]<4)) {
00304                         block=CreateCacheBlock(chandler,ip_point,dynamic_core_cache_block_size);
00305                 } else {
00306                         Bitu old_cycles=CPU_Cycles;
00307                         CPU_Cycles=1;
00308                         Bits nc_retcode=CPU_Core_Normal_Run();
00309                         if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00310                         if (!nc_retcode) {
00311                                 CPU_Cycles=old_cycles-1;
00312                                 goto restart_core;
00313                         }
00314                         CPU_CycleLeft+=old_cycles;
00315                         return nc_retcode; 
00316                 }
00317         }
00318 run_block:
00319         Bitu CPU_CyclesOld = CPU_Cycles;
00320         cache.block.running=0;
00321         BlockReturn ret=gen_runcode(block->cache.start);
00322         cycle_count += CPU_CyclesOld - CPU_Cycles;
00323         switch (ret) {
00324         case BR_Iret:
00325 #if C_DEBUG
00326 #if C_HEAVY_DEBUG
00327                 if (DEBUG_HeavyIsBreakpoint()) {
00328                         if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00329                         return debugCallback;
00330                 }
00331 #endif
00332 #endif
00333                 if (!GETFLAG(TF)) {
00334                         if (GETFLAG(IF) && PIC_IRQCheck) {
00335                                 if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00336                                 return CBRET_NONE;
00337                         }
00338                         goto restart_core;
00339                 }
00340                 cpudecoder=CPU_Core_Dyn_X86_Trap_Run;
00341                 if (!dyn_dh_fpu.state_used) return CBRET_NONE;
00342                 DH_FPU_SAVE_REINIT
00343                 return CBRET_NONE;
00344         case BR_Normal:
00345                 /* Maybe check if we staying in the same page? */
00346 #if C_DEBUG
00347 #if C_HEAVY_DEBUG
00348                 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
00349 #endif
00350 #endif
00351                 goto restart_core;
00352         case BR_Cycles:
00353 #if C_DEBUG
00354 #if C_HEAVY_DEBUG                       
00355                 if (DEBUG_HeavyIsBreakpoint()) return debugCallback;
00356 #endif
00357 #endif
00358                 if (!dyn_dh_fpu.state_used) return CBRET_NONE;
00359                 DH_FPU_SAVE_REINIT
00360                 return CBRET_NONE;
00361         case BR_CallBack:
00362                 if (!dyn_dh_fpu.state_used) return core_dyn.callback;
00363                 DH_FPU_SAVE_REINIT
00364                 return core_dyn.callback;
00365         case BR_SMCBlock:
00366 //              LOG_MSG("selfmodification of running block at %x:%x",SegValue(cs),reg_eip);
00367                 cpu.exception.which=0;
00368                 // fallthrough, let the normal core handle the block-modifying instruction
00369         case BR_Opcode:
00370                 CPU_CycleLeft+=CPU_Cycles;
00371                 CPU_Cycles=1;
00372                 if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00373                 return CPU_Core_Normal_Run();
00374 #if (C_DEBUG)
00375         case BR_OpcodeFull:
00376                 CPU_CycleLeft+=CPU_Cycles;
00377                 CPU_Cycles=1;
00378                 if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00379                 return CPU_Core_Full_Run();
00380 #endif
00381         case BR_Link1:
00382         case BR_Link2:
00383                 {
00384                         Bitu temp_ip=SegPhys(cs)+reg_eip;
00385                         CodePageHandler * temp_handler=(CodePageHandler *)get_tlb_readhandler(temp_ip);
00386                         if (temp_handler->getFlags() & PFLAG_HASCODE) {
00387                                 block=temp_handler->FindCacheBlock(temp_ip & 4095);
00388                                 if (!block) goto restart_core;
00389                                 cache.block.running->LinkTo(ret==BR_Link2,block);
00390                                 goto run_block;
00391                         }
00392                 }
00393                 goto restart_core;
00394         }
00395         if (dyn_dh_fpu.state_used) DH_FPU_SAVE_REINIT
00396         return CBRET_NONE;
00397 }
00398 
00399 Bits CPU_Core_Dyn_X86_Trap_Run(void) {
00400         Bits oldCycles = CPU_Cycles;
00401         CPU_Cycles = 1;
00402         cpu.trap_skip = false;
00403 
00404         Bits ret=CPU_Core_Normal_Run();
00405         if (!cpu.trap_skip) CPU_HW_Interrupt(1);
00406         CPU_Cycles = oldCycles-1;
00407         cpudecoder = &CPU_Core_Dyn_X86_Run;
00408 
00409         return ret;
00410 }
00411 
00412 void CPU_Core_Dyn_X86_Shutdown(void) {
00413         gen_free();
00414 }
00415 
00416 void CPU_Core_Dyn_X86_Init(void) {
00417         Bits i;
00418         /* Setup the global registers and their flags */
00419         for (i=0;i<G_MAX;i++) DynRegs[i].genreg=0;
00420         DynRegs[G_EAX].data=&reg_eax;
00421         DynRegs[G_EAX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00422         DynRegs[G_ECX].data=&reg_ecx;
00423         DynRegs[G_ECX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00424         DynRegs[G_EDX].data=&reg_edx;
00425         DynRegs[G_EDX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00426         DynRegs[G_EBX].data=&reg_ebx;
00427         DynRegs[G_EBX].flags=DYNFLG_HAS8|DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00428 
00429         DynRegs[G_EBP].data=&reg_ebp;
00430         DynRegs[G_EBP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00431         DynRegs[G_ESP].data=&reg_esp;
00432         DynRegs[G_ESP].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00433         DynRegs[G_EDI].data=&reg_edi;
00434         DynRegs[G_EDI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00435         DynRegs[G_ESI].data=&reg_esi;
00436         DynRegs[G_ESI].flags=DYNFLG_HAS16|DYNFLG_LOAD|DYNFLG_SAVE;
00437 
00438         DynRegs[G_ES].data=&Segs.phys[es];
00439         DynRegs[G_ES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00440         DynRegs[G_CS].data=&Segs.phys[cs];
00441         DynRegs[G_CS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00442         DynRegs[G_SS].data=&Segs.phys[ss];
00443         DynRegs[G_SS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00444         DynRegs[G_DS].data=&Segs.phys[ds];
00445         DynRegs[G_DS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00446         DynRegs[G_FS].data=&Segs.phys[fs];
00447         DynRegs[G_FS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00448         DynRegs[G_GS].data=&Segs.phys[gs];
00449         DynRegs[G_GS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00450 
00451         DynRegs[G_FLAGS].data=&reg_flags;
00452         DynRegs[G_FLAGS].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00453 
00454         DynRegs[G_NEWESP].data=&extra_regs.newesp;
00455         DynRegs[G_NEWESP].flags=0;
00456 
00457         DynRegs[G_EIP].data=&reg_eip;
00458         DynRegs[G_EIP].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00459 
00460         DynRegs[G_EA].data=&extra_regs.ea;
00461         DynRegs[G_EA].flags=0;
00462         DynRegs[G_STACK].data=&extra_regs.stack;
00463         DynRegs[G_STACK].flags=0;
00464         DynRegs[G_CYCLES].data=&CPU_Cycles;
00465         DynRegs[G_CYCLES].flags=DYNFLG_LOAD|DYNFLG_SAVE;
00466         DynRegs[G_TMPB].data=&extra_regs.tmpb;
00467         DynRegs[G_TMPB].flags=DYNFLG_HAS8|DYNFLG_HAS16;
00468         DynRegs[G_TMPW].data=&extra_regs.tmpd;
00469         DynRegs[G_TMPW].flags=DYNFLG_HAS16;
00470         DynRegs[G_SHIFT].data=&extra_regs.shift;
00471         DynRegs[G_SHIFT].flags=DYNFLG_HAS8|DYNFLG_HAS16;
00472         DynRegs[G_EXIT].data=0;
00473         DynRegs[G_EXIT].flags=DYNFLG_HAS16;
00474         /* Init the generator */
00475         gen_init();
00476 
00477         /* Init the fpu state */
00478         dyn_dh_fpu.dh_fpu_enabled=true;
00479         dyn_dh_fpu.state_used=false;
00480         dyn_dh_fpu.cw=0x37f;
00481 #if defined (_MSC_VER)
00482         __asm {
00483         __asm   finit
00484         __asm   fsave   dyn_dh_fpu.state[0]
00485         __asm   fstcw   dyn_dh_fpu.host_cw
00486         }
00487 #else
00488         __asm__ volatile (
00489                 "finit                                  \n"
00490                 "fsave          %0                      \n"
00491                 "fstcw          %1                      \n"
00492                 :       "=m" (dyn_dh_fpu.state[0]), "=m" (dyn_dh_fpu.host_cw)
00493                 :
00494                 :       "memory"
00495         );
00496 #endif
00497 
00498         return;
00499 }
00500 
00501 void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache) {
00502         /* Initialize code cache and dynamic blocks */
00503         cache_init(enable_cache);
00504 }
00505 
00506 void CPU_Core_Dyn_X86_Cache_Close(void) {
00507         cache_close();
00508 }
00509 
00510 void CPU_Core_Dyn_X86_Cache_Reset(void) {
00511         cache_reset();
00512 }
00513 
00514 void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu) {
00515         dyn_dh_fpu.dh_fpu_enabled=dh_fpu;
00516 }
00517 
00518 Bit32u fpu_state[32];
00519 
00520 void CPU_Core_Dyn_X86_SaveDHFPUState(void) {
00521         if (dyn_dh_fpu.dh_fpu_enabled) {
00522                 if (dyn_dh_fpu.state_used!=0) {
00523 #if defined (_MSC_VER)
00524                         __asm {
00525                         __asm   fsave   fpu_state[0]
00526                         __asm   finit
00527                         }
00528 #else
00529                         __asm__ volatile (
00530                                 "fsave          %0                      \n"
00531                                 "finit                                  \n"
00532                                 :       "=m" (fpu_state[0])
00533                                 :
00534                                 :       "memory"
00535                         );
00536 #endif
00537                 }
00538         }
00539 }
00540 
00541 void CPU_Core_Dyn_X86_RestoreDHFPUState(void) {
00542         if (dyn_dh_fpu.dh_fpu_enabled) {
00543                 if (dyn_dh_fpu.state_used!=0) {
00544 #if defined (_MSC_VER)
00545                         __asm {
00546                         __asm   frstor  fpu_state[0]
00547                         }
00548 #else
00549                         __asm__ volatile (
00550                                 "frstor         %0                      \n"
00551                                 :
00552                                 :       "m" (fpu_state[0])
00553                                 :
00554                         );
00555 #endif
00556                 }
00557         }
00558 }
00559 
00560 #else
00561 
00562 void CPU_Core_Dyn_X86_SaveDHFPUState(void) {
00563 }
00564 
00565 void CPU_Core_Dyn_X86_RestoreDHFPUState(void) {
00566 }
00567 
00568 #endif