DOSBox-X
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator
src/cpu/cpu.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 <assert.h>
00021 #include <sstream>
00022 #include <stddef.h>
00023 #include "dosbox.h"
00024 #include "cpu.h"
00025 #include "memory.h"
00026 #include "debug.h"
00027 #include "mapper.h"
00028 #include "setup.h"
00029 #include "programs.h"
00030 #include "paging.h"
00031 #include "callback.h"
00032 #include "lazyflags.h"
00033 #include "support.h"
00034 #include "control.h"
00035 
00036 #if defined(_MSC_VER)
00037 /* we don't care about switch statements with no case labels */
00038 #pragma warning(disable:4065)
00039 #endif
00040 
00041 /* caution: do not uncomment unless you want a lot of spew */
00042 //#define CPU_DEBUG_SPEW
00043 
00044 #if defined(CPU_DEBUG_SPEW)
00045 # define _LOG LOG
00046 #else
00047 class _LOG : public LOG { // HACK
00048 public:
00049         _LOG(LOG_TYPES type , LOG_SEVERITIES severity) : LOG(type,severity) { }
00050 };
00051 # undef LOG
00052 # if defined (_MSC_VER)
00053 #  define LOG(X,Y)
00054 # else
00055 #  define LOG(X,Y) CPU_LOG
00056 # define CPU_LOG(...)
00057 # endif
00058 #endif
00059 
00060 bool enable_weitek = false;
00061 
00062 bool CPU_NMI_gate = true;
00063 bool CPU_NMI_active = false;
00064 bool CPU_NMI_pending = false;
00065 bool do_seg_limits = false;
00066 
00067 bool enable_fpu = true;
00068 bool enable_msr = true;
00069 bool enable_cmpxchg8b = true;
00070 bool ignore_undefined_msr = true;
00071 
00072 extern bool ignore_opcode_63;
00073 
00074 extern bool use_dynamic_core_with_paging;
00075 
00076 bool cpu_double_fault_enable;
00077 bool cpu_triple_fault_reset;
00078 
00079 int cpu_rep_max = 0;
00080 
00081 Bitu DEBUG_EnableDebugger(void);
00082 extern void GFX_SetTitle(Bit32s cycles, Bits frameskip, Bits timing, bool paused);
00083 
00084 CPU_Regs cpu_regs;
00085 CPUBlock cpu;
00086 Segments Segs;
00087 
00088 /* [cpu] setting realbig16.
00089  * If set, allow code to switch back to real mode with the B (big) set in the
00090  * code selector, and retain the state of the B bit while running in 16-bit
00091  * real mode. Needed for demos like Project Angel.
00092  *
00093  * Modifications are a derivative of this patch:
00094  *
00095  * cpu.diff from http://www.vogons.org/viewtopic.php?f=33&t=28226&start=5
00096  *
00097  * The main difference between that patch and the modifications derived is that
00098  * I modified additional points to keep the big bit set (the original cpu.diff
00099  * missed the CALL and JMP emulation that also reset the flag)
00100  *
00101  * It's well known that DOS programs can access all 4GB of addressable memory by
00102  * jumping into protected mode, loading segment registers with a 4GB limit, then
00103  * jumping back to real mode without reloading the segment registers, knowing
00104  * that Intel processors will not update the shadow part of the segment register
00105  * in real mode. I'm guessing that what Project Angel is doing, is using the same
00106  * abuse of protected mode to also set the B (big) bit in the code segment so that
00107  * it's code segment can extend past 64KB (huge unreal mode), which works until
00108  * something like an interrupt chops off the top 16 bits of the instruction pointer.
00109  *
00110  * I want to clarify that realbig16 is an OPTION that is off by default, because
00111  * I am uncertain at this time whether or not the patch breaks any DOS games or
00112  * OS emulation. It is rare for a DOS game or demo to actually abuse the CPU in
00113  * that way, so it is set up that you have to enable it if you need it. --J.C.
00114  *
00115  * J.C. TODO: Write a program that abuses the B (big) bit in real mode in the same
00116  *            way that Project Angel supposedly does, see if it works, then test it
00117  *            and Project Angel on some old 386/486/Pentium systems lying around to
00118  *            see how compatible such abuse is with PC hardware. That would make a
00119  *            good Hackipedia.org episode as well. --J.C.
00120  *
00121  * 2014/01/19: I can attest that this patch does indeed allow Project Angel to
00122  *             run when realbig16=true. And if GUS emulation is active, there is
00123  *             music as well. Now as for reliability... testing shows that one of
00124  *             three things can happen when you run the demo:
00125  *
00126  *             1) the demo hangs on startup, either right away or after it starts
00127  *                the ominous music (if you sit for 30 seconds waiting for the
00128  *                music to build up and nothing happens, consider closing the
00129  *                emulator and trying again).
00130  *
00131  *             2) the demo runs perfectly fine, but timing is slightly screwed up,
00132  *                and parts of the music sound badly out of sync with each other,
00133  *                or randomly slows to about 1/2 speed in some sections, animations
00134  *                run slow sometimes. If this happens, make sure you didn't set
00135  *                forcerate=ntsc.
00136  *
00137  *             3) the demo runs perfectly fine, with no timing issues, except that
00138  *                DOSBox's S3 emulation is not quite on-time and the bottom 1/4th
00139  *                of the screen flickers with the contents of the next frame that
00140  *                the demo is still drawing :(
00141  *
00142  *             --J.C. */
00143 bool cpu_allow_big16 = false;
00144 
00145 cpu_cycles_count_t CPU_Cycles = 0;
00146 cpu_cycles_count_t CPU_CycleLeft = 3000;
00147 cpu_cycles_count_t CPU_CycleMax = 3000;
00148 cpu_cycles_count_t CPU_OldCycleMax = 3000;
00149 cpu_cycles_count_t CPU_CyclePercUsed = 100;
00150 cpu_cycles_count_t CPU_CycleLimit = -1;
00151 cpu_cycles_count_t CPU_CycleUp = 0;
00152 cpu_cycles_count_t CPU_CycleDown = 0;
00153 cpu_cycles_count_t CPU_CyclesSet = 3000;
00154 cpu_cycles_count_t CPU_IODelayRemoved = 0;
00155 char core_mode[16];
00156 CPU_Decoder * cpudecoder;
00157 bool CPU_CycleAutoAdjust = false;
00158 bool CPU_SkipCycleAutoAdjust = false;
00159 unsigned char CPU_AutoDetermineMode = 0;
00160 
00161 unsigned char CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
00162 
00163 Bitu CPU_extflags_toggle=0;     // ID and AC flags may be toggled depending on emulated CPU architecture
00164 
00165 unsigned int CPU_PrefetchQueueSize=0;
00166 
00167 void CPU_Core_Full_Init(void);
00168 void CPU_Core_Normal_Init(void);
00169 void CPU_Core_Simple_Init(void);
00170 #if (C_DYNAMIC_X86)
00171 void CPU_Core_Dyn_X86_Init(void);
00172 void CPU_Core_Dyn_X86_Cache_Init(bool enable_cache);
00173 void CPU_Core_Dyn_X86_Cache_Close(void);
00174 void CPU_Core_Dyn_X86_SetFPUMode(bool dh_fpu);
00175 void CPU_Core_Dyn_X86_Cache_Reset(void);
00176 #endif
00177 
00178 void menu_update_cputype(void) {
00179         Section_prop * cpu_section = static_cast<Section_prop *>(control->GetSection("cpu"));
00180         const std::string cpu_sec_type = cpu_section->Get_string("cputype");
00181 
00182     bool is486 =
00183         (CPU_ArchitectureType == CPU_ARCHTYPE_486OLD) ||
00184         (CPU_ArchitectureType == CPU_ARCHTYPE_486NEW);
00185 
00186     mainMenu.get_item("cputype_auto").
00187         check(CPU_ArchitectureType == CPU_ARCHTYPE_MIXED).
00188         refresh_item(mainMenu);
00189     mainMenu.get_item("cputype_8086").
00190         check(CPU_ArchitectureType == CPU_ARCHTYPE_8086 && (cpudecoder != &CPU_Core_Prefetch_Run)).
00191         refresh_item(mainMenu);
00192     mainMenu.get_item("cputype_8086_prefetch").
00193         check(CPU_ArchitectureType == CPU_ARCHTYPE_8086 && (cpudecoder == &CPU_Core_Prefetch_Run)).
00194         enable(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00195         refresh_item(mainMenu);
00196     mainMenu.get_item("cputype_80186").
00197         check(CPU_ArchitectureType == CPU_ARCHTYPE_80186 && (cpudecoder != &CPU_Core_Prefetch_Run)).
00198         refresh_item(mainMenu);
00199     mainMenu.get_item("cputype_80186_prefetch").
00200         check(CPU_ArchitectureType == CPU_ARCHTYPE_80186 && (cpudecoder == &CPU_Core_Prefetch_Run)).
00201         enable(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00202         refresh_item(mainMenu);
00203     mainMenu.get_item("cputype_286").
00204         check(CPU_ArchitectureType == CPU_ARCHTYPE_286 && (cpudecoder != &CPU_Core_Prefetch_Run)).
00205         refresh_item(mainMenu);
00206     mainMenu.get_item("cputype_286_prefetch").
00207         check(CPU_ArchitectureType == CPU_ARCHTYPE_286 && (cpudecoder == &CPU_Core_Prefetch_Run)).
00208         enable(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00209         refresh_item(mainMenu);
00210     mainMenu.get_item("cputype_386").
00211         check(CPU_ArchitectureType == CPU_ARCHTYPE_386 && (cpudecoder != &CPU_Core_Prefetch_Run)).
00212         refresh_item(mainMenu);
00213     mainMenu.get_item("cputype_386_prefetch").
00214         check(CPU_ArchitectureType == CPU_ARCHTYPE_386 && (cpudecoder == &CPU_Core_Prefetch_Run)).
00215         enable(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00216         refresh_item(mainMenu);
00217     mainMenu.get_item("cputype_486").
00218         check(is486 && (cpudecoder != &CPU_Core_Prefetch_Run)).
00219         refresh_item(mainMenu);
00220     mainMenu.get_item("cputype_486_prefetch").
00221         check(is486 && (cpudecoder == &CPU_Core_Prefetch_Run)).
00222         enable(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00223         refresh_item(mainMenu);
00224     mainMenu.get_item("cputype_pentium").
00225         check(CPU_ArchitectureType == CPU_ARCHTYPE_PENTIUM).
00226         refresh_item(mainMenu);
00227     mainMenu.get_item("cputype_pentium_mmx").
00228         check(CPU_ArchitectureType == CPU_ARCHTYPE_P55CSLOW).
00229         refresh_item(mainMenu);
00230     mainMenu.get_item("cputype_ppro_slow").
00231         check(CPU_ArchitectureType == CPU_ARCHTYPE_PPROSLOW).
00232         refresh_item(mainMenu);
00233 }
00234 
00235 void menu_update_core(void) {
00236         Section_prop * cpu_section = static_cast<Section_prop *>(control->GetSection("cpu"));
00237         const std::string cpu_sec_type = cpu_section->Get_string("cputype");
00238     bool allow_dynamic = false;
00239 
00240     (void)cpu_section;
00241     (void)cpu_sec_type;
00242     (void)allow_dynamic;
00243 
00244     /* cannot select Dynamic core if prefetch cpu types are in use */
00245     allow_dynamic = (strstr(cpu_sec_type.c_str(),"_prefetch") == NULL);
00246 
00247     mainMenu.get_item("mapper_normal").
00248         check(cpudecoder == &CPU_Core_Normal_Run || cpudecoder == &CPU_Core_Prefetch_Run).
00249         refresh_item(mainMenu);
00250     mainMenu.get_item("mapper_simple").
00251         check(cpudecoder == &CPU_Core_Simple_Run).
00252         enable(cpudecoder != &CPU_Core_Prefetch_Run).
00253         refresh_item(mainMenu);
00254     mainMenu.get_item("mapper_full").
00255         check(cpudecoder == &CPU_Core_Full_Run).
00256         enable(cpudecoder != &CPU_Core_Prefetch_Run).
00257         refresh_item(mainMenu);
00258 #if (C_DYNAMIC_X86)
00259     mainMenu.get_item("mapper_dynamic").
00260         check(cpudecoder == &CPU_Core_Dyn_X86_Run).
00261         enable(allow_dynamic && (cpudecoder != &CPU_Core_Prefetch_Run)).
00262         refresh_item(mainMenu);
00263 #endif
00264 }
00265 
00266 void menu_update_autocycle(void) {
00267     DOSBoxMenu::item &item = mainMenu.get_item("mapper_cycauto");
00268     if (CPU_CycleAutoAdjust)
00269         item.set_text("Auto cycles [max]");
00270     else if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES)
00271         item.set_text("Auto cycles [auto]");
00272     else
00273         item.set_text("Auto cycles [off]");
00274 
00275     item.check(CPU_CycleAutoAdjust || (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES));
00276     item.refresh_item(mainMenu);
00277 }
00278 
00279 /* called to signal an NMI. */
00280 
00281 /* NTS: From the Intel 80386 programmer's reference manual:
00282  *
00283  * "
00284  *   9.2.1 NMI Masks Further NMIs
00285  *
00286  *   While an NMI handler is executing, the processor ignores further interrupt
00287  *   signals at the NMI pin until the next IRET instruction is executed.
00288  * "
00289  *
00290  * This is why, further down, CPU_IRET() clears the CPU_NMI_active flag.
00291  *
00292  *
00293  * And, in response to my father's incredulous response regarding the fact that
00294  * NMI is edge-triggered (from the Intel 386SX Microprocessor datasheet):
00295  *
00296  * "
00297  *   Non-Maskable Interrupt Request (NMI))
00298  *
00299  *   This input indicates a request for interrupt service
00300  *   which cannot be masked by software. The non-
00301  *   maskable interrupt request is always processed ac-
00302  *   cording to the pointer or gate in slot 2 of the interrupt
00303  *   table. Because of the fixed NMI slot assignment, no
00304  *   interrupt acknowledge cycles are performed when
00305  *   processing NMI.
00306  *
00307  *   NMI is an active HIGH, rising edge-sensitive asyn-
00308  *   chronous signal. Setup and hold times, t27 and and t28,
00309  *   relative to the CLK2 signal must be met to guarantee
00310  *   recognition at a particular clock edge. To assure rec-
00311  *   ognition of NMI, it must be inactive for at least eight
00312  *   CLK2 periods, and then be active for at least eight
00313  *   CLK2 periods before the beginning of the instruction
00314  *   boundary in the Intel386 SX Microprocessor's Exe-
00315  *   cution Unit.
00316  *
00317  *   Once NMI processing has begun, no additional
00318  *   NMI's are processed until after the next IRET in-
00319  *   struction, which is typically the end of the NMI serv-
00320  *   ice routine. If NMI is re-asserted prior to that time,
00321  *   however, one rising edge on NMI will be remem-
00322  *   bered for processing after executing the next IRET
00323  *   instruction
00324  * "
00325  *
00326  * From the Pentium Pro Processor datasheet:
00327  *
00328  * "
00329  *   A.38 NMI (I)
00330  *
00331  *   The NMI signal is the Non-maskable Interrupt signal.
00332  *   It is the state of the LINT1 signal when APIC is
00333  *   disabled. Asserting NMI causes an interrupt with an
00334  *   internally supplied vector value of 2. An external
00335  *   interrupt-acknowledge transaction is not generated. If
00336  *   NMI is asserted during the execution of an NMI
00337  *   service routine, it remains pending and is recognized
00338  *   after the IRET is executed by the NMI service
00339  *   routine. At most, one assertion of NMI is held
00340  *   pending.
00341  *
00342  *   NMI is rising-edge sensitive. Recognition of NMI is
00343  *   guaranteed in a specific clock if it is asserted
00344  *   synchronously and meets the setup and hold times. If
00345  *   asserted asynchronously, active and inactive pulse
00346  *   widths must be a minimum of two clocks. In FRC
00347  *   mode, NMI must be synchronous to BCLK.
00348  * "
00349  *
00350  * Similar references exist in the Pentium III and Pentium 4
00351  * datasheets, while later on in the Core 2 datasheets there
00352  * is no mention whatsoever to the NMI that I can find.
00353  */
00354 void CPU_NMI_Interrupt() {
00355         if (CPU_NMI_active) E_Exit("CPU_NMI_Interrupt() called while NMI already active");
00356         CPU_NMI_active = true;
00357         CPU_NMI_pending = false;
00358         CPU_Interrupt(2/*INT 2 = NMI*/,0,reg_eip);
00359 }
00360 
00361 void CPU_Raise_NMI() {
00362         CPU_NMI_pending = true;
00363         if (!CPU_NMI_active && CPU_NMI_gate) CPU_NMI_Interrupt();
00364 }
00365 
00366 void CPU_Check_NMI() {
00367         if (!CPU_NMI_active && CPU_NMI_gate && CPU_NMI_pending) CPU_NMI_Interrupt();
00368 }
00369 
00370 /* In debug mode exceptions are tested and dosbox exits when 
00371  * a unhandled exception state is detected. 
00372  * USE CHECK_EXCEPT to raise an exception in that case to see if that exception
00373  * solves the problem.
00374  * 
00375  * In non-debug mode dosbox doesn't do detection (and hence doesn't crash at
00376  * that point). (game might crash later due to the unhandled exception) */
00377 
00378 #define CPU_CHECK_EXCEPT 1
00379 // #define CPU_CHECK_IGNORE 1
00380 
00381 #if C_DEBUG
00382 // #define CPU_CHECK_EXCEPT 1
00383 // #define CPU_CHECK_IGNORE 1
00384  /* Use CHECK_EXCEPT when something doesn't work to see if a exception is 
00385  * needed that isn't enabled by default.*/
00386 #else
00387 /* NORMAL NO CHECKING => More Speed */
00388 //#define CPU_CHECK_IGNORE 1
00389 #endif /* C_DEBUG */
00390 
00391 #if defined(CPU_CHECK_IGNORE)
00392 #define CPU_CHECK_COND(cond,msg,exc,sel) {      \
00393         if (cond) do {} while (0);                              \
00394 }
00395 #elif defined(CPU_CHECK_EXCEPT)
00396 #define CPU_CHECK_COND(cond,msg,exc,sel) {      \
00397         if (cond) {                                     \
00398                 CPU_Exception(exc,sel);         \
00399                 return;                         \
00400         }                                       \
00401 }
00402 #else
00403 #define CPU_CHECK_COND(cond,msg,exc,sel) {      \
00404         if (cond) E_Exit(msg);                  \
00405 }
00406 #endif
00407 
00408 
00409 void Descriptor::Load(PhysPt address) {
00410         cpu.mpl=0;
00411         Bit32u* data = (Bit32u*)&saved;
00412         *data     = mem_readd(address);
00413         *(data+1) = mem_readd(address+4);
00414         cpu.mpl=3;
00415 }
00416 void Descriptor:: Save(PhysPt address) {
00417         cpu.mpl=0;
00418         Bit32u* data = (Bit32u*)&saved;
00419         mem_writed(address,*data);
00420         mem_writed(address+4,*(data+1));
00421         cpu.mpl=3;
00422 }
00423 
00424 
00425 void CPU_Push16(Bitu value) {
00426         Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-2)&cpu.stack.mask);
00427         mem_writew(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value);
00428         reg_esp=new_esp;
00429 }
00430 
00431 void CPU_Push32(Bitu value) {
00432         Bit32u new_esp=(reg_esp&cpu.stack.notmask)|((reg_esp-4)&cpu.stack.mask);
00433         mem_writed(SegPhys(ss) + (new_esp & cpu.stack.mask) ,value);
00434         reg_esp=new_esp;
00435 }
00436 
00437 Bitu CPU_Pop16(void) {
00438         Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
00439         reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
00440         return val;
00441 }
00442 
00443 Bitu CPU_Pop32(void) {
00444         Bitu val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
00445         reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
00446         return val;
00447 }
00448 
00449 PhysPt SelBase(Bitu sel) {
00450         if (cpu.cr0 & CR0_PROTECTION) {
00451                 Descriptor desc;
00452                 cpu.gdt.GetDescriptor(sel,desc);
00453                 return desc.GetBase();
00454         } else {
00455                 return sel<<4;
00456         }
00457 }
00458 
00459 void CPU_SetCPL(Bitu newcpl) {
00460         if (newcpl != cpu.cpl) {
00461                 if (paging.enabled) {
00462                         if ( ((cpu.cpl < 3) && (newcpl == 3)) || ((cpu.cpl == 3) && (newcpl < 3)) )
00463                         PAGING_SwitchCPL(newcpl == 3);
00464                 }
00465                 cpu.cpl = newcpl;
00466         }
00467 }
00468 
00469 void CPU_SetFlags(Bitu word,Bitu mask) {
00470         /* 8086/286 flags manipulation.
00471          * For more information read about the Intel CPU detection algorithm and other bits of info:
00472          * [http://www.rcollins.org/ddj/Sep96/Sep96.html] */
00473 
00474         /* 8086: bits 12-15 cannot be zeroed */
00475         if (CPU_ArchitectureType <= CPU_ARCHTYPE_80186) {
00476                 /* update mask and word to ensure bits 12-15 are set */
00477                 word |= 0xF000U;
00478                 mask |= 0xF000U;
00479         }
00480         /* 286 real mode: bits 12-15 bits cannot be set, always zero */
00481         else if (CPU_ArchitectureType <= CPU_ARCHTYPE_286) {
00482                 if (!(cpu.cr0 & CR0_PROTECTION)) {
00483                         /* update mask and word to ensure bits 12-15 are zero */
00484                         word &= ~0xF000U;
00485                         mask |= 0xF000U;
00486                 }
00487         }
00488         else {
00489                 mask |= CPU_extflags_toggle;    // ID-flag and AC-flag can be toggled on CPUID-supporting CPUs
00490         }
00491 
00492         reg_flags=(reg_flags & ~mask)|(word & mask)|2U;
00493         cpu.direction=1 - (int)((reg_flags & FLAG_DF) >> 9U);
00494         // ^ NTS: Notice the DF flag is bit 10. This code computes (reg_flags & FLAG_DF) >> 9 on purpose.
00495         //        It's not a typo (9 vs 10), it's done to set cpu.direction to either 1 or -1.
00496 }
00497 
00498 bool CPU_PrepareException(Bitu which,Bitu error) {
00499         cpu.exception.which=which;
00500         cpu.exception.error=error;
00501         return true;
00502 }
00503 
00504 bool CPU_CLI(void) {
00505         if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
00506                 return CPU_PrepareException(EXCEPTION_GP,0);
00507         } else {
00508                 SETFLAGBIT(IF,false);
00509                 return false;
00510         }
00511 }
00512 
00513 bool CPU_STI(void) {
00514         if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
00515                 return CPU_PrepareException(EXCEPTION_GP,0);
00516         } else {
00517                 SETFLAGBIT(IF,true);
00518                 return false;
00519         }
00520 }
00521 
00522 bool CPU_POPF(Bitu use32) {
00523         if (cpu.pmode && GETFLAG(VM) && (GETFLAG(IOPL)!=FLAG_IOPL)) {
00524                 /* Not enough privileges to execute POPF */
00525                 return CPU_PrepareException(EXCEPTION_GP,0);
00526         }
00527         Bitu mask=FMASK_ALL;
00528         /* IOPL field can only be modified when CPL=0 or in real mode: */
00529         if (cpu.pmode && (cpu.cpl>0)) mask &= (~FLAG_IOPL);
00530         if (cpu.pmode && !GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) mask &= (~FLAG_IF);
00531         if (use32)
00532                 CPU_SetFlags(CPU_Pop32(),mask);
00533         else CPU_SetFlags(CPU_Pop16(),mask & 0xffff);
00534         DestroyConditionFlags();
00535         return false;
00536 }
00537 
00538 bool CPU_PUSHF(Bitu use32) {
00539         if (cpu.pmode && GETFLAG(VM) && (GETFLAG(IOPL)!=FLAG_IOPL)) {
00540                 /* Not enough privileges to execute PUSHF */
00541                 return CPU_PrepareException(EXCEPTION_GP,0);
00542         }
00543         FillFlags();
00544         if (use32) 
00545                 CPU_Push32(reg_flags & 0xfcffff);
00546         else CPU_Push16(reg_flags);
00547         return false;
00548 }
00549 
00550 void CPU_CheckSegment(const enum SegNames segi) {
00551         bool needs_invalidation=false;
00552         Descriptor desc;
00553 
00554     if (!cpu.gdt.GetDescriptor(SegValue(segi),desc)) {
00555         needs_invalidation=true;
00556     }
00557     else {
00558         switch (desc.Type()) {
00559             case DESC_DATA_EU_RO_NA:    case DESC_DATA_EU_RO_A: case DESC_DATA_EU_RW_NA:    case DESC_DATA_EU_RW_A:
00560             case DESC_DATA_ED_RO_NA:    case DESC_DATA_ED_RO_A: case DESC_DATA_ED_RW_NA:    case DESC_DATA_ED_RW_A:
00561             case DESC_CODE_N_NC_A:      case DESC_CODE_N_NC_NA: case DESC_CODE_R_NC_A:      case DESC_CODE_R_NC_NA:
00562                 if (cpu.cpl>desc.DPL()) needs_invalidation=true;
00563                 break;
00564             default:
00565                 break;
00566         }
00567     }
00568 
00569     if (needs_invalidation)
00570         CPU_SetSegGeneral(segi,0);
00571 }
00572 
00573 void CPU_CheckSegments(void) {
00574     CPU_CheckSegment(es);
00575     CPU_CheckSegment(ds);
00576     CPU_CheckSegment(fs);
00577     CPU_CheckSegment(gs);
00578 }
00579 
00580 class TaskStateSegment {
00581 public:
00582         TaskStateSegment() {
00583                 valid=false;
00584         }
00585         bool IsValid(void) {
00586                 return valid;
00587         }
00588         Bitu Get_back(void) {
00589                 cpu.mpl=0;
00590                 Bit16u backlink=mem_readw(base);
00591                 cpu.mpl=3;
00592                 return backlink;
00593         }
00594         void SaveSelector(void) {
00595                 cpu.gdt.SetDescriptor(selector,desc);
00596         }
00597         void Get_SSx_ESPx(Bitu level,Bitu & _ss,Bitu & _esp) {
00598                 cpu.mpl=0;
00599                 if (is386) {
00600                         PhysPt where=base+offsetof(TSS_32,esp0)+level*8;
00601                         _esp=mem_readd(where);
00602                         _ss=mem_readw(where+4);
00603                 } else {
00604                         PhysPt where=base+offsetof(TSS_16,sp0)+level*4;
00605                         _esp=mem_readw(where);
00606                         _ss=mem_readw(where+2);
00607                 }
00608                 cpu.mpl=3;
00609         }
00610         bool SetSelector(Bitu new_sel) {
00611                 valid=false;
00612                 if ((new_sel & 0xfffc)==0) {
00613                         selector=0;
00614                         base=0;
00615                         limit=0;
00616                         is386=1;
00617                         return true;
00618                 }
00619                 if (new_sel&4) return false;
00620                 if (!cpu.gdt.GetDescriptor(new_sel,desc)) return false;
00621                 switch (desc.Type()) {
00622                         case DESC_286_TSS_A:            case DESC_286_TSS_B:
00623                         case DESC_386_TSS_A:            case DESC_386_TSS_B:
00624                                 break;
00625                         default:
00626                                 return false;
00627                 }
00628                 if (!desc.saved.seg.p) return false;
00629                 selector=new_sel;
00630                 valid=true;
00631                 base=desc.GetBase();
00632                 limit=desc.GetLimit();
00633                 is386=desc.Is386();
00634                 return true;
00635         }
00636 
00637         void SaveState( std::ostream& stream );
00638         void LoadState( std::istream& stream );
00639 
00640         TSS_Descriptor desc;
00641         Bitu selector;
00642         PhysPt base;
00643         Bitu limit;
00644         Bitu is386;
00645         bool valid;
00646 };
00647 
00648 TaskStateSegment cpu_tss;
00649 
00650 enum TSwitchType {
00651         TSwitch_JMP,TSwitch_CALL_INT,TSwitch_IRET
00652 };
00653 
00654 bool CPU_SwitchTask(Bitu new_tss_selector,TSwitchType tstype,Bitu old_eip) {
00655         bool old_allow = dosbox_allow_nonrecursive_page_fault;
00656 
00657         /* this code isn't very easy to make interruptible. so temporarily revert to recursive PF handling method */
00658         dosbox_allow_nonrecursive_page_fault = false;
00659 
00660         FillFlags();
00661         TaskStateSegment new_tss;
00662         if (!new_tss.SetSelector(new_tss_selector)) 
00663                 E_Exit("Illegal TSS for switch, selector=%x, switchtype=%lx",(int)new_tss_selector,(unsigned long)tstype);
00664         if (tstype==TSwitch_IRET) {
00665                 if (!new_tss.desc.IsBusy())
00666                         E_Exit("TSS not busy for IRET");
00667         } else {
00668                 if (new_tss.desc.IsBusy())
00669                         E_Exit("TSS busy for JMP/CALL/INT");
00670         }
00671         Bitu new_cr3=0;
00672         Bitu new_eax,new_ebx,new_ecx,new_edx,new_esp,new_ebp,new_esi,new_edi;
00673         Bitu new_es,new_cs,new_ss,new_ds,new_fs,new_gs;
00674         Bitu new_ldt,new_eip,new_eflags;
00675         /* Read new context from new TSS */
00676         if (new_tss.is386) {
00677                 new_cr3=mem_readd(new_tss.base+offsetof(TSS_32,cr3));
00678                 new_eip=mem_readd(new_tss.base+offsetof(TSS_32,eip));
00679                 new_eflags=mem_readd(new_tss.base+offsetof(TSS_32,eflags));
00680                 new_eax=mem_readd(new_tss.base+offsetof(TSS_32,eax));
00681                 new_ecx=mem_readd(new_tss.base+offsetof(TSS_32,ecx));
00682                 new_edx=mem_readd(new_tss.base+offsetof(TSS_32,edx));
00683                 new_ebx=mem_readd(new_tss.base+offsetof(TSS_32,ebx));
00684                 new_esp=mem_readd(new_tss.base+offsetof(TSS_32,esp));
00685                 new_ebp=mem_readd(new_tss.base+offsetof(TSS_32,ebp));
00686                 new_edi=mem_readd(new_tss.base+offsetof(TSS_32,edi));
00687                 new_esi=mem_readd(new_tss.base+offsetof(TSS_32,esi));
00688 
00689                 new_es=mem_readw(new_tss.base+offsetof(TSS_32,es));
00690                 new_cs=mem_readw(new_tss.base+offsetof(TSS_32,cs));
00691                 new_ss=mem_readw(new_tss.base+offsetof(TSS_32,ss));
00692                 new_ds=mem_readw(new_tss.base+offsetof(TSS_32,ds));
00693                 new_fs=mem_readw(new_tss.base+offsetof(TSS_32,fs));
00694                 new_gs=mem_readw(new_tss.base+offsetof(TSS_32,gs));
00695                 new_ldt=mem_readw(new_tss.base+offsetof(TSS_32,ldt));
00696         } else {
00697                 E_Exit("286 task switch");
00698                 new_cr3=0;
00699                 new_eip=0;
00700                 new_eflags=0;
00701                 new_eax=0;      new_ecx=0;      new_edx=0;      new_ebx=0;
00702                 new_esp=0;      new_ebp=0;      new_edi=0;      new_esi=0;
00703 
00704                 new_es=0;       new_cs=0;       new_ss=0;       new_ds=0;       new_fs=0;       new_gs=0;
00705                 new_ldt=0;
00706         }
00707 
00708         /* Check if we need to clear busy bit of old TASK */
00709         if (tstype==TSwitch_JMP || tstype==TSwitch_IRET) {
00710                 cpu_tss.desc.SetBusy(false);
00711                 cpu_tss.SaveSelector();
00712         }
00713         Bit32u old_flags = reg_flags;
00714         if (tstype==TSwitch_IRET) old_flags &= (~FLAG_NT);
00715 
00716         /* Save current context in current TSS */
00717         if (cpu_tss.is386) {
00718                 mem_writed(cpu_tss.base+offsetof(TSS_32,eflags),old_flags);
00719                 mem_writed(cpu_tss.base+offsetof(TSS_32,eip),old_eip);
00720 
00721                 mem_writed(cpu_tss.base+offsetof(TSS_32,eax),reg_eax);
00722                 mem_writed(cpu_tss.base+offsetof(TSS_32,ecx),reg_ecx);
00723                 mem_writed(cpu_tss.base+offsetof(TSS_32,edx),reg_edx);
00724                 mem_writed(cpu_tss.base+offsetof(TSS_32,ebx),reg_ebx);
00725                 mem_writed(cpu_tss.base+offsetof(TSS_32,esp),reg_esp);
00726                 mem_writed(cpu_tss.base+offsetof(TSS_32,ebp),reg_ebp);
00727                 mem_writed(cpu_tss.base+offsetof(TSS_32,esi),reg_esi);
00728                 mem_writed(cpu_tss.base+offsetof(TSS_32,edi),reg_edi);
00729 
00730                 mem_writed(cpu_tss.base+offsetof(TSS_32,es),SegValue(es));
00731                 mem_writed(cpu_tss.base+offsetof(TSS_32,cs),SegValue(cs));
00732                 mem_writed(cpu_tss.base+offsetof(TSS_32,ss),SegValue(ss));
00733                 mem_writed(cpu_tss.base+offsetof(TSS_32,ds),SegValue(ds));
00734                 mem_writed(cpu_tss.base+offsetof(TSS_32,fs),SegValue(fs));
00735                 mem_writed(cpu_tss.base+offsetof(TSS_32,gs),SegValue(gs));
00736         } else {
00737                 E_Exit("286 task switch");
00738         }
00739 
00740         /* Setup a back link to the old TSS in new TSS */
00741         if (tstype==TSwitch_CALL_INT) {
00742                 if (new_tss.is386) {
00743                         mem_writed(new_tss.base+offsetof(TSS_32,back),cpu_tss.selector);
00744                 } else {
00745                         mem_writew(new_tss.base+offsetof(TSS_16,back),cpu_tss.selector);
00746                 }
00747                 /* And make the new task's eflag have the nested task bit */
00748                 new_eflags|=FLAG_NT;
00749         }
00750         /* Set the busy bit in the new task */
00751         if (tstype==TSwitch_JMP || tstype==TSwitch_CALL_INT) {
00752                 new_tss.desc.SetBusy(true);
00753                 new_tss.SaveSelector();
00754         }
00755 
00756 //      cpu.cr0|=CR0_TASKSWITCHED;
00757         if (new_tss_selector == cpu_tss.selector) {
00758                 reg_eip = old_eip;
00759                 new_cs = SegValue(cs);
00760                 new_ss = SegValue(ss);
00761                 new_ds = SegValue(ds);
00762                 new_es = SegValue(es);
00763                 new_fs = SegValue(fs);
00764                 new_gs = SegValue(gs);
00765         } else {
00766         
00767                 /* Setup the new cr3 */
00768                 if (paging.cr3 != new_cr3)
00769                         // if they are the same it is not flushed
00770                         // according to the 386 manual
00771                 PAGING_SetDirBase(new_cr3);
00772 
00773                 /* Load new context */
00774                 if (new_tss.is386) {
00775                         reg_eip=new_eip;
00776                         CPU_SetFlags(new_eflags,FMASK_ALL | FLAG_VM);
00777                         reg_eax=new_eax;
00778                         reg_ecx=new_ecx;
00779                         reg_edx=new_edx;
00780                         reg_ebx=new_ebx;
00781                         reg_esp=new_esp;
00782                         reg_ebp=new_ebp;
00783                         reg_edi=new_edi;
00784                         reg_esi=new_esi;
00785 
00786 //                      new_cs=mem_readw(new_tss.base+offsetof(TSS_32,cs));
00787                 } else {
00788                         E_Exit("286 task switch");
00789                 }
00790         }
00791         /* Load the new selectors */
00792         if (reg_flags & FLAG_VM) {
00793                 SegSet16(cs,new_cs);
00794                 cpu.code.big=false;
00795                 CPU_SetCPL(3);                  //We don't have segment caches so this will do
00796         } else {
00797                 /* Protected mode task */
00798                 if (new_ldt!=0) CPU_LLDT(new_ldt);
00799                 /* Load the new CS*/
00800                 Descriptor cs_desc;
00801                 CPU_SetCPL(new_cs & 3);
00802                 if (!cpu.gdt.GetDescriptor(new_cs,cs_desc))
00803                         E_Exit("Task switch with CS beyond limits");
00804                 if (!cs_desc.saved.seg.p)
00805                         E_Exit("Task switch with non present code-segment");
00806                 switch (cs_desc.Type()) {
00807                 case DESC_CODE_N_NC_A:          case DESC_CODE_N_NC_NA:
00808                 case DESC_CODE_R_NC_A:          case DESC_CODE_R_NC_NA:
00809                         if (cpu.cpl != cs_desc.DPL()) E_Exit("Task CS RPL != DPL");
00810                         goto doconforming;
00811                 case DESC_CODE_N_C_A:           case DESC_CODE_N_C_NA:
00812                 case DESC_CODE_R_C_A:           case DESC_CODE_R_C_NA:
00813                         if (cpu.cpl < cs_desc.DPL()) E_Exit("Task CS RPL < DPL");
00814 doconforming:
00815                         Segs.expanddown[cs]=cs_desc.GetExpandDown();
00816                         Segs.limit[cs]=do_seg_limits?cs_desc.GetLimit():((PhysPt)(~0UL));
00817                         Segs.phys[cs]=cs_desc.GetBase();
00818                         cpu.code.big=cs_desc.Big()>0;
00819                         Segs.val[cs]=new_cs;
00820                         break;
00821                 default:
00822                         E_Exit("Task switch CS Type %d",(int)cs_desc.Type());
00823                 }
00824         }
00825         CPU_SetSegGeneral(es,new_es);
00826         CPU_SetSegGeneral(ss,new_ss);
00827         CPU_SetSegGeneral(ds,new_ds);
00828         CPU_SetSegGeneral(fs,new_fs);
00829         CPU_SetSegGeneral(gs,new_gs);
00830         if (!cpu_tss.SetSelector(new_tss_selector)) {
00831                 LOG(LOG_CPU,LOG_NORMAL)("TaskSwitch: set tss selector %X failed",new_tss_selector);
00832         }
00833 //      cpu_tss.desc.SetBusy(true);
00834 //      cpu_tss.SaveSelector();
00835 //      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);
00836 
00837         dosbox_allow_nonrecursive_page_fault = old_allow;
00838         return true;
00839 }
00840 
00841 bool CPU_IO_Exception(Bitu port,Bitu size) {
00842         if (cpu.pmode && ((GETFLAG_IOPL<cpu.cpl) || GETFLAG(VM))) {
00843                 cpu.mpl=0;
00844                 if (!cpu_tss.is386) goto doexception;
00845                 PhysPt bwhere=cpu_tss.base+0x66;
00846                 Bitu ofs=mem_readw(bwhere);
00847                 if (ofs>cpu_tss.limit) goto doexception;
00848                 bwhere=cpu_tss.base+ofs+(port/8);
00849                 Bitu map=mem_readw(bwhere);
00850                 Bitu mask=(0xffffu >> (16u - size)) << (port & 7u);
00851                 if (map & mask) goto doexception;
00852                 cpu.mpl=3;
00853         }
00854         return false;
00855 doexception:
00856         cpu.mpl=3;
00857         LOG(LOG_CPU,LOG_NORMAL)("IO Exception port %X",port);
00858         return CPU_PrepareException(EXCEPTION_GP,0);
00859 }
00860 
00861 #include <stack>
00862 
00863 int CPU_Exception_Level[0x20] = {0};
00864 std::stack<int> CPU_Exception_In_Progress;
00865 
00866 void CPU_Exception_Level_Reset() {
00867         int i;
00868 
00869         for (i=0;i < 0x20;i++)
00870                 CPU_Exception_Level[i] = 0;
00871         while (!CPU_Exception_In_Progress.empty())
00872                 CPU_Exception_In_Progress.pop();
00873 }
00874 
00875 bool has_printed_double_fault = false;
00876 bool has_printed_triple_fault = false;
00877 bool always_report_double_fault = false;
00878 bool always_report_triple_fault = false;
00879 
00880 void On_Software_CPU_Reset();
00881 
00882 void CPU_Exception(Bitu which,Bitu error ) {
00883         assert(which < 0x20);
00884 //      LOG_MSG("Exception %d error %x",which,error);
00885         if (CPU_Exception_Level[which] != 0) {
00886                 if (CPU_Exception_Level[EXCEPTION_DF] != 0 && cpu_triple_fault_reset) {
00887                         if (always_report_triple_fault || !has_printed_triple_fault) {
00888                                 LOG_MSG("CPU_Exception: Double fault already in progress == Triple Fault. Resetting CPU.");
00889                                 has_printed_triple_fault = true;
00890                         }
00891 
00892                         // Triple fault -> special shutdown cycle -> reset signal -> reset.
00893                         // Sickening, I know, but that's how IBM wired things a long long time ago.
00894                         On_Software_CPU_Reset();
00895                         E_Exit("Triple fault reset call unexpectedly returned");
00896                 }
00897 
00898                 if (always_report_double_fault || !has_printed_double_fault) {
00899                         LOG_MSG("CPU_Exception: Exception %d already in progress, triggering double fault instead",(int)which);
00900                         has_printed_double_fault = true;
00901                 }
00902                 which = EXCEPTION_DF;
00903                 error = 0;
00904         }
00905 
00906         if (cpu_double_fault_enable) {
00907                 /* CPU_Interrupt() could cause another fault during memory access. This needs to happen here */
00908                 CPU_Exception_Level[which]++;
00909                 CPU_Exception_In_Progress.push(which);
00910         }
00911 
00912         cpu.exception.error=error;
00913         CPU_Interrupt(which,CPU_INT_EXCEPTION | ((which>=8) ? CPU_INT_HAS_ERROR : 0),reg_eip);
00914 
00915         /* allow recursive page faults. required for multitasking OSes like Windows 95.
00916          * we set this AFTER CPU_Interrupt so that if CPU_Interrupt faults while starting
00917          * a page fault we still trigger double fault. */
00918         if (which == EXCEPTION_PF || which == EXCEPTION_GP) {
00919                 if (CPU_Exception_Level[which] > 0)
00920                         CPU_Exception_Level[which]--;
00921 
00922                 if (!CPU_Exception_In_Progress.empty()) {
00923                         if ((Bitu)CPU_Exception_In_Progress.top() == which)
00924                                 CPU_Exception_In_Progress.pop();
00925                         else
00926                                 LOG_MSG("Top of fault stack not the same as what I'm handling");
00927                 }
00928         }
00929 }
00930 
00931 Bit8u lastint;
00932 void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) {
00933         lastint=num;
00934         FillFlags();
00935 #if C_DEBUG
00936 # if C_HEAVY_DEBUG
00937     bool DEBUG_IntBreakpoint(Bit8u intNum);
00938     Bitu DEBUG_EnableDebugger(void);
00939 
00940     if (type != CPU_INT_SOFTWARE) { /* CPU core already takes care of SW interrupts */
00941         if (DEBUG_IntBreakpoint(num))
00942             DEBUG_EnableDebugger();
00943     }
00944 # endif
00945 
00946         switch (num) {
00947         case 0xcd:
00948 #if C_HEAVY_DEBUG
00949                 LOG(LOG_CPU,LOG_ERROR)("Call to interrupt 0xCD this is BAD");
00950                 DEBUG_HeavyWriteLogInstruction();
00951                 E_Exit("Call to interrupt 0xCD this is BAD");
00952 #endif
00953                 break;
00954         case 0x03:
00955                 if (DEBUG_Breakpoint()) {
00956                         CPU_Cycles=0;
00957                         return;
00958                 }
00959         };
00960 #endif
00961         if (!cpu.pmode) {
00962                 /* Save everything on a 16-bit stack */
00963                 CPU_Push16(reg_flags & 0xffff);
00964                 CPU_Push16(SegValue(cs));
00965                 CPU_Push16(oldeip);
00966                 SETFLAGBIT(IF,false);
00967                 SETFLAGBIT(TF,false);
00968                 /* Get the new CS:IP from vector table */
00969                 PhysPt base=cpu.idt.GetBase();
00970                 reg_eip=mem_readw(base+(num << 2));
00971                 Segs.val[cs]=mem_readw(base+(num << 2)+2);
00972                 Segs.phys[cs]=Segs.val[cs]<<4;
00973                 if (!cpu_allow_big16) cpu.code.big=false;
00974                 return;
00975         } else {
00976                 /* Protected Mode Interrupt */
00977                 if ((reg_flags & FLAG_VM) && (type&CPU_INT_SOFTWARE) && !(type&CPU_INT_NOIOPLCHECK)) {
00978 //                      LOG_MSG("Software int in v86, AH %X IOPL %x",reg_ah,(reg_flags & FLAG_IOPL) >>12);
00979                         if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
00980                                 CPU_Exception(EXCEPTION_GP,0);
00981                                 return;
00982                         }
00983                 } 
00984 
00985                 Descriptor gate;
00986                 if (!cpu.idt.GetDescriptor(num<<3,gate)) {
00987                         // zone66
00988                         CPU_Exception(EXCEPTION_GP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1);
00989                         return;
00990                 }
00991 
00992                 if ((type&CPU_INT_SOFTWARE) && (gate.DPL()<cpu.cpl)) {
00993                         // zone66, win3.x e
00994                         CPU_Exception(EXCEPTION_GP,num*8+2);
00995                         return;
00996                 }
00997 
00998                 Bitu old_esp,old_ss,old_cpl;
00999 
01000                 old_esp = reg_esp;
01001                 old_ss = SegValue(ss);
01002                 old_cpl = cpu.cpl;
01003 
01004                 try {
01005                 switch (gate.Type()) {
01006                 case DESC_286_INT_GATE:         case DESC_386_INT_GATE:
01007                 case DESC_286_TRAP_GATE:        case DESC_386_TRAP_GATE:
01008                         {
01009                                 CPU_CHECK_COND(!gate.saved.seg.p,
01010                                         "INT:Gate segment not present",
01011                                         EXCEPTION_NP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1)
01012 
01013                                 Descriptor cs_desc;
01014                                 Bitu gate_sel=gate.GetSelector();
01015                                 Bitu gate_off=gate.GetOffset();
01016                                 CPU_CHECK_COND((gate_sel & 0xfffc)==0,
01017                                         "INT:Gate with CS zero selector",
01018                                         EXCEPTION_GP,(type&CPU_INT_SOFTWARE)?0:1)
01019                                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(gate_sel,cs_desc),
01020                                         "INT:Gate with CS beyond limit",
01021                                         EXCEPTION_GP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01022 
01023                                 Bitu cs_dpl=cs_desc.DPL();
01024                                 CPU_CHECK_COND(cs_dpl>cpu.cpl,
01025                                         "Interrupt to higher privilege",
01026                                         EXCEPTION_GP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01027                                 switch (cs_desc.Type()) {
01028                                 case DESC_CODE_N_NC_A:  case DESC_CODE_N_NC_NA:
01029                                 case DESC_CODE_R_NC_A:  case DESC_CODE_R_NC_NA:
01030                                         if (cs_dpl<cpu.cpl) {
01031                                                 /* Prepare for gate to inner level */
01032                                                 CPU_CHECK_COND(!cs_desc.saved.seg.p,
01033                                                         "INT:Inner level:CS segment not present",
01034                                                         EXCEPTION_NP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01035                                                 CPU_CHECK_COND((reg_flags & FLAG_VM) && (cs_dpl!=0),
01036                                                         "V86 interrupt calling codesegment with DPL>0",
01037                                                         EXCEPTION_GP,gate_sel & 0xfffc)
01038 
01039                                                 Bitu n_ss,n_esp;
01040                                                 Bitu o_ss,o_esp;
01041                                                 o_ss=SegValue(ss);
01042                                                 o_esp=reg_esp;
01043                                                 cpu_tss.Get_SSx_ESPx(cs_dpl,n_ss,n_esp);
01044                                                 CPU_CHECK_COND((n_ss & 0xfffc)==0,
01045                                                         "INT:Gate with SS zero selector",
01046                                                         EXCEPTION_TS,(type&CPU_INT_SOFTWARE)?0:1)
01047                                                 Descriptor n_ss_desc;
01048                                                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
01049                                                         "INT:Gate with SS beyond limit",
01050                                                         EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01051                                                 CPU_CHECK_COND(((n_ss & 3)!=cs_dpl) || (n_ss_desc.DPL()!=cs_dpl),
01052                                                         "INT:Inner level with CS_DPL!=SS_DPL and SS_RPL",
01053                                                         EXCEPTION_TS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01054 
01055                                                 // check if stack segment is a writable data segment
01056                                                 switch (n_ss_desc.Type()) {
01057                                                 case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
01058                                                 case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
01059                                                         break;
01060                                                 default:
01061                                                         E_Exit("INT:Inner level:Stack segment not writable.");          // or #TS(ss_sel+EXT)
01062                                                 }
01063                                                 CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
01064                                                         "INT:Inner level with nonpresent SS",
01065                                                         EXCEPTION_SS,(n_ss & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01066 
01067                                                 // commit point
01068                                                 Segs.expanddown[ss]=n_ss_desc.GetExpandDown();
01069                                                 Segs.limit[ss]=do_seg_limits?n_ss_desc.GetLimit():((PhysPt)(~0UL));
01070                                                 Segs.phys[ss]=n_ss_desc.GetBase();
01071                                                 Segs.val[ss]=n_ss;
01072                                                 if (n_ss_desc.Big()) {
01073                                                         cpu.stack.big=true;
01074                                                         cpu.stack.mask=0xffffffff;
01075                                                         cpu.stack.notmask=0;
01076                                                         reg_esp=n_esp;
01077                                                 } else {
01078                                                         cpu.stack.big=false;
01079                                                         cpu.stack.mask=0xffff;
01080                                                         cpu.stack.notmask=0xffff0000;
01081                                                         reg_sp=n_esp & 0xffff;
01082                                                 }
01083 
01084                                                 CPU_SetCPL(cs_dpl);
01085                                                 if (gate.Type() & 0x8) {        /* 32-bit Gate */
01086                                                         if (reg_flags & FLAG_VM) {
01087                                                                 CPU_Push32(SegValue(gs));SegSet16(gs,0x0);
01088                                                                 CPU_Push32(SegValue(fs));SegSet16(fs,0x0);
01089                                                                 CPU_Push32(SegValue(ds));SegSet16(ds,0x0);
01090                                                                 CPU_Push32(SegValue(es));SegSet16(es,0x0);
01091                                                         }
01092                                                         CPU_Push32(o_ss);
01093                                                         CPU_Push32(o_esp);
01094                                                 } else {                                        /* 16-bit Gate */
01095                                                         if (reg_flags & FLAG_VM) E_Exit("V86 to 16-bit gate");
01096                                                         CPU_Push16(o_ss);
01097                                                         CPU_Push16(o_esp);
01098                                                 }
01099 //                                              LOG_MSG("INT:Gate to inner level SS:%X SP:%X",n_ss,n_esp);
01100                                                 goto do_interrupt;
01101                                         } 
01102                                         if (cs_dpl!=cpu.cpl)
01103                                                 E_Exit("Non-conforming intra privilege INT with DPL!=CPL");
01104                                 case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
01105                                 case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
01106                                         /* Prepare stack for gate to same priviledge */
01107                                         CPU_CHECK_COND(!cs_desc.saved.seg.p,
01108                                                         "INT:Same level:CS segment not present",
01109                                                 EXCEPTION_NP,(gate_sel & 0xfffc)+(type&CPU_INT_SOFTWARE)?0:1)
01110                                         if ((reg_flags & FLAG_VM) && (cs_dpl<cpu.cpl))
01111                                                 E_Exit("V86 interrupt doesn't change to pl0");  // or #GP(cs_sel)
01112 
01113                                         // commit point
01114 do_interrupt:
01115                                         if (gate.Type() & 0x8) {        /* 32-bit Gate */
01116                                                 CPU_Push32(reg_flags);
01117                                                 CPU_Push32(SegValue(cs));
01118                                                 CPU_Push32(oldeip);
01119                                                 if (type & CPU_INT_HAS_ERROR) CPU_Push32(cpu.exception.error);
01120                                         } else {                                        /* 16-bit gate */
01121                                                 CPU_Push16(reg_flags & 0xffff);
01122                                                 CPU_Push16(SegValue(cs));
01123                                                 CPU_Push16(oldeip);
01124                                                 if (type & CPU_INT_HAS_ERROR) CPU_Push16(cpu.exception.error);
01125                                         }
01126                                         break;          
01127                                 default:
01128                                         E_Exit("INT:Gate Selector points to illegal descriptor with type %x",(int)cs_desc.Type());
01129                                 }
01130 
01131                                 Segs.val[cs]=(gate_sel&0xfffc) | cpu.cpl;
01132                                 Segs.phys[cs]=cs_desc.GetBase();
01133                                 Segs.limit[cs]=do_seg_limits?cs_desc.GetLimit():((PhysPt)(~0UL));
01134                                 Segs.expanddown[cs]=cs_desc.GetExpandDown();
01135                                 cpu.code.big=cs_desc.Big()>0;
01136                                 reg_eip=gate_off;
01137 
01138                                 if (!(gate.Type()&1)) {
01139                                         SETFLAGBIT(IF,false);
01140                                 }
01141                                 SETFLAGBIT(TF,false);
01142                                 SETFLAGBIT(NT,false);
01143                                 SETFLAGBIT(VM,false);
01144                                 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");
01145                                 return;
01146                         }
01147                 case DESC_TASK_GATE:
01148                         CPU_CHECK_COND(!gate.saved.seg.p,
01149                                 "INT:Gate segment not present",
01150                                 EXCEPTION_NP,num*8+2+(type&CPU_INT_SOFTWARE)?0:1)
01151 
01152                         CPU_SwitchTask(gate.GetSelector(),TSwitch_CALL_INT,oldeip);
01153                         if (type & CPU_INT_HAS_ERROR) {
01154                                 //TODO Be sure about this, seems somewhat unclear
01155                                 if (cpu_tss.is386) CPU_Push32(cpu.exception.error);
01156                                 else CPU_Push16(cpu.exception.error);
01157                         }
01158                         return;
01159                 default:
01160                         E_Exit("Illegal descriptor type %X for int %X",(int)gate.Type(),(int)num);
01161                 }
01162                 }
01163                 catch (GuestPageFaultException &pf) {
01164             (void)pf;//UNUSED
01165                         LOG_MSG("CPU_Interrupt() interrupted");
01166                         CPU_SetSegGeneral(ss,old_ss);
01167                         reg_esp = old_esp;
01168                         CPU_SetCPL(old_cpl);
01169                         throw;
01170                 }
01171         }
01172         assert(1);
01173         return ; // make compiler happy
01174 }
01175 
01176 
01177 void CPU_IRET(bool use32,Bitu oldeip) {
01178         Bitu orig_esp = reg_esp;
01179 
01180         /* x86 CPUs consider IRET the completion of an NMI, no matter where it happens */
01181         /* FIXME: If the IRET causes an exception, is it still considered the end of the NMI? */
01182         CPU_NMI_active = false;
01183 
01184         /* Fault emulation */
01185         if (!CPU_Exception_In_Progress.empty()) {
01186                 int which = CPU_Exception_In_Progress.top();
01187                 CPU_Exception_In_Progress.pop();
01188                 assert(which < 0x20);
01189 
01190                 if (CPU_Exception_Level[which] > 0)
01191                         CPU_Exception_Level[which]--;
01192 
01193 //              LOG_MSG("Leaving CPU exception %d",which);
01194         }
01195 
01196         if (!cpu.pmode) {                                       /* RealMode IRET */
01197                 if (use32) {
01198                         reg_eip=CPU_Pop32();
01199                         SegSet16(cs,CPU_Pop32());
01200                         CPU_SetFlags(CPU_Pop32(),FMASK_ALL);
01201                 } else {
01202                         reg_eip=CPU_Pop16();
01203                         SegSet16(cs,CPU_Pop16());
01204                         CPU_SetFlags(CPU_Pop16(),FMASK_ALL & 0xffff);
01205                 }
01206                 if (!cpu_allow_big16) cpu.code.big=false;
01207                 DestroyConditionFlags();
01208                 return;
01209         } else {        /* Protected mode IRET */
01210                 if (reg_flags & FLAG_VM) {
01211                         if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
01212                                 // win3.x e
01213                                 CPU_Exception(EXCEPTION_GP,0);
01214                                 return;
01215                         } else {
01216                                 try {
01217                                 if (use32) {
01218                                         Bit32u new_eip=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
01219                                         Bit32u tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
01220                                         Bit32u new_cs=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
01221                                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
01222                                         Bit32u new_flags=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
01223                                         reg_esp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
01224 
01225                                         reg_eip=new_eip;
01226                                         SegSet16(cs,(Bit16u)(new_cs&0xffff));
01227                                         /* IOPL can not be modified in v86 mode by IRET */
01228                                         CPU_SetFlags(new_flags,FMASK_NORMAL|FLAG_NT);
01229                                 } else {
01230                                         Bit16u new_eip=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
01231                                         Bit32u tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
01232                                         Bit16u new_cs=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01233                                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
01234                                         Bit16u new_flags=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01235                                         reg_esp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
01236 
01237                                         reg_eip=(Bit32u)new_eip;
01238                                         SegSet16(cs,new_cs);
01239                                         /* IOPL can not be modified in v86 mode by IRET */
01240                                         CPU_SetFlags(new_flags,FMASK_NORMAL|FLAG_NT);
01241                                 }
01242                                 }
01243                                 catch (GuestPageFaultException &pf) {
01244                     (void)pf;//UNUSED
01245                     LOG_MSG("CPU_IRET() interrupted prot vm86");
01246                                         reg_esp = orig_esp;
01247                                         throw;
01248                                 }
01249                                 cpu.code.big=false;
01250                                 DestroyConditionFlags();
01251                                 return;
01252                         }
01253                 }
01254                 /* Check if this is task IRET */        
01255                 if (GETFLAG(NT)) {
01256                         if (GETFLAG(VM)) E_Exit("Pmode IRET with VM bit set");
01257                         CPU_CHECK_COND(!cpu_tss.IsValid(),
01258                                 "TASK Iret without valid TSS",
01259                                 EXCEPTION_TS,cpu_tss.selector & 0xfffc)
01260                         if (!cpu_tss.desc.IsBusy()) {
01261                                 LOG(LOG_CPU,LOG_ERROR)("TASK Iret:TSS not busy");
01262                         }
01263                         Bitu back_link=cpu_tss.Get_back();
01264                         CPU_SwitchTask(back_link,TSwitch_IRET,oldeip);
01265                         return;
01266                 }
01267                 Bitu n_cs_sel,n_eip,n_flags;
01268                 Bit32u tempesp;
01269                 if (use32) {
01270                         n_eip=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
01271                         tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+4)&cpu.stack.mask);
01272                         n_cs_sel=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask)) & 0xffff;
01273                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
01274                         n_flags=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
01275                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
01276 
01277                         if ((n_flags & FLAG_VM) && (cpu.cpl==0)) {
01278                                 // commit point
01279                                 try {
01280                                 reg_esp=tempesp;
01281                                 reg_eip=n_eip & 0xffff;
01282                                 Bitu n_ss,n_esp,n_es,n_ds,n_fs,n_gs;
01283                                 n_esp=CPU_Pop32();
01284                                 n_ss=CPU_Pop32() & 0xffff;
01285                                 n_es=CPU_Pop32() & 0xffff;
01286                                 n_ds=CPU_Pop32() & 0xffff;
01287                                 n_fs=CPU_Pop32() & 0xffff;
01288                                 n_gs=CPU_Pop32() & 0xffff;
01289 
01290                                 CPU_SetFlags(n_flags,FMASK_ALL | FLAG_VM);
01291                                 DestroyConditionFlags();
01292                                 CPU_SetCPL(3);
01293 
01294                                 CPU_SetSegGeneral(ss,n_ss);
01295                                 CPU_SetSegGeneral(es,n_es);
01296                                 CPU_SetSegGeneral(ds,n_ds);
01297                                 CPU_SetSegGeneral(fs,n_fs);
01298                                 CPU_SetSegGeneral(gs,n_gs);
01299                                 reg_esp=n_esp;
01300                                 cpu.code.big=false;
01301                                 SegSet16(cs,n_cs_sel);
01302                                 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);      
01303                                 return;
01304                                 }
01305                                 catch (GuestPageFaultException &pf) {
01306                     (void)pf;//UNUSED
01307                     LOG_MSG("CPU_IRET() interrupted prot use32");
01308                                         reg_esp = orig_esp;
01309                                         throw;
01310                                 }
01311                         }
01312                         if (n_flags & FLAG_VM) E_Exit("IRET from pmode to v86 with CPL!=0");
01313                 } else {
01314                         n_eip=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
01315                         tempesp=(reg_esp&cpu.stack.notmask)|((reg_esp+2)&cpu.stack.mask);
01316                         n_cs_sel=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01317                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
01318                         n_flags=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01319                         n_flags|=(reg_flags & 0xffff0000);
01320                         tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
01321 
01322                         if (n_flags & FLAG_VM) E_Exit("VM Flag in 16-bit iret");
01323                 }
01324                 CPU_CHECK_COND((n_cs_sel & 0xfffc)==0,
01325                         "IRET:CS selector zero",
01326                         EXCEPTION_GP,0)
01327                 Bitu n_cs_rpl=n_cs_sel & 3;
01328                 Descriptor n_cs_desc;
01329                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc),
01330                         "IRET:CS selector beyond limits",
01331                         EXCEPTION_GP,n_cs_sel & 0xfffc)
01332                 CPU_CHECK_COND(n_cs_rpl<cpu.cpl,
01333                         "IRET to lower privilege",
01334                         EXCEPTION_GP,n_cs_sel & 0xfffc)
01335 
01336                 switch (n_cs_desc.Type()) {
01337                 case DESC_CODE_N_NC_A:  case DESC_CODE_N_NC_NA:
01338                 case DESC_CODE_R_NC_A:  case DESC_CODE_R_NC_NA:
01339                         CPU_CHECK_COND(n_cs_rpl!=n_cs_desc.DPL(),
01340                                 "IRET:NC:DPL!=RPL",
01341                                 EXCEPTION_GP,n_cs_sel & 0xfffc)
01342                         break;
01343                 case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
01344                 case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
01345                         CPU_CHECK_COND(n_cs_desc.DPL()>n_cs_rpl,
01346                                 "IRET:C:DPL>RPL",
01347                                 EXCEPTION_GP,n_cs_sel & 0xfffc)
01348                         break;
01349                 default:
01350                         E_Exit("IRET:Illegal descriptor type %X",(int)n_cs_desc.Type());
01351                 }
01352                 CPU_CHECK_COND(!n_cs_desc.saved.seg.p,
01353                         "IRET with nonpresent code segment",
01354                         EXCEPTION_NP,n_cs_sel & 0xfffc)
01355 
01356                 if (n_cs_rpl==cpu.cpl) {        
01357                         /* Return to same level */
01358 
01359                         // commit point
01360                         reg_esp=tempesp;
01361                         Segs.expanddown[cs]=n_cs_desc.GetExpandDown();
01362                         Segs.limit[cs]=do_seg_limits?n_cs_desc.GetLimit():((PhysPt)(~0UL));
01363                         Segs.phys[cs]=n_cs_desc.GetBase();
01364                         cpu.code.big=n_cs_desc.Big()>0;
01365                         Segs.val[cs]=n_cs_sel;
01366                         reg_eip=n_eip;
01367 
01368                         Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL;
01369                         if (GETFLAG_IOPL<cpu.cpl) mask &= (~FLAG_IF);
01370                         CPU_SetFlags(n_flags,mask);
01371                         DestroyConditionFlags();
01372                         LOG(LOG_CPU,LOG_NORMAL)("IRET:Same level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
01373                 } else {
01374                         /* Return to outer level */
01375                         Bitu n_ss,n_esp;
01376                         if (use32) {
01377                                 n_esp=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask));
01378                                 tempesp=(tempesp&cpu.stack.notmask)|((tempesp+4)&cpu.stack.mask);
01379                                 n_ss=mem_readd(SegPhys(ss) + (tempesp & cpu.stack.mask)) & 0xffff;
01380                         } else {
01381                                 n_esp=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01382                                 tempesp=(tempesp&cpu.stack.notmask)|((tempesp+2)&cpu.stack.mask);
01383                                 n_ss=mem_readw(SegPhys(ss) + (tempesp & cpu.stack.mask));
01384                         }
01385                         CPU_CHECK_COND((n_ss & 0xfffc)==0,
01386                                 "IRET:Outer level:SS selector zero",
01387                                 EXCEPTION_GP,0)
01388                         CPU_CHECK_COND((n_ss & 3)!=n_cs_rpl,
01389                                 "IRET:Outer level:SS rpl!=CS rpl",
01390                                 EXCEPTION_GP,n_ss & 0xfffc)
01391                         Descriptor n_ss_desc;
01392                         CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
01393                                 "IRET:Outer level:SS beyond limit",
01394                                 EXCEPTION_GP,n_ss & 0xfffc)
01395                         CPU_CHECK_COND(n_ss_desc.DPL()!=n_cs_rpl,
01396                                 "IRET:Outer level:SS dpl!=CS rpl",
01397                                 EXCEPTION_GP,n_ss & 0xfffc)
01398 
01399                         // check if stack segment is a writable data segment
01400                         switch (n_ss_desc.Type()) {
01401                         case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
01402                         case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
01403                                 break;
01404                         default:
01405                                 E_Exit("IRET:Outer level:Stack segment not writable");          // or #GP(ss_sel)
01406                         }
01407                         CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
01408                                 "IRET:Outer level:Stack segment not present",
01409                                 EXCEPTION_NP,n_ss & 0xfffc)
01410 
01411                         // commit point
01412 
01413                         Segs.expanddown[cs]=n_cs_desc.GetExpandDown();
01414                         Segs.limit[cs]=do_seg_limits?n_cs_desc.GetLimit():((PhysPt)(~0UL));
01415                         Segs.phys[cs]=n_cs_desc.GetBase();
01416                         cpu.code.big=n_cs_desc.Big()>0;
01417                         Segs.val[cs]=n_cs_sel;
01418 
01419                         Bitu mask=cpu.cpl ? (FMASK_NORMAL | FLAG_NT) : FMASK_ALL;
01420                         if (GETFLAG_IOPL<cpu.cpl) mask &= (~FLAG_IF);
01421                         CPU_SetFlags(n_flags,mask);
01422                         DestroyConditionFlags();
01423 
01424                         CPU_SetCPL(n_cs_rpl);
01425                         reg_eip=n_eip;
01426 
01427                         Segs.val[ss]=n_ss;
01428                         Segs.phys[ss]=n_ss_desc.GetBase();
01429                         Segs.limit[ss]=do_seg_limits?n_ss_desc.GetLimit():((PhysPt)(~0UL));
01430                         Segs.expanddown[ss]=n_ss_desc.GetExpandDown();
01431                         if (n_ss_desc.Big()) {
01432                                 cpu.stack.big=true;
01433                                 cpu.stack.mask=0xffffffff;
01434                                 cpu.stack.notmask=0;
01435                                 reg_esp=n_esp;
01436                         } else {
01437                                 cpu.stack.big=false;
01438                                 cpu.stack.mask=0xffff;
01439                                 cpu.stack.notmask=0xffff0000;
01440                                 reg_sp=n_esp & 0xffff;
01441                         }
01442 
01443                         // borland extender, zrdx
01444                         CPU_CheckSegments();
01445 
01446                         LOG(LOG_CPU,LOG_NORMAL)("IRET:Outer level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
01447                 }
01448                 return;
01449         }
01450 }
01451 
01452 
01453 void CPU_JMP(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
01454         if (!cpu.pmode || (reg_flags & FLAG_VM)) {
01455                 if (!use32) {
01456                         reg_eip=offset&0xffff;
01457                 } else {
01458                         reg_eip=offset;
01459                 }
01460                 SegSet16(cs,selector);
01461                 if (!cpu_allow_big16) cpu.code.big=false;
01462                 return;
01463         } else {
01464                 CPU_CHECK_COND((selector & 0xfffc)==0,
01465                         "JMP:CS selector zero",
01466                         EXCEPTION_GP,0)
01467                 Bitu rpl=selector & 3;
01468                 Descriptor desc;
01469                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,desc),
01470                         "JMP:CS beyond limits",
01471                         EXCEPTION_GP,selector & 0xfffc)
01472                 switch (desc.Type()) {
01473                 case DESC_CODE_N_NC_A:          case DESC_CODE_N_NC_NA:
01474                 case DESC_CODE_R_NC_A:          case DESC_CODE_R_NC_NA:
01475                         CPU_CHECK_COND(rpl>cpu.cpl,
01476                                 "JMP:NC:RPL>CPL",
01477                                 EXCEPTION_GP,selector & 0xfffc)
01478                         CPU_CHECK_COND(cpu.cpl!=desc.DPL(),
01479                                 "JMP:NC:RPL != DPL",
01480                                 EXCEPTION_GP,selector & 0xfffc)
01481                         LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:NC to %X:%X big %d",selector,offset,desc.Big());
01482                         goto CODE_jmp;
01483                 case DESC_CODE_N_C_A:           case DESC_CODE_N_C_NA:
01484                 case DESC_CODE_R_C_A:           case DESC_CODE_R_C_NA:
01485                         LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:C to %X:%X big %d",selector,offset,desc.Big());
01486                         CPU_CHECK_COND(cpu.cpl<desc.DPL(),
01487                                 "JMP:C:CPL < DPL",
01488                                 EXCEPTION_GP,selector & 0xfffc)
01489 CODE_jmp:
01490                         if (!desc.saved.seg.p) {
01491                                 // win
01492                                 CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
01493                                 return;
01494                         }
01495 
01496                         /* Normal jump to another selector:offset */
01497                         Segs.expanddown[cs]=desc.GetExpandDown();
01498                         Segs.limit[cs]=do_seg_limits?desc.GetLimit():((PhysPt)(~0UL));
01499                         Segs.phys[cs]=desc.GetBase();
01500                         cpu.code.big=desc.Big()>0;
01501                         Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
01502                         reg_eip=offset;
01503                         return;
01504                 case DESC_386_TSS_A:
01505                         CPU_CHECK_COND(desc.DPL()<cpu.cpl,
01506                                 "JMP:TSS:dpl<cpl",
01507                                 EXCEPTION_GP,selector & 0xfffc)
01508                         CPU_CHECK_COND(desc.DPL()<rpl,
01509                                 "JMP:TSS:dpl<rpl",
01510                                 EXCEPTION_GP,selector & 0xfffc)
01511                         LOG(LOG_CPU,LOG_NORMAL)("JMP:TSS to %X",selector);
01512                         CPU_SwitchTask(selector,TSwitch_JMP,oldeip);
01513                         break;
01514                 default:
01515                         E_Exit("JMP Illegal descriptor type %X",(int)desc.Type());
01516                 }
01517         }
01518         assert(1);
01519 }
01520 
01521 
01522 void CPU_CALL(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
01523         Bit32u old_esp = reg_esp;
01524         Bit32u old_eip = reg_eip;
01525 
01526         if (!cpu.pmode || (reg_flags & FLAG_VM)) {
01527                 try {
01528                 if (!use32) {
01529                         CPU_Push16(SegValue(cs));
01530                         CPU_Push16(oldeip);
01531                         reg_eip=offset&0xffff;
01532                 } else {
01533                         CPU_Push32(SegValue(cs));
01534                         CPU_Push32(oldeip);
01535                         reg_eip=offset;
01536                 }
01537                 }
01538                 catch (GuestPageFaultException &pf) {
01539             (void)pf;//UNUSED
01540                         reg_esp = old_esp;
01541                         reg_eip = old_eip;
01542                         throw;
01543                 }
01544                 if (!cpu_allow_big16) cpu.code.big=false;
01545                 SegSet16(cs,selector);
01546                 return;
01547         } else {
01548                 CPU_CHECK_COND((selector & 0xfffc)==0,
01549                         "CALL:CS selector zero",
01550                         EXCEPTION_GP,0)
01551                 Bitu rpl=selector & 3;
01552                 Descriptor call;
01553                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,call),
01554                         "CALL:CS beyond limits",
01555                         EXCEPTION_GP,selector & 0xfffc)
01556                 /* Check for type of far call */
01557                 switch (call.Type()) {
01558                 case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
01559                 case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
01560                         CPU_CHECK_COND(rpl>cpu.cpl,
01561                                 "CALL:CODE:NC:RPL>CPL",
01562                                 EXCEPTION_GP,selector & 0xfffc)
01563                         CPU_CHECK_COND(call.DPL()!=cpu.cpl,
01564                                 "CALL:CODE:NC:DPL!=CPL",
01565                                 EXCEPTION_GP,selector & 0xfffc)
01566                         LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:NC to %X:%X",selector,offset);
01567                         goto call_code; 
01568                 case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
01569                 case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
01570                         CPU_CHECK_COND(call.DPL()>cpu.cpl,
01571                                 "CALL:CODE:C:DPL>CPL",
01572                                 EXCEPTION_GP,selector & 0xfffc)
01573                         LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:C to %X:%X",selector,offset);
01574 call_code:
01575                         if (!call.saved.seg.p) {
01576                                 // borland extender (RTM)
01577                                 CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
01578                                 return;
01579                         }
01580                         // commit point
01581                         try {
01582                         if (!use32) {
01583                                 CPU_Push16(SegValue(cs));
01584                                 CPU_Push16(oldeip);
01585                                 reg_eip=offset & 0xffff;
01586                         } else {
01587                                 CPU_Push32(SegValue(cs));
01588                                 CPU_Push32(oldeip);
01589                                 reg_eip=offset;
01590                         }
01591                         }
01592                         catch (GuestPageFaultException &pf) {
01593                 (void)pf;//UNUSED
01594                 reg_esp = old_esp;
01595                                 reg_eip = old_eip;
01596                                 throw;
01597                         }
01598 
01599                         Segs.expanddown[cs]=call.GetExpandDown();
01600                         Segs.limit[cs]=do_seg_limits?call.GetLimit():((PhysPt)(~0UL));
01601                         Segs.phys[cs]=call.GetBase();
01602                         cpu.code.big=call.Big()>0;
01603                         Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
01604                         return;
01605                 case DESC_386_CALL_GATE: 
01606                 case DESC_286_CALL_GATE:
01607                         {
01608                                 CPU_CHECK_COND(call.DPL()<cpu.cpl,
01609                                         "CALL:Gate:Gate DPL<CPL",
01610                                         EXCEPTION_GP,selector & 0xfffc)
01611                                 CPU_CHECK_COND(call.DPL()<rpl,
01612                                         "CALL:Gate:Gate DPL<RPL",
01613                                         EXCEPTION_GP,selector & 0xfffc)
01614                                 CPU_CHECK_COND(!call.saved.seg.p,
01615                                         "CALL:Gate:Segment not present",
01616                                         EXCEPTION_NP,selector & 0xfffc)
01617                                 Descriptor n_cs_desc;
01618                                 Bitu n_cs_sel=call.GetSelector();
01619 
01620                                 CPU_CHECK_COND((n_cs_sel & 0xfffc)==0,
01621                                         "CALL:Gate:CS selector zero",
01622                                         EXCEPTION_GP,0)
01623                                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc),
01624                                         "CALL:Gate:CS beyond limits",
01625                                         EXCEPTION_GP,n_cs_sel & 0xfffc)
01626                                 Bitu n_cs_dpl   = n_cs_desc.DPL();
01627                                 CPU_CHECK_COND(n_cs_dpl>cpu.cpl,
01628                                         "CALL:Gate:CS DPL>CPL",
01629                                         EXCEPTION_GP,n_cs_sel & 0xfffc)
01630 
01631                                 CPU_CHECK_COND(!n_cs_desc.saved.seg.p,
01632                                         "CALL:Gate:CS not present",
01633                                         EXCEPTION_NP,n_cs_sel & 0xfffc)
01634 
01635                                 Bitu n_eip              = call.GetOffset();
01636                                 switch (n_cs_desc.Type()) {
01637                                 case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
01638                                 case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
01639                                         /* Check if we goto inner priviledge */
01640                                         if (n_cs_dpl < cpu.cpl) {
01641                                                 /* Get new SS:ESP out of TSS */
01642                                                 Bitu n_ss_sel,n_esp;
01643                                                 Descriptor n_ss_desc;
01644                                                 cpu_tss.Get_SSx_ESPx(n_cs_dpl,n_ss_sel,n_esp);
01645                                                 CPU_CHECK_COND((n_ss_sel & 0xfffc)==0,
01646                                                         "CALL:Gate:NC:SS selector zero",
01647                                                         EXCEPTION_TS,0)
01648                                                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss_sel,n_ss_desc),
01649                                                         "CALL:Gate:Invalid SS selector",
01650                                                         EXCEPTION_TS,n_ss_sel & 0xfffc)
01651                                                 CPU_CHECK_COND(((n_ss_sel & 3)!=n_cs_desc.DPL()) || (n_ss_desc.DPL()!=n_cs_desc.DPL()),
01652                                                         "CALL:Gate:Invalid SS selector privileges",
01653                                                         EXCEPTION_TS,n_ss_sel & 0xfffc)
01654 
01655                                                 switch (n_ss_desc.Type()) {
01656                                                 case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
01657                                                 case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
01658                                                         // writable data segment
01659                                                         break;
01660                                                 default:
01661                                                         E_Exit("Call:Gate:SS no writable data segment");        // or #TS(ss_sel)
01662                                                 }
01663                                                 CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
01664                                                         "CALL:Gate:Stack segment not present",
01665                                                         EXCEPTION_SS,n_ss_sel & 0xfffc)
01666 
01667                                                 /* Load the new SS:ESP and save data on it */
01668                                                 Bitu o_esp              = reg_esp;
01669                                                 Bitu o_ss               = SegValue(ss);
01670                                                 PhysPt o_stack  = SegPhys(ss)+(reg_esp & cpu.stack.mask);
01671 
01672 
01673                                                 // catch pagefaults
01674                                                 if (call.saved.gate.paramcount&31) {
01675                                                         if (call.Type()==DESC_386_CALL_GATE) {
01676                                                                 for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) 
01677                                                                         mem_readd(o_stack+i*4);
01678                                                         } else {
01679                                                                 for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
01680                                                                         mem_readw(o_stack+i*2);
01681                                                         }
01682                                                 }
01683 
01684                                                 bool old_allow = dosbox_allow_nonrecursive_page_fault;
01685 
01686                                                 /* this code isn't very easy to make interruptible. so temporarily revert to recursive PF handling method */
01687                                                 dosbox_allow_nonrecursive_page_fault = false;
01688 
01689                                                 // commit point
01690                                                 Segs.val[ss]=n_ss_sel;
01691                                                 Segs.phys[ss]=n_ss_desc.GetBase();
01692                                                 Segs.limit[ss]=do_seg_limits?n_ss_desc.GetLimit():((PhysPt)(~0UL));
01693                                                 Segs.expanddown[ss]=n_ss_desc.GetExpandDown();
01694                                                 if (n_ss_desc.Big()) {
01695                                                         cpu.stack.big=true;
01696                                                         cpu.stack.mask=0xffffffff;
01697                                                         cpu.stack.notmask=0;
01698                                                         reg_esp=n_esp;
01699                                                 } else {
01700                                                         cpu.stack.big=false;
01701                                                         cpu.stack.mask=0xffff;
01702                                                         cpu.stack.notmask=0xffff0000;
01703                                                         reg_sp=n_esp & 0xffff;
01704                                                 }
01705 
01706                                                 CPU_SetCPL(n_cs_desc.DPL());
01707                                                 Bit16u oldcs    = SegValue(cs);
01708                                                 /* Switch to new CS:EIP */
01709                                                 Segs.expanddown[cs]=n_cs_desc.GetExpandDown();
01710                                                 Segs.limit[cs]  = do_seg_limits?n_cs_desc.GetLimit():((PhysPt)(~0UL));
01711                                                 Segs.phys[cs]   = n_cs_desc.GetBase();
01712                                                 Segs.val[cs]    = (n_cs_sel & 0xfffc) | cpu.cpl;
01713                                                 cpu.code.big    = n_cs_desc.Big()>0;
01714                                                 reg_eip                 = n_eip;
01715                                                 if (!use32)     reg_eip&=0xffff;
01716 
01717                                                 if (call.Type()==DESC_386_CALL_GATE) {
01718                                                         CPU_Push32(o_ss);               //save old stack
01719                                                         CPU_Push32(o_esp);
01720                                                         if (call.saved.gate.paramcount&31)
01721                                                                 for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) 
01722                                                                         CPU_Push32(mem_readd(o_stack+i*4));
01723                                                         CPU_Push32(oldcs);
01724                                                         CPU_Push32(oldeip);
01725                                                 } else {
01726                                                         CPU_Push16(o_ss);               //save old stack
01727                                                         CPU_Push16(o_esp);
01728                                                         if (call.saved.gate.paramcount&31)
01729                                                                 for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
01730                                                                         CPU_Push16(mem_readw(o_stack+i*2));
01731                                                         CPU_Push16(oldcs);
01732                                                         CPU_Push16(oldeip);
01733                                                 }
01734 
01735                                                 dosbox_allow_nonrecursive_page_fault = old_allow;
01736                                                 break;          
01737                                         } else if (n_cs_dpl > cpu.cpl)
01738                                                 E_Exit("CALL:GATE:CS DPL>CPL");         // or #GP(sel)
01739                                 case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
01740                                 case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
01741                                         // zrdx extender
01742 
01743                                         try {
01744                                         if (call.Type()==DESC_386_CALL_GATE) {
01745                                                 CPU_Push32(SegValue(cs));
01746                                                 CPU_Push32(oldeip);
01747                                         } else {
01748                                                 CPU_Push16(SegValue(cs));
01749                                                 CPU_Push16(oldeip);
01750                                         }
01751                                         }
01752                                         catch (GuestPageFaultException &pf) {
01753                         (void)pf;//UNUSED
01754                         reg_esp = old_esp;
01755                                                 reg_eip = old_eip;
01756                                                 throw;
01757                                         }
01758 
01759                                         /* Switch to new CS:EIP */
01760                                         Segs.expanddown[cs]=n_cs_desc.GetExpandDown();
01761                                         Segs.limit[cs]  = do_seg_limits?n_cs_desc.GetLimit():((PhysPt)(~0UL));
01762                                         Segs.phys[cs]   = n_cs_desc.GetBase();
01763                                         Segs.val[cs]    = (n_cs_sel & 0xfffc) | cpu.cpl;
01764                                         cpu.code.big    = n_cs_desc.Big()>0;
01765                                         reg_eip                 = n_eip;
01766                                         if (!use32)     reg_eip&=0xffff;
01767                                         break;
01768                                 default:
01769                                         E_Exit("CALL:GATE:CS no executable segment");
01770                                 }
01771                         }                       /* Call Gates */
01772                         break;
01773                 case DESC_386_TSS_A:
01774                         CPU_CHECK_COND(call.DPL()<cpu.cpl,
01775                                 "CALL:TSS:dpl<cpl",
01776                                 EXCEPTION_GP,selector & 0xfffc)
01777                         CPU_CHECK_COND(call.DPL()<rpl,
01778                                 "CALL:TSS:dpl<rpl",
01779                                 EXCEPTION_GP,selector & 0xfffc)
01780 
01781                         CPU_CHECK_COND(!call.saved.seg.p,
01782                                 "CALL:TSS:Segment not present",
01783                                 EXCEPTION_NP,selector & 0xfffc)
01784 
01785                         LOG(LOG_CPU,LOG_NORMAL)("CALL:TSS to %X",selector);
01786                         CPU_SwitchTask(selector,TSwitch_CALL_INT,oldeip);
01787                         break;
01788                 case DESC_DATA_EU_RW_NA:        // vbdos
01789                 case DESC_INVALID:                      // used by some installers
01790                         CPU_Exception(EXCEPTION_GP,selector & 0xfffc);
01791                         return;
01792                 default:
01793                         E_Exit("CALL:Descriptor type %x unsupported",(int)call.Type());
01794                 }
01795         }
01796         assert(1);
01797 }
01798 
01799 
01800 void CPU_RET(bool use32,Bitu bytes,Bitu oldeip) {
01801     (void)oldeip;//UNUSED
01802 
01803         Bitu orig_esp = reg_esp;
01804 
01805         if (!cpu.pmode || (reg_flags & FLAG_VM)) {
01806                 try {
01807                 Bitu new_ip,new_cs;
01808                 if (!use32) {
01809                         new_ip=CPU_Pop16();
01810                         new_cs=CPU_Pop16();
01811                 } else {
01812                         new_ip=CPU_Pop32();
01813                         new_cs=CPU_Pop32() & 0xffff;
01814                 }
01815                 reg_esp+=bytes;
01816                 SegSet16(cs,new_cs);
01817                 reg_eip=new_ip;
01818                 if (!cpu_allow_big16) cpu.code.big=false;
01819                 return;
01820                 }
01821                 catch (GuestPageFaultException &pf) {
01822             (void)pf;//UNUSED
01823             LOG_MSG("CPU_RET() interrupted real/vm86");
01824                         reg_esp = orig_esp;
01825                         throw;
01826                 }
01827         } else {
01828                 Bitu offset,selector;
01829                 if (!use32) selector    = mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 2);
01830                 else            selector        = mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 4) & 0xffff;
01831 
01832                 Descriptor desc;
01833                 Bitu rpl=selector & 3;
01834                 if(rpl < cpu.cpl) {
01835                         // win setup
01836                         CPU_Exception(EXCEPTION_GP,selector & 0xfffc);
01837                         return;
01838                 }
01839 
01840                 CPU_CHECK_COND((selector & 0xfffc)==0,
01841                         "RET:CS selector zero",
01842                         EXCEPTION_GP,0)
01843                 CPU_CHECK_COND(!cpu.gdt.GetDescriptor(selector,desc),
01844                         "RET:CS beyond limits",
01845                         EXCEPTION_GP,selector & 0xfffc)
01846 
01847                 if (cpu.cpl==rpl) {     
01848                         /* Return to same level */
01849                         switch (desc.Type()) {
01850                         case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
01851                         case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
01852                                 CPU_CHECK_COND(cpu.cpl!=desc.DPL(),
01853                                         "RET to NC segment of other privilege",
01854                                         EXCEPTION_GP,selector & 0xfffc)
01855                                 goto RET_same_level;
01856                         case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
01857                         case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
01858                                 CPU_CHECK_COND(desc.DPL()>cpu.cpl,
01859                                         "RET to C segment of higher privilege",
01860                                         EXCEPTION_GP,selector & 0xfffc)
01861                                 break;
01862                         default:
01863                                 E_Exit("RET from illegal descriptor type %X",(int)desc.Type());
01864                         }
01865 RET_same_level:
01866                         if (!desc.saved.seg.p) {
01867                                 // borland extender (RTM)
01868                                 CPU_Exception(EXCEPTION_NP,selector & 0xfffc);
01869                                 return;
01870                         }
01871 
01872                         // commit point
01873                         try {
01874                         if (!use32) {
01875                                 offset=CPU_Pop16();
01876                                 selector=CPU_Pop16();
01877                         } else {
01878                                 offset=CPU_Pop32();
01879                                 selector=CPU_Pop32() & 0xffff;
01880                         }
01881                         }
01882                         catch (GuestPageFaultException &pf) {
01883                 (void)pf;//UNUSED
01884                 LOG_MSG("CPU_RET() interrupted prot rpl==cpl");
01885                                 reg_esp = orig_esp;
01886                                 throw;
01887                         }
01888 
01889                         Segs.expanddown[cs]=desc.GetExpandDown();
01890                         Segs.limit[cs]=do_seg_limits?desc.GetLimit():((PhysPt)(~0UL));
01891                         Segs.phys[cs]=desc.GetBase();
01892                         cpu.code.big=desc.Big()>0;
01893                         Segs.val[cs]=selector;
01894                         reg_eip=offset;
01895                         if (cpu.stack.big) {
01896                                 reg_esp+=bytes;
01897                         } else {
01898                                 reg_sp+=bytes;
01899                         }
01900                         LOG(LOG_CPU,LOG_NORMAL)("RET - Same level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
01901                         return;
01902                 } else {
01903                         /* Return to outer level */
01904                         switch (desc.Type()) {
01905                         case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
01906                         case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
01907                                 CPU_CHECK_COND(desc.DPL()!=rpl,
01908                                         "RET to outer NC segment with DPL!=RPL",
01909                                         EXCEPTION_GP,selector & 0xfffc)
01910                                 break;
01911                         case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
01912                         case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
01913                                 CPU_CHECK_COND(desc.DPL()>rpl,
01914                                         "RET to outer C segment with DPL>RPL",
01915                                         EXCEPTION_GP,selector & 0xfffc)
01916                                 break;
01917                         default:
01918                                 E_Exit("RET from illegal descriptor type %X",(int)desc.Type());         // or #GP(selector)
01919                         }
01920 
01921                         CPU_CHECK_COND(!desc.saved.seg.p,
01922                                 "RET:Outer level:CS not present",
01923                                 EXCEPTION_NP,selector & 0xfffc)
01924 
01925                         // commit point
01926                         Bitu n_esp,n_ss;
01927                         try {
01928                         if (use32) {
01929                                 offset=CPU_Pop32();
01930                                 selector=CPU_Pop32() & 0xffff;
01931                                 reg_esp+=bytes;
01932                                 n_esp = CPU_Pop32();
01933                                 n_ss = CPU_Pop32() & 0xffff;
01934                         } else {
01935                                 offset=CPU_Pop16();
01936                                 selector=CPU_Pop16();
01937                                 reg_esp+=bytes;
01938                                 n_esp = CPU_Pop16();
01939                                 n_ss = CPU_Pop16();
01940                         }
01941                         }
01942                         catch (GuestPageFaultException &pf) {
01943                 (void)pf;//UNUSED
01944                 LOG_MSG("CPU_RET() interrupted prot #2");
01945                                 reg_esp = orig_esp;
01946                                 throw;
01947                         }
01948 
01949                         CPU_CHECK_COND((n_ss & 0xfffc)==0,
01950                                 "RET to outer level with SS selector zero",
01951                                 EXCEPTION_GP,0)
01952 
01953                         Descriptor n_ss_desc;
01954                         CPU_CHECK_COND(!cpu.gdt.GetDescriptor(n_ss,n_ss_desc),
01955                                 "RET:SS beyond limits",
01956                                 EXCEPTION_GP,n_ss & 0xfffc)
01957 
01958                         CPU_CHECK_COND(((n_ss & 3)!=rpl) || (n_ss_desc.DPL()!=rpl),
01959                                 "RET to outer segment with invalid SS privileges",
01960                                 EXCEPTION_GP,n_ss & 0xfffc)
01961                         switch (n_ss_desc.Type()) {
01962                         case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
01963                         case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
01964                                 break;
01965                         default:
01966                                 E_Exit("RET:SS selector type no writable data segment");        // or #GP(selector)
01967                         }
01968                         CPU_CHECK_COND(!n_ss_desc.saved.seg.p,
01969                                 "RET:Stack segment not present",
01970                                 EXCEPTION_SS,n_ss & 0xfffc)
01971 
01972                         CPU_SetCPL(rpl);
01973                         Segs.expanddown[cs]=desc.GetExpandDown();
01974                         Segs.limit[cs]=do_seg_limits?desc.GetLimit():((PhysPt)(~0UL));
01975                         Segs.phys[cs]=desc.GetBase();
01976                         cpu.code.big=desc.Big()>0;
01977                         Segs.val[cs]=(selector&0xfffc) | cpu.cpl;
01978                         reg_eip=offset;
01979 
01980                         Segs.val[ss]=n_ss;
01981                         Segs.phys[ss]=n_ss_desc.GetBase();
01982                         Segs.limit[ss]=do_seg_limits?n_ss_desc.GetLimit():((PhysPt)(~0UL));
01983                         Segs.expanddown[ss]=n_ss_desc.GetExpandDown();
01984                         if (n_ss_desc.Big()) {
01985                                 cpu.stack.big=true;
01986                                 cpu.stack.mask=0xffffffff;
01987                                 cpu.stack.notmask=0;
01988                                 reg_esp=n_esp+bytes;
01989                         } else {
01990                                 cpu.stack.big=false;
01991                                 cpu.stack.mask=0xffff;
01992                                 cpu.stack.notmask=0xffff0000;
01993                                 reg_sp=(n_esp & 0xffff)+bytes;
01994                         }
01995 
01996                         CPU_CheckSegments();
01997 
01998 //                      LOG(LOG_MISC,LOG_ERROR)("RET - Higher level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
01999                         return;
02000                 }
02001                 LOG(LOG_CPU,LOG_NORMAL)("Prot ret %lX:%lX",(unsigned long)selector,(unsigned long)offset);
02002                 return;
02003         }
02004         assert(1);
02005 }
02006 
02007 
02008 Bitu CPU_SLDT(void) {
02009         return cpu.gdt.SLDT();
02010 }
02011 
02012 bool CPU_LLDT(Bitu selector) {
02013         if (!cpu.gdt.LLDT(selector)) {
02014                 LOG(LOG_CPU,LOG_ERROR)("LLDT failed, selector=%X",selector);
02015                 return true;
02016         }
02017         LOG(LOG_CPU,LOG_NORMAL)("LDT Set to %X",selector);
02018         return false;
02019 }
02020 
02021 Bitu CPU_STR(void) {
02022         return cpu_tss.selector;
02023 }
02024 
02025 bool CPU_LTR(Bitu selector) {
02026         if ((selector & 0xfffc)==0) {
02027                 cpu_tss.SetSelector(selector);
02028                 return false;
02029         }
02030         TSS_Descriptor desc;
02031         if ((selector & 4) || (!cpu.gdt.GetDescriptor(selector,desc))) {
02032                 LOG(LOG_CPU,LOG_ERROR)("LTR failed, selector=%X",selector);
02033                 return CPU_PrepareException(EXCEPTION_GP,selector);
02034         }
02035 
02036         if ((desc.Type()==DESC_286_TSS_A) || (desc.Type()==DESC_386_TSS_A)) {
02037                 if (!desc.saved.seg.p) {
02038                         LOG(LOG_CPU,LOG_ERROR)("LTR failed, selector=%X (not present)",selector);
02039                         return CPU_PrepareException(EXCEPTION_NP,selector);
02040                 }
02041                 if (!cpu_tss.SetSelector(selector)) E_Exit("LTR failed, selector=%X",(int)selector);
02042                 cpu_tss.desc.SetBusy(true);
02043                 cpu_tss.SaveSelector();
02044         } else {
02045                 /* Descriptor was no available TSS descriptor */ 
02046                 LOG(LOG_CPU,LOG_NORMAL)("LTR failed, selector=%X (type=%X)",selector,desc.Type());
02047                 return CPU_PrepareException(EXCEPTION_GP,selector);
02048         }
02049         return false;
02050 }
02051 
02052 void CPU_LGDT(Bitu limit,Bitu base) {
02053         LOG(LOG_CPU,LOG_NORMAL)("GDT Set to base:%X limit:%X",base,limit);
02054         cpu.gdt.SetLimit(limit);
02055         cpu.gdt.SetBase(base);
02056 }
02057 
02058 void CPU_LIDT(Bitu limit,Bitu base) {
02059         LOG(LOG_CPU,LOG_NORMAL)("IDT Set to base:%X limit:%X",base,limit);
02060         cpu.idt.SetLimit(limit);
02061         cpu.idt.SetBase(base);
02062 }
02063 
02064 Bitu CPU_SGDT_base(void) {
02065         return cpu.gdt.GetBase();
02066 }
02067 Bitu CPU_SGDT_limit(void) {
02068         return cpu.gdt.GetLimit();
02069 }
02070 
02071 Bitu CPU_SIDT_base(void) {
02072         return cpu.idt.GetBase();
02073 }
02074 Bitu CPU_SIDT_limit(void) {
02075         return cpu.idt.GetLimit();
02076 }
02077 
02078 static bool snap_cpu_snapped=false;
02079 static Bit32u snap_cpu_saved_cr0;
02080 static Bit32u snap_cpu_saved_cr2;
02081 static Bit32u snap_cpu_saved_cr3;
02082 
02083 /* On shutdown, DOSBox needs to snap back to real mode
02084  * so that it's shutdown code doesn't cause page faults
02085  * trying to clean up DOS structures when we've booted
02086  * a 32-bit OS. It shouldn't be cleaning up DOS structures
02087  * anyway in that case considering they're likely obliterated
02088  * by the guest OS, but that's something we'll clean up
02089  * later. */
02090 void CPU_Snap_Back_To_Real_Mode() {
02091     if (snap_cpu_snapped) return;
02092 
02093     SETFLAGBIT(IF,false);       /* forcibly clear interrupt flag */
02094 
02095     cpu.code.big = false;   /* force back to 16-bit */
02096     cpu.stack.big = false;
02097     cpu.stack.mask = 0xffff;
02098     cpu.stack.notmask = 0xffff0000;
02099 
02100     snap_cpu_saved_cr0 = cpu.cr0;
02101     snap_cpu_saved_cr2 = paging.cr2;
02102     snap_cpu_saved_cr3 = paging.cr3;
02103 
02104     CPU_SET_CRX(0,0);   /* force CPU to real mode */
02105     CPU_SET_CRX(2,0);   /* disable paging */
02106     CPU_SET_CRX(3,0);   /* clear the page table dir */
02107 
02108     cpu.idt.SetBase(0);         /* or ELSE weird things will happen when INTerrupts are run */
02109     cpu.idt.SetLimit(1023);
02110 
02111     snap_cpu_snapped = true;
02112 }
02113 
02114 void CPU_Snap_Back_Restore() {
02115         if (!snap_cpu_snapped) return;
02116 
02117         CPU_SET_CRX(0,snap_cpu_saved_cr0);
02118         CPU_SET_CRX(2,snap_cpu_saved_cr2);
02119         CPU_SET_CRX(3,snap_cpu_saved_cr3);
02120 
02121         snap_cpu_snapped = false;
02122 }
02123 
02124 void CPU_Snap_Back_Forget() {
02125         snap_cpu_snapped = false;
02126 }
02127 
02128 static bool printed_cycles_auto_info = false;
02129 void CPU_SET_CRX(Bitu cr,Bitu value) {
02130         switch (cr) {
02131         case 0:
02132                 {
02133                         value|=CR0_FPUPRESENT;
02134                         Bitu changed=cpu.cr0 ^ value;
02135                         if (!changed) return;
02136                         if (GCC_UNLIKELY(changed & CR0_WRITEPROTECT)) {
02137                                 if (CPU_ArchitectureType >= CPU_ARCHTYPE_486OLD)
02138                                         PAGING_SetWP((value&CR0_WRITEPROTECT)? true:false);
02139                         }
02140                         cpu.cr0=value;
02141                         if (value & CR0_PROTECTION) {
02142                                 cpu.pmode=true;
02143                                 LOG(LOG_CPU,LOG_NORMAL)("Protected mode");
02144                                 PAGING_Enable((value&CR0_PAGING)? true:false);
02145 
02146                                 if (!(CPU_AutoDetermineMode&CPU_AUTODETERMINE_MASK)) break;
02147 
02148                                 if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
02149                                         CPU_CycleAutoAdjust=true;
02150                                         CPU_CycleLeft=0;
02151                                         CPU_Cycles=0;
02152                                         CPU_OldCycleMax=CPU_CycleMax;
02153                                         GFX_SetTitle(CPU_CyclePercUsed,-1,-1,false);
02154                                         if(!printed_cycles_auto_info) {
02155                                                 printed_cycles_auto_info = true;
02156                                                 LOG_MSG("DOSBox switched to max cycles, because of the setting: cycles=auto. If the game runs too fast try a fixed cycles amount in DOSBox's options.");
02157                                         }
02158                     menu_update_autocycle();
02159                                 } else {
02160                                         GFX_SetTitle(-1,-1,-1,false);
02161                                 }
02162 #if (C_DYNAMIC_X86)
02163                                 if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CORE) {
02164                                         CPU_Core_Dyn_X86_Cache_Init(true);
02165                                         cpudecoder=&CPU_Core_Dyn_X86_Run;
02166                                         strcpy(core_mode, "dynamic");
02167                                 }
02168 #endif
02169                                 CPU_AutoDetermineMode<<=CPU_AUTODETERMINE_SHIFT;
02170                         } else {
02171                                 cpu.pmode=false;
02172                                 if (value & CR0_PAGING) LOG_MSG("Paging requested without PE=1");
02173                                 PAGING_Enable(false);
02174                                 LOG(LOG_CPU,LOG_NORMAL)("Real mode");
02175                         }
02176                         break;
02177                 }
02178         case 2:
02179                 paging.cr2=value;
02180                 break;
02181         case 3:
02182                 PAGING_SetDirBase(value);
02183                 break;
02184         default:
02185                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV CR%d,%X",cr,value);
02186                 break;
02187         }
02188 }
02189 
02190 bool CPU_WRITE_CRX(Bitu cr,Bitu value) {
02191         /* Check if privileged to access control registers */
02192         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02193         if ((cr==1) || (cr>4)) return CPU_PrepareException(EXCEPTION_UD,0);
02194         if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLD) {
02195                 if (cr==4) return CPU_PrepareException(EXCEPTION_UD,0);
02196         }
02197         CPU_SET_CRX(cr,value);
02198         return false;
02199 }
02200 
02201 Bitu CPU_GET_CRX(Bitu cr) {
02202         switch (cr) {
02203         case 0:
02204                 if (CPU_ArchitectureType>=CPU_ARCHTYPE_PENTIUM) return cpu.cr0;
02205                 else if (CPU_ArchitectureType>=CPU_ARCHTYPE_486OLD) return (cpu.cr0 & 0xe005003f);
02206                 else return (cpu.cr0 | 0x7ffffff0);
02207         case 2:
02208                 return paging.cr2;
02209         case 3:
02210                 return PAGING_GetDirBase() & 0xfffff000;
02211         default:
02212                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, CR%d",cr);
02213                 break;
02214         }
02215         return 0;
02216 }
02217 
02218 bool CPU_READ_CRX(Bitu cr,Bit32u & retvalue) {
02219         /* Check if privileged to access control registers */
02220         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02221         if ((cr==1) || (cr>4)) return CPU_PrepareException(EXCEPTION_UD,0);
02222         if (CPU_ArchitectureType<CPU_ARCHTYPE_486OLD) {
02223                 if (cr==4) return CPU_PrepareException(EXCEPTION_UD,0);
02224         }
02225         retvalue=CPU_GET_CRX(cr);
02226         return false;
02227 }
02228 
02229 
02230 bool CPU_WRITE_DRX(Bitu dr,Bitu value) {
02231         /* Check if privileged to access control registers */
02232         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02233         switch (dr) {
02234         case 0:
02235         case 1:
02236         case 2:
02237         case 3:
02238                 cpu.drx[dr]=value;
02239                 break;
02240         case 4:
02241         case 6:
02242                 cpu.drx[6]=(value|0xffff0ff0) & 0xffffefff;
02243                 break;
02244         case 5:
02245         case 7:
02246                 if (CPU_ArchitectureType<CPU_ARCHTYPE_PENTIUM) {
02247                         cpu.drx[7]=(value|0x400) & 0xffff2fff;
02248                 } else {
02249                         cpu.drx[7]=(value|0x400);
02250                 }
02251                 break;
02252         default:
02253                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV DR%d,%X",dr,value);
02254                 break;
02255         }
02256         return false;
02257 }
02258 
02259 bool CPU_READ_DRX(Bitu dr,Bit32u & retvalue) {
02260         /* Check if privileged to access control registers */
02261         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02262         switch (dr) {
02263         case 0:
02264         case 1:
02265         case 2:
02266         case 3:
02267         case 6:
02268         case 7:
02269                 retvalue=cpu.drx[dr];
02270                 break;
02271         case 4:
02272                 retvalue=cpu.drx[6];
02273                 break;
02274         case 5:
02275                 retvalue=cpu.drx[7];
02276                 break;
02277         default:
02278                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, DR%d",dr);
02279                 retvalue=0;
02280                 break;
02281         }
02282         return false;
02283 }
02284 
02285 bool CPU_WRITE_TRX(Bitu tr,Bitu value) {
02286         /* Check if privileged to access control registers */
02287         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02288         switch (tr) {
02289 //      case 3:
02290         case 6:
02291         case 7:
02292                 cpu.trx[tr]=value;
02293                 return false;
02294         default:
02295                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV TR%d,%X",tr,value);
02296                 break;
02297         }
02298         return CPU_PrepareException(EXCEPTION_UD,0);
02299 }
02300 
02301 bool CPU_READ_TRX(Bitu tr,Bit32u & retvalue) {
02302         /* Check if privileged to access control registers */
02303         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02304         switch (tr) {
02305 //      case 3:
02306         case 6:
02307         case 7:
02308                 retvalue=cpu.trx[tr];
02309                 return false;
02310         default:
02311                 LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, TR%d",tr);
02312                 break;
02313         }
02314         return CPU_PrepareException(EXCEPTION_UD,0);
02315 }
02316 
02317 
02318 Bitu CPU_SMSW(void) {
02319         return cpu.cr0;
02320 }
02321 
02322 bool CPU_LMSW(Bitu word) {
02323         if (cpu.pmode && (cpu.cpl>0)) return CPU_PrepareException(EXCEPTION_GP,0);
02324         word&=0xf;
02325         if (cpu.cr0 & 1) word|=1; 
02326         word|=(cpu.cr0&0xfffffff0);
02327         CPU_SET_CRX(0,word);
02328         return false;
02329 }
02330 
02331 void CPU_ARPL(Bitu & dest_sel,Bitu src_sel) {
02332         FillFlags();
02333         if ((dest_sel & 3) < (src_sel & 3)) {
02334                 dest_sel=(dest_sel & 0xfffc) + (src_sel & 3);
02335 //              dest_sel|=0xff3f0000;
02336                 SETFLAGBIT(ZF,true);
02337         } else {
02338                 SETFLAGBIT(ZF,false);
02339         } 
02340 }
02341         
02342 void CPU_LAR(Bitu selector,Bitu & ar) {
02343         FillFlags();
02344         if (selector == 0) {
02345                 SETFLAGBIT(ZF,false);
02346                 return;
02347         }
02348         Descriptor desc;Bitu rpl=selector & 3;
02349         if (!cpu.gdt.GetDescriptor(selector,desc)){
02350                 SETFLAGBIT(ZF,false);
02351                 return;
02352         }
02353         switch (desc.Type()){
02354         case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
02355         case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
02356                 break;
02357 
02358         case DESC_286_INT_GATE:         case DESC_286_TRAP_GATE:        {
02359         case DESC_386_INT_GATE:         case DESC_386_TRAP_GATE:
02360                 SETFLAGBIT(ZF,false);
02361                 return;
02362         }
02363 
02364         case DESC_LDT:
02365         case DESC_TASK_GATE:
02366 
02367         case DESC_286_TSS_A:            case DESC_286_TSS_B:
02368         case DESC_286_CALL_GATE:
02369 
02370         case DESC_386_TSS_A:            case DESC_386_TSS_B:
02371         case DESC_386_CALL_GATE:
02372         
02373 
02374         case DESC_DATA_EU_RO_NA:        case DESC_DATA_EU_RO_A:
02375         case DESC_DATA_EU_RW_NA:        case DESC_DATA_EU_RW_A:
02376         case DESC_DATA_ED_RO_NA:        case DESC_DATA_ED_RO_A:
02377         case DESC_DATA_ED_RW_NA:        case DESC_DATA_ED_RW_A:
02378         case DESC_CODE_N_NC_A:          case DESC_CODE_N_NC_NA:
02379         case DESC_CODE_R_NC_A:          case DESC_CODE_R_NC_NA:
02380                 if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
02381                         SETFLAGBIT(ZF,false);
02382                         return;
02383                 }
02384                 break;
02385         default:
02386                 SETFLAGBIT(ZF,false);
02387                 return;
02388         }
02389         /* Valid descriptor */
02390         ar=desc.saved.fill[1] & 0x00ffff00;
02391         SETFLAGBIT(ZF,true);
02392 }
02393 
02394 void CPU_LSL(Bitu selector,Bitu & limit) {
02395         FillFlags();
02396         if (selector == 0) {
02397                 SETFLAGBIT(ZF,false);
02398                 return;
02399         }
02400         Descriptor desc;Bitu rpl=selector & 3;
02401         if (!cpu.gdt.GetDescriptor(selector,desc)){
02402                 SETFLAGBIT(ZF,false);
02403                 return;
02404         }
02405         switch (desc.Type()){
02406         case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
02407         case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
02408                 break;
02409 
02410         case DESC_LDT:
02411         case DESC_286_TSS_A:
02412         case DESC_286_TSS_B:
02413         
02414         case DESC_386_TSS_A:
02415         case DESC_386_TSS_B:
02416 
02417         case DESC_DATA_EU_RO_NA:        case DESC_DATA_EU_RO_A:
02418         case DESC_DATA_EU_RW_NA:        case DESC_DATA_EU_RW_A:
02419         case DESC_DATA_ED_RO_NA:        case DESC_DATA_ED_RO_A:
02420         case DESC_DATA_ED_RW_NA:        case DESC_DATA_ED_RW_A:
02421         
02422         case DESC_CODE_N_NC_A:          case DESC_CODE_N_NC_NA:
02423         case DESC_CODE_R_NC_A:          case DESC_CODE_R_NC_NA:
02424                 if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
02425                         SETFLAGBIT(ZF,false);
02426                         return;
02427                 }
02428                 break;
02429         default:
02430                 SETFLAGBIT(ZF,false);
02431                 return;
02432         }
02433         limit=desc.GetLimit();
02434         SETFLAGBIT(ZF,true);
02435 }
02436 
02437 void CPU_VERR(Bitu selector) {
02438         FillFlags();
02439         if (selector == 0) {
02440                 SETFLAGBIT(ZF,false);
02441                 return;
02442         }
02443         Descriptor desc;Bitu rpl=selector & 3;
02444         if (!cpu.gdt.GetDescriptor(selector,desc)){
02445                 SETFLAGBIT(ZF,false);
02446                 return;
02447         }
02448         switch (desc.Type()){
02449         case DESC_CODE_R_C_A:           case DESC_CODE_R_C_NA:  
02450                 //Conforming readable code segments can be always read 
02451                 break;
02452         case DESC_DATA_EU_RO_NA:        case DESC_DATA_EU_RO_A:
02453         case DESC_DATA_EU_RW_NA:        case DESC_DATA_EU_RW_A:
02454         case DESC_DATA_ED_RO_NA:        case DESC_DATA_ED_RO_A:
02455         case DESC_DATA_ED_RW_NA:        case DESC_DATA_ED_RW_A:
02456 
02457         case DESC_CODE_R_NC_A:          case DESC_CODE_R_NC_NA:
02458                 if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
02459                         SETFLAGBIT(ZF,false);
02460                         return;
02461                 }
02462                 break;
02463         default:
02464                 SETFLAGBIT(ZF,false);
02465                 return;
02466         }
02467         SETFLAGBIT(ZF,true);
02468 }
02469 
02470 void CPU_VERW(Bitu selector) {
02471         FillFlags();
02472         if (selector == 0) {
02473                 SETFLAGBIT(ZF,false);
02474                 return;
02475         }
02476         Descriptor desc;Bitu rpl=selector & 3;
02477         if (!cpu.gdt.GetDescriptor(selector,desc)){
02478                 SETFLAGBIT(ZF,false);
02479                 return;
02480         }
02481         switch (desc.Type()){
02482         case DESC_DATA_EU_RW_NA:        case DESC_DATA_EU_RW_A:
02483         case DESC_DATA_ED_RW_NA:        case DESC_DATA_ED_RW_A:
02484                 if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
02485                         SETFLAGBIT(ZF,false);
02486                         return;
02487                 }
02488                 break;
02489         default:
02490                 SETFLAGBIT(ZF,false);
02491                 return;
02492         }
02493         SETFLAGBIT(ZF,true);
02494 }
02495 
02496 bool CPU_SetSegGeneral(SegNames seg,Bitu value) {
02497         value &= 0xffff;
02498         if (!cpu.pmode || (reg_flags & FLAG_VM)) {
02499                 Segs.val[seg]=value;
02500                 Segs.phys[seg]=value << 4;
02501                 if (seg==ss) {
02502                         cpu.stack.big=false;
02503                         cpu.stack.mask=0xffff;
02504                         cpu.stack.notmask=0xffff0000;
02505                 }
02506 
02507                 /* real mode: loads do not change the limit. "Flat real mode" would not be possible otherwise.
02508                  * vm86: loads are fixed at 64KB (right?) */
02509                 if (reg_flags & FLAG_VM)
02510                         Segs.limit[seg] = 0xFFFF;
02511 
02512                 return false;
02513         } else {
02514                 if (seg==ss) {
02515                         // Stack needs to be non-zero
02516                         if ((value & 0xfffc)==0) {
02517                                 E_Exit("CPU_SetSegGeneral: Stack segment zero");
02518 //                              return CPU_PrepareException(EXCEPTION_GP,0);
02519                         }
02520                         Descriptor desc;
02521                         if (!cpu.gdt.GetDescriptor(value,desc)) {
02522                                 E_Exit("CPU_SetSegGeneral: Stack segment beyond limits");
02523 //                              return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02524                         }
02525                         if (((value & 3)!=cpu.cpl) || (desc.DPL()!=cpu.cpl)) {
02526                                 E_Exit("CPU_SetSegGeneral: Stack segment with invalid privileges");
02527 //                              return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02528                         }
02529 
02530                         switch (desc.Type()) {
02531                         case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
02532                         case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
02533                                 break;
02534                         default:
02535                                 //Earth Siege 1
02536                                 return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02537                         }
02538 
02539                         if (!desc.saved.seg.p) {
02540 //                              E_Exit("CPU_SetSegGeneral: Stack segment not present"); // or #SS(sel)
02541                                 return CPU_PrepareException(EXCEPTION_SS,value & 0xfffc);
02542                         }
02543 
02544                         Segs.val[seg]=value;
02545                         Segs.phys[seg]=desc.GetBase();
02546                         Segs.limit[seg]=do_seg_limits?desc.GetLimit():((PhysPt)(~0UL));
02547                         Segs.expanddown[seg]=desc.GetExpandDown();
02548                         if (desc.Big()) {
02549                                 cpu.stack.big=true;
02550                                 cpu.stack.mask=0xffffffff;
02551                                 cpu.stack.notmask=0;
02552                         } else {
02553                                 cpu.stack.big=false;
02554                                 cpu.stack.mask=0xffff;
02555                                 cpu.stack.notmask=0xffff0000;
02556                         }
02557                 } else {
02558                         if ((value & 0xfffc)==0) {
02559                                 Segs.val[seg]=value;
02560                                 Segs.phys[seg]=0;       // ??
02561                                 return false;
02562                         }
02563                         Descriptor desc;
02564                         if (!cpu.gdt.GetDescriptor(value,desc)) {
02565                                 return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02566                         }
02567                         switch (desc.Type()) {
02568                         case DESC_DATA_EU_RO_NA:                case DESC_DATA_EU_RO_A:
02569                         case DESC_DATA_EU_RW_NA:                case DESC_DATA_EU_RW_A:
02570                         case DESC_DATA_ED_RO_NA:                case DESC_DATA_ED_RO_A:
02571                         case DESC_DATA_ED_RW_NA:                case DESC_DATA_ED_RW_A:
02572                         case DESC_CODE_R_NC_A:                  case DESC_CODE_R_NC_NA:
02573                                 if (((value & 3)>desc.DPL()) || (cpu.cpl>desc.DPL())) {
02574                                         // extreme pinball
02575                                         return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02576                                 }
02577                                 break;
02578                         case DESC_CODE_R_C_A:                   case DESC_CODE_R_C_NA:
02579                                 break;
02580                         default:
02581                                 // gabriel knight
02582                                 return CPU_PrepareException(EXCEPTION_GP,value & 0xfffc);
02583 
02584                         }
02585                         if (!desc.saved.seg.p) {
02586                                 // win
02587                                 return CPU_PrepareException(EXCEPTION_NP,value & 0xfffc);
02588                         }
02589 
02590                         Segs.val[seg]=value;
02591                         Segs.phys[seg]=desc.GetBase();
02592                         Segs.limit[seg]=do_seg_limits?desc.GetLimit():((PhysPt)(~0UL));
02593                         Segs.expanddown[seg]=desc.GetExpandDown();
02594                 }
02595 
02596                 return false;
02597         }
02598 }
02599 
02600 bool CPU_PopSeg(SegNames seg,bool use32) {
02601         Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
02602         if (CPU_SetSegGeneral(seg,val)) return true;
02603         Bitu addsp=use32?0x04:0x02;
02604         reg_esp=(reg_esp&cpu.stack.notmask)|((reg_esp+addsp)&cpu.stack.mask);
02605         return false;
02606 }
02607 
02608 extern bool enable_fpu;
02609 
02610 bool CPU_CPUID(void) {
02611         if (CPU_ArchitectureType < CPU_ARCHTYPE_486NEW) return false;
02612         switch (reg_eax) {
02613         case 0: /* Vendor ID String and maximum level? */
02614                 reg_eax=1;  /* Maximum level */ 
02615                 reg_ebx='G' | ('e' << 8) | ('n' << 16) | ('u'<< 24); 
02616                 reg_edx='i' | ('n' << 8) | ('e' << 16) | ('I'<< 24); 
02617                 reg_ecx='n' | ('t' << 8) | ('e' << 16) | ('l'<< 24); 
02618                 break;
02619         case 1: /* get processor type/family/model/stepping and feature flags */
02620                 if ((CPU_ArchitectureType == CPU_ARCHTYPE_486NEW) ||
02621                         (CPU_ArchitectureType == CPU_ARCHTYPE_MIXED)) {
02622                         reg_eax=0x402;          /* intel 486dx */
02623                         reg_ebx=0;                      /* Not Supported */
02624                         reg_ecx=0;                      /* No features */
02625                         reg_edx=enable_fpu?1:0; /* FPU */
02626                 } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PENTIUM) {
02627                         reg_eax=0x513;          /* intel pentium */
02628                         reg_ebx=0;                      /* Not Supported */
02629                         reg_ecx=0;                      /* No features */
02630                         reg_edx=0x00000010|(enable_fpu?1:0);    /* FPU+TimeStamp/RDTSC */
02631                         if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */
02632             if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */
02633                 } else if (CPU_ArchitectureType == CPU_ARCHTYPE_P55CSLOW) {
02634                         reg_eax=0x543;          /* intel pentium mmx (P55C) */
02635                         reg_ebx=0;                      /* Not Supported */
02636                         reg_ecx=0;                      /* No features */
02637                         reg_edx=0x00800010|(enable_fpu?1:0);    /* FPU+TimeStamp/RDTSC+MMX+ModelSpecific/MSR */
02638                         if (enable_msr) reg_edx |= 0x20; /* ModelSpecific/MSR */
02639             if (enable_cmpxchg8b) reg_edx |= 0x100; /* CMPXCHG8B */
02640                 } else if (CPU_ArchitectureType == CPU_ARCHTYPE_PPROSLOW) {
02641                         reg_eax=0x612;          /* intel pentium pro */
02642                         reg_ebx=0;                      /* Not Supported */
02643                         reg_ecx=0;                      /* No features */
02644                         reg_edx=0x00008011;     /* FPU+TimeStamp/RDTSC */
02645                 } else {
02646                         return false;
02647                 }
02648                 break;
02649         default:
02650                 LOG(LOG_CPU,LOG_ERROR)("Unhandled CPUID Function %x",reg_eax);
02651                 reg_eax=0;
02652                 reg_ebx=0;
02653                 reg_ecx=0;
02654                 reg_edx=0;
02655                 break;
02656         }
02657         return true;
02658 }
02659 
02660 Bits HLT_Decode(void) {
02661         /* Once an interrupt occurs, it should change cpu core */
02662         if (reg_eip!=cpu.hlt.eip || SegValue(cs) != cpu.hlt.cs) {
02663                 cpudecoder=cpu.hlt.old_decoder;
02664         } else {
02665                 CPU_IODelayRemoved += CPU_Cycles;
02666                 CPU_Cycles=0;
02667         }
02668         return 0;
02669 }
02670 
02671 void CPU_HLT(Bitu oldeip) {
02672         /* Since cpu.hlt.old_decoder assigns the current decoder to old, and relies on restoring
02673          * it back when finished, setting cpudecoder to HLT_Decode while already HLT_Decode effectively
02674          * hangs DOSBox and makes it complete unresponsive. Don't want that! */
02675         if (cpudecoder == &HLT_Decode) E_Exit("CPU_HLT attempted to set HLT_Decode while CPU decoder already HLT_Decode");
02676 
02677         reg_eip=oldeip;
02678         CPU_IODelayRemoved += CPU_Cycles;
02679         CPU_Cycles=0;
02680         cpu.hlt.cs=SegValue(cs);
02681         cpu.hlt.eip=reg_eip;
02682         cpu.hlt.old_decoder=cpudecoder;
02683         cpudecoder=&HLT_Decode;
02684 }
02685 
02686 void CPU_ENTER(bool use32,Bitu bytes,Bitu level) {
02687         level&=0x1f;
02688         Bitu sp_index=reg_esp&cpu.stack.mask;
02689         Bitu bp_index=reg_ebp&cpu.stack.mask;
02690         if (!use32) {
02691                 sp_index-=2;
02692                 mem_writew(SegPhys(ss)+sp_index,reg_bp);
02693                 reg_bp=(Bit16u)(reg_esp-2);
02694                 if (level) {
02695                         for (Bitu i=1;i<level;i++) {    
02696                                 sp_index-=2;bp_index-=2;
02697                                 mem_writew(SegPhys(ss)+sp_index,mem_readw(SegPhys(ss)+bp_index));
02698                         }
02699                         sp_index-=2;
02700                         mem_writew(SegPhys(ss)+sp_index,reg_bp);
02701                 }
02702         } else {
02703                 sp_index-=4;
02704         mem_writed(SegPhys(ss)+sp_index,reg_ebp);
02705                 reg_ebp=(reg_esp-4);
02706                 if (level) {
02707                         for (Bitu i=1;i<level;i++) {    
02708                                 sp_index-=4;bp_index-=4;
02709                                 mem_writed(SegPhys(ss)+sp_index,mem_readd(SegPhys(ss)+bp_index));
02710                         }
02711                         sp_index-=4;
02712                         mem_writed(SegPhys(ss)+sp_index,reg_ebp);
02713                 }
02714         }
02715         sp_index-=bytes;
02716         reg_esp=(reg_esp&cpu.stack.notmask)|((sp_index)&cpu.stack.mask);
02717 }
02718 
02719 void CPU_CycleIncrease(bool pressed) {
02720         if (!pressed) return;
02721         if (CPU_CycleAutoAdjust) {
02722                 CPU_CyclePercUsed+=5;
02723                 if (CPU_CyclePercUsed>105) CPU_CyclePercUsed=105;
02724                 LOG_MSG("CPU speed: max %ld percent.",CPU_CyclePercUsed);
02725                 GFX_SetTitle(CPU_CyclePercUsed,-1,-1,false);
02726         } else {
02727                 Bit32s old_cycles=CPU_CycleMax;
02728                 if (CPU_CycleUp < 100) {
02729                         CPU_CycleMax = (Bit32s)(CPU_CycleMax * (1 + (float)CPU_CycleUp / 100.0));
02730                 } else {
02731                         CPU_CycleMax = (Bit32s)(CPU_CycleMax + CPU_CycleUp);
02732                 }
02733             
02734                 CPU_CycleLeft=0;CPU_Cycles=0;
02735                 if (CPU_CycleMax==old_cycles) CPU_CycleMax++;
02736                 if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
02737                     LOG_MSG("CPU:%ld cycles (auto)",CPU_CycleMax);
02738                 } else {
02739                     CPU_CyclesSet=CPU_CycleMax;
02740 #if (C_DYNAMIC_X86)
02741             if (CPU_CycleMax > 15000 && cpudecoder != &CPU_Core_Dyn_X86_Run)
02742                 LOG_MSG("CPU speed: fixed %ld cycles. If you need more than 20000, try core=dynamic in DOSBox's options.",CPU_CycleMax);
02743             else
02744 #endif
02745                 LOG_MSG("CPU speed: fixed %ld cycles.",CPU_CycleMax);
02746         }
02747                 GFX_SetTitle(CPU_CycleMax,-1,-1,false);
02748         }
02749 }
02750 
02751 void CPU_CycleDecrease(bool pressed) {
02752         if (!pressed) return;
02753         if (CPU_CycleAutoAdjust) {
02754                 CPU_CyclePercUsed-=5;
02755                 if (CPU_CyclePercUsed<=0) CPU_CyclePercUsed=1;
02756                 if(CPU_CyclePercUsed <=70)
02757                         LOG_MSG("CPU speed: max %ld percent. If the game runs too fast, try a fixed cycles amount in DOSBox's options.",CPU_CyclePercUsed);
02758                 else
02759                         LOG_MSG("CPU speed: max %ld percent.",CPU_CyclePercUsed);
02760                 GFX_SetTitle(CPU_CyclePercUsed,-1,-1,false);
02761         } else {
02762                 if (CPU_CycleDown < 100) {
02763                         CPU_CycleMax = (Bit32s)(CPU_CycleMax / (1 + (float)CPU_CycleDown / 100.0));
02764                 } else {
02765                         CPU_CycleMax = (Bit32s)(CPU_CycleMax - CPU_CycleDown);
02766                 }
02767                 CPU_CycleLeft=0;CPU_Cycles=0;
02768                 if (CPU_CycleMax <= 0) CPU_CycleMax=1;
02769                 if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
02770                     LOG_MSG("CPU:%ld cycles (auto)",CPU_CycleMax);
02771                 } else {
02772                     CPU_CyclesSet=CPU_CycleMax;
02773                     LOG_MSG("CPU speed: fixed %ld cycles.",CPU_CycleMax);
02774                 }
02775                 GFX_SetTitle(CPU_CycleMax,-1,-1,false);
02776         }
02777 }
02778 
02779 static void CPU_ToggleAutoCycles(bool pressed) {
02780     if (!pressed)
02781         return;
02782 
02783     Section* sec=control->GetSection("cpu");
02784     if (sec) {
02785         std::string tmp("cycles=");
02786         if (CPU_CycleAutoAdjust) {
02787             std::ostringstream str;
02788             str << "fixed " << CPU_CyclesSet;
02789             tmp.append(str.str());
02790         } else if (CPU_AutoDetermineMode&CPU_AUTODETERMINE_CYCLES) {
02791             tmp.append("max");
02792         } else {
02793             tmp.append("auto");
02794         }
02795 
02796         sec->HandleInputline(tmp);
02797     }
02798 }
02799 
02800 static void CPU_ToggleFullCore(bool pressed) {
02801     if (!pressed)
02802         return;
02803     Section* sec=control->GetSection("cpu");
02804     if(sec) {
02805         std::string tmp="core=full";
02806         sec->HandleInputline(tmp);
02807     }
02808 }
02809 
02810 static void CPU_ToggleNormalCore(bool pressed) {
02811     if (!pressed)
02812         return;
02813     Section* sec=control->GetSection("cpu");
02814     if(sec) {
02815         std::string tmp="core=normal";
02816         sec->HandleInputline(tmp);
02817     }
02818 }
02819 
02820 #if (C_DYNAMIC_X86)
02821 static void CPU_ToggleDynamicCore(bool pressed) {
02822     if (!pressed)
02823         return;
02824     Section* sec=control->GetSection("cpu");
02825     if(sec) {
02826         std::string tmp="core=dynamic";
02827         sec->HandleInputline(tmp);
02828     }
02829 }
02830 #endif
02831 
02832 static void CPU_ToggleSimpleCore(bool pressed) {
02833     if (!pressed)
02834         return;
02835     Section* sec=control->GetSection("cpu");
02836     std::string tmp="core=simple";
02837     if(sec) {
02838         sec->HandleInputline(tmp);
02839     }
02840 }
02841 
02842 void CPU_Enable_SkipAutoAdjust(void) {
02843         if (CPU_CycleAutoAdjust) {
02844                 CPU_CycleMax /= 2;
02845                 if (CPU_CycleMax < CPU_CYCLES_LOWER_LIMIT)
02846                         CPU_CycleMax = CPU_CYCLES_LOWER_LIMIT;
02847         }
02848         CPU_SkipCycleAutoAdjust=true;
02849 }
02850 
02851 void CPU_Disable_SkipAutoAdjust(void) {
02852         CPU_SkipCycleAutoAdjust=false;
02853 }
02854 
02855 
02856 extern Bit32s ticksDone;
02857 extern Bit32u ticksScheduled;
02858 extern int dynamic_core_cache_block_size;
02859 
02860 void CPU_Reset_AutoAdjust(void) {
02861         CPU_IODelayRemoved = 0;
02862         ticksDone = 0;
02863         ticksScheduled = 0;
02864 }
02865 
02866 class Weitek_PageHandler : public PageHandler {
02867 public:
02868         Weitek_PageHandler(HostPt /*addr*/){
02869                 flags=PFLAG_NOCODE;
02870         }
02871 
02872         ~Weitek_PageHandler() {
02873         }
02874 
02875         Bitu readb(PhysPt addr);
02876         void writeb(PhysPt addr,Bitu val);
02877         Bitu readw(PhysPt addr);
02878         void writew(PhysPt addr,Bitu val);
02879         Bitu readd(PhysPt addr);
02880         void writed(PhysPt addr,Bitu val);
02881 };
02882 
02883 Bitu Weitek_PageHandler::readb(PhysPt addr) {
02884     LOG_MSG("Weitek stub: readb at 0x%lx",(unsigned long)addr);
02885         return (Bitu)-1;
02886 }
02887 void Weitek_PageHandler::writeb(PhysPt addr,Bitu val) {
02888     LOG_MSG("Weitek stub: writeb at 0x%lx val=0x%lx",(unsigned long)addr,(unsigned long)val);
02889 }
02890 
02891 Bitu Weitek_PageHandler::readw(PhysPt addr) {
02892     LOG_MSG("Weitek stub: readw at 0x%lx",(unsigned long)addr);
02893         return (Bitu)-1;
02894 }
02895 
02896 void Weitek_PageHandler::writew(PhysPt addr,Bitu val) {
02897     LOG_MSG("Weitek stub: writew at 0x%lx val=0x%lx",(unsigned long)addr,(unsigned long)val);
02898 }
02899 
02900 Bitu Weitek_PageHandler::readd(PhysPt addr) {
02901     LOG_MSG("Weitek stub: readd at 0x%lx",(unsigned long)addr);
02902         return (Bitu)-1;
02903 }
02904 
02905 void Weitek_PageHandler::writed(PhysPt addr,Bitu val) {
02906     LOG_MSG("Weitek stub: writed at 0x%lx val=0x%lx",(unsigned long)addr,(unsigned long)val);
02907 }
02908 
02909 Weitek_PageHandler weitek_pagehandler(0);
02910 
02911 PageHandler* weitek_memio_cb(MEM_CalloutObject &co,Bitu phys_page) {
02912     (void)co; // UNUSED
02913     (void)phys_page; // UNUSED
02914     return &weitek_pagehandler;
02915 }
02916 
02917 bool CpuType_Auto(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
02918     (void)menu;//UNUSED
02919     (void)menuitem;//UNUSED
02920     Section* sec=control->GetSection("cpu");
02921     if (sec) sec->HandleInputline("cputype=auto");
02922     return true;
02923 }
02924 
02925 bool CpuType_ByName(DOSBoxMenu * const menu,DOSBoxMenu::item * const menuitem) {
02926     (void)menu;//UNUSED
02927 
02928     const char *name = menuitem->get_name().c_str();
02929 
02930     /* name should be cputype_... */
02931     if (!strncmp(name,"cputype_",8)) name += 8;
02932     else abort();
02933 
02934     Section* sec=control->GetSection("cpu");
02935     if (sec) sec->HandleInputline(std::string("cputype=")+name);
02936     return true;
02937 }
02938 
02939 class CPU: public Module_base {
02940 private:
02941         static bool inited;
02942 public:
02943         CPU(Section* configuration):Module_base(configuration) {
02944                 Section_prop * section=static_cast<Section_prop *>(configuration);
02945                 DOSBoxMenu::item *item;
02946 
02947                 if(inited) {
02948                         Change_Config(configuration);
02949                         return;
02950                 }
02951 //              Section_prop * section=static_cast<Section_prop *>(configuration);
02952                 inited=true;
02953                 reg_eax=0;
02954                 reg_ebx=0;
02955                 reg_ecx=0;
02956                 reg_edx=0;
02957                 reg_edi=0;
02958                 reg_esi=0;
02959                 reg_ebp=0;
02960                 reg_esp=0;
02961 
02962                 do_seg_limits = section->Get_bool("segment limits");
02963         
02964                 SegSet16(cs,0); Segs.limit[cs] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[cs] = false;
02965                 SegSet16(ds,0); Segs.limit[ds] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[ds] = false;
02966                 SegSet16(es,0); Segs.limit[es] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[es] = false;
02967                 SegSet16(fs,0); Segs.limit[fs] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[fs] = false;
02968                 SegSet16(gs,0); Segs.limit[gs] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[gs] = false;
02969                 SegSet16(ss,0); Segs.limit[ss] = do_seg_limits ? 0xFFFF : ((PhysPt)(~0UL)); Segs.expanddown[ss] = false;
02970         
02971                 CPU_SetFlags(FLAG_IF,FMASK_ALL);                //Enable interrupts
02972                 cpu.cr0=0xffffffff;
02973                 CPU_SET_CRX(0,0);                                               //Initialize
02974                 cpu.code.big=false;
02975                 cpu.stack.mask=0xffff;
02976                 cpu.stack.notmask=0xffff0000;
02977                 cpu.stack.big=false;
02978                 cpu.trap_skip=false;
02979                 cpu.idt.SetBase(0);
02980                 cpu.idt.SetLimit(1023);
02981 
02982                 for (Bitu i=0; i<7; i++) {
02983                         cpu.drx[i]=0;
02984                         cpu.trx[i]=0;
02985                 }
02986                 if (CPU_ArchitectureType>=CPU_ARCHTYPE_PENTIUM) {
02987                         cpu.drx[6]=0xffff0ff0;
02988                 } else {
02989                         cpu.drx[6]=0xffff1ff0;
02990                 }
02991                 cpu.drx[7]=0x00000400;
02992 
02993                 /* Init the cpu cores */
02994                 CPU_Core_Normal_Init();
02995                 CPU_Core_Simple_Init();
02996                 CPU_Core_Full_Init();
02997 #if (C_DYNAMIC_X86)
02998                 CPU_Core_Dyn_X86_Init();
02999 #endif
03000                 MAPPER_AddHandler(CPU_CycleDecrease,MK_minus,MMODHOST,"cycledown","Dec Cycles",&item);
03001                 item->set_text("Decrement cycles");
03002 
03003                 MAPPER_AddHandler(CPU_CycleIncrease,MK_equals,MMODHOST,"cycleup"  ,"Inc Cycles",&item);
03004                 item->set_text("Increment cycles");
03005 
03006                 MAPPER_AddHandler(CPU_ToggleAutoCycles,MK_nothing,0,"cycauto","AutoCycles",&item);
03007                 item->set_text("Auto cycles");
03008                 item->set_description("Enable automatic cycle count");
03009 
03010                 MAPPER_AddHandler(CPU_ToggleNormalCore,MK_nothing,0,"normal"  ,"NormalCore", &item);
03011                 item->set_text("Normal core");
03012 
03013                 MAPPER_AddHandler(CPU_ToggleFullCore,MK_nothing,0,"full","Full Core", &item);
03014                 item->set_text("Full core");
03015 
03016                 MAPPER_AddHandler(CPU_ToggleSimpleCore,MK_nothing,0,"simple","SimpleCore", &item);
03017                 item->set_text("Simple core");
03018 #if (C_DYNAMIC_X86)
03019                 MAPPER_AddHandler(CPU_ToggleDynamicCore,MK_nothing,0,"dynamic","DynCore",&item);
03020                 item->set_text("Dynamic core");
03021 #endif
03022 
03023         /* these are not mapper shortcuts, and probably should not be mapper shortcuts */
03024         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_auto").
03025             set_text("Auto").set_callback_function(CpuType_Auto);
03026         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_8086").
03027             set_text("8086").set_callback_function(CpuType_ByName);
03028         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_8086_prefetch").
03029             set_text("8086 with prefetch").set_callback_function(CpuType_ByName);
03030         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_80186").
03031             set_text("80186").set_callback_function(CpuType_ByName);
03032         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_80186_prefetch").
03033             set_text("80186 with prefetch").set_callback_function(CpuType_ByName);
03034         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_286").
03035             set_text("286").set_callback_function(CpuType_ByName);
03036         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_286_prefetch").
03037             set_text("286 with prefetch").set_callback_function(CpuType_ByName);
03038         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_386").
03039             set_text("386").set_callback_function(CpuType_ByName);
03040         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_386_prefetch").
03041             set_text("386 with prefetch").set_callback_function(CpuType_ByName);
03042         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_486").
03043             set_text("486").set_callback_function(CpuType_ByName);
03044         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_486_prefetch").
03045             set_text("486 with prefetch").set_callback_function(CpuType_ByName);
03046         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_pentium").
03047             set_text("Pentium").set_callback_function(CpuType_ByName);
03048         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_pentium_mmx").
03049             set_text("Pentium MMX").set_callback_function(CpuType_ByName);
03050         mainMenu.alloc_item(DOSBoxMenu::item_type_id,"cputype_ppro_slow").
03051             set_text("Pentium Pro").set_callback_function(CpuType_ByName);
03052 
03053                 Change_Config(configuration);   
03054                 CPU_JMP(false,0,0,0);                                   //Setup the first cpu core
03055         }
03056         bool Change_Config(Section* newconfig){
03057                 Section_prop * section=static_cast<Section_prop *>(newconfig);
03058                 CPU_AutoDetermineMode=CPU_AUTODETERMINE_NONE;
03059                 //CPU_CycleLeft=0;//needed ?
03060                 CPU_Cycles=0;
03061                 CPU_SkipCycleAutoAdjust=false;
03062 
03063                 ignore_opcode_63 = section->Get_bool("ignore opcode 63");
03064                 use_dynamic_core_with_paging = section->Get_bool("use dynamic core with paging on");
03065                 cpu_double_fault_enable = section->Get_bool("double fault");
03066                 cpu_triple_fault_reset = section->Get_bool("reset on triple fault");
03067                 cpu_allow_big16 = section->Get_bool("realbig16");
03068 
03069         if (cpu_allow_big16) {
03070             /* FIXME: GCC 4.8: How is this an empty body? Explain. */
03071             LOG(LOG_CPU,LOG_DEBUG)("Emulation of the B (big) bit in real mode enabled\n");
03072         }
03073 
03074                 always_report_double_fault = section->Get_bool("always report double fault");
03075                 always_report_triple_fault = section->Get_bool("always report triple fault");
03076 
03077                 dynamic_core_cache_block_size = section->Get_int("dynamic core cache block size");
03078                 if (dynamic_core_cache_block_size < 1 || dynamic_core_cache_block_size > 65536) dynamic_core_cache_block_size = 32;
03079 
03080                 Prop_multival* p = section->Get_multival("cycles");
03081                 std::string type = p->GetSection()->Get_string("type");
03082                 std::string str ;
03083                 CommandLine cmd(0,p->GetSection()->Get_string("parameters"));
03084                 if (type=="max") {
03085                         CPU_CycleMax=0;
03086                         CPU_CyclePercUsed=100;
03087                         CPU_CycleAutoAdjust=true;
03088                         CPU_CycleLimit=-1;
03089                         for (Bitu cmdnum=1; cmdnum<=cmd.GetCount(); cmdnum++) {
03090                                 if (cmd.FindCommand(cmdnum,str)) {
03091                                         if (str.find('%')==str.length()-1) {
03092                                                 str.erase(str.find('%'));
03093                                                 int percval=0;
03094                                                 std::istringstream stream(str);
03095                                                 stream >> percval;
03096                                                 if ((percval>0) && (percval<=105)) CPU_CyclePercUsed=(Bit32s)percval;
03097                                         } else if (str=="limit") {
03098                                                 cmdnum++;
03099                                                 if (cmd.FindCommand(cmdnum,str)) {
03100                                                         int cyclimit=0;
03101                                                         std::istringstream stream(str);
03102                                                         stream >> cyclimit;
03103                                                         if (cyclimit>0) CPU_CycleLimit=cyclimit;
03104                                                 }
03105                                         }
03106                                 }
03107                         }
03108                 } else {
03109                         if (type=="auto") {
03110                                 CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CYCLES;
03111                                 CPU_CycleMax=3000;
03112                                 CPU_OldCycleMax=3000;
03113                                 CPU_CyclePercUsed=100;
03114                                 for (Bitu cmdnum=0; cmdnum<=cmd.GetCount(); cmdnum++) {
03115                                         if (cmd.FindCommand(cmdnum,str)) {
03116                                                 if (str.find('%')==str.length()-1) {
03117                                                         str.erase(str.find('%'));
03118                                                         int percval=0;
03119                                                         std::istringstream stream(str);
03120                                                         stream >> percval;
03121                                                         if ((percval>0) && (percval<=105)) CPU_CyclePercUsed=(Bit32s)percval;
03122                                                 } else if (str=="limit") {
03123                                                         cmdnum++;
03124                                                         if (cmd.FindCommand(cmdnum,str)) {
03125                                                                 int cyclimit=0;
03126                                                                 std::istringstream stream(str);
03127                                                                 stream >> cyclimit;
03128                                                                 if (cyclimit>0) CPU_CycleLimit=cyclimit;
03129                                                         }
03130                                                 } else {
03131                                                         int rmdval=0;
03132                                                         std::istringstream stream(str);
03133                                                         stream >> rmdval;
03134                                                         if (rmdval>0) {
03135                                                                 CPU_CycleMax=(Bit32s)rmdval;
03136                                                                 CPU_OldCycleMax=(Bit32s)rmdval;
03137                                                         }
03138                                                 }
03139                                         }
03140                                 }
03141                         } else if(type =="fixed") {
03142                                 cmd.FindCommand(1,str);
03143                                 int rmdval=0;
03144                                 std::istringstream stream(str);
03145                                 stream >> rmdval;
03146                                 CPU_CycleMax=(Bit32s)rmdval;
03147                         } else {
03148                                 std::istringstream stream(type);
03149                                 int rmdval=0;
03150                                 stream >> rmdval;
03151                                 if(rmdval) CPU_CycleMax=(Bit32s)rmdval;
03152                                 if(rmdval) CPU_CyclesSet=(Bit32s)rmdval;
03153                         }
03154                         CPU_CycleAutoAdjust=false;
03155                 }
03156 
03157         menu_update_autocycle();
03158 
03159                 enable_fpu=section->Get_bool("fpu");
03160                 cpu_rep_max=section->Get_int("interruptible rep string op");
03161                 ignore_undefined_msr=section->Get_bool("ignore undefined msr");
03162                 enable_msr=section->Get_bool("enable msr");
03163         enable_cmpxchg8b=section->Get_bool("enable cmpxchg8b");
03164                 CPU_CycleUp=section->Get_int("cycleup");
03165                 CPU_CycleDown=section->Get_int("cycledown");
03166                 std::string core(section->Get_string("core"));
03167                 cpudecoder=&CPU_Core_Normal_Run;
03168                 safe_strncpy(core_mode,core.c_str(),15);
03169                 core_mode[15] = '\0';
03170                 if (core == "normal") {
03171                         cpudecoder=&CPU_Core_Normal_Run;
03172                 } else if (core =="simple") {
03173                         cpudecoder=&CPU_Core_Simple_Run;
03174                 } else if (core == "full") {
03175                         cpudecoder=&CPU_Core_Full_Run;
03176                 } else if (core == "auto") {
03177                         cpudecoder=&CPU_Core_Normal_Run;
03178                         CPU_AutoDetermineMode|=CPU_AUTODETERMINE_CORE;
03179 #if (C_DYNAMIC_X86)
03180                 } else if (core == "dynamic") {
03181                         cpudecoder=&CPU_Core_Dyn_X86_Run;
03182                         CPU_Core_Dyn_X86_SetFPUMode(true);
03183                 } else if (core == "dynamic_nodhfpu") {
03184                         cpudecoder=&CPU_Core_Dyn_X86_Run;
03185                         CPU_Core_Dyn_X86_SetFPUMode(false);
03186 #endif
03187                 } else {
03188                         strcpy(core_mode,"normal");
03189                         cpudecoder=&CPU_Core_Normal_Run;
03190                         LOG_MSG("CPU:Unknown core type %s, switching back to normal.",core.c_str());
03191                 }
03192 
03193 #if (C_DYNAMIC_X86)
03194                 CPU_Core_Dyn_X86_Cache_Init((core == "dynamic") || (core == "dynamic_nodhfpu"));
03195 #endif
03196 
03197                 CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
03198                 std::string cputype(section->Get_string("cputype"));
03199                 if (cputype == "auto") {
03200                         CPU_ArchitectureType = CPU_ARCHTYPE_MIXED;
03201                 } else if (cputype == "8086") {
03202                         CPU_ArchitectureType = CPU_ARCHTYPE_8086;
03203                         cpudecoder=&CPU_Core8086_Normal_Run;
03204                 } else if (cputype == "8086_prefetch") { /* 6-byte prefetch queue ref [http://www.phatcode.net/res/224/files/html/ch11/11-02.html] */
03205                         CPU_ArchitectureType = CPU_ARCHTYPE_8086;
03206                         if (core == "normal") {
03207                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03208                                 CPU_PrefetchQueueSize = 4; /* Emulate the 8088, which was more common in home PCs than having an 8086 */
03209                         } else if (core == "auto") {
03210                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03211                                 CPU_PrefetchQueueSize = 4; /* Emulate the 8088, which was more common in home PCs than having an 8086 */
03212                                 CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
03213                         } else {
03214                                 E_Exit("prefetch queue emulation requires the normal core setting.");
03215                         }
03216                 } else if (cputype == "80186") {
03217                         CPU_ArchitectureType = CPU_ARCHTYPE_80186;
03218                         cpudecoder=&CPU_Core286_Normal_Run;
03219                 } else if (cputype == "80186_prefetch") { /* 6-byte prefetch queue ref [http://www.phatcode.net/res/224/files/html/ch11/11-02.html] */
03220                         CPU_ArchitectureType = CPU_ARCHTYPE_80186;
03221                         if (core == "normal") {
03222                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03223                                 CPU_PrefetchQueueSize = 6;
03224                         } else if (core == "auto") {
03225                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03226                                 CPU_PrefetchQueueSize = 6;
03227                                 CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
03228                         } else {
03229                                 E_Exit("prefetch queue emulation requires the normal core setting.");
03230                         }
03231                 } else if (cputype == "286") {
03232                         CPU_ArchitectureType = CPU_ARCHTYPE_286;
03233                         cpudecoder=&CPU_Core286_Normal_Run;
03234                 } else if (cputype == "286_prefetch") { /* 6-byte prefetch queue ref [http://www.phatcode.net/res/224/files/html/ch11/11-02.html] */
03235                         CPU_ArchitectureType = CPU_ARCHTYPE_286;
03236                         if (core == "normal") {
03237                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03238                                 CPU_PrefetchQueueSize = 6;
03239                         } else if (core == "auto") {
03240                                 cpudecoder=&CPU_Core_Prefetch_Run; /* TODO: Alternate 16-bit only decoder for 286 that does NOT include 386+ instructions */
03241                                 CPU_PrefetchQueueSize = 6;
03242                                 CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
03243                         } else {
03244                                 E_Exit("prefetch queue emulation requires the normal core setting.");
03245                         }
03246                 } else if (cputype == "386") {
03247                         CPU_ArchitectureType = CPU_ARCHTYPE_386;
03248                 } else if (cputype == "386_prefetch") {
03249                         CPU_ArchitectureType = CPU_ARCHTYPE_386;
03250                         if (core == "normal") {
03251                                 cpudecoder=&CPU_Core_Prefetch_Run;
03252                                 CPU_PrefetchQueueSize = 16;
03253                         } else if (core == "auto") {
03254                                 cpudecoder=&CPU_Core_Prefetch_Run;
03255                                 CPU_PrefetchQueueSize = 16;
03256                                 CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
03257                         } else {
03258                                 E_Exit("prefetch queue emulation requires the normal core setting.");
03259                         }
03260                 } else if (cputype == "486") {
03261                         CPU_ArchitectureType = CPU_ARCHTYPE_486NEW;
03262                 } else if (cputype == "486_prefetch") {
03263                         CPU_ArchitectureType = CPU_ARCHTYPE_486NEW;
03264                         if (core == "normal") {
03265                                 cpudecoder=&CPU_Core_Prefetch_Run;
03266                                 CPU_PrefetchQueueSize = 32;
03267                         } else if (core == "auto") {
03268                                 cpudecoder=&CPU_Core_Prefetch_Run;
03269                                 CPU_PrefetchQueueSize = 32;
03270                                 CPU_AutoDetermineMode&=(~CPU_AUTODETERMINE_CORE);
03271                         } else {
03272                                 E_Exit("prefetch queue emulation requires the normal core setting.");
03273                         }
03274                 } else if (cputype == "pentium") {
03275                         CPU_ArchitectureType = CPU_ARCHTYPE_PENTIUM;
03276                 } else if (cputype == "pentium_mmx") {
03277                         CPU_ArchitectureType = CPU_ARCHTYPE_P55CSLOW;
03278                 } else if (cputype == "ppro_slow") {
03279                         CPU_ArchitectureType = CPU_ARCHTYPE_PPROSLOW;
03280                 }
03281 
03282                 /* WARNING */
03283                 if (CPU_ArchitectureType == CPU_ARCHTYPE_8086) {
03284                         LOG_MSG("CPU warning: 8086 cpu type is experimental at this time");
03285                 }
03286                 else if (CPU_ArchitectureType == CPU_ARCHTYPE_80186) {
03287                         LOG_MSG("CPU warning: 80186 cpu type is experimental at this time");
03288                 }
03289 
03290                 if (CPU_ArchitectureType>=CPU_ARCHTYPE_486NEW) CPU_extflags_toggle=(FLAG_ID|FLAG_AC);
03291                 else if (CPU_ArchitectureType>=CPU_ARCHTYPE_486OLD) CPU_extflags_toggle=(FLAG_AC);
03292                 else CPU_extflags_toggle=0;
03293 
03294     // weitek coprocessor emulation?
03295         if (CPU_ArchitectureType == CPU_ARCHTYPE_386 || CPU_ArchitectureType == CPU_ARCHTYPE_486OLD || CPU_ArchitectureType == CPU_ARCHTYPE_486NEW) {
03296                 Section_prop *dsection = static_cast<Section_prop *>(control->GetSection("dosbox"));
03297 
03298             enable_weitek = dsection->Get_bool("weitek");
03299             if (enable_weitek) {
03300                 LOG_MSG("Weitek coprocessor emulation enabled");
03301 
03302                 static Bitu weitek_lfb = 0xC0000000UL;
03303                 static Bitu weitek_lfb_pages = 0x2000000UL >> 12UL; /* "The coprocessor will respond to memory addresses 0xC0000000-0xC1FFFFFF" */
03304                 static MEM_Callout_t weitek_lfb_cb = MEM_Callout_t_none;
03305 
03306                 if (weitek_lfb_cb == MEM_Callout_t_none) {
03307                     weitek_lfb_cb = MEM_AllocateCallout(MEM_TYPE_MB);
03308                     if (weitek_lfb_cb == MEM_Callout_t_none) E_Exit("Unable to allocate weitek cb for LFB");
03309                 }
03310 
03311                 {
03312                     MEM_CalloutObject *cb = MEM_GetCallout(weitek_lfb_cb);
03313 
03314                     assert(cb != NULL);
03315 
03316                     cb->Uninstall();
03317 
03318                     cb->Install(weitek_lfb>>12UL,MEMMASK_Combine(MEMMASK_FULL,MEMMASK_Range(weitek_lfb_pages)),weitek_memio_cb);
03319 
03320                     MEM_PutCallout(cb);
03321                 }
03322             }
03323         }
03324         else {
03325             enable_weitek = false;
03326         }
03327 
03328                 if (cpu_rep_max < 0) cpu_rep_max = 4;   /* compromise to help emulation speed without too much loss of accuracy */
03329 
03330                 if(CPU_CycleMax <= 0) CPU_CycleMax = 3000;
03331                 if(CPU_CycleUp <= 0)   CPU_CycleUp = 500;
03332                 if(CPU_CycleDown <= 0) CPU_CycleDown = 20;
03333 
03334         if (enable_cmpxchg8b && CPU_ArchitectureType >= CPU_ARCHTYPE_PENTIUM) LOG_MSG("Pentium CMPXCHG8B emulation is enabled");
03335 
03336                 menu_update_core();
03337                 menu_update_cputype();
03338 
03339         void CPU_Core_Prefetch_reset(void);
03340         CPU_Core_Prefetch_reset();
03341 
03342                 if (CPU_CycleAutoAdjust) GFX_SetTitle(CPU_CyclePercUsed,-1,-1,false);
03343                 else GFX_SetTitle(CPU_CycleMax,-1,-1,false);
03344                 return true;
03345         }
03346         ~CPU(){ /* empty */};
03347 };
03348         
03349 static CPU * test;
03350 
03351 void CPU_ShutDown(Section* sec) {
03352     (void)sec;//UNUSED
03353 
03354 #if (C_DYNAMIC_X86)
03355         CPU_Core_Dyn_X86_Cache_Close();
03356 #endif
03357         delete test;
03358 }
03359 
03360 void CPU_OnReset(Section* sec) {
03361     (void)sec;//UNUSED
03362 
03363         LOG(LOG_CPU,LOG_DEBUG)("CPU reset");
03364 
03365         CPU_Snap_Back_To_Real_Mode();
03366         CPU_Snap_Back_Forget();
03367         CPU_SetFlags(0,~0UL);
03368 
03369         Segs.limit[cs]=0xFFFF;
03370         Segs.expanddown[cs]=false;
03371         if (CPU_ArchitectureType >= CPU_ARCHTYPE_386) {
03372                 /* 386 and later start at F000:FFF0 with CS base set to FFFF0000 (really?) */
03373                 SegSet16(cs,0xF000);
03374                 reg_eip=0xFFF0;
03375                 Segs.phys[cs]=0xFFFF0000;
03376         }
03377         else if (CPU_ArchitectureType >= CPU_ARCHTYPE_286) {
03378                 /* 286 start at F000:FFF0 (FFFF0) */
03379                 SegSet16(cs,0xF000);
03380                 reg_eip=0xFFF0;
03381         }
03382         else {
03383                 /* 8086 start at FFFF:0000 (FFFF0) */
03384                 SegSet16(cs,0xFFFF);
03385                 reg_eip=0x0000;
03386         }
03387 }
03388 
03389 void CPU_OnSectionPropChange(Section *x) {
03390         if (test != NULL) test->Change_Config(x);
03391 }
03392 
03393 void CPU_Init() {
03394         LOG(LOG_MISC,LOG_DEBUG)("Initializing CPU");
03395 
03396         control->GetSection("cpu")->onpropchange.push_back(&CPU_OnSectionPropChange);
03397 
03398         test = new CPU(control->GetSection("cpu"));
03399         AddExitFunction(AddExitFunctionFuncPair(CPU_ShutDown),true);
03400         AddVMEventFunction(VM_EVENT_RESET,AddVMEventFunctionFuncPair(CPU_OnReset));
03401 }
03402 //initialize static members
03403 bool CPU::inited=false;
03404 
03405 
03406 Bit16u CPU_FindDecoderType( CPU_Decoder *decoder )
03407 {
03408     (void)decoder;//UNUSED
03409 
03410         Bit16u decoder_idx;
03411 
03412         decoder_idx = 0xffff;
03413 
03414 
03415         if(0) {}
03416         else if( cpudecoder == &CPU_Core_Normal_Run ) decoder_idx = 0;
03417         else if( cpudecoder == &CPU_Core_Prefetch_Run ) decoder_idx = 1;
03418         else if( cpudecoder == &CPU_Core_Simple_Run ) decoder_idx = 2;
03419         else if( cpudecoder == &CPU_Core_Full_Run ) decoder_idx = 3;
03420 #if C_DYNAMIC_X86
03421         else if( cpudecoder == &CPU_Core_Dyn_X86_Run ) decoder_idx = 4;
03422 #endif
03423         else if( cpudecoder == &CPU_Core_Normal_Trap_Run ) decoder_idx = 100;
03424 #if C_DYNAMIC_X86
03425         else if( cpudecoder == &CPU_Core_Dyn_X86_Trap_Run ) decoder_idx = 101;
03426 #endif
03427         else if( cpudecoder == &HLT_Decode ) decoder_idx = 200;
03428 
03429 
03430         return decoder_idx;
03431 }
03432 
03433 
03434 CPU_Decoder *CPU_IndexDecoderType( Bit16u decoder_idx )
03435 {
03436         CPU_Decoder *cpudecoder;
03437 
03438 
03439         cpudecoder = 0;
03440         switch( decoder_idx ) {
03441                 case 0: cpudecoder = &CPU_Core_Normal_Run; break;
03442                 case 1: cpudecoder = &CPU_Core_Prefetch_Run; break;
03443                 case 2: cpudecoder = &CPU_Core_Simple_Run; break;
03444                 case 3: cpudecoder = &CPU_Core_Full_Run; break;
03445 #if C_DYNAMIC_X86
03446                 case 4: cpudecoder = &CPU_Core_Dyn_X86_Run; break;
03447 #endif
03448                 case 100: cpudecoder = &CPU_Core_Normal_Trap_Run; break;
03449 #if C_DYNAMIC_X86
03450                 case 101: cpudecoder = &CPU_Core_Dyn_X86_Trap_Run; break;
03451 #endif
03452                 case 200: cpudecoder = &HLT_Decode; break;
03453         }
03454 
03455 
03456         return cpudecoder;
03457 }
03458 
03459 Bitu vm86_fake_io_seg = 0xF000; /* unused area in BIOS for IO instruction */
03460 Bitu vm86_fake_io_off = 0x0700;
03461 Bitu vm86_fake_io_offs[3*2]={0};        /* offsets from base off because of dynamic core cache */
03462 
03463 void init_vm86_fake_io() {
03464         Bitu phys = (vm86_fake_io_seg << 4) + vm86_fake_io_off;
03465         Bitu wo = 0;
03466 
03467         if (vm86_fake_io_offs[0] != 0)
03468                 return;
03469 
03470         /* read */
03471         vm86_fake_io_offs[0] = vm86_fake_io_off + wo;
03472         phys_writeb(phys+wo+0x00,(Bit8u)0xEC);  /* IN AL,DX */
03473         phys_writeb(phys+wo+0x01,(Bit8u)0xCB);  /* RETF */
03474         wo += 2;
03475 
03476         vm86_fake_io_offs[1] = vm86_fake_io_off + wo;
03477         phys_writeb(phys+wo+0x00,(Bit8u)0xED);  /* IN AX,DX */
03478         phys_writeb(phys+wo+0x01,(Bit8u)0xCB);  /* RETF */
03479         wo += 2;
03480 
03481         vm86_fake_io_offs[2] = vm86_fake_io_off + wo;
03482         phys_writeb(phys+wo+0x00,(Bit8u)0x66);  /* IN EAX,DX */
03483         phys_writeb(phys+wo+0x01,(Bit8u)0xED);
03484         phys_writeb(phys+wo+0x02,(Bit8u)0xCB);  /* RETF */
03485         wo += 3;
03486 
03487         /* write */
03488         vm86_fake_io_offs[3] = vm86_fake_io_off + wo;
03489         phys_writeb(phys+wo+0x00,(Bit8u)0xEE);  /* OUT DX,AL */
03490         phys_writeb(phys+wo+0x01,(Bit8u)0xCB);  /* RETF */
03491         wo += 2;
03492 
03493         vm86_fake_io_offs[4] = vm86_fake_io_off + wo;
03494         phys_writeb(phys+wo+0x00,(Bit8u)0xEF);  /* OUT DX,AX */
03495         phys_writeb(phys+wo+0x01,(Bit8u)0xCB);  /* RETF */
03496         wo += 2;
03497 
03498         vm86_fake_io_offs[5] = vm86_fake_io_off + wo;
03499         phys_writeb(phys+wo+0x00,(Bit8u)0x66);  /* OUT DX,EAX */
03500         phys_writeb(phys+wo+0x01,(Bit8u)0xEF);
03501         phys_writeb(phys+wo+0x02,(Bit8u)0xCB);  /* RETF */
03502         wo += 3;
03503 }
03504 
03505 Bitu CPU_ForceV86FakeIO_In(Bitu port,Bitu len) {
03506         Bitu old_ax,old_dx,ret;
03507 
03508         /* save EAX:EDX and setup DX for IN instruction */
03509         old_ax = reg_eax;
03510         old_dx = reg_edx;
03511 
03512         reg_edx = port;
03513 
03514         /* make the CPU execute that instruction */
03515         CALLBACK_RunRealFar(vm86_fake_io_seg,vm86_fake_io_offs[(len==4?2:(len-1))+0]);
03516 
03517         /* take whatever the CPU or OS v86 trap left in EAX and return it */
03518         ret = reg_eax;
03519         if (len == 1) ret &= 0xFF;
03520         else if (len == 2) ret &= 0xFFFF;
03521 
03522         /* then restore EAX:EDX */
03523         reg_eax = old_ax;
03524         reg_edx = old_dx;
03525 
03526         return ret;
03527 }
03528 
03529 void CPU_ForceV86FakeIO_Out(Bitu port,Bitu val,Bitu len) {
03530         Bitu old_ax,old_dx;
03531 
03532         /* save EAX:EDX and setup DX/AX for OUT instruction */
03533         old_ax = reg_eax;
03534         old_dx = reg_edx;
03535 
03536         reg_edx = port;
03537         reg_eax = val;
03538 
03539         /* make the CPU execute that instruction */
03540         CALLBACK_RunRealFar(vm86_fake_io_seg,vm86_fake_io_offs[(len==4?2:(len-1))+3]);
03541 
03542         /* then restore EAX:EDX */
03543         reg_eax = old_ax;
03544         reg_edx = old_dx;
03545 }
03546 
03547 /* pentium machine-specific registers */
03548 bool CPU_RDMSR() {
03549         if (!enable_msr) return false;
03550 
03551         switch (reg_ecx) {
03552                 default:
03553                         LOG(LOG_CPU,LOG_NORMAL)("RDMSR: Unknown register 0x%08lx",(unsigned long)reg_ecx);
03554                         break;
03555         }
03556 
03557         if (ignore_undefined_msr) {
03558                 /* wing it and hope nobody notices */
03559                 reg_edx = reg_eax = 0;
03560                 return true;
03561         }
03562 
03563         return false; /* unknown reg, signal illegal opcode */
03564 }
03565 
03566 bool CPU_WRMSR() {
03567         if (!enable_msr) return false;
03568 
03569         switch (reg_ecx) {
03570                 default:
03571                         LOG(LOG_CPU,LOG_NORMAL)("WRMSR: Unknown register 0x%08lx (write 0x%08lx:0x%08lx)",(unsigned long)reg_ecx,(unsigned long)reg_edx,(unsigned long)reg_eax);
03572                         break;
03573         }
03574 
03575         if (ignore_undefined_msr) return true; /* ignore */
03576         return false; /* unknown reg, signal illegal opcode */
03577 }
03578 
03579 /* NTS: Hopefully by implementing this Windows ME can stop randomly crashing when cputype=pentium */
03580 void CPU_CMPXCHG8B(PhysPt eaa) {
03581     uint32_t hi,lo;
03582 
03583     /* NTS: We assume that, if reading doesn't cause a page fault, writing won't either */
03584     hi = (uint32_t)mem_readd(eaa+(PhysPt)4);
03585     lo = (uint32_t)mem_readd(eaa);
03586 
03587     LOG_MSG("Experimental CMPXCHG8B implementation executed. EDX:EAX=0x%08lx%08lx ECX:EBX=0x%08lx%08lx EA=0x%08lx MEM64=0x%08lx%08lx",
03588         (unsigned long)reg_edx,
03589         (unsigned long)reg_eax,
03590         (unsigned long)reg_ecx,
03591         (unsigned long)reg_ebx,
03592         (unsigned long)eaa,
03593         (unsigned long)hi,
03594         (unsigned long)lo);
03595 
03596     /* Compare EDX:EAX with 64-bit DWORD at memaddr 'eaa'.
03597      * if they match, ZF=1 and write ECX:EBX to memaddr 'eaa'.
03598      * else, ZF=0 and load memaddr 'eaa' into EDX:EAX */
03599     if (reg_edx == hi && reg_eax == lo) {
03600         mem_writed(eaa+(PhysPt)4,reg_ecx);
03601         mem_writed(eaa,          reg_ebx);
03602                 SETFLAGBIT(ZF,true);
03603     }
03604     else {
03605                 SETFLAGBIT(ZF,false);
03606         reg_edx = hi;
03607         reg_eax = lo;
03608     }
03609 }
03610