/*************************************************************************** * * All modifications in this file to the original code are * (C) Copyright 1992, ..., 2014 the "DOSEMU-Development-Team". * * for details see file COPYING in the DOSEMU distribution * * * SIMX86 a Intel 80x86 cpu emulator * Copyright (C) 1997,2001 Alberto Vignani, FIAT Research Center * a.vignani@crf.it * * 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., 675 Mass Ave, Cambridge, MA 02139, USA. * * Additional copyright notes: * * 1. The kernel-level vm86 handling was taken out of the Linux kernel * (linux/arch/i386/kernel/vm86.c). This code originaly was written by * Linus Torvalds with later enhancements by Lutz Molgedey and Hans Lermen. * ***************************************************************************/ #ifdef X86_EMULATOR #include #include /* for memset */ #include #include #include "emu.h" #include "timers.h" #include "pic.h" #include "mhpdbg.h" #include "cpu-emu.h" #include "emu86.h" #include "codegen-arch.h" #include "dpmi.h" #include "mapping.h" #include "dis8086.h" #include "sig.h" /* ======================================================================= */ #ifdef PROFILE hitimer_t AddTime, SearchTime, ExecTime, CleanupTime; // for debug hitimer_t GenTime, LinkTime; #endif static hitimer_t TotalTime; static int iniflag = 0; static hitimer_t sigEMUtime = 0; static hitimer_t lastEMUsig = 0; static unsigned long sigEMUdelta = 0; int eTimeCorrect; /* This needs to be merged someday with 'mode' */ volatile int CEmuStat = 0; int IsV86Emu = 1; int IsDpmiEmu = 1; /* This keeps the delta time in CPU clocks between SIGALRMs and * SIGPROFs, i.e. the time spent by the system outside of dosemu, * which should be considered in time stretching */ int e_sigpa_count; int in_vm86_emu = 0; int in_dpmi_emu = 0; jmp_buf jmp_env; union _SynCPU TheCPU_union; int Running = 0; volatile int InCompiledCode = 0; unsigned int trans_addr, return_addr; // PC static void e_gen_sigprof(sigcontext_t *scp, siginfo_t *si); #ifdef DEBUG_TREE FILE *tLog = NULL; #endif #ifdef ASM_DUMP FILE *aLog = NULL; #endif /* * -------------------------------------------------------------- * x86 sigcontext: * 00 unsigned short gs, __gsh; * 01 unsigned short fs, __fsh; * 02 unsigned short es, __esh; * 03 unsigned short ds, __dsh; * 04 unsigned long edi; * 05 unsigned long esi; * 06 unsigned long ebp; * 07 unsigned long esp; * 08 unsigned long ebx; * 09 unsigned long edx; * 10 unsigned long ecx; * 11 unsigned long eax; * 12 unsigned long trapno; * 13 unsigned long err; * 14 unsigned long eip; * 15 unsigned short cs, __csh; * 16 unsigned long eflags; * 17 unsigned long esp_at_signal; * 18 unsigned short ss, __ssh; * 19 fpregset_t fpstate; * 20 unsigned long oldmask; * 21 unsigned long cr2; * * x86 vm86plus regs: * 00 long ebx; * 01 long ecx; * 02 long edx; * 03 long esi; * 04 long edi; * 05 long ebp; * 06 long eax; * 07 long __null_ds; * 08 long __null_es; * 09 long __null_fs; * 10 long __null_gs; * 11 long orig_eax; * 12 long eip; * 13 unsigned short cs, __csh; * 14 long eflags; * 15 long esp; * 16 unsigned short ss, __ssh; * 17 unsigned short es, __esh; * 18 unsigned short ds, __dsh; * 19 unsigned short fs, __fsh; * 20 unsigned short gs, __gsh; * -------------------------------------------------------------- */ sigcontext_t e_scp; /* initialized to 0 */ /* ======================================================================= */ unsigned long eTSSMASK = 0; void e_priv_iopl(int pl) { pl &= 3; TheCPU.eflags = (TheCPU.eflags & ~EFLAGS_IOPL) | (pl << 12); e_printf("eIOPL: set IOPL to %d, flags=%#x\n",pl,TheCPU.eflags); } void InvalidateSegs(void) { CS_DTR.Attrib=0; SS_DTR.Attrib=0; DS_DTR.Attrib=0; ES_DTR.Attrib=0; FS_DTR.Attrib=0; GS_DTR.Attrib=0; } /* ======================================================================= */ static char ehextab[] = "0123456789abcdef"; /* WARNING - do not convert spaces to tabs! */ /* WARNING: the below code is written by some idiots, don't read! */ #define ERB_LLEN 0x39 #define ERB_LEFTM 5 #define ERB_L1 0x00 #define ERB_L2 (ERB_L1+ERB_LLEN) #define ERB_L3 (ERB_L1+2*ERB_LLEN) #define ERB_L4 (ERB_L1+3*ERB_LLEN) #define ERB_L5 (ERB_L1+4*ERB_LLEN) #define ERB_L6 (ERB_L5+0x23) #define ERB_L7 (ERB_L6+0x37) static char eregbuf[] = /*00*/ "\teax=00000000 ebx=00000000 ecx=00000000 edx=00000000 \n" /*39*/ "\tesi=00000000 edi=00000000 ebp=00000000 esp=00000000 \n" /*72*/ "\t vf=00000000 cs=0000 ds=0000 es=0000 \n" /*ab*/ "\t fs=0000 gs=0000 ss=0000 eip=00000000 \n" /*e4*/ "\tops=00 00 00 00 00 00 00 00 00 00\n" "\tstk=0000 0000 0000 0000 0000 0000 0000 0000 0000 0000\n"; static inline void exprintl(unsigned long val,char *bf,unsigned int pos) { char *p=bf+pos+7; unsigned long v = val; #ifdef __x86_64__ v &= 0xffffffffffff; if (v > 0xffffffff) p += 4; #endif while (v) { *p-- = ehextab[v&15]; v>>=4; } } static inline void exprintw(unsigned short val,char *bf,unsigned int pos) { char *p=bf+pos+3; unsigned long v = val; while (v) { *p-- = ehextab[v&15]; v>>=4; } } static inline void exprintb(unsigned char val,char *bf,unsigned int pos) { char *p=bf+pos+1; unsigned long v = val; while (v) { *p-- = ehextab[v&15]; v>>=4; } } char *e_print_regs(void) { static char buf[300]; char *p = buf; char *q = eregbuf; while (*q) *p++ = *q++; *p=0; exprintl(rEAX,buf,(ERB_L1+ERB_LEFTM)); exprintl(rEBX,buf,(ERB_L1+ERB_LEFTM)+13); exprintl(rECX,buf,(ERB_L1+ERB_LEFTM)+26); exprintl(rEDX,buf,(ERB_L1+ERB_LEFTM)+39); exprintl(rESI,buf,(ERB_L2+ERB_LEFTM)); exprintl(rEDI,buf,(ERB_L2+ERB_LEFTM)+13); exprintl(rEBP,buf,(ERB_L2+ERB_LEFTM)+26); exprintl(rESP,buf,(ERB_L2+ERB_LEFTM)+39); if (!(TheCPU.eflags&EFLAGS_VM)) exprintl(get_FLAGS(TheCPU.eflags),buf,(ERB_L3+ERB_LEFTM)); exprintw(TheCPU.cs,buf,(ERB_L3+ERB_LEFTM)+13); exprintw(TheCPU.ds,buf,(ERB_L3+ERB_LEFTM)+26); exprintw(TheCPU.es,buf,(ERB_L3+ERB_LEFTM)+39); exprintw(TheCPU.fs,buf,(ERB_L4+ERB_LEFTM)); exprintw(TheCPU.gs,buf,(ERB_L4+ERB_LEFTM)+13); exprintw(TheCPU.ss,buf,(ERB_L4+ERB_LEFTM)+26); exprintl(TheCPU.eip,buf,(ERB_L4+ERB_LEFTM)+39); { int i; dosaddr_t csp = LONG_CS+TheCPU.eip; dosaddr_t st = LONG_SS+TheCPU.esp; if (csp < 0x110000 || dpmi_is_valid_range(csp, 4096)) { unsigned char *op = MEM_BASE32(csp); for (i=(ERB_L5+ERB_LEFTM); i<(ERB_L6); i+=3) { exprintb(*op++,buf,i); } } if (st < 0x110000 || dpmi_is_valid_range(st, 4096)) { unsigned short *stk = (unsigned short *)MEM_BASE32(st); for (i=(ERB_L6+ERB_LEFTM); i<(ERB_L7-2); i+=5) { exprintw(*stk++,buf,i); } } } return buf; } #if MAX_SELECTORS != 8192 #error MAX_SELECTORS needs to be 8192 #endif #define GetSegmentBaseAddress(s) GetSegmentBase(s) #define IsSegment32(s) dpmi_segment_is32(s) char *e_print_scp_regs(sigcontext_t *scp, int pmode) { static char buf[300]; unsigned short *stk; int i, j; i = sprintf(buf, "RAX: %08"PRI_RG" RBX: %08"PRI_RG" RCX: %08"PRI_RG" RDX: %08"PRI_RG " VFLAGS(h): %08x\n", _rax, _rbx, _rcx, _rdx, _eflags); i += sprintf(buf + i, "RSI: %08"PRI_RG" RDI: %08"PRI_RG" RBP: %08"PRI_RG" RSP: %08"PRI_RG"\n", _rsi, _rdi, _rbp, _rsp); i += sprintf(buf + i, "CS: %04x DS: %04x ES: %04x FS: %04x GS: %04x SS: %04x\n", _cs, _ds, _es, _fs, _gs, _ss); if (pmode & 2) { // buf[(ERB_L4+ERB_LEFTM)+47] = 0; } else { if (pmode & 1) stk = SEL_ADR(_ss, _esp); else stk = MK_FP32(_ss,_LWORD(esp)); i += sprintf(buf + i, "Stack:"); for (j = 0; j < 16; j++) i += sprintf(buf + i, " %04hx", *stk++); i += sprintf(buf + i, "\n"); } return buf; } char *e_emu_disasm(unsigned char *org, int is32, unsigned int refseg) { static char buf[512]; static char frmtbuf[256]; int rc = 0; int i; char *p = buf, *p1; dosaddr_t code; dosaddr_t org2; unsigned int segbase; #ifdef USE_MHPDBG unsigned int ref; #endif if (in_dpmi_emu) segbase = GetSegmentBase(refseg); else segbase = refseg * 16; code = DOSADDR_REL(org); org2 = code - segbase; #ifdef USE_MHPDBG rc = dis_8086(code, frmtbuf, is32, &ref, segbase); p = buf + sprintf(buf,"%08x: ",code); #endif for (i=0; ics <= 0xffff) csp2 = GetSegmentBase(seg); org = csp2 + _eip; } if (org==lasta) { insrep=1; return buf; } /* skip 'rep xxx' steps */ lasta = org; insrep = 0; rc = dis_8086(org, frmtbuf, pmode&&IsSegment32(seg), &ref, (pmode? csp2 : refseg * 16)); pb = buf; org2 = MEM_BASE32(org); while ((*org2&0xfc)==0x64) org2++; /* skip most prefixes */ if ((debug_level('t')>3)||(InterOps[*org2]&2)) pb += sprintf(pb,"%s",e_print_scp_regs(scp,pmode)); p = pb + sprintf(pb," %08x: ",org); for (i=0; i> (ifpr<<1)) & 3) { case 0: case 1: #endif p += sprintf(p,"\tFp%d\t%16.8Lf%s\n\t", ifpr, *q, buf2); #ifdef FPU_TAGS break; case 2: p += sprintf(p,"\tFp%d\tNaN/Inf%s\n\t", ifpr, buf2); break; case 3: p += sprintf(p,"\tFp%d\t****%s\n\t", ifpr, buf2); break; } #endif ifpr = (ifpr+1) & 7; } p += sprintf(p,"\tst=%d(%p) sw=%04x cw=%04x tag=%04x\n", TheCPU.fpstt, FPRSTT, TheCPU.fpus, TheCPU.fpuc, TheCPU.fptag); return buf; } void GCPrint(unsigned char *cp, unsigned char *cbase, int len) { int i; while (len) { dbug_printf(">>> %08tx:",cp-cbase); for (i=0; (i<16) && len; i++,len--) dbug_printf(" %02x",*cp++); dbug_printf("\n"); } } char *showreg(signed char r) { static const char *s4[] = { "cUNP","xUNP","ZERO"," GS"," FS"," ES"," DS"," EDI", " ESI"," EBP"," ESP"," EBX"," EDX"," ECX"," EAX","TPNO", "SCPE"," EIP"," CS","EFLG"," SS"," CR2","FPCS","FPST", "FNI0","FNI1","FNI2"," SIG"," ERR","SMSK","MODE"," CR0", "FPRG","xFPR","TIML","TIMH"," GSl"," GSu"," GSo"," FSl", " FSu"," FSo"," ESl"," ESu"," XES"," DSl"," DSu"," DSo", " CSl"," CSu"," CSo"," SSl"," SSh"," SSo"," s16","xs16", " s32","xs32"," w8"," xw8"," w16","xw16"," w32","xw32" }; static char m1[32]; static int i = 0; char *p; unsigned int ix = r; i = (i+8) & 0x18; p = m1+i; // for side effects in printf if ((ix&0xff)==0xff) { *p=0; return p; } ix = (ix>>2)&0x3f; *((int *)p) = *((const int *)(s4[ix])); if ((r&3)==0) p[4]=0; else { p[4]='+'; p[5]=(r&3)+'0'; p[6]=0; } return p; } char *showmode(unsigned int m) { static char m0[28] = "ADBIRSGLrnMNXbsd....CoOi"; static char m1[28]; int i,j; m &= 0xf0ffff; j = 24; for (i=0; (i<24)&&(m!=0); i++) { if (m&1) m1[--j]=m0[i]; m>>=1; } m1[24]=0; return m1+j; } /* ======================================================================= */ /* * Register movements between the dosemu REGS and the emulator cpu * A - Real and VM86 mode */ /* * Enter emulator in VM86 mode (sys_vm86) */ static void Reg2Cpu (int mode) { unsigned long flg; /* * Enter VM86 */ /* From now on we'll work on the cpuemu eflags (BUT vm86s eflags can be * changed asynchronously by signals) * Note that IOPL=3 in the emulated flags so cpuemu works directly with IF, not VIF */ TheCPU.eflags = (vm86s.regs.eflags & SAFE_MASK) | IOPL_MASK; if (isset_IF()) TheCPU.eflags |= EFLAGS_IF; /* get the protected mode flags. Note that RF and VM are cleared * by pushfd (but not by ints and traps). Equivalent to regs32->eflags * in vm86.c */ flg = getflags(); TheCPU.eflags |= (flg & notSAFE_MASK & ~EFLAGS_IF); // which VIP do we get here? TheCPU.eflags |= (VM | RF); // RF is cosmetic... TheCPU.df_increments = (TheCPU.eflags&DF)?0xfcfeff:0x040201; if (debug_level('e')>1) e_printf("Reg2Cpu> vm86=%08x dpm=%08x emu=%08x\n", REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); TheCPU.eax = REG(eax); /* 2c -> 18 */ TheCPU.ebx = REG(ebx); /* 20 -> 00 */ TheCPU.ecx = REG(ecx); /* 28 -> 04 */ TheCPU.edx = REG(edx); /* 24 -> 08 */ TheCPU.esi = REG(esi); /* 14 -> 0c */ TheCPU.edi = REG(edi); /* 10 -> 10 */ TheCPU.ebp = REG(ebp); /* 18 -> 14 */ TheCPU.esp = REG(esp); /* 1c -> 3c */ TheCPU.err = 0; TheCPU.eip = vm86s.regs.eip&0xffff; TheCPU.int_revectored = vm86s.int_revectored; SetSegReal(SREG(cs),Ofs_CS); SetSegReal(SREG(ss),Ofs_SS); SetSegReal(SREG(ds),Ofs_DS); SetSegReal(SREG(es),Ofs_ES); SetSegReal(SREG(fs),Ofs_FS); SetSegReal(SREG(gs),Ofs_GS); trans_addr = LONG_CS + TheCPU.eip; if (debug_level('e')>1) { if (debug_level('e')==9) e_printf("Reg2Cpu< vm86=%08x dpm=%08x emu=%08x\n%s\n", REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags, e_print_regs()); else e_printf("Reg2Cpu< vm86=%08x dpm=%08x emu=%08x\n", REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); } } /* * Exit emulator in VM86 mode and return to dosemu (return_to_32bit) */ void Cpu2Reg (void) { if (debug_level('e')>1) e_printf("Cpu2Reg> vm86=%08x dpm=%08x emu=%08x\n", REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); REG(eax) = TheCPU.eax; REG(ebx) = TheCPU.ebx; REG(ecx) = TheCPU.ecx; REG(edx) = TheCPU.edx; REG(esi) = TheCPU.esi; REG(edi) = TheCPU.edi; REG(ebp) = TheCPU.ebp; REG(esp) = TheCPU.esp; SREG(ds) = TheCPU.ds; SREG(es) = TheCPU.es; SREG(ss) = TheCPU.ss; SREG(fs) = TheCPU.fs; SREG(gs) = TheCPU.gs; SREG(cs) = TheCPU.cs; REG(eip) = TheCPU.eip; /* * move flags from EFLAGS to eflags; resync vm86s eflags * from the emulated ones. * The cpuemu should not change VIP, the good one is always in vm86s. */ REG(eflags) = (REG(eflags) & VIP) | (TheCPU.eflags & ~VIP); if (TheCPU.eflags & EFLAGS_IF) set_IF(); else clear_IF(); REG(eflags) |= EFLAGS_IF; if (debug_level('e')>1) e_printf("Cpu2Reg< vm86=%08x dpm=%08x emu=%08x\n", REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); } /* ======================================================================= */ static void Scp2Cpu (sigcontext_t *scp) { TheCPU.eax = _eax; TheCPU.ebx = _ebx; TheCPU.ecx = _ecx; TheCPU.edx = _edx; TheCPU.esi = _esi; TheCPU.edi = _edi; TheCPU.ebp = _ebp; TheCPU.esp = _esp; TheCPU.eip = _eip; TheCPU.eflags = _eflags; TheCPU.cs = _cs; TheCPU.fs = _fs; TheCPU.gs = _gs; TheCPU.ds = _ds; TheCPU.es = _es; TheCPU.scp_err = 0; TheCPU.ss = _ss; TheCPU.cr2 = _cr2; TheCPU.df_increments = (TheCPU.eflags&DF)?0xfcfeff:0x040201; /* __fpstate is loaded later on demand for JIT, not used for simulator */ TheCPU.fpstate = __fpstate; } /* * Build a sigcontext structure to enter fault handling from DPMI */ static void Cpu2Scp (sigcontext_t *scp, int trapno) { if (debug_level('e')>1) e_printf("Cpu2Scp> scp=%08x dpm=%08x fl=%08x\n", _eflags,get_FLAGS(TheCPU.eflags),TheCPU.eflags); /* setup stack context from cpu registers */ _eax = TheCPU.eax; _ebx = TheCPU.ebx; _ecx = TheCPU.ecx; _edx = TheCPU.edx; _esi = TheCPU.esi; _edi = TheCPU.edi; _ebp = TheCPU.ebp; _esp = TheCPU.esp; _eip = TheCPU.eip; _cs = TheCPU.cs; _fs = TheCPU.fs; _gs = TheCPU.gs; _ds = TheCPU.ds; _es = TheCPU.es; _err = TheCPU.scp_err; _ss = TheCPU.ss; _cr2 = TheCPU.cr2; _trapno = trapno; /* Error code format: * b31-b16 = 0 (undef) * b15-b0 = selector, where b2=LDT/GDT, b1=IDT, b0=EXT * (b0-b1 are currently unimplemented here) */ if (!TheCPU.err) _err = 0; //??? if (TheCPU.fpstate == NULL) { if (!CONFIG_CPUSIM) savefpstate(*__fpstate); /* there is no real need to save and restore the FPU state of the emulator itself: savefpstate (fnsave) also resets the current FPU state using fninit; fesetenv then restores trapping of division by zero and overflow which is good enough for calling FPU-using routines. */ fesetenv(&dosemu_fenv); } /* push running flags - same as eflags, RF is cosmetic */ _eflags = (TheCPU.eflags & (eTSSMASK|0xfd5)) | 0x10002; if (TheCPU.eflags & EFLAGS_IF) set_IF(); else clear_IF(); _eflags |= EFLAGS_IF; if (debug_level('e')>1) e_printf("Cpu2Scp< scp=%08x vm86=%08x dpm=%08x fl=%08x\n", _eflags,REG(eflags),get_FLAGS(TheCPU.eflags),TheCPU.eflags); } /* ======================================================================= */ /* * Register movements between the dosemu REGS and the emulator cpu * B - DPMI and PM mode */ /* * Enter emulator in DPMI mode (context_switch) */ static int Scp2CpuD(sigcontext_t *scp) { unsigned char big; int mode=0, amask, oldfl; /* copy registers from current dpmi client to our cpu */ oldfl = TheCPU.eflags & ~(EFLAGS_VM|EFLAGS_RF); /* to preserve IOPL */ Scp2Cpu(scp); mode |= ADDR16; TheCPU.err = SetSegProt(mode&ADDR16,Ofs_CS,&big,TheCPU.cs); if (TheCPU.err) goto erseg; if (big) mode=0; else mode |= DATA16; TheCPU.err = SetSegProt(mode&ADDR16,Ofs_DS,&big,TheCPU.ds); if (TheCPU.err) goto erseg; TheCPU.err = SetSegProt(mode&ADDR16,Ofs_SS,&big,TheCPU.ss); if (TheCPU.err) goto erseg; TheCPU.StackMask = (big? 0xffffffff : 0x0000ffff); TheCPU.err = SetSegProt(mode&ADDR16,Ofs_ES,&big,TheCPU.es); if (TheCPU.err) goto erseg; TheCPU.err = SetSegProt(mode&ADDR16,Ofs_FS,&big,TheCPU.fs); if (TheCPU.err) goto erseg; TheCPU.err = SetSegProt(mode&ADDR16,Ofs_GS,&big,TheCPU.gs); erseg: /* push scp flags, pop eflags - this clears RF,VM */ amask = (CPL==0? 0:EFLAGS_IOPL_MASK) | (CPL<=IOPL? 0:EFLAGS_IF) | (EFLAGS_VM|EFLAGS_RF) | 2; TheCPU.eflags = (oldfl & amask) | ((_eflags&(eTSSMASK|0xdd7))&~amask); if (isset_IF()) // move VIF (stored in RM flags) to IF TheCPU.eflags |= EFLAGS_IF; TheCPU.df_increments = (TheCPU.eflags&DF)?0xfcfeff:0x040201; trans_addr = LONG_CS + _eip; if (debug_level('e')>1) { if (debug_level('e')==3) e_printf("Scp2CpuD%s: %08x -> %08x\n\tIP=%08x:%08x\n%s\n", (TheCPU.err? " ERR":""), _eflags, TheCPU.eflags, LONG_CS, _eip, e_print_regs()); else e_printf("Scp2CpuD%s: %08x -> %08x\n", (TheCPU.err? " ERR":""), _eflags, TheCPU.eflags); } return mode; } /* ======================================================================= */ void reset_emu_cpu(void) { TheCPU.cr[0] = 0x13; /* valid bits: 0xe005003f */ TheCPU.cr[4] = CR4_VME; TheCPU.dr[4] = 0xffff1ff0; TheCPU.dr[5] = 0x400; TheCPU.dr[6] = 0xffff1ff0; TheCPU.dr[7] = 0x400; TheCPU.GDTR.Limit = TheCPU.IDTR.Limit = TheCPU.LDTR.Limit = TheCPU.TR.Limit = 0xffff; TheCPU.cs_cache.BoundL = 0x400; TheCPU.cs_cache.BoundH = 0x10ffff; TheCPU.ss_cache.BoundL = 0x100; TheCPU.ss_cache.BoundH = 0x10ffff; TheCPU.ds_cache.BoundL = 0; TheCPU.ds_cache.BoundH = 0x10ffff; TheCPU.es_cache.BoundL = 0; TheCPU.es_cache.BoundH = 0x10ffff; TheCPU.fs_cache.BoundL = 0; TheCPU.fs_cache.BoundH = 0x10ffff; TheCPU.gs_cache.BoundL = 0; TheCPU.gs_cache.BoundH = 0x10ffff; Reg2Cpu(ADDR16|DATA16); TheCPU.StackMask = 0x0000ffff; } void init_emu_cpu(void) { struct itimerval itv; unsigned int realdelta = config.update / TIMER_DIVISOR; eTimeCorrect = 0; // full backtime stretch #ifdef HOST_ARCH_X86 if (!CONFIG_CPUSIM) eTimeCorrect = 1; // 1/2 backtime stretch #endif if (!config.rdtsc) eTimeCorrect = -1; // if we can't trust the TSC for time keeping // then don't use it to stretch either if (Ofs_END > 128) { error("CPUEMU: Ofs_END is too large, %i\n", Ofs_END); config.exitearly = 1; } init_emu_npu(); switch (vm86s.cpu_type) { case CPU_286: eTSSMASK = 0; break; case CPU_386: eTSSMASK = NT_MASK | IOPL_MASK; break; case CPU_486: eTSSMASK = AC_MASK | NT_MASK | IOPL_MASK; break; default: eTSSMASK = ID_MASK | AC_MASK | NT_MASK | IOPL_MASK; break; } e_printf("EMU86: tss mask=%08lx\n", eTSSMASK); #ifdef HOST_ARCH_X86 if (config.cpusim) InitGen_sim(); else InitGen_x86(); #else InitGen_sim(); #endif if (config.realcpu < CPU_586) { fprintf(stderr,"Cannot execute CPUEMU without TSC counter\n"); leavedos_main(0); } IDT = NULL; if (GDT==NULL) { /* The GDT is not really used (yet?) but some instructions like verr/verw refer to it, so just allocate a 0 GDT */ GDT = calloc(65536,1); } /* use the cached LDT used by dpmi (w/o GDT) */ if (LDT==NULL) { LDT = (Descriptor *)dpmi_get_ldt_buffer(); e_printf("LDT allocated at %p\n",LDT); TheCPU.LDTR.Base = (long)LDT; TheCPU.LDTR.Limit = 0xffff; } #ifdef HOST_ARCH_X86 TheCPU.unprotect_stub = stub_rep; TheCPU.stub_wri_8 = stub_wri_8; TheCPU.stub_wri_16 = stub_wri_16; TheCPU.stub_wri_32 = stub_wri_32; TheCPU.stub_stk_16 = stub_stk_16; TheCPU.stub_stk_32 = stub_stk_32; TheCPU.stub_read_8 = stub_read_8; TheCPU.stub_read_16 = stub_read_16; TheCPU.stub_read_32 = stub_read_32; #endif /* Only set the timer (for internal debug purposes only) if ITIMER_PROF is not already set for gprof. */ getitimer(ITIMER_PROF, &itv); if (itv.it_interval.tv_sec == 0 && itv.it_interval.tv_usec == 0 && itv.it_interval.tv_sec == 0 && itv.it_interval.tv_usec == 0) { itv.it_interval.tv_usec = realdelta; itv.it_value.tv_usec = realdelta; e_printf("TIME: using %d usec for updating PROF timer\n", realdelta); setitimer(ITIMER_VIRTUAL, &itv, NULL); registersig(SIGVTALRM, e_gen_sigprof); } Running = 1; } /* * Under cpuemu, SIGALRM is redirected here. We need a source of * asynchronous signals because without it any badly-behaved pgm * can stop us forever. */ void e_gen_sigalrm(sigcontext_t *scp) { if(!IS_EMU()) return; /* here we come from the kernel with cs==UCODESEL, as * the passed context is that of dosemu, NOT that of the * emulated CPU! */ e_sigpa_count += sigEMUdelta; if (!in_vm86_emu && !in_dpmi_emu) { /* if into dosemu itself */ if (eTimeCorrect >= 0) { TheCPU.EMUtime = GETTSC(); /* resync emulator time */ sigEMUtime = TheCPU.EMUtime; /* and generate signal */ } } else { TheCPU.sigalrm_pending = 1; /* tested by loops */ } if (eTimeCorrect < 0) return; else if (TheCPU.EMUtime >= sigEMUtime) { lastEMUsig = TheCPU.EMUtime; sigEMUtime += sigEMUdelta; /* we can't call sigalrm() because of the way the * context parameter is passed. */ return; /* -> signal_save */ } /* here we return back to dosemu */ return; } static void e_gen_sigprof(sigcontext_t *scp, siginfo_t *si) { e_sigpa_count -= sigEMUdelta; TheCPU.sigprof_pending += 1; } void enter_cpu_emu(void) { unsigned int realdelta = config.update / TIMER_DIVISOR; /* The simulator uses dosaddr_t throughout, the JIT adds mem_base to the segment bases */ TheCPU.mem_base = CONFIG_CPUSIM ? 0 : (uintptr_t)mem_base; if (eTimeCorrect >= 0) { TheCPU.EMUtime = GETTSC(); sigEMUdelta = realdelta*config.CPUSpeedInMhz; sigEMUtime = TheCPU.EMUtime + sigEMUdelta; } if (debug_level('e')) { TotalTime = 0; #ifdef PROFILE SearchTime = AddTime = ExecTime = CleanupTime = GenTime = LinkTime = 0; #endif dbug_printf("EMU86: delta alrm=%d speed=%d\n", realdelta,config.CPUSpeedInMhz); } e_sigpa_count = 0; #ifdef DEBUG_TREE tLog = fopen(DEBUG_TREE_FILE,"w"); #endif #ifdef ASM_DUMP aLog = fopen(ASM_DUMP_FILE,"w"); #endif dbug_printf("======================= ENTER CPU-EMU ===============\n"); flush_log(); iniflag = 1; } static void print_statistics(void) { dbug_printf("Total cpuemu time %16lld us (incl.trace)\n", (long long)TotalTime/config.CPUSpeedInMhz); #ifdef PROFILE dbug_printf("Total codgen time %16lld us\n", (long long)GenTime/config.CPUSpeedInMhz); dbug_printf("Total linker time %16lld us\n", (long long)LinkTime/config.CPUSpeedInMhz); dbug_printf("Total exec time %16lld us (incl.faults)\n", (long long)ExecTime/config.CPUSpeedInMhz); dbug_printf("Total insert time %16lld us\n", (long long)AddTime/config.CPUSpeedInMhz); dbug_printf("Total search time %16lld us\n", (long long)SearchTime/config.CPUSpeedInMhz); dbug_printf("Total clean time %16lld us\n", (long long)CleanupTime/config.CPUSpeedInMhz); dbug_printf("Max tree nodes %16d\n",MaxNodes); dbug_printf("Max node size %16d\n",MaxNodeSize); dbug_printf("Max tree depth %16d\n",MaxDepth); dbug_printf("Nodes parsed %16d\n",TotalNodesParsed); dbug_printf("Find misses %16d\n",NodesNotFound); dbug_printf("Nodes executed %16d\n",TotalNodesExecd); if (TotalNodesExecd) { unsigned long long k; k = ((long long)NodesFound * 100UL) / (long long)TotalNodesExecd; dbug_printf("Find hits %16d (%lld%%)\n",NodesFound,k); k = ((long long)NodesFastFound * 100UL) / (long long)TotalNodesExecd; dbug_printf("Find last hits %16d (%lld%%)\n", NodesFastFound,k); } dbug_printf("Page faults %16d\n",PageFaults); dbug_printf("Signals received %16d\n",EmuSignals); dbug_printf("Tree cleanups %16d\n",TreeCleanups); #endif } void leave_cpu_emu(void) { if (IS_EMU() && iniflag) { iniflag = 0; #ifdef SKIP_EMU_VBIOS if (IOFF(0x10)==CPUEMU_WATCHER_OFF) IOFF(0x10)=INT10_WATCHER_OFF; #endif EndGen(); #ifdef DEBUG_TREE fclose(tLog); tLog = NULL; #endif #ifdef ASM_DUMP fclose(aLog); aLog = NULL; #endif mprot_end(); free(GDT); LDT = NULL; GDT = NULL; IDT = NULL; dbug_printf("======================= LEAVE CPU-EMU ===============\n"); if (debug_level('e')) print_statistics(); } flush_log(); } /* ======================================================================= * kernel-level vm86 handling - this has been mostly moved into interp.c * from /linux/arch/i386/kernel/vm86.c * original code by Linus Torvalds and later enhancements by * Lutz Molgedey and Hans Lermen. */ static int handle_vm86_fault(int *error_code) { unsigned int csp, ip; unsigned char op; csp = SEGOFF2LINEAR(_CS, 0); ip = _IP; op = popb(csp, ip); if (debug_level('e')>1) e_printf("EMU86: vm86 fault %#x at %#x:%#x\n", op, _CS, _IP); /* int xx */ if (op==0xcd) { int intno=popb(csp, ip); _IP += 2; if (debug_level('e')>1) dbug_printf("EMU86: calling revectored int %#x\n", intno); return VM86_INTx + (intno << 8); } else return VM86_UNKNOWN; } /* * ======================================================================= */ static const char *retdescs[] = { "VM86_SIGNAL","VM86_UNKNOWN","VM86_INTx","VM86_STI", "VM86_PICRET","???","VM86_TRAP","???" }; int e_vm86(void) { hitimer_t tt0 = 0; int xval,retval,mode; #ifdef SKIP_VM86_TRACE int demusav; #endif int errcode; if (iniflag==0) enter_cpu_emu(); #ifdef PROFILE if (debug_level('e')) tt0 = GETTSC(); #endif e_sigpa_count = 0; mode = ADDR16 | DATA16 | MREALA; TheCPU.StackMask = 0x0000ffff; /* FPU state is loaded later on demand for JIT, not used for simulator */ TheCPU.fpstate = vm86_fpu_state; if (eTimeCorrect >= 0) TheCPU.EMUtime = GETTSC(); #ifdef SKIP_VM86_TRACE demusav=debug_level('e'); if (debug_level('e')) set_debug_level('e', 1); #endif // if (lastEMUsig && (debug_level('e')>1)) // e_printf("EMU86: last sig at %lld, curr=%lld, next=%lld\n",lastEMUsig>>16, // TheCPU.EMUtime>>16,sigEMUtime>>16); /* This emulates VM86_ENTER */ /* ------ OUTER LOOP: exit for code >=0 and return to dosemu code */ do { Reg2Cpu(mode); if (CONFIG_CPUSIM) { RFL.valid = V_INVALID; } /* ---- INNER LOOP: exit with error or code>0 (vm86 fault) ---- */ do { /* enter VM86 mode */ in_vm86_emu = 1; if (debug_level('e')>1) dbug_printf("INTERP: enter=%08x\n",trans_addr); return_addr = Interp86(trans_addr, mode); if (debug_level('e')>1) dbug_printf("INTERP: exit=%08x err=%d\n",return_addr,TheCPU.err-1); xval = TheCPU.err; in_vm86_emu = 0; /* 0 if ok, else exception code+1 or negative if dosemu err */ if (xval < 0) { error("EMU86: error %d\n", -xval); in_vm86=0; leavedos_main(1); } trans_addr = return_addr; } while (xval==0); /* ---- INNER LOOP -- exit for exception ---------------------- */ if (CONFIG_CPUSIM) FlagSync_All(); Cpu2Reg(); if (debug_level('e')>1) e_printf("---------------------\n\t EMU86: EXCP %#x\n", xval-1); retval = -1; E_TIME_STRETCH; if (xval==EXCP_SIGNAL) { /* coming here for async interruptions */ if (CEmuStat & (CeS_SIGPEND|CeS_SIGACT)) { CEmuStat &= ~(CeS_SIGPEND|CeS_SIGACT); retval=VM86_SIGNAL; } } else if (xval==EXCP_PICSIGNAL) { retval = VM86_PICRETURN; } else if (xval==EXCP_STISIGNAL) { retval = VM86_STI; } else if (xval==EXCP_GOBACK) { retval = 0; } else { switch (xval) { case EXCP0D_GPF: { /* to kernel vm86 */ retval=handle_vm86_fault(&errcode); /* kernel level */ #ifdef SKIP_EMU_VBIOS /* are we into the VBIOS? If so, exit and reenter e_vm86 */ if ((SREG(cs)&0xf000)==config.vbios_seg) { if (retval<0) retval=0; /* force exit even if handled */ } #endif } break; case EXCP01_SSTP: case EXCP03_INT3: { /* to kernel vm86 */ retval = VM86_TRAP + ((xval-1) << 8); break; } default: { /* FAULT, handled via signal callback */ if (debug_level('e')) TotalTime += (GETTSC() - tt0); vm86_fault(xval-1, TheCPU.scp_err, DOSADDR_REL(LINP(TheCPU.cr2))); if (debug_level('e')) tt0 = GETTSC(); retval = VM86_SIGNAL; break; } } } } while (retval < 0); /* ------ OUTER LOOP -- exit to user level ---------------------- */ if (TheCPU.fpstate == NULL) { if (!CONFIG_CPUSIM) savefpstate(*vm86_fpu_state); fesetenv(&dosemu_fenv); } if (debug_level('e')>1) e_printf("EMU86: retval=%s\n", retdescs[retval&7]); #ifdef SKIP_VM86_TRACE set_debug_level('e',demusav); #endif if (debug_level('e')) { TotalTime += (GETTSC() - tt0); /* this time should become >0 only when dosemu was descheduled * during a vm86/dpmi call. It will be necessary to subtract this * value from ZeroBase to account for the elapsed time, maybe. */ if (debug_level('e')>1) dbug_printf("Sys timers d=%d\n",e_sigpa_count); } return retval; } /* ======================================================================= */ int e_dpmi(sigcontext_t *scp) { volatile hitimer_t tt0 = 0; int xval,retval,mode; if (iniflag==0) enter_cpu_emu(); if (debug_level('e')) tt0 = GETTSC(); e_sigpa_count = 0; if (eTimeCorrect >= 0) TheCPU.EMUtime = GETTSC(); /* make clear we are in PM now */ TheCPU.cr[0] |= 1; if (debug_level('e')>2) { D_printf("EMU86: DPMI enter at %08x\n",DTgetSelBase(_cs)+_eip); } // if (lastEMUsig) // e_printf("DPM86: last sig at %lld, curr=%lld, next=%lld\n",lastEMUsig>>16, // TheCPU.EMUtime>>16,sigEMUtime>>16); /* ------ OUTER LOOP: exit for code >=0 and return to dosemu code */ do { TheCPU.err = 0; mode = Scp2CpuD (scp); if (CONFIG_CPUSIM) RFL.valid = V_INVALID; if (TheCPU.err) { error("DPM86: segment error %d\n", TheCPU.err); leavedos_main(0); } /* ---- INNER LOOP: exit with error or code>0 (vm86 fault) ---- */ do { /* switch to DPMI process */ in_dpmi_emu = 1; e_printf("INTERP: enter=%08x mode=%04x\n",trans_addr,mode); return_addr = Interp86(trans_addr, mode); e_printf("INTERP: exit=%08x err=%d\n",return_addr,TheCPU.err-1); xval = TheCPU.err; in_dpmi_emu = 0; /* 0 if ok, else exception code+1 or negative if dosemu err */ if (xval < 0) { error("DPM86: error %d\n", -xval); error("@\n%s",e_print_regs()); leavedos_main(0); } trans_addr = return_addr; } while (xval==0); /* ---- INNER LOOP -- exit for exception ---------------------- */ if (CONFIG_CPUSIM) FlagSync_All(); if (debug_level('e')>1) e_printf("DPM86: EXCP %#x eflags=%08x\n", xval-1, REG(eflags)); Cpu2Scp (scp, xval-1); retval = DPMI_RET_CLIENT; E_TIME_STRETCH; if ((xval==EXCP_SIGNAL) || (xval==EXCP_PICSIGNAL) || (xval==EXCP_STISIGNAL)) { if (debug_level('e')>2) e_printf("DPMI sigpending = %d\n",signal_pending()); if (signal_pending()) { retval = DPMI_RET_DOSEMU; } } else if (xval==EXCP_GOBACK) { retval = DPMI_RET_DOSEMU; } else if (xval == EXCP0E_PAGE && vga_emu_fault(DOSADDR_REL(LINP(_cr2)),_err,scp)==True) { retval = dpmi_check_return(); } else { if (debug_level('e')) TotalTime += (GETTSC() - tt0); retval = dpmi_fault(scp); if (debug_level('e')) tt0 = GETTSC(); } } while (retval == DPMI_RET_CLIENT); /* ------ OUTER LOOP -- exit to user level ---------------------- */ if (debug_level('e')) { dbug_printf("DPM86: retval=%#x\n", retval); TotalTime += (GETTSC() - tt0); dbug_printf("Sys timers d=%d\n",e_sigpa_count); } return retval; } /* ======================================================================= */ /* file: src/cwsdpmi/exphdlr.c */ void e_dpmi_b0x(int op,sigcontext_t *scp) { switch (op) { case 0: { /* set */ int enabled = TheCPU.dr[7]; int i; for (i=0; i<4; i++) { if ((~enabled >> (i*2)) & 3) { unsigned mask; TheCPU.dr[i] = (_LWORD(ebx) << 16) | _LWORD(ecx); TheCPU.dr[7] |= (3 << (i*2)); mask = _HI(dx) & 3; if (mask==2) mask++; mask |= ((_LO(dx)-1) << 2) & 0x0c; TheCPU.dr[7] |= mask << (i*4 + 16); _LWORD(ebx) = i; _eflags &= ~CF; break; } } } break; case 1: { /* clear */ int i = _LWORD(ebx) & 3; TheCPU.dr[6] &= ~(1 << i); TheCPU.dr[7] &= ~(3 << (i*2)); TheCPU.dr[7] &= ~(15 << (i*4+16)); _eflags &= ~CF; break; } case 2: { /* get */ int i = _LWORD(ebx) & 3; _LWORD(eax) = (TheCPU.dr[6] >> i); _eflags &= ~CF; break; } case 3: { /* reset */ int i = _LWORD(ebx) & 3; TheCPU.dr[6] &= ~(1 << i); _eflags &= ~CF; break; } default: _LWORD(eax) = 0x8016; _eflags |= CF; break; } if (TheCPU.dr[7] & 0xff) CEmuStat|=CeS_DRTRAP; else CEmuStat&=~CeS_DRTRAP; /* e_printf("DR0=%08lx DR1=%08lx DR2=%08lx DR3=%08lx\n",DRs[0],DRs[1],DRs[2],DRs[3]); e_printf("DR6=%08lx DR7=%08lx\n",DRs[6],DRs[7]); */ } int e_debug_check(unsigned int PC) { register unsigned long d7 = TheCPU.dr[7]; if (d7&0x03) { if (d7&0x30000) return 0; /* only execute(00) bkp */ if (PC==TheCPU.dr[0]) { e_printf("DBRK: DR0 hit at %08x\n",PC); TheCPU.dr[6] |= 1; return 1; } } if (d7&0x0c) { if (d7&0x300000) return 0; if (PC==TheCPU.dr[1]) { e_printf("DBRK: DR1 hit at %08x\n",PC); TheCPU.dr[6] |= 2; return 1; } } if (d7&0x30) { if (d7&0x3000000) return 0; if (PC==TheCPU.dr[2]) { e_printf("DBRK: DR2 hit at %08x\n",PC); TheCPU.dr[6] |= 4; return 1; } } if (d7&0xc0) { if (d7&0x30000000) return 0; if (PC==TheCPU.dr[3]) { e_printf("DBRK: DR3 hit at %08x\n",PC); TheCPU.dr[6] |= 8; return 1; } } return 0; } int e_in_compiled_code(void) { return InCompiledCode; } /* ======================================================================= */ #endif /* X86_EMULATOR */