/* * Copyright (C) 2002-2005 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: cpu.cpp,v 1.68 2005-03-25 11:45:37 qbix79 Exp $ */ #include #include "dosbox.h" #include "cpu.h" #include "memory.h" #include "debug.h" #include "mapper.h" #include "setup.h" #include "paging.h" #include "support.h" Bitu DEBUG_EnableDebugger(void); #if 1 #undef LOG #define LOG(X,Y) #endif CPU_Regs cpu_regs; CPUBlock cpu; Segments Segs; Bits CPU_Cycles = 0; Bits CPU_CycleLeft = 0; Bits CPU_CycleMax = 2500; Bits CPU_CycleUp = 0; Bits CPU_CycleDown = 0; CPU_Decoder * cpudecoder; void CPU_Core_Full_Init(void); void CPU_Core_Normal_Init(void); void CPU_Core_Simple_Init(void); void CPU_Core_Dyn_X86_Init(void); #define EXCEPTION_TS 10 #define EXCEPTION_NP 11 #define EXCEPTION_SS 12 #define EXCEPTION_GP 13 /* In debug mode exceptions are tested and dosbox exits when * a unhandled exception state is detected. * USE CHECK_EXCEPT to raise an exception in that case to see if that exception * solves the problem. * * In non-debug mode dosbox doesn't do detection (and hence doesn't crash at * that point). (game might crash later due to the unhandled exception) */ #if C_DEBUG // #define CPU_CHECK_EXCEPT 1 // #define CPU_CHECK_IGNORE 1 /* Use CHECK_EXCEPT when something doesn't work to see if a exception is * needed that isn't enabled by default.*/ #else /* NORMAL NO CHECKING => More Speed */ #define CPU_CHECK_IGNORE 1 #endif /* C_DEBUG */ #if defined(CPU_CHECK_IGNORE) #define CPU_CHECK_COND(cond,msg,exc,sel) { \ cond; \ } #elif defined(CPU_CHECK_EXCEPT) #define CPU_CHECK_COND(cond,msg,exc,sel) { \ if (cond) { CPU_Exception(exc,sel); \ return; \ } \ } #else #define CPU_CHECK_COND(cond,msg,exc,sel) { \ if (cond) E_Exit(msg); \ } #endif void CPU_Push16(Bitu value) { Bit32u new_esp=(reg_esp&~cpu.stack.mask)|((reg_esp-2)&cpu.stack.mask); mem_writew(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value); reg_esp=new_esp; } void CPU_Push32(Bitu value) { Bit32u new_esp=(reg_esp&~cpu.stack.mask)|((reg_esp-4)&cpu.stack.mask); mem_writed(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value); reg_esp=new_esp; } Bitu CPU_Pop16(void) { Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask)); reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+2)&cpu.stack.mask); return val; } Bitu CPU_Pop32(void) { Bitu val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask)); reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+4)&cpu.stack.mask); return val; } PhysPt SelBase(Bitu sel) { if (cpu.cr0 & CR0_PROTECTION) { Descriptor desc; cpu.gdt.GetDescriptor(sel,desc); return desc.GetBase(); } else { return sel<<4; } } void CPU_SetFlags(Bitu word,Bitu mask) { reg_flags=(reg_flags & ~mask)|(word & mask)|2|FLAG_ID; cpu.direction=1-((reg_flags & FLAG_DF) >> 9); } bool CPU_PrepareException(Bitu which,Bitu error) { cpu.exception.which=which; cpu.exception.error=error; return true; } bool CPU_CLI(void) { if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL0; Segs.val[cs]=new_cs; break; default: E_Exit("Task switch CS Type %d",cs_desc.Type()); } } CPU_SetSegGeneral(es,new_es); CPU_SetSegGeneral(ss,new_ss); CPU_SetSegGeneral(ds,new_ds); CPU_SetSegGeneral(fs,new_fs); CPU_SetSegGeneral(gs,new_gs); CPU_LTR(new_tss_selector); // LOG_MSG("Task CPL %X CS:%X IP:%X SS:%X SP:%X eflags %x",cpu.cpl,SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags); return true; } bool CPU_IO_Exception(Bitu port,Bitu size) { if (cpu.pmode && ((GETFLAG_IOPLcpu_tss.limit) goto doexception; where=cpu_tss.base+ofs+(port/8); Bitu map=mem_readw(where); Bitu mask=(0xffff>>(16-size)) << (port&7); if (map & mask) goto doexception; } return false; doexception: LOG(LOG_CPU,LOG_NORMAL)("IO Exception port %X",port); return CPU_PrepareException(EXCEPTION_GP,0); } void CPU_Exception(Bitu which,Bitu error ) { // LOG_MSG("Exception %d error %x",which,error); cpu.exception.error=error; CPU_Interrupt(which,CPU_INT_EXCEPTION | ((which>=8) ? CPU_INT_HAS_ERROR : 0),reg_eip); } Bit8u lastint; void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) { lastint=num; #if C_DEBUG switch (num) { case 0xcd: #if C_HEAVY_DEBUG LOG(LOG_CPU,LOG_ERROR)("Call to interrupt 0xCD this is BAD"); DEBUG_HeavyWriteLogInstruction(); #endif E_Exit("Call to interrupt 0xCD this is BAD"); case 0x03: if (DEBUG_Breakpoint()) { CPU_Cycles=0; return; } }; #endif if (!cpu.pmode) { /* Save everything on a 16-bit stack */ CPU_Push16(reg_flags & 0xffff); CPU_Push16(SegValue(cs)); CPU_Push16(oldeip); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); /* Get the new CS:IP from vector table */ PhysPt base=cpu.idt.GetBase(); reg_eip=mem_readw(base+(num << 2)); Segs.val[cs]=mem_readw(base+(num << 2)+2); Segs.phys[cs]=Segs.val[cs]<<4; cpu.code.big=false; return; } else { /* Protected Mode Interrupt */ if ((reg_flags & FLAG_VM) && (type&CPU_INT_SOFTWARE)) { // LOG_MSG("Software int in v86, AH %X IOPL %x",reg_ah,(reg_flags & FLAG_IOPL) >>12); if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) { CPU_Exception(EXCEPTION_GP,0); return; } } Descriptor gate; if (!cpu.idt.GetDescriptor(num<<3,gate)) { // zone66 CPU_Exception(EXCEPTION_GP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1); return; } if ((type&CPU_INT_SOFTWARE) && (gate.DPL()cpu.cpl, "Interrupt to higher privilege", EXCEPTION_GP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1) switch (cs_desc.Type()) { case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cs_dpl0", EXCEPTION_GP,gate_sel & 0xfffc) Bitu n_ss,n_esp; Bitu o_ss,o_esp; o_ss=SegValue(ss); o_esp=reg_esp; cpu_tss.Get_SSx_ESPx(cs_dpl,n_ss,n_esp); CPU_CHECK_COND((n_ss & 0xfffc)==0, "INT:Gate with SS zero selector", EXCEPTION_TS,(type&CPU_INT_SOFTWARE)?0:1) Descriptor n_ss_desc; CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc), "INT:Gate with SS beyond limit", EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1) CPU_CHECK_COND(((n_ss & 3)!=cs_dpl) || (n_ss_desc.DPL()!=cs_dpl), "INT:Inner level with CS_DPL!=SS_DPL and SS_RPL", EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1) // check if stack segment is a writable data segment switch (n_ss_desc.Type()) { case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: break; default: E_Exit("INT:Inner level:Stack segment not writable."); // or #TS(ss_sel+EXT) } CPU_CHECK_COND(!n_ss_desc.saved.seg.p, "INT:Inner level with nonpresent SS", EXCEPTION_SS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1) // commit point Segs.phys[ss]=n_ss_desc.GetBase(); Segs.val[ss]=n_ss; if (n_ss_desc.Big()) { cpu.stack.big=true; cpu.stack.mask=0xffffffff; reg_esp=n_esp; } else { cpu.stack.big=false; cpu.stack.mask=0xffff; reg_sp=n_esp & 0xffff; } cpu.cpl=cs_dpl; if (gate.Type() & 0x8) { /* 32-bit Gate */ if (reg_flags & FLAG_VM) { CPU_Push32(SegValue(gs));SegSet16(gs,0x0); CPU_Push32(SegValue(fs));SegSet16(fs,0x0); CPU_Push32(SegValue(ds));SegSet16(ds,0x0); CPU_Push32(SegValue(es));SegSet16(es,0x0); } CPU_Push32(o_ss); CPU_Push32(o_esp); } else { /* 16-bit Gate */ if (reg_flags & FLAG_VM) E_Exit("V86 to 16-bit gate"); CPU_Push16(o_ss); CPU_Push16(o_esp); } // LOG_MSG("INT:Gate to inner level SS:%X SP:%X",n_ss,n_esp); goto do_interrupt; } if (cs_dpl!=cpu.cpl) E_Exit("Non-conforming intra privilege INT with DPL!=CPL"); case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA: /* Prepare stack for gate to same priviledge */ CPU_CHECK_COND(!cs_desc.saved.seg.p, "INT:Same level:CS segment not present", EXCEPTION_NP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1) if ((reg_flags & FLAG_VM) && (cs_dpl0; reg_eip=gate_off; if (!(gate.Type()&1)) SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); SETFLAGBIT(NT,false); SETFLAGBIT(VM,false); LOG(LOG_CPU,LOG_NORMAL)("INT:Gate to %X:%X big %d %s",gate_sel,gate_off,cs_desc.Big(),gate.Type() & 0x8 ? "386" : "286"); return; } case DESC_TASK_GATE: CPU_SwitchTask(gate.GetSelector(),TSwitch_CALL_INT,oldeip); if (type & CPU_INT_HAS_ERROR) { //TODO Be sure about this, seems somewhat unclear if (cpu_tss.is386) CPU_Push32(cpu.exception.error); else CPU_Push16(cpu.exception.error); } return; default: E_Exit("Illegal descriptor type %X for int %X",gate.Type(),num); } } assert(1); return ; // make compiler happy } void CPU_IRET(bool use32,Bitu oldeip) { if (!cpu.pmode) { /* RealMode IRET */ realmode_iret: if (use32) { reg_eip=CPU_Pop32(); SegSet16(cs,CPU_Pop32()); CPU_SetFlagsd(CPU_Pop32()); } else { reg_eip=CPU_Pop16(); SegSet16(cs,CPU_Pop16()); CPU_SetFlagsw(CPU_Pop16()); } cpu.code.big=false; return; } else { /* Protected mode IRET */ if (reg_flags & FLAG_VM) { if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) { // win3.x e CPU_Exception(EXCEPTION_GP,0); return; } else goto realmode_iret; } /* Check if this is task IRET */ if (GETFLAG(NT)) { if (GETFLAG(VM)) E_Exit("Pmode IRET with VM bit set"); CPU_CHECK_COND(!cpu_tss.IsValid(), "TASK Iret without valid TSS", EXCEPTION_TS,cpu_tss.selector & 0xfffc) // check if busy is set switch (cpu_tss.desc.Type()) { case DESC_286_TSS_B: case DESC_386_TSS_B: break; default: E_Exit("TASK Iret:TSS not busy"); // or #TS(sel) } Bitu back_link=cpu_tss.Get_back(); CPU_SwitchTask(back_link,TSwitch_IRET,oldeip); return; } Bitu n_cs_sel,n_eip,n_flags; if (use32) { // commit point n_eip=CPU_Pop32(); n_cs_sel=CPU_Pop32() & 0xffff; n_flags=CPU_Pop32(); if ((n_flags & FLAG_VM) && (cpu.cpl==0)) { reg_eip=n_eip & 0xffff; Bitu n_ss,n_esp,n_es,n_ds,n_fs,n_gs; n_esp=CPU_Pop32(); n_ss=CPU_Pop32() & 0xffff; n_es=CPU_Pop32() & 0xffff; n_ds=CPU_Pop32() & 0xffff; n_fs=CPU_Pop32() & 0xffff; n_gs=CPU_Pop32() & 0xffff; CPU_SetFlags(n_flags,FMASK_ALL | FLAG_VM); cpu.cpl=3; CPU_SetSegGeneral(ss,n_ss); CPU_SetSegGeneral(es,n_es); CPU_SetSegGeneral(ds,n_ds); CPU_SetSegGeneral(fs,n_fs); CPU_SetSegGeneral(gs,n_gs); reg_esp=n_esp; cpu.code.big=false; SegSet16(cs,n_cs_sel); LOG(LOG_CPU,LOG_NORMAL)("IRET:Back to V86: CS:%X IP %X SS:%X SP %X FLAGS:%X",SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags); return; } if (n_flags & FLAG_VM) E_Exit("IRET from pmode to v86 with CPL!=0"); } else { n_eip=CPU_Pop16(); n_cs_sel=CPU_Pop16(); n_flags=(reg_flags & 0xffff0000) | CPU_Pop16(); if (n_flags & FLAG_VM) E_Exit("VM Flag in 16-bit iret"); } CPU_CHECK_COND((n_cs_sel & 0xfffc)==0, "IRET:CS selector zero", EXCEPTION_GP,0) Bitu n_cs_rpl=n_cs_sel & 3; Descriptor n_cs_desc; CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc), "IRET:CS selector beyond limits", EXCEPTION_GP,n_cs_sel & 0xfffc) CPU_CHECK_COND(n_cs_rpln_cs_rpl, "IRET:C:DPL>RPL", EXCEPTION_GP,n_cs_sel & 0xfffc) break; default: E_Exit("IRET:Illegal descriptor type %X",n_cs_desc.Type()); } CPU_CHECK_COND(!n_cs_desc.saved.seg.p, "IRET with nonpresent code segment", EXCEPTION_NP,n_cs_sel & 0xfffc) if (n_cs_rpl==cpu.cpl) { /* Return to same level */ Segs.phys[cs]=n_cs_desc.GetBase(); cpu.code.big=n_cs_desc.Big()>0; Segs.val[cs]=n_cs_sel; reg_eip=n_eip; Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL; if (GETFLAG_IOPL0; Segs.val[cs]=n_cs_sel; Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL; if (GETFLAG_IOPLdesc.DPL()) CPU_SetSegGeneral(es,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(ds),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(ds,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(fs),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(fs,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(gs),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(gs,0); break; default: break; } LOG(LOG_CPU,LOG_NORMAL)("IRET:Outer level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big); } return; } } void CPU_JMP(bool use32,Bitu selector,Bitu offset,Bitu oldeip) { if (!cpu.pmode || (reg_flags & FLAG_VM)) { if (!use32) { reg_eip=offset&0xffff; } else { reg_eip=offset; } SegSet16(cs,selector); cpu.code.big=false; return; } else { CPU_CHECK_COND((selector & 0xfffc)==0, "JMP:CS selector zero", EXCEPTION_GP,0) Bitu rpl=selector & 3; Descriptor desc; CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,desc), "JMP:CS beyond limits", EXCEPTION_GP,selector & 0xfffc) switch (desc.Type()) { case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: CPU_CHECK_COND(rpl>cpu.cpl, "JMP:NC:RPL>CPL", EXCEPTION_GP,selector & 0xfffc) CPU_CHECK_COND(cpu.cpl!=desc.DPL(), "JMP:NC:RPL != DPL", EXCEPTION_GP,selector & 0xfffc) LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:NC to %X:%X big %d",selector,offset,desc.Big()); goto CODE_jmp; case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA: LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:C to %X:%X big %d",selector,offset,desc.Big()); CPU_CHECK_COND(cpu.cpl0; Segs.val[cs]=(selector & 0xfffc) | cpu.cpl; reg_eip=offset; return; case DESC_386_TSS_A: CPU_CHECK_COND(desc.DPL()cpu.cpl, "CALL:CODE:NC:RPL>CPL", EXCEPTION_GP,selector & 0xfffc) CPU_CHECK_COND(call.DPL()!=cpu.cpl, "CALL:CODE:NC:DPL!=CPL", EXCEPTION_GP,selector & 0xfffc) LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:NC to %X:%X",selector,offset); goto call_code; case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA: CPU_CHECK_COND(call.DPL()>cpu.cpl, "CALL:CODE:C:DPL>CPL", EXCEPTION_GP,selector & 0xfffc) LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:C to %X:%X",selector,offset); call_code: if (!call.saved.seg.p) { // borland extender (RTM) CPU_Exception(EXCEPTION_NP,selector & 0xfffc); return; } // commit point if (!use32) { CPU_Push16(SegValue(cs)); CPU_Push16(oldeip); reg_eip=offset & 0xffff; } else { CPU_Push32(SegValue(cs)); CPU_Push32(oldeip); reg_eip=offset; } Segs.phys[cs]=call.GetBase(); cpu.code.big=call.Big()>0; Segs.val[cs]=(selector & 0xfffc) | cpu.cpl; return; case DESC_386_CALL_GATE: case DESC_286_CALL_GATE: { CPU_CHECK_COND(call.DPL()cpu.cpl, "CALL:Gate:CS DPL>CPL", EXCEPTION_GP,n_cs_sel & 0xfffc) Bitu n_cs_rpl = n_cs_sel & 3; Bitu n_eip = call.GetOffset(); switch (n_cs_desc.Type()) { case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA: /* Check if we goto inner priviledge */ if (n_cs_dpl < cpu.cpl) { CPU_CHECK_COND(!n_cs_desc.saved.seg.p, "CALL:Gate:CS not present", EXCEPTION_NP,n_cs_sel & 0xfffc) /* Get new SS:ESP out of TSS */ Bitu n_ss_sel,n_esp; Descriptor n_ss_desc; cpu_tss.Get_SSx_ESPx(n_cs_dpl,n_ss_sel,n_esp); CPU_CHECK_COND((n_ss_sel & 0xfffc)==0, "CALL:Gate:NC:SS selector zero", EXCEPTION_TS,0) CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss_sel,n_ss_desc), "CALL:Gate:Invalid SS selector", EXCEPTION_TS,n_ss_sel & 0xfffc) CPU_CHECK_COND(((n_ss_sel & 3)!=n_cs_desc.DPL()) || (n_ss_desc.DPL()!=n_cs_desc.DPL()), "CALL:Gate:Invalid SS selector privileges", EXCEPTION_TS,n_ss_sel & 0xfffc) switch (n_ss_desc.Type()) { case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: // writable data segment break; default: E_Exit("Call:Gate:SS no writable data segment"); // or #TS(ss_sel) } CPU_CHECK_COND(!n_ss_desc.saved.seg.p, "CALL:Gate:Stack segment not present", EXCEPTION_SS,n_ss_sel & 0xfffc) /* Load the new SS:ESP and save data on it */ Bitu o_esp = reg_esp; Bitu o_ss = SegValue(ss); PhysPt o_stack = SegPhys(ss)+(reg_esp & cpu.stack.mask); // catch pagefaults if (call.saved.gate.paramcount&31) { if (call.Type()==DESC_386_CALL_GATE) { for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) mem_readd(o_stack+i*4); } else { for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) mem_readw(o_stack+i*2); } } // commit point Segs.val[ss]=n_ss_sel; Segs.phys[ss]=n_ss_desc.GetBase(); if (n_ss_desc.Big()) { cpu.stack.big=true; cpu.stack.mask=0xffffffff; reg_esp=n_esp; } else { cpu.stack.big=false; cpu.stack.mask=0xffff; reg_sp=n_esp & 0xffff; } cpu.cpl = n_cs_desc.DPL(); Bit16u oldcs = SegValue(cs); /* Switch to new CS:EIP */ Segs.phys[cs] = n_cs_desc.GetBase(); Segs.val[cs] = (n_cs_sel & 0xfffc) | cpu.cpl; cpu.code.big = n_cs_desc.Big()>0; reg_eip = n_eip; if (!use32) reg_eip&=0xffff; if (call.Type()==DESC_386_CALL_GATE) { CPU_Push32(o_ss); //save old stack CPU_Push32(o_esp); if (call.saved.gate.paramcount&31) for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) CPU_Push32(mem_readd(o_stack+i*4)); CPU_Push32(oldcs); CPU_Push32(oldeip); } else { CPU_Push16(o_ss); //save old stack CPU_Push16(o_esp); if (call.saved.gate.paramcount&31) for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) CPU_Push16(mem_readw(o_stack+i*2)); CPU_Push16(oldcs); CPU_Push16(oldeip); } break; } else if (n_cs_dpl > cpu.cpl) E_Exit("CALL:GATE:CS DPL>CPL"); // or #GP(sel) case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA: // zrdx extender if (call.Type()==DESC_386_CALL_GATE) { CPU_Push32(SegValue(cs)); CPU_Push32(oldeip); } else { CPU_Push16(SegValue(cs)); CPU_Push16(oldeip); } /* Switch to new CS:EIP */ Segs.phys[cs] = n_cs_desc.GetBase(); Segs.val[cs] = (n_cs_sel & 0xfffc) | cpu.cpl; cpu.code.big = n_cs_desc.Big()>0; reg_eip = n_eip; if (!use32) reg_eip&=0xffff; break; default: E_Exit("CALL:GATE:CS no executable segment"); } } /* Call Gates */ break; case DESC_386_TSS_A: CPU_CHECK_COND(call.DPL()cpu.cpl, "RET to C segment of higher privilege", EXCEPTION_GP,selector & 0xfffc) break; default: E_Exit("RET from illegal descriptor type %X",desc.Type()); } RET_same_level: if (!desc.saved.seg.p) { // borland extender (RTM) CPU_Exception(EXCEPTION_NP,selector & 0xfffc); return; } // commit point if (!use32) { offset=CPU_Pop16(); selector=CPU_Pop16(); } else { offset=CPU_Pop32(); selector=CPU_Pop32() & 0xffff; } Segs.phys[cs]=desc.GetBase(); cpu.code.big=desc.Big()>0; Segs.val[cs]=selector; reg_eip=offset; if (cpu.stack.big) { reg_esp+=bytes; } else { reg_sp+=bytes; } LOG(LOG_CPU,LOG_NORMAL)("RET - Same level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL()); return; } else { /* Return to outer level */ if (bytes) E_Exit("RETF outer level with immediate value"); switch (desc.Type()) { case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA: CPU_CHECK_COND(desc.DPL()!=rpl, "RET to outer NC segment with DPL!=RPL", EXCEPTION_GP,selector & 0xfffc) break; case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA: CPU_CHECK_COND(desc.DPL()>rpl, "RET to outer C segment with DPL>RPL", EXCEPTION_GP,selector & 0xfffc) break; default: E_Exit("RET from illegal descriptor type %X",desc.Type()); // or #GP(selector) } CPU_CHECK_COND(!desc.saved.seg.p, "RET:Outer level:CS not present", EXCEPTION_NP,selector & 0xfffc) // commit point Bitu n_esp,n_ss; if (use32) { offset=CPU_Pop32(); selector=CPU_Pop32() & 0xffff; n_esp = CPU_Pop32(); n_ss = CPU_Pop32() & 0xffff; } else { offset=CPU_Pop16(); selector=CPU_Pop16(); n_esp = CPU_Pop16(); n_ss = CPU_Pop16(); } CPU_CHECK_COND((n_ss & 0xfffc)==0, "RET to outer level with SS selector zero", EXCEPTION_GP,0) Descriptor n_ss_desc; CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc), "RET:SS beyond limits", EXCEPTION_GP,n_ss & 0xfffc) CPU_CHECK_COND(((n_ss & 3)!=rpl) || (n_ss_desc.DPL()!=rpl), "RET to outer segment with invalid SS privileges", EXCEPTION_GP,n_ss & 0xfffc) switch (n_ss_desc.Type()) { case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: break; default: E_Exit("RET:SS selector type no writable data segment"); // or #GP(selector) } CPU_CHECK_COND(!n_ss_desc.saved.seg.p, "RET:Stack segment not present", EXCEPTION_SS,n_ss & 0xfffc) cpu.cpl = rpl; Segs.phys[cs]=desc.GetBase(); cpu.code.big=desc.Big()>0; Segs.val[cs]=(selector&0xfffc) | cpu.cpl; reg_eip=offset; Segs.val[ss]=n_ss; Segs.phys[ss]=n_ss_desc.GetBase(); if (n_ss_desc.Big()) { cpu.stack.big=true; cpu.stack.mask=0xffffffff; reg_esp=n_esp; } else { cpu.stack.big=false; cpu.stack.mask=0xffff; reg_sp=n_esp & 0xffff; } Descriptor desc; cpu.gdt.GetDescriptor(SegValue(es),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(es,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(ds),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(ds,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(fs),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(fs,0); break; default: break; } cpu.gdt.GetDescriptor(SegValue(gs),desc); switch (desc.Type()) { case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (cpu.cpl>desc.DPL()) CPU_SetSegGeneral(gs,0); break; default: break; } // LOG(LOG_MISC,LOG_ERROR)("RET - Higher level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL()); return; } LOG(LOG_CPU,LOG_NORMAL)("Prot ret %X:%X",selector,offset); return; } assert(1); } void CPU_SLDT(Bitu & selector) { selector=cpu.gdt.SLDT(); } void CPU_LLDT(Bitu selector) { cpu.gdt.LLDT(selector); LOG(LOG_CPU,LOG_NORMAL)("LDT Set to %X",selector); } void CPU_STR(Bitu & selector) { selector=cpu_tss.selector; } void CPU_LTR(Bitu selector) { cpu_tss.SetSelector(selector); cpu_tss.desc.SetBusy(true); } Bitu gdt_count=0; void CPU_LGDT(Bitu limit,Bitu base) { LOG(LOG_CPU,LOG_NORMAL)("GDT Set to base:%X limit:%X count %d",base,limit,gdt_count++); cpu.gdt.SetLimit(limit); cpu.gdt.SetBase(base); // if (gdt_count>20) DEBUG_EnableDebugger(); // DEBUG_EnableDebugger(); } void CPU_LIDT(Bitu limit,Bitu base) { LOG(LOG_CPU,LOG_NORMAL)("IDT Set to base:%X limit:%X",base,limit); cpu.idt.SetLimit(limit); cpu.idt.SetBase(base); } void CPU_SGDT(Bitu & limit,Bitu & base) { limit=cpu.gdt.GetLimit(); base=cpu.gdt.GetBase(); } void CPU_SIDT(Bitu & limit,Bitu & base) { limit=cpu.idt.GetLimit(); base=cpu.idt.GetBase(); } bool CPU_SET_CRX(Bitu cr,Bitu value) { switch (cr) { case 0: { Bitu changed=cpu.cr0 ^ value; if (!changed) return false; cpu.cr0=value; if (value & CR0_PROTECTION) { cpu.pmode=true; LOG(LOG_CPU,LOG_NORMAL)("Protected mode"); PAGING_Enable((value & CR0_PAGING)>0); } else { cpu.pmode=false; PAGING_Enable(false); LOG(LOG_CPU,LOG_NORMAL)("Real mode"); } break; } case 2: paging.cr2=value; break; case 3: PAGING_SetDirBase(value); break; default: LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV CR%d,%X",cr,value); break; } return false; } Bitu CPU_GET_CRX(Bitu cr) { switch (cr) { case 0: return cpu.cr0; case 2: return paging.cr2; case 3: return PAGING_GetDirBase(); default: LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, CR%d",cr); break; } return 0; } void CPU_SMSW(Bitu & word) { word=cpu.cr0; } Bitu CPU_LMSW(Bitu word) { word&=0xf; if (cpu.cr0 & 1) word|=1; word|=(cpu.cr0&0xfffffff0); CPU_SET_CRX(0,word); return false; } void CPU_ARPL(Bitu & dest_sel,Bitu src_sel) { if ((dest_sel & 3) < (src_sel & 3)) { dest_sel=(dest_sel & 0xfffc) + (src_sel & 3); // dest_sel|=0xff3f0000; SETFLAGBIT(ZF,true); } else { SETFLAGBIT(ZF,false); } } void CPU_LAR(Bitu selector,Bitu & ar) { if (selector == 0) { SETFLAGBIT(ZF,false); return; } Descriptor desc;Bitu rpl=selector & 3; if (!cpu.gdt.GetDescriptor(selector,desc)){ SETFLAGBIT(ZF,false); return; } switch (desc.Type()){ case DESC_CODE_N_C_A: case DESC_CODE_N_C_NA: case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA: break; case DESC_286_INT_GATE: case DESC_286_TRAP_GATE: { case DESC_386_INT_GATE: case DESC_386_TRAP_GATE: SETFLAGBIT(ZF,false); return; } case DESC_LDT: case DESC_TASK_GATE: case DESC_286_TSS_A: case DESC_286_TSS_B: case DESC_286_CALL_GATE: case DESC_386_TSS_A: case DESC_386_TSS_B: case DESC_386_CALL_GATE: case DESC_DATA_EU_RO_NA: case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA: case DESC_DATA_EU_RW_A: case DESC_DATA_ED_RO_NA: case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA: case DESC_DATA_ED_RW_A: case DESC_CODE_N_NC_A: case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A: case DESC_CODE_R_NC_NA: if (desc.DPL()desc.DPL()) || (cpu.cpl>desc.DPL())) { E_Exit("CPU_SetSegGeneral: Invalid privileges"); // return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); } break; case DESC_CODE_R_C_A: case DESC_CODE_R_C_NA: break; default: // gabriel knight return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc); } if (!desc.saved.seg.p) { // win return CPU_PrepareException(EXCEPTION_NP,value & 0xfffc); } Segs.val[seg]=value; Segs.phys[seg]=desc.GetBase(); } return false; } } bool CPU_PopSeg(SegNames seg,bool use32) { Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask)); if (CPU_SetSegGeneral(seg,val)) return true; Bitu addsp=use32?0x04:0x02; reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+addsp)&cpu.stack.mask); return false; } void CPU_CPUID(void) { switch (reg_eax) { case 0: /* Vendor ID String and maximum level? */ reg_eax=1; reg_eax=1; /* Maximum level */ reg_ebx='G' | ('e' << 8) | ('n' << 16) | ('u'<< 24); reg_edx='i' | ('n' << 8) | ('e' << 16) | ('I'<< 24); reg_ecx='n' | ('t' << 8) | ('e' << 16) | ('l'<< 24); break; case 1: /* get processor type/family/model/stepping and feature flags */ reg_eax=0x402; /* intel 486 sx? */ reg_ebx=0; /* Not Supported */ reg_ecx=0; /* No features */ reg_edx=1; /* FPU */ break; default: LOG(LOG_CPU,LOG_ERROR)("Unhandled CPUID Function %x",reg_eax); break; } } static Bits HLT_Decode(void) { /* Once an interrupt occurs, it should change cpu core */ if (reg_eip!=cpu.hlt.eip || SegValue(cs) != cpu.hlt.cs) { cpudecoder=cpu.hlt.old_decoder; } else { CPU_Cycles=0; } return 0; } void CPU_HLT(Bitu oldeip) { if (cpu.cpl) { CPU_Exception(EXCEPTION_GP,0); return; } reg_eip=oldeip; CPU_Cycles=0; cpu.hlt.cs=SegValue(cs); cpu.hlt.eip=reg_eip; cpu.hlt.old_decoder=cpudecoder; cpudecoder=&HLT_Decode; return; } void CPU_ENTER(bool use32,Bitu bytes,Bitu level) { level&=0x1f; Bitu sp_index=reg_esp&cpu.stack.mask; Bitu bp_index=reg_ebp&cpu.stack.mask; if (!use32) { sp_index-=2; mem_writew(SegPhys(ss)+sp_index,reg_bp); reg_bp=(Bit16u)(reg_esp-2); if (level) { for (Bitu i=1;i(configuration); reg_eax=0; reg_ebx=0; reg_ecx=0; reg_edx=0; reg_edi=0; reg_esi=0; reg_ebp=0; reg_esp=0; SegSet16(cs,0); SegSet16(ds,0); SegSet16(es,0); SegSet16(fs,0); SegSet16(gs,0); SegSet16(ss,0); CPU_SetFlags(FLAG_IF,FMASK_ALL); //Enable interrupts cpu.cr0=0xffffffff; CPU_SET_CRX(0,0); //Initialize cpu.code.big=false; cpu.stack.mask=0xffff; cpu.stack.big=false; cpu.idt.SetBase(0); cpu.idt.SetLimit(1023); /* Init the cpu cores */ CPU_Core_Normal_Init(); CPU_Core_Simple_Init(); CPU_Core_Full_Init(); #if (C_DYNAMIC_X86) CPU_Core_Dyn_X86_Init(); #endif MAPPER_AddHandler(CPU_CycleDecrease,MK_f11,MMOD1,"cycledown","Dec Cycles"); MAPPER_AddHandler(CPU_CycleIncrease,MK_f12,MMOD1,"cycleup" ,"Inc Cycles"); Change_Config(configuration); CPU_JMP(false,0,0,0); //Setup the first cpu core } bool Change_Config(Section* newconfig){ Section_prop * section=static_cast(newconfig); CPU_CycleLeft=0;//needed ? CPU_Cycles=0; CPU_CycleMax=section->Get_int("cycles");; CPU_CycleUp=section->Get_int("cycleup"); CPU_CycleDown=section->Get_int("cycledown"); const char * core=section->Get_string("core"); cpudecoder=&CPU_Core_Normal_Run; if (!strcasecmp(core,"normal")) { cpudecoder=&CPU_Core_Normal_Run; } else if (!strcasecmp(core,"simple")) { cpudecoder=&CPU_Core_Simple_Run; } else if (!strcasecmp(core,"full")) { cpudecoder=&CPU_Core_Full_Run; } #if (C_DYNAMIC_X86) else if (!strcasecmp(core,"dynamic")) { cpudecoder=&CPU_Core_Dyn_X86_Run; } #endif else { LOG_MSG("CPU:Unknown core type %s, switcing back to normal.",core); } if (!CPU_CycleMax) CPU_CycleMax = 2500; if(!CPU_CycleUp) CPU_CycleUp = 500; if(!CPU_CycleDown) CPU_CycleDown = 20; GFX_SetTitle(CPU_CycleMax,-1,false); return true; } ~CPU(){ /* empty */}; }; static CPU * test; void CPU_ShutDown(Section* sec) { delete test; } void CPU_Init(Section* sec) { test = new CPU(sec); sec->AddDestroyFunction(&CPU_ShutDown,true); } //initialize static members bool CPU::inited=false;