Logo Search packages:      
Sourcecode: dosbox version File versions  Download package

cpu.cpp

/*
 *  Copyright (C) 2002-2004  The DOSBox Team
 *
 *  This program is free software; you can redistribute it and/or modify
 *  it under the terms of the GNU General Public License as published by
 *  the Free Software Foundation; either version 2 of the License, or
 *  (at your option) any later version.
 *
 *  This program is distributed in the hope that it will be useful,
 *  but WITHOUT ANY WARRANTY; without even the implied warranty of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 *  GNU General Public License for more details.
 *
 *  You should have received a copy of the GNU General Public License
 *  along with this program; if not, write to the Free Software
 *  Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
 */

/* $Id: cpu.cpp,v 1.61 2004/08/23 12:17:29 harekiet Exp $ */

#include <assert.h>
#include "dosbox.h"
#include "cpu.h"
#include "memory.h"
#include "debug.h"
#include "mapper.h"
#include "setup.h"
#include "paging.h"
#include "support.h"

Bitu DEBUG_EnableDebugger(void);

#if 1
#undef LOG
#define LOG(X,Y)
#endif

CPU_Regs cpu_regs;
CPUBlock cpu;
Segments Segs;

Bits CPU_Cycles = 0;
Bits CPU_CycleLeft = 0;
Bits CPU_CycleMax = 2500;
Bits CPU_CycleUp = 0;
Bits CPU_CycleDown = 0;
CPU_Decoder * cpudecoder;

void CPU_Core_Full_Init(void);
void CPU_Core_Normal_Init(void);
void CPU_Core_Simple_Init(void);
void CPU_Core_Dyn_X86_Init(void);

void CPU_Push16(Bitu value) {
      reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp-2)&cpu.stack.mask);
      mem_writew(SegPhys(ss) + (reg_esp & cpu.stack.mask) ,value);
}

void CPU_Push32(Bitu value) {
      reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp-4)&cpu.stack.mask);
      mem_writed(SegPhys(ss) + (reg_esp & cpu.stack.mask) ,value);
}

Bitu CPU_Pop16(void) {
      Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
      reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+2)&cpu.stack.mask);
      return val;
}

Bitu CPU_Pop32(void) {
      Bitu val=mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask));
      reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+4)&cpu.stack.mask);
      return val;
}

PhysPt SelBase(Bitu sel) {
      if (cpu.cr0 & CR0_PROTECTION) {
            Descriptor desc;
            cpu.gdt.GetDescriptor(sel,desc);
            return desc.GetBase();
      } else {
            return sel<<4;
      }
}

void CPU_SetFlags(Bitu word,Bitu mask) {
      reg_flags=(reg_flags & ~mask)|(word & mask)|2|FLAG_ID;
      cpu.direction=1-((reg_flags & FLAG_DF) >> 9);
}

bool CPU_PrepareException(Bitu which,Bitu error) {
      cpu.exception.which=which;
      cpu.exception.error=error;
      return true;
}

bool CPU_CLI(void) {
      if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
            return CPU_PrepareException(13,0);
      } else {
            SETFLAGBIT(IF,false);
            return false;
      }
}

bool CPU_STI(void) {
      if (cpu.pmode && ((!GETFLAG(VM) && (GETFLAG_IOPL<cpu.cpl)) || (GETFLAG(VM) && (GETFLAG_IOPL<3)))) {
            return CPU_PrepareException(13,0);
      } else {
            SETFLAGBIT(IF,true);
            return false;
      }
}

bool CPU_POPF(Bitu use32) {
      if ((reg_flags & FLAG_VM) && (GETFLAG(IOPL)!=FLAG_IOPL))
            return CPU_PrepareException(13,0);
      if (use32) 
            CPU_SetFlagsd(CPU_Pop32());
      else CPU_SetFlagsw(CPU_Pop16());
      return false;
}

bool CPU_PUSHF(Bitu use32) {
      if ((reg_flags & FLAG_VM) && (GETFLAG(IOPL)!=FLAG_IOPL))
            return CPU_PrepareException(13,0);
      if (use32) 
            CPU_Push32(reg_flags);
      else CPU_Push16(reg_flags);
      return false;
}

class TaskStateSegment {
public:
      TaskStateSegment() {
            valid=false;
      }
      bool IsValid(void) {
            return valid;
      }
      Bitu Get_back(void) {
            return mem_readw(base);
      }
      void SaveSelector(void) {
            cpu.gdt.SetDescriptor(selector,desc);
      }
      void Get_SSx_ESPx(Bitu level,Bitu & _ss,Bitu & _esp) {
            if (is386) {
                  PhysPt where=base+offsetof(TSS_32,esp0)+level*8;
                  _esp=mem_readd(where);
                  _ss=mem_readw(where+4);
            } else {
                  PhysPt where=base+offsetof(TSS_16,sp0)+level*4;
                  _esp=mem_readw(where);
                  _ss=mem_readw(where+2);
            }
      }
      bool SetSelector(Bitu new_sel) {
            valid=false;
            selector=new_sel;
            if (!cpu.gdt.GetDescriptor(selector,desc)) return false;
            switch (desc.Type()) {
                  case DESC_286_TSS_A:          case DESC_286_TSS_B:
                  case DESC_386_TSS_A:          case DESC_386_TSS_B:
                        break;
                  default:
                        valid=false;
                        return false;
            }
            valid=true;
            base=desc.GetBase();
            limit=desc.GetLimit();
            is386=desc.Is386();
            return true;
      }
      TSS_Descriptor desc;
      Bitu selector;
      PhysPt base;
      Bitu limit;
      Bitu is386;
      bool valid;
};

TaskStateSegment cpu_tss;

enum TSwitchType {
      TSwitch_JMP,TSwitch_CALL_INT,TSwitch_IRET
};

bool CPU_SwitchTask(Bitu new_tss_selector,TSwitchType tstype,Bitu old_eip) {
      TaskStateSegment new_tss;
      if (!new_tss.SetSelector(new_tss_selector)) 
            E_Exit("Illegal TSS for switch");
      /* Save current context in current TSS */
      /* Check if we need to clear busy bit of old TASK */
      if (tstype==TSwitch_JMP || tstype==TSwitch_IRET) {
            cpu_tss.desc.SetBusy(false);
            cpu_tss.SaveSelector();
      }
      Bitu new_cr3=0;
      Bitu new_es,new_cs,new_ss,new_ds,new_fs,new_gs;
      Bitu new_ldt;
      if (cpu_tss.is386) {
            mem_writed(cpu_tss.base+offsetof(TSS_32,eflags),reg_flags);
            mem_writed(cpu_tss.base+offsetof(TSS_32,eip),old_eip);

            mem_writed(cpu_tss.base+offsetof(TSS_32,eax),reg_eax);
            mem_writed(cpu_tss.base+offsetof(TSS_32,ecx),reg_ecx);
            mem_writed(cpu_tss.base+offsetof(TSS_32,edx),reg_edx);
            mem_writed(cpu_tss.base+offsetof(TSS_32,ebx),reg_ebx);
            mem_writed(cpu_tss.base+offsetof(TSS_32,esp),reg_esp);
            mem_writed(cpu_tss.base+offsetof(TSS_32,ebp),reg_ebp);
            mem_writed(cpu_tss.base+offsetof(TSS_32,esi),reg_esi);
            mem_writed(cpu_tss.base+offsetof(TSS_32,edi),reg_edi);

            mem_writed(cpu_tss.base+offsetof(TSS_32,es),SegValue(es));
            mem_writed(cpu_tss.base+offsetof(TSS_32,cs),SegValue(cs));
            mem_writed(cpu_tss.base+offsetof(TSS_32,ss),SegValue(ss));
            mem_writed(cpu_tss.base+offsetof(TSS_32,ds),SegValue(ds));
            mem_writed(cpu_tss.base+offsetof(TSS_32,fs),SegValue(fs));
            mem_writed(cpu_tss.base+offsetof(TSS_32,gs),SegValue(gs));
      } else {
            E_Exit("286 task switch");
      }
      /* Load new context from new TSS */
      if (new_tss.is386) {
            new_cr3=mem_readd(new_tss.base+offsetof(TSS_32,cr3));
            reg_eip=mem_readd(new_tss.base+offsetof(TSS_32,eip));
            CPU_SetFlags(mem_readd(new_tss.base+offsetof(TSS_32,eflags)),FMASK_ALL | FLAG_VM);
            reg_eax=mem_readd(new_tss.base+offsetof(TSS_32,eax));
            reg_ecx=mem_readd(new_tss.base+offsetof(TSS_32,ecx));
            reg_edx=mem_readd(new_tss.base+offsetof(TSS_32,edx));
            reg_ebx=mem_readd(new_tss.base+offsetof(TSS_32,ebx));
            reg_esp=mem_readd(new_tss.base+offsetof(TSS_32,esp));
            reg_ebp=mem_readd(new_tss.base+offsetof(TSS_32,ebp));
            reg_edi=mem_readd(new_tss.base+offsetof(TSS_32,edi));
            reg_esi=mem_readd(new_tss.base+offsetof(TSS_32,esi));

            new_es=mem_readw(new_tss.base+offsetof(TSS_32,es));
            new_cs=mem_readw(new_tss.base+offsetof(TSS_32,cs));
            new_ss=mem_readw(new_tss.base+offsetof(TSS_32,ss));
            new_ds=mem_readw(new_tss.base+offsetof(TSS_32,ds));
            new_fs=mem_readw(new_tss.base+offsetof(TSS_32,fs));
            new_gs=mem_readw(new_tss.base+offsetof(TSS_32,gs));
            new_ldt=mem_readw(new_tss.base+offsetof(TSS_32,ldt));
      } else {
            E_Exit("286 task switch");
      }
      /* Setup a back link to the old TSS in new TSS */
      if (tstype==TSwitch_CALL_INT) {
            if (new_tss.is386) {
                  mem_writed(new_tss.base+offsetof(TSS_32,back),cpu_tss.selector);
            } else {
                  mem_writew(new_tss.base+offsetof(TSS_16,back),cpu_tss.selector);
            }
            /* And make the new task's eflag have the nested task bit */
            reg_flags|=FLAG_NT;
      }
      /* Set the busy bit in the new task */
      if (tstype==TSwitch_JMP || tstype==TSwitch_IRET) {
            new_tss.desc.SetBusy(true);
            new_tss.SaveSelector();
      }
      /* Setup the new cr3 */
      PAGING_SetDirBase(new_cr3);
      /* Load the new selectors */
      if (reg_flags & FLAG_VM) {
//          LOG_MSG("Entering v86 task");
            SegSet16(cs,new_cs);
            cpu.code.big=false;
            cpu.cpl=3;              //We don't have segment caches so this will do
      } else {
            //DEBUG_EnableDebugger();
            /* Protected mode task */
            CPU_LLDT(new_ldt);
            /* Load the new CS*/
            Descriptor cs_desc;
            cpu.cpl=new_cs & 3;
            cpu.gdt.GetDescriptor(new_cs,cs_desc);
            if (!cs_desc.saved.seg.p) {
                  E_Exit("Task switch with non present code-segment");
                  return false;
            }
            switch (cs_desc.Type()) {
            case DESC_CODE_N_NC_A:        case DESC_CODE_N_NC_NA:
            case DESC_CODE_R_NC_A:        case DESC_CODE_R_NC_NA:
                  if (cpu.cpl != cs_desc.DPL()) E_Exit("Task CS RPL != DPL");
                  goto doconforming;
            case DESC_CODE_N_C_A:         case DESC_CODE_N_C_NA:
            case DESC_CODE_R_C_A:         case DESC_CODE_R_C_NA:
doconforming:
                  Segs.phys[cs]=cs_desc.GetBase();
                  cpu.code.big=cs_desc.Big()>0;
                  Segs.val[cs]=new_cs;
                  break;
            default:
                  E_Exit("Task switch CS Type %d",cs_desc.Type());
            }
      }
      CPU_SetSegGeneral(es,new_es);
      CPU_SetSegGeneral(ss,new_ss);
      CPU_SetSegGeneral(ds,new_ds);
      CPU_SetSegGeneral(fs,new_fs);
      CPU_SetSegGeneral(gs,new_gs);
      CPU_LTR(new_tss_selector);
//    LOG_MSG("Task CPL %X CS:%X IP:%X SS:%X SP:%X eflags %x",cpu.cpl,SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags);
      return true;
}

bool CPU_IO_Exception(Bitu port,Bitu size) {
      if (cpu.pmode && ((GETFLAG_IOPL<cpu.cpl) || GETFLAG(VM))) {
            if (!cpu_tss.is386) goto doexception;
            PhysPt where=cpu_tss.base+0x66;
            Bitu ofs=mem_readw(where);
            if (ofs>cpu_tss.limit) goto doexception;
            where=cpu_tss.base+ofs+(port/8);
            Bitu map=mem_readw(where);
            Bitu mask=(0xffff>>(16-size)) << (port&7);
            if (map & mask) goto doexception;
      }
      return false;
doexception:
      LOG_MSG("Exception");
      return CPU_PrepareException(13,0);
}

void CPU_Exception(Bitu which,Bitu error ) {
//    LOG_MSG("Exception %d error %x",which,error);
      cpu.exception.error=error;
      CPU_Interrupt(which,CPU_INT_EXCEPTION | ((which>=8) ? CPU_INT_HAS_ERROR : 0),reg_eip);
}

Bit8u lastint;
void CPU_Interrupt(Bitu num,Bitu type,Bitu oldeip) {
      lastint=num;
#if C_DEBUG
      switch (num) {
      case 0xcd:
#if C_HEAVY_DEBUG
            LOG(LOG_CPU,LOG_ERROR)("Call to interrupt 0xCD this is BAD");
            DEBUG_HeavyWriteLogInstruction();
#endif
            E_Exit("Call to interrupt 0xCD this is BAD");
      case 0x03:
            if (DEBUG_Breakpoint()) {
                  CPU_Cycles=0;
                  return;
            }
      };
#endif
      if (!cpu.pmode) {
                  /* Save everything on a 16-bit stack */
            CPU_Push16(reg_flags & 0xffff);
            CPU_Push16(SegValue(cs));
            CPU_Push16(oldeip);
            SETFLAGBIT(IF,false);
            SETFLAGBIT(TF,false);
            /* Get the new CS:IP from vector table */
            PhysPt base=cpu.idt.GetBase();
            reg_eip=mem_readw(base+(num << 2));
            Segs.val[cs]=mem_readw(base+(num << 2)+2);
            Segs.phys[cs]=Segs.val[cs]<<4;
            cpu.code.big=false;
            return;
      } else {
            /* Protected Mode Interrupt */
//          if (type&CPU_INT_SOFTWARE && cpu.v86) goto realmode_interrupt;
//          DEBUG_EnableDebugger();
//          LOG_MSG("interrupt start CPL %d v86 %d",cpu.cpl,cpu.v86);
            if ((reg_flags & FLAG_VM) && (type&CPU_INT_SOFTWARE)) {
//                LOG_MSG("Software int in v86, AH %X IOPL %x",reg_ah,(reg_flags & FLAG_IOPL) >>12);
                  if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
                        CPU_Exception(13,0);
                        return;
                  }
            } 
            Descriptor gate;
//TODO Check for software interrupt and check gate's dpl<cpl
            cpu.idt.GetDescriptor(num<<3,gate);
            if (type&CPU_INT_SOFTWARE && gate.DPL()<cpu.cpl) {
                  CPU_Exception(13,num*8+2);
                  return;
            }
            switch (gate.Type()) {
            case DESC_286_INT_GATE:       case DESC_386_INT_GATE:
            case DESC_286_TRAP_GATE:      case DESC_386_TRAP_GATE:
                  {
                        Descriptor cs_desc;
                        Bitu gate_sel=gate.GetSelector();
                        Bitu gate_off=gate.GetOffset();
                        cpu.gdt.GetDescriptor(gate_sel,cs_desc);
                        Bitu cs_dpl=cs_desc.DPL();
                        if (cs_dpl>cpu.cpl)     E_Exit("Interrupt to higher privilege");
                        switch (cs_desc.Type()) {
                        case DESC_CODE_N_NC_A:  case DESC_CODE_N_NC_NA:
                        case DESC_CODE_R_NC_A:  case DESC_CODE_R_NC_NA:
                              if (cs_dpl<cpu.cpl) {
                                    /* Prepare for gate to inner level */
                                    Bitu n_ss,n_esp;
                                    Bitu o_ss,o_esp;
                                    o_ss=SegValue(ss);
                                    o_esp=reg_esp;
                                    cpu_tss.Get_SSx_ESPx(cs_dpl,n_ss,n_esp);
                                    Descriptor n_ss_desc;
                                    cpu.gdt.GetDescriptor(n_ss,n_ss_desc);
                                    Segs.phys[ss]=n_ss_desc.GetBase();
                                    Segs.val[ss]=n_ss;
                                    if (n_ss_desc.Big()) {
                                          cpu.stack.big=true;
                                          cpu.stack.mask=0xffffffff;
                                          reg_esp=n_esp;
                                    } else {
                                          cpu.stack.big=false;
                                          cpu.stack.mask=0xffff;
                                          reg_sp=n_esp;
                                    }
                                    if (gate.Type() & 0x8) {      /* 32-bit Gate */
                                          if (reg_flags & FLAG_VM) {
                                                CPU_Push32(SegValue(gs));SegSet16(gs,0x0);
                                                CPU_Push32(SegValue(fs));SegSet16(fs,0x0);
                                                CPU_Push32(SegValue(ds));SegSet16(ds,0x0);
                                                CPU_Push32(SegValue(es));SegSet16(es,0x0);
                                          }
                                          CPU_Push32(o_ss);
                                          CPU_Push32(o_esp);
                                    } else {                            /* 16-bit Gate */
                                          if (reg_flags & FLAG_VM) E_Exit("V86 to 16-bit gate");
                                          CPU_Push16(o_ss);
                                          CPU_Push16(o_esp);
                                    }
//                                  LOG_MSG("INT:Gate to inner level SS:%X SP:%X",n_ss,n_esp);
                                    cpu.cpl=cs_dpl;
                                    goto do_interrupt;
                              } 
                        case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
                        case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
                        /* Prepare stack for gate to same priviledge */
                              if (reg_flags & FLAG_VM) 
                                    E_Exit("V86 interrupt doesn't change to pl0");

do_interrupt:
                              if (gate.Type() & 0x8) {      /* 32-bit Gate */
                                    CPU_Push32(reg_flags);
                                    CPU_Push32(SegValue(cs));
                                    CPU_Push32(oldeip);
                                    if (type & CPU_INT_HAS_ERROR) CPU_Push32(cpu.exception.error);
                              } else {                            /* 16-bit gate */
                                    CPU_Push16(reg_flags & 0xffff);
                                    CPU_Push16(SegValue(cs));
                                    CPU_Push16(oldeip);
                                    if (type & CPU_INT_HAS_ERROR) CPU_Push16(cpu.exception.error);
                              }
                              break;            
                        default:
                              E_Exit("INT:Gate Selector points to illegal descriptor with type %x",cs_desc.Type());
                        }
                        if (!(gate.Type()&1))
                              SETFLAGBIT(IF,false);
                        SETFLAGBIT(TF,false);
                        SETFLAGBIT(NT,false);
                        SETFLAGBIT(VM,false);
                        Segs.val[cs]=(gate_sel&0xfffc) | cpu.cpl;
                        Segs.phys[cs]=cs_desc.GetBase();
                        cpu.code.big=cs_desc.Big()>0;
                        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");
                        reg_eip=gate_off;
                        return;
                  }
            case DESC_TASK_GATE:
                  CPU_SwitchTask(gate.GetSelector(),TSwitch_CALL_INT,oldeip);
                  if (type & CPU_INT_HAS_ERROR) {
                        //TODO Be sure about this, seems somewhat unclear
                        if (cpu_tss.is386) CPU_Push32(cpu.exception.error);
                        else CPU_Push16(cpu.exception.error);
                  }
                  return;
            default:
                  E_Exit("Illegal descriptor type %X for int %X",gate.Type(),num);
            }
      }
      assert(1);
      return ; // make compiler happy
}

void CPU_IRET(bool use32,Bitu oldeip) {
      if (!cpu.pmode) {                         /* RealMode IRET */
realmode_iret:
            if (use32) {
                  reg_eip=CPU_Pop32();
                  SegSet16(cs,CPU_Pop32());
                  CPU_SetFlagsd(CPU_Pop32());
            } else {
                  reg_eip=CPU_Pop16();
                  SegSet16(cs,CPU_Pop16());
                  CPU_SetFlagsw(CPU_Pop16());
            }
            cpu.code.big=false;
            return;
      } else {    /* Protected mode IRET */
            if (reg_flags & FLAG_VM) {
                  if ((reg_flags & FLAG_IOPL)!=FLAG_IOPL) {
                        CPU_Exception(13,0);
                        return;
                  } else goto realmode_iret;
            }
//          DEBUG_EnableDebugger();
//          LOG_MSG("IRET start CPL %d v86 %d",cpu.cpl,cpu.v86);
            /* Check if this is task IRET */    
            if (GETFLAG(NT)) {
                  if (GETFLAG(VM)) E_Exit("Pmode IRET with VM bit set");
                  if (!cpu_tss.IsValid()) E_Exit("TASK Iret without valid TSS");
                  Bitu back_link=cpu_tss.Get_back();
                  CPU_SwitchTask(back_link,TSwitch_IRET,oldeip);
                  return;
            }
            Bitu n_cs_sel,n_eip,n_flags;
            if (use32) {
                  n_eip=CPU_Pop32();
                  n_cs_sel=CPU_Pop32() & 0xffff;
                  n_flags=CPU_Pop32();
                  if (n_flags & FLAG_VM) {
                        cpu.cpl=3;
                        CPU_SetFlags(n_flags,FMASK_ALL | FLAG_VM);
                        Bitu n_ss,n_esp,n_es,n_ds,n_fs,n_gs;
                        n_esp=CPU_Pop32();
                        n_ss=CPU_Pop32() & 0xffff;

                        n_es=CPU_Pop32() & 0xffff;
                        n_ds=CPU_Pop32() & 0xffff;
                        n_fs=CPU_Pop32() & 0xffff;
                        n_gs=CPU_Pop32() & 0xffff;
                        CPU_SetSegGeneral(ss,n_ss);
                        CPU_SetSegGeneral(es,n_es);
                        CPU_SetSegGeneral(ds,n_ds);
                        CPU_SetSegGeneral(fs,n_fs);
                        CPU_SetSegGeneral(gs,n_gs);
                        reg_eip=n_eip & 0xffff;
                        reg_esp=n_esp;
                        cpu.code.big=false;
                        SegSet16(cs,n_cs_sel);
                        LOG(LOG_CPU,LOG_NORMAL)("IRET:Back to V86: CS:%X IP %X SS:%X SP %X FLAGS:%X",SegValue(cs),reg_eip,SegValue(ss),reg_esp,reg_flags);  
                        return;
                  }
            } else {
                  n_eip=CPU_Pop16();
                  n_cs_sel=CPU_Pop16();
                  n_flags=(reg_flags & 0xffff0000) | CPU_Pop16();
                  if (n_flags & FLAG_VM) E_Exit("VM Flag in 16-bit iret");
            }
            Bitu n_cs_rpl=n_cs_sel & 3;
            Descriptor n_cs_desc;
            cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc);
            if (n_cs_rpl<cpu.cpl) 
                  E_Exit("IRET to lower privilege");
            if (n_cs_rpl==cpu.cpl) {      
                  /* Return to same level */
                  switch (n_cs_desc.Type()) {
                  case DESC_CODE_N_NC_A:  case DESC_CODE_N_NC_NA:
                  case DESC_CODE_R_NC_A:  case DESC_CODE_R_NC_NA:
                        if (!(cpu.cpl==n_cs_desc.DPL())) E_Exit("IRET:Same Level:NC:DPL!=CPL");
                        break;
                  case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
                  case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
                        if (!(n_cs_desc.DPL()>=cpu.cpl)) E_Exit("IRET:Same level:C:DPL<CPL");
                        break;
                  default:
                        E_Exit("IRET:Same level:Illegal descriptor type %X",n_cs_desc.Type());
                  }
                  Segs.phys[cs]=n_cs_desc.GetBase();
                  cpu.code.big=n_cs_desc.Big()>0;
                  Segs.val[cs]=n_cs_sel;
                  reg_eip=n_eip;

                  CPU_SetFlagsd(n_flags);
                  LOG(LOG_CPU,LOG_NORMAL)("IRET:Same level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
            } else {
                  /* Return to outer level */
                  switch (n_cs_desc.Type()) {
                  case DESC_CODE_N_NC_A:  case DESC_CODE_N_NC_NA:
                  case DESC_CODE_R_NC_A:  case DESC_CODE_R_NC_NA:
                        if (n_cs_desc.DPL()!=n_cs_rpl) E_Exit("IRET:Outer level:NC:CS RPL != CS DPL");
                        break;
                  case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
                  case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
                        if (n_cs_desc.DPL()<=cpu.cpl) E_Exit("IRET:Outer level:C:DPL <= CPL");
                        break;
                  default:
                        E_Exit("IRET:Outer level:Illegal descriptor type %X",n_cs_desc.Type());
                  }
                  Segs.phys[cs]=n_cs_desc.GetBase();
                  cpu.code.big=n_cs_desc.Big()>0;
                  Segs.val[cs]=n_cs_sel;

                  CPU_SetFlagsd(n_flags);

                  cpu.cpl=n_cs_rpl;
                  reg_eip=n_eip;
                  Bitu n_ss,n_esp;
                  if (use32) {
                        n_esp=CPU_Pop32();
                        n_ss=CPU_Pop32() & 0xffff;
                  } else {
                        n_esp=CPU_Pop16();
                        n_ss=CPU_Pop16();
                  }
                  CPU_SetSegGeneral(ss,n_ss);
                  if (cpu.stack.big) {
                        reg_esp=n_esp;
                  } else {
                        reg_sp=n_esp;
                  }

                  //TODO Maybe validate other segments, but why would anyone use them?
                  LOG(LOG_CPU,LOG_NORMAL)("IRET:Outer level:%X:%X big %d",n_cs_sel,n_eip,cpu.code.big);
            }
            return;
      }
}

void CPU_JMP(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
      if (!cpu.pmode || (reg_flags & FLAG_VM)) {
            if (!use32) {
                  reg_eip=offset&0xffff;
            } else {
                  reg_eip=offset;
            }
            SegSet16(cs,selector);
            cpu.code.big=false;
            return;
      } else {
            Bitu rpl=selector & 3;
            Descriptor desc;
            cpu.gdt.GetDescriptor(selector,desc);
            if (!desc.saved.seg.p) {
                  CPU_Exception(0x0B,selector & 0xfffc);
                  return;
            }
            switch (desc.Type()) {
            case DESC_CODE_N_NC_A:        case DESC_CODE_N_NC_NA:
            case DESC_CODE_R_NC_A:        case DESC_CODE_R_NC_NA:
                  if (rpl>cpu.cpl) E_Exit("JMP:NC:RPL>CPL");
                  if (cpu.cpl!=desc.DPL()) E_Exit("JMP:NC:RPL != DPL");
                  LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:NC to %X:%X big %d",selector,offset,desc.Big());
                  goto CODE_jmp;
            case DESC_CODE_N_C_A:         case DESC_CODE_N_C_NA:
            case DESC_CODE_R_C_A:         case DESC_CODE_R_C_NA:
                  LOG(LOG_CPU,LOG_NORMAL)("JMP:Code:C to %X:%X big %d",selector,offset,desc.Big());
CODE_jmp:
                  /* Normal jump to another selector:offset */
                  Segs.phys[cs]=desc.GetBase();
                  cpu.code.big=desc.Big()>0;
                  Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
                  reg_eip=offset;
                  return;
            case DESC_386_TSS_A:
                  if (desc.DPL()<cpu.cpl) E_Exit("JMP:TSS:dpl<cpl");
                  if (desc.DPL()<rpl) E_Exit("JMP:TSS:dpl<rpl");
                  LOG(LOG_CPU,LOG_NORMAL)("JMP:TSS to %X",selector);
                  CPU_SwitchTask(selector,TSwitch_JMP,oldeip);
                  break;
            default:
                  E_Exit("JMP Illegal descriptor type %X",desc.Type());
            }
      }
      assert(1);
}



void CPU_CALL(bool use32,Bitu selector,Bitu offset,Bitu oldeip) {
      if (!cpu.pmode || (reg_flags & FLAG_VM)) {
            if (!use32) {
                  CPU_Push16(SegValue(cs));
                  CPU_Push16(oldeip);
                  reg_eip=offset&0xffff;
            } else {
                  CPU_Push32(SegValue(cs));
                  CPU_Push32(oldeip);
                  reg_eip=offset;
            }
            cpu.code.big=false;
            SegSet16(cs,selector);
            return;
      } else {
            Descriptor call;
            Bitu rpl=selector & 3;
            cpu.gdt.GetDescriptor(selector,call);
            if (!call.saved.seg.p) {
                  CPU_Exception(0x0B,selector & 0xfffc);
                  return;
            }
            /* Check for type of far call */
            switch (call.Type()) {
            case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
            case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
                  if (rpl>cpu.cpl) E_Exit("CALL:CODE:NC:RPL>CPL");
                  if (call.DPL()!=cpu.cpl) E_Exit("CALL:CODE:NC:DPL!=CPL");
                  LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:NC to %X:%X",selector,offset);
                  goto call_code;   
            case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
            case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
                  if (call.DPL()>cpu.cpl) E_Exit("CALL:CODE:C:DPL>CPL");
                  LOG(LOG_CPU,LOG_NORMAL)("CALL:CODE:C to %X:%X",selector,offset);
call_code:
                  if (!use32) {
                        CPU_Push16(SegValue(cs));
                        CPU_Push16(oldeip);
                        reg_eip=offset & 0xffff;
                  } else {
                        CPU_Push32(SegValue(cs));
                        CPU_Push32(oldeip);
                        reg_eip=offset;
                  }
                  Segs.phys[cs]=call.GetBase();
                  cpu.code.big=call.Big()>0;
                  Segs.val[cs]=(selector & 0xfffc) | cpu.cpl;
                  return;
            case DESC_386_CALL_GATE: 
            case DESC_286_CALL_GATE:
                  {
                        if (call.DPL()<cpu.cpl) E_Exit("Call:Gate:Gate DPL<CPL");
                        if (call.DPL()<rpl)           E_Exit("Call:Gate:Gate DPL<RPL");               
                        Descriptor n_cs_desc;
                        Bitu n_cs_sel=call.GetSelector();
                        if (!cpu.gdt.GetDescriptor(n_cs_sel,n_cs_desc)) E_Exit("Call:Gate:Invalid CS selector.");
                        if (!n_cs_desc.saved.seg.p) {
                              CPU_Exception(0x0B,selector & 0xfffc);
                              return;
                        }
                        Bitu n_cs_dpl     = n_cs_desc.DPL();
                        Bitu n_cs_rpl     = n_cs_sel & 3;
                        Bitu n_eip        = call.GetOffset();
                        switch (n_cs_desc.Type()) {
                        case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
                        case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
                              /* Check if we goto innter priviledge */
                              if (n_cs_dpl < cpu.cpl) {
                                    /* Get new SS:ESP out of TSS */
                                    Bitu n_ss_sel,n_esp;
                                    Descriptor n_ss_desc;
                                    cpu_tss.Get_SSx_ESPx(n_cs_dpl,n_ss_sel,n_esp);
                                    if (!cpu.gdt.GetDescriptor(n_ss_sel,n_ss_desc)) E_Exit("Call:Gate:Invalid SS selector.");
                                    /* New CPL is new SS DPL */
                                    cpu.cpl                 = n_ss_desc.DPL();
                                    /* Load the new SS:ESP and save data on it */
                                    Bitu o_esp        = reg_esp;
                                    Bitu o_ss         = SegValue(ss);
                                    PhysPt o_stack  = SegPhys(ss)+(reg_esp & cpu.stack.mask);
                              
                                    CPU_SetSegGeneral(ss,n_ss_sel);
                                    if (cpu.stack.big)      reg_esp=n_esp;
                                    else reg_sp=n_esp;
                                    if (call.Type()==DESC_386_CALL_GATE) {
                                          CPU_Push32(o_ss);       //save old stack
                                          CPU_Push32(o_esp);
                                          if (call.saved.gate.paramcount&31)
                                                for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--) 
                                                      CPU_Push32(mem_readd(o_stack+i*4));
                                          CPU_Push32(SegValue(cs));
                                          CPU_Push32(oldeip);
                                    } else {
                                          CPU_Push16(o_ss);       //save old stack
                                          CPU_Push16(o_esp);
                                          if (call.saved.gate.paramcount&31)
                                                for (Bits i=(call.saved.gate.paramcount&31)-1;i>=0;i--)
                                                      CPU_Push16(mem_readw(o_stack+i*2));
                                          CPU_Push16(SegValue(cs));
                                          CPU_Push16(oldeip);
                                    }

                                    cpu.cpl = n_cs_desc.DPL();
                                    /* Switch to new CS:EIP */
                                    Segs.phys[cs]     = n_cs_desc.GetBase();
                                    Segs.val[cs]      = (n_cs_sel & 0xfffc) | cpu.cpl;
                                    cpu.code.big      = n_cs_desc.Big()>0;
                                    reg_eip                 = n_eip;
                                    if (!use32) reg_eip&=0xffff;
                                    break;            
                              }
                        case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
                        case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
call_gate_same_privilege:
                              E_Exit("Call gate to same priviledge");
                              break;
                        }
                  }                 /* Call Gates */
                  break;
            case DESC_386_TSS_A:
                  if (call.DPL()<cpu.cpl) E_Exit("CALL:TSS:dpl<cpl");
                  if (call.DPL()<rpl) E_Exit("CALL:TSS:dpl<rpl");
                  LOG(LOG_CPU,LOG_NORMAL)("CALL:TSS to %X",selector);
                  CPU_SwitchTask(selector,TSwitch_CALL_INT,oldeip);
                  break;
            default:
                  E_Exit("CALL:Descriptor type %x unsupported",call.Type());

            }
      }
      assert(1);
}


void CPU_RET(bool use32,Bitu bytes,Bitu oldeip) {
      if (!cpu.pmode || (reg_flags & FLAG_VM)) {
            Bitu new_ip,new_cs;
            if (!use32) {
                  new_ip=CPU_Pop16();
                  new_cs=CPU_Pop16();
            } else {
                  new_ip=CPU_Pop32();
                  new_cs=CPU_Pop32() & 0xffff;
            }
            reg_esp+=bytes;
            SegSet16(cs,new_cs);
            reg_eip=new_ip;
            cpu.code.big=false;
            return;
      } else {
            Bitu offset,selector;
            if (!use32) selector    = mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 2);
            else        selector    = mem_readd(SegPhys(ss) + (reg_esp & cpu.stack.mask) + 4) & 0xffff;

            Descriptor desc;
            Bitu rpl=selector & 3;
            if (rpl<cpu.cpl) {
                  CPU_Exception(0x0B,selector & 0xfffc);
                  return;
            }
            cpu.gdt.GetDescriptor(selector,desc);

            if (!desc.saved.seg.p) {
                  CPU_Exception(0x0B,selector & 0xfffc);
                  return;
            };

            if (!use32) {
                  offset=CPU_Pop16();
                  selector=CPU_Pop16();
            } else {
                  offset=CPU_Pop32();
                  selector=CPU_Pop32() & 0xffff;
            }
            if (cpu.cpl==rpl) {     
                  /* Return to same level */
                  switch (desc.Type()) {
                  case DESC_CODE_N_NC_A:case DESC_CODE_N_NC_NA:
                  case DESC_CODE_R_NC_A:case DESC_CODE_R_NC_NA:
                        if (!(cpu.cpl==desc.DPL())) E_Exit("RET to NC segment of other privilege");
                        goto RET_same_level;
                  case DESC_CODE_N_C_A:case DESC_CODE_N_C_NA:
                  case DESC_CODE_R_C_A:case DESC_CODE_R_C_NA:
                        if (!(desc.DPL()>=cpu.cpl)) E_Exit("RET to C segment of higher privilege");
                        break;
                  default:
                        E_Exit("RET from illegal descriptor type %X",desc.Type());
                  }
RET_same_level:

                  Segs.phys[cs]=desc.GetBase();
                  cpu.code.big=desc.Big()>0;
                  Segs.val[cs]=selector;
                  reg_eip=offset;
                  if (cpu.stack.big) {
                        reg_esp+=bytes;
                  } else {
                        reg_sp+=bytes;
                  }
                  LOG(LOG_CPU,LOG_NORMAL)("RET - Same level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
                  return;
            } else {
                  /* Return to outer level */
                  if (bytes) E_Exit("RETF outer level with immediate value");
                  Bitu n_esp,n_ss;
                  if (use32) {
                        n_esp = CPU_Pop32();
                        n_ss = CPU_Pop32() & 0xffff;
                  } else {
                        n_esp = CPU_Pop16();
                        n_ss = CPU_Pop16();
                  }
                  cpu.cpl = rpl;

                  CPU_SetSegGeneral(ss,n_ss);
                  if (cpu.stack.big) {
                reg_esp = n_esp;
                  } else {
                        reg_sp = n_esp;
                  }

                  Segs.phys[cs]=desc.GetBase();
                  cpu.code.big=desc.Big()>0;
                  Segs.val[cs]=(selector&0xfffc) | cpu.cpl;
                  reg_eip=offset;

//                LOG(LOG_MISC,LOG_ERROR)("RET - Higher level to %X:%X RPL %X DPL %X",selector,offset,rpl,desc.DPL());
                  return;
            }
            LOG(LOG_CPU,LOG_NORMAL)("Prot ret %X:%X",selector,offset);
            return;
      }
      assert(1);
}


void CPU_SLDT(Bitu & selector) {
      selector=cpu.gdt.SLDT();
}

void CPU_LLDT(Bitu selector) {
      cpu.gdt.LLDT(selector);
      LOG(LOG_CPU,LOG_NORMAL)("LDT Set to %X",selector);
}

void CPU_STR(Bitu & selector) {
      selector=cpu_tss.selector;
}

void CPU_LTR(Bitu selector) {
      cpu_tss.SetSelector(selector);
}

Bitu gdt_count=0;

void CPU_LGDT(Bitu limit,Bitu base) {
      LOG(LOG_CPU,LOG_NORMAL)("GDT Set to base:%X limit:%X count %d",base,limit,gdt_count++);
      cpu.gdt.SetLimit(limit);
      cpu.gdt.SetBase(base);
//    if (gdt_count>20) DEBUG_EnableDebugger();
//    DEBUG_EnableDebugger();
}

void CPU_LIDT(Bitu limit,Bitu base) {
      LOG(LOG_CPU,LOG_NORMAL)("IDT Set to base:%X limit:%X",base,limit);
      cpu.idt.SetLimit(limit);
      cpu.idt.SetBase(base);
}

void CPU_SGDT(Bitu & limit,Bitu & base) {
      limit=cpu.gdt.GetLimit();
      base=cpu.gdt.GetBase();
}

void CPU_SIDT(Bitu & limit,Bitu & base) {
      limit=cpu.idt.GetLimit();
      base=cpu.idt.GetBase();
}


bool CPU_SET_CRX(Bitu cr,Bitu value) {
      switch (cr) {
      case 0:
            {
                  Bitu changed=cpu.cr0 ^ value;       
                  if (!changed) return false;
                  cpu.cr0=value;
                  if (value & CR0_PROTECTION) {
                        cpu.pmode=true;
                        LOG(LOG_CPU,LOG_NORMAL)("Protected mode");
                        PAGING_Enable((value & CR0_PAGING)>0);
                  } else {
                        cpu.pmode=false;
                        PAGING_Enable(false);
                        LOG(LOG_CPU,LOG_NORMAL)("Real mode");
                  }
                  break;
            }
      case 2:
            paging.cr2=value;
            break;
      case 3:
            PAGING_SetDirBase(value);
            break;
      default:
            LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV CR%d,%X",cr,value);
            break;
      }
      return false;
}

Bitu CPU_GET_CRX(Bitu cr) {
      switch (cr) {
      case 0:
            return cpu.cr0;
      case 2:
            return paging.cr2;
      case 3:
            return PAGING_GetDirBase();
      default:
            LOG(LOG_CPU,LOG_ERROR)("Unhandled MOV XXX, CR%d",cr);
            break;
      }
      return 0;
}


void CPU_SMSW(Bitu & word) {
      word=cpu.cr0;
}

Bitu CPU_LMSW(Bitu word) {
      word&=0xf;
      if (cpu.cr0 & 1) word|=1; 
      word|=(cpu.cr0&0xfffffff0);
      CPU_SET_CRX(0,word);
      return false;
}

void CPU_ARPL(Bitu & dest_sel,Bitu src_sel) {
      if ((dest_sel & 3) < (src_sel & 3)) {
            dest_sel=(dest_sel & 0xfffc) + (src_sel & 3);
//          dest_sel|=0xff3f0000;
            SETFLAGBIT(ZF,true);
      } else {
            SETFLAGBIT(ZF,false);
      } 
}
      
void CPU_LAR(Bitu selector,Bitu & ar) {
      if (selector == 0) {
            SETFLAGBIT(ZF,false);
            return;
      }
      Descriptor desc;Bitu rpl=selector & 3;
      if (!cpu.gdt.GetDescriptor(selector,desc)){
            SETFLAGBIT(ZF,false);
            return;
      }
      switch (desc.Type()){
      case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
      case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
            break;

      case DESC_286_INT_GATE:       case DESC_286_TRAP_GATE:      {
      case DESC_386_INT_GATE:       case DESC_386_TRAP_GATE:
            SETFLAGBIT(ZF,false);
            return;
      }

      case DESC_LDT:
      case DESC_TASK_GATE:

      case DESC_286_TSS_A:          case DESC_286_TSS_B:
      case DESC_286_CALL_GATE:

      case DESC_386_TSS_A:          case DESC_386_TSS_B:
      case DESC_386_CALL_GATE:
      

      case DESC_DATA_EU_RO_NA:      case DESC_DATA_EU_RO_A:
      case DESC_DATA_EU_RW_NA:      case DESC_DATA_EU_RW_A:
      case DESC_DATA_ED_RO_NA:      case DESC_DATA_ED_RO_A:
      case DESC_DATA_ED_RW_NA:      case DESC_DATA_ED_RW_A:
      case DESC_CODE_N_NC_A:        case DESC_CODE_N_NC_NA:
      case DESC_CODE_R_NC_A:        case DESC_CODE_R_NC_NA:
            if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
                  SETFLAGBIT(ZF,false);
                  return;
            }
            break;
      default:
            SETFLAGBIT(ZF,false);
            return;
      }
      /* Valid descriptor */
      ar=desc.saved.fill[1] & 0x00ffff00;
      SETFLAGBIT(ZF,true);
}

void CPU_LSL(Bitu selector,Bitu & limit) {
      if (selector == 0) {
            SETFLAGBIT(ZF,false);
            return;
      }
      Descriptor desc;Bitu rpl=selector & 3;
      if (!cpu.gdt.GetDescriptor(selector,desc)){
            SETFLAGBIT(ZF,false);
            return;
      }
      switch (desc.Type()){
      case DESC_CODE_N_C_A:   case DESC_CODE_N_C_NA:
      case DESC_CODE_R_C_A:   case DESC_CODE_R_C_NA:
            break;

      case DESC_LDT:
      case DESC_286_TSS_A:
      case DESC_286_TSS_B:
      
      case DESC_386_TSS_A:
      case DESC_386_TSS_B:

      case DESC_DATA_EU_RO_NA:      case DESC_DATA_EU_RO_A:
      case DESC_DATA_EU_RW_NA:      case DESC_DATA_EU_RW_A:
      case DESC_DATA_ED_RO_NA:      case DESC_DATA_ED_RO_A:
      case DESC_DATA_ED_RW_NA:      case DESC_DATA_ED_RW_A:
      
      case DESC_CODE_N_NC_A:        case DESC_CODE_N_NC_NA:
      case DESC_CODE_R_NC_A:        case DESC_CODE_R_NC_NA:
            if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
                  SETFLAGBIT(ZF,false);
                  return;
            }
            break;
      default:
            SETFLAGBIT(ZF,false);
            return;
      }
      limit=desc.GetLimit();
      SETFLAGBIT(ZF,true);
}

void CPU_VERR(Bitu selector) {
      if (selector == 0) {
            SETFLAGBIT(ZF,false);
            return;
      }
      Descriptor desc;Bitu rpl=selector & 3;
      if (!cpu.gdt.GetDescriptor(selector,desc)){
            SETFLAGBIT(ZF,false);
            return;
      }
      switch (desc.Type()){
      case DESC_CODE_R_C_A:         case DESC_CODE_R_C_NA:  
            //Conforming readable code segments can be always read 
            break;
      case DESC_DATA_EU_RO_NA:      case DESC_DATA_EU_RO_A:
      case DESC_DATA_EU_RW_NA:      case DESC_DATA_EU_RW_A:
      case DESC_DATA_ED_RO_NA:      case DESC_DATA_ED_RO_A:
      case DESC_DATA_ED_RW_NA:      case DESC_DATA_ED_RW_A:

      case DESC_CODE_R_NC_A:        case DESC_CODE_R_NC_NA:
            if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
                  SETFLAGBIT(ZF,false);
                  return;
            }
            break;
      default:
            SETFLAGBIT(ZF,false);
            return;
      }
      SETFLAGBIT(ZF,true);
}

void CPU_VERW(Bitu selector) {
      if (selector == 0) {
            SETFLAGBIT(ZF,false);
            return;
      }
      Descriptor desc;Bitu rpl=selector & 3;
      if (!cpu.gdt.GetDescriptor(selector,desc)){
            SETFLAGBIT(ZF,false);
            return;
      }
      switch (desc.Type()){
      case DESC_DATA_EU_RW_NA:      case DESC_DATA_EU_RW_A:
      case DESC_DATA_ED_RW_NA:      case DESC_DATA_ED_RW_A:
            if (desc.DPL()<cpu.cpl || desc.DPL() < rpl) {
                  SETFLAGBIT(ZF,false);
                  return;
            }
            break;
      default:
            SETFLAGBIT(ZF,false);
            return;
      }
      SETFLAGBIT(ZF,true);
}

bool CPU_SetSegGeneral(SegNames seg,Bitu value) {
      value &= 0xffff;
      if (!cpu.pmode || (reg_flags & FLAG_VM)) {
            Segs.val[seg]=value;
            Segs.phys[seg]=value << 4;
            if (seg==ss) {
                  cpu.stack.big=false;
                  cpu.stack.mask=0xffff;
            }
            return false;
      } else {
            Descriptor desc;
            cpu.gdt.GetDescriptor(value,desc);

            if (value!=0) {
                  if (!desc.saved.seg.p) {
                        if (seg==ss) E_Exit("CPU_SetSegGeneral: Stack segment not present.");
                        // Throw Exception 0x0B - Segment not present
                        return CPU_PrepareException(0x0B,value & 0xfffc);
                  } else if (seg==ss) {
                        // Stack segment loaded with illegal segment ?
                        if ((desc.saved.seg.type<DESC_DATA_EU_RO_NA) || (desc.saved.seg.type>DESC_DATA_ED_RW_A)) {
                              return CPU_PrepareException(0x0D,value & 0xfffc);
                        }
                  }
            }
            Segs.val[seg]=value;
            Segs.phys[seg]=desc.GetBase();
            if (seg==ss) {
                  if (desc.Big()) {
                        cpu.stack.big=true;
                        cpu.stack.mask=0xffffffff;
                  } else {
                        cpu.stack.big=false;
                        cpu.stack.mask=0xffff;
                  }
            }
            return false;
      }
}

bool CPU_PopSeg(SegNames seg,bool use32) {
      Bitu val=mem_readw(SegPhys(ss) + (reg_esp & cpu.stack.mask));
      if (CPU_SetSegGeneral(seg,val)) return true;
      Bitu addsp=2 << use32;
      reg_esp=(reg_esp&~cpu.stack.mask)|((reg_esp+addsp)&cpu.stack.mask);
      return false;
}

void CPU_CPUID(void) {
      switch (reg_eax) {
      case 0:     /* Vendor ID String and maximum level? */
            reg_eax=0;
            reg_ebx=('G'<< 24) || ('e' << 16) || ('n' << 8) || 'u';
            reg_edx=('i'<< 24) || ('n' << 16) || ('e' << 8) || 'T';
            reg_ecx=('n'<< 24) || ('t' << 16) || ('e' << 8) || 'l';
            break;
      case 1:     /* get processor type/family/model/stepping and feature flags */
            reg_eax=0x402;          /* intel 486 sx? */
            reg_ebx=0;              /* Not Supported */
            reg_ecx=0;              /* No features */
            reg_edx=0;              /* Nothing either */
            break;
      default:
            LOG(LOG_CPU,LOG_ERROR)("Unhandled CPUID Function %x",reg_eax);
            break;
      }
}

static Bits HLT_Decode(void) {
      /* Once an interrupt occurs, it should change cpu core */
      if (reg_eip!=cpu.hlt.eip || SegValue(cs) != cpu.hlt.cs) {
            cpudecoder=cpu.hlt.old_decoder;
      } else {
            CPU_Cycles=0;
      }
      return 0;
}

void CPU_HLT(Bitu oldeip) {
      if (cpu.cpl) {
            CPU_Exception(13,0);    
            return;
      }
      reg_eip=oldeip;
      CPU_Cycles=0;
      cpu.hlt.cs=SegValue(cs);
      cpu.hlt.eip=reg_eip;
      cpu.hlt.old_decoder=cpudecoder;
      cpudecoder=&HLT_Decode;
      return;
}

void CPU_ENTER(bool use32,Bitu bytes,Bitu level) {
      level&=0x1f;
      Bitu sp_index=reg_esp&cpu.stack.mask;
      Bitu bp_index=reg_ebp&cpu.stack.mask;
      if (!use32) {
            sp_index-=2;
            mem_writew(SegPhys(ss)+sp_index,reg_bp);
            reg_bp=(Bit16u)(reg_esp-2);
            if (level) {
                  for (Bitu i=1;i<level;i++) {  
                        sp_index-=2;bp_index-=2;
                        mem_writew(SegPhys(ss)+sp_index,mem_readw(SegPhys(ss)+bp_index));
                  }
                  sp_index-=2;
                  mem_writew(SegPhys(ss)+sp_index,reg_bp);
            }
      } else {
            sp_index-=4;
        mem_writed(SegPhys(ss)+sp_index,reg_ebp);
            reg_ebp=(reg_esp-4);
            if (level) {
                  for (Bitu i=1;i<level;i++) {  
                        sp_index-=4;bp_index-=4;
                        mem_writed(SegPhys(ss)+sp_index,mem_readd(SegPhys(ss)+bp_index));
                  }
                  sp_index-=4;
                  mem_writed(SegPhys(ss)+sp_index,reg_ebp);
            }
      }
      sp_index-=bytes;
      reg_esp=(reg_esp&~cpu.stack.mask)|((sp_index)&cpu.stack.mask);
}

extern void GFX_SetTitle(Bits cycles ,Bits frameskip,bool paused);
static void CPU_CycleIncrease(void) {
      Bits old_cycles=CPU_CycleMax;
      if(CPU_CycleUp < 100){
            CPU_CycleMax = (Bits)(CPU_CycleMax * (1 + (float)CPU_CycleUp / 100.0));
      } else {
            CPU_CycleMax = (Bits)(CPU_CycleMax + CPU_CycleUp);
      }
    
      CPU_CycleLeft=0;CPU_Cycles=0;
      if (CPU_CycleMax==old_cycles) CPU_CycleMax++;
      LOG_MSG("CPU:%d cycles",CPU_CycleMax);
      GFX_SetTitle(CPU_CycleMax,-1,false);
}

static void CPU_CycleDecrease(void) {
      if(CPU_CycleDown < 100){
            CPU_CycleMax = (Bits)(CPU_CycleMax / (1 + (float)CPU_CycleDown / 100.0));
      } else {
            CPU_CycleMax = (Bits)(CPU_CycleMax - CPU_CycleDown);
      }
      CPU_CycleLeft=0;CPU_Cycles=0;
      if (CPU_CycleMax <= 0) CPU_CycleMax=1;
      LOG_MSG("CPU:%d cycles",CPU_CycleMax);
      GFX_SetTitle(CPU_CycleMax,-1,false);
}

void CPU_Init(Section* sec) {
      Section_prop * section=static_cast<Section_prop *>(sec);
      reg_eax=0;
      reg_ebx=0;
      reg_ecx=0;
      reg_edx=0;
      reg_edi=0;
      reg_esi=0;
      reg_ebp=0;
      reg_esp=0;

      SegSet16(cs,0);
      SegSet16(ds,0);
      SegSet16(es,0);
      SegSet16(fs,0);
      SegSet16(gs,0);
      SegSet16(ss,0);

      CPU_SetFlags(FLAG_IF,FMASK_ALL);          //Enable interrupts
      cpu.cr0=0xffffffff;
      CPU_SET_CRX(0,0);                               //Initialize
      cpu.code.big=false;
      cpu.stack.mask=0xffff;
      cpu.stack.big=false;
      cpu.idt.SetBase(0);
      cpu.idt.SetLimit(1023);
      
      /* Init the cpu cores */
      CPU_Core_Normal_Init();
      CPU_Core_Simple_Init();
      CPU_Core_Full_Init();
#if (C_DYNAMIC_X86)
      CPU_Core_Dyn_X86_Init();
#endif
      MAPPER_AddHandler(CPU_CycleDecrease,MK_f11,MMOD1,"cycledown","Dec Cycles");
      MAPPER_AddHandler(CPU_CycleIncrease,MK_f12,MMOD1,"cycleup"  ,"Inc Cycles");
      CPU_Cycles=0;
      CPU_CycleMax=section->Get_int("cycles");;
      CPU_CycleUp=section->Get_int("cycleup");
      CPU_CycleDown=section->Get_int("cycledown");
      const char * core=section->Get_string("core");
      cpudecoder=&CPU_Core_Normal_Run;
      if (!strcasecmp(core,"normal")) {
            cpudecoder=&CPU_Core_Normal_Run;
      } else if (!strcasecmp(core,"simple")) {
            cpudecoder=&CPU_Core_Simple_Run;
      } else if (!strcasecmp(core,"full")) {
            cpudecoder=&CPU_Core_Full_Run;
      } 
#if (C_DYNAMIC_X86)
      else if (!strcasecmp(core,"dynamic")) {
            cpudecoder=&CPU_Core_Dyn_X86_Run;
      } 
#endif
      else {
            LOG_MSG("CPU:Unknown core type %s, switcing back to normal.",core);
      }
      CPU_JMP(false,0,0,0);                           //Setup the first cpu core

      if (!CPU_CycleMax) CPU_CycleMax = 2500;
      if(!CPU_CycleUp)   CPU_CycleUp = 500;
      if(!CPU_CycleDown) CPU_CycleDown = 20;
      CPU_CycleLeft=0;
      GFX_SetTitle(CPU_CycleMax,-1,false);
}


Generated by  Doxygen 1.6.0   Back to index