/* * Copyright (C) 2002-2003 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 Library 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. */ #include #include #include "dosbox.h" #include "dos_inc.h" #include "callback.h" #include "mem.h" #include "regs.h" #include "dos_system.h" #include "setup.h" #include "inout.h" #include "cpu.h" //#define DPMI_LOG LOG(LOG_MISC,LOG_ERROR) #define DPMI_LOG #define DPMI_LOG_ERROR LOG(LOG_MISC,LOG_ERROR) //#define DPMI_LOG_ERROR #define DPMI_ALLOC_NEEDEDMEM_HIGH 1 #define DPMI_DPL 3 #define GDT_ZERO 0 #define GDT_LDT ((0x1 << 3) | DPMI_DPL) #define GDT_CODE ((0x2 << 3) | DPMI_DPL) #define GDT_PROTCODE ((0x3 << 3) | DPMI_DPL) #define GDT_DOSDATA ((0x4 << 3) | DPMI_DPL) #define GDT_ENVIRONMENT ((0x5 << 3) | DPMI_DPL) // TEMP #define GDT_DOSSEG40 (0x40) /* Amount of descriptors in each table */ #define GDT_SIZE 32 #define IDT_SIZE 256 #define LDT_SIZE 2048 #define INT_SIZE 256 #define TOTAL_SIZE ((GDT_SIZE+IDT_SIZE+LDT_SIZE+INT_SIZE)*8) #define LDT_ENTRY(BLAH_) (BLAH_ << 3) #define LDT_FIRSTSELECTOR 16 #define DPMI_ERROR_UNSUPPORTED 0x8001 #define DPMI_ERROR_DESCRIPTOR_UNAVAILABLE 0x8011 #define DPMI_ERROR_LINEAR_MEMORY_UNAVAILABLE 0x8012 #define DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE 0x8013 #define DPMI_ERROR_CALLBACK_UNAVAILABLE 0x8015 #define DPMI_ERROR_INVALID_SELECTOR 0x8022 #define DPMI_ERROR_INVALID_VALUE 0x8022 #define DPMI_ERROR_INVALID_HANDLE 0x8023 #define DPMI_ERROR_INVALID_CALLBACK 0x8024 #define DPMI_ERROR_INVALID_LINEAR_ADDRESS 0x8025 #define DPMI_XMSHANDLES_MAX 256 #define DPMI_XMSHANDLE_FREE 0xFFFF #define DPMI_EXCEPTION_MAX 0x20 #define DPMI_PAGE_SIZE (4*1024) #define DPMI_REALMODE_CALLBACK_MAX 32 #define DPMI_REALMODE_STACKSIZE 4096 #define DPMI_PROTMODE_STACK_MAX 3 #define DPMI_PROTMODE_STACKSIZE (4*1024) #define DPMI_REALVEC_MAX 17 #define DPMI_SAVESTACK_MAX 1024 #define DPMI_CB_APIMSDOSENTRY_OFFSET 256*8 #define DPMI_CB_ENTERREALMODE_OFFSET 257*8 #define DPMI_CB_SAVESTATE_OFFSET 258*8 #define DPMI_CB_EXCEPTION_OFFSET 259*8 #define DPMI_CB_EXCEPTIONRETURN_OFFSET 260*8 #define DPMI_CB_VENDORENTRY_OFFSET 261*8 #define DPMI_HOOK_HARDWARE_INTS 1 static Bitu rmIndexToInt[DPMI_REALVEC_MAX] = { 0x08,0x09,0x0A,0x0B,0x0C,0x0D,0x0E,0x0F,0x70,0x71,0x72,0x73,0x74,0x75,0x76,0x77,0x1C }; // General functions void CALLBACK32_SCF(bool val) { Bit32u tempf=mem_readd(SegPhys(ss)+reg_esp+8) & 0xFFFFFFFE; Bit32u newCF=(val==true); mem_writed(SegPhys(ss)+reg_esp+8,(tempf | newCF)); }; #define DPMI_CALLBACK_SCF(b) if (dpmi.client.bit32) CALLBACK32_SCF(b); else CALLBACK_SCF(b) // ********************************************** // SetDescriptor Class // ********************************************** #pragma pack(1) class SetDescriptor : public Descriptor { public: void Save(PhysPt address) { Bit32u* data = (Bit32u*)&saved; mem_writed(address,*data); mem_writed(address+4,*(data+1)); } void SetBase(Bitu _base) { saved.seg.base_24_31=_base >> 24; saved.seg.base_16_23=_base >> 16; saved.seg.base_0_15=_base; } void SetLimit (Bitu _limit) { if (_limit<1048576) saved.seg.g=false; else { saved.seg.g=true; _limit>>=12; } saved.seg.limit_0_15=_limit; saved.seg.limit_16_19=_limit>>16; } void SetOffset(Bitu _offset) { saved.gate.offset_0_15=_offset; saved.gate.offset_16_31=(_offset>>16); } void SetSelector(Bitu _selector) { saved.gate.selector=_selector; } void SetType(Bitu _type) { saved.seg.type=_type; } void Clear(void) { saved.fill[0]=saved.fill[1]=0; } }; #pragma pack() // ********************************************** // Shared Memory // ********************************************** typedef struct SSharedMem { std::string name; Bitu handle; Bitu pages; } TSharedMem; std::list g_sharedMemList; // ********************************************** // DPMI Class // ********************************************** class DPMI { public: DPMI (void); ~DPMI (void); // Settp/Startup methods void Setup (void); Bitu Entrypoint (void); RealPt HookInterrupt (Bitu num, Bitu intHandler); void RestoreHookedInterrupt (Bitu num, RealPt oldVec); void CreateStackSpace (void); bool HasClient (void) { return dpmi.client.have; }; void Terminate (void); void Reactivate (void); // DPMI Services Bitu AllocateLDTDescriptor (Bitu count,Bitu & base); Bitu AllocateLDTDescriptor2 (Bitu count,Bitu & base); // TEMP bool AllocateMem (Bitu size, Bitu& outHandle, Bitu& linear); Bitu CreateAlias (Bitu selector, Bit16u& alias); void ReloadSegments (Bitu selector); bool SetAccessRights (Bitu selector, SetDescriptor& desc, Bitu rights); // Special Interrupt handlers Bitu Int2fHandler (void); Bitu Int31Handler (void); // Exceptions void CreateException (Bitu num, Bitu errorCode); Bitu ExceptionReturn (void); // Realmode callbacks bool AllocateRealModeCallback(Bitu codeSel,Bitu codeOff,Bitu dataSel, Bitu dataOff, Bitu& segment, Bitu& offset); Bitu RealModeCallback (void); Bitu RealModeCallbackReturn (void); // Real mode reflection callbacks void PrepareReflectToReal (Bitu num); Bitu CallRealIRETFrame (void); Bitu CallRealIRETFrameReturn (void); Bitu SimulateInt (void); Bitu SimulateIntReturn (void); Bitu ptorHandler (void); Bitu ptorHandlerReturn (void); Bitu Int21Handler (void); Bitu Int21HandlerReturn (void); Bitu HWIntDefaultHandler (void); void RemoveIntCallbacks (void); void RestoreIntCallbacks (void); // Switching modes void SaveRegisterState (Bitu num); void LoadRegisterState (Bitu num); Bitu EnterProtMode (void); Bitu EnterRealMode (void); Bitu RealSaveState (void); Bitu ProtSaveState (void); // virtual interrupt flag bool GetVirtualIntFlag (void); void SetVirtualIntFlag (bool on); // Internal Stack for saving processor status void PushStack (Bitu val) { saveStack[savePtr++] = val; }; Bitu PopStack (void) { return saveStack[--savePtr]; }; void SaveSegments (void); void SaveRegister (void); void RestoreSegments (void); void RestoreRegister (void); void CopyRegistersToBuffer (PhysPt data); void LoadRegistersFromBuffer (PhysPt data); void ProvideRealModeStack (PhysPt prStack, Bitu toCopy); void UpdateRealModeStack (void); // xms handle information void SetXMSHandle (Bitu handle); void ClearXMSHandles (void); void FreeXMSHandle (Bitu handle); // shared memory void SetSharedMem (const char* name, Bitu handle, Bitu pages); bool GetSharedMem (const char* name, Bitu& handle, Bitu& pages); bool IsSharedMem (Bitu handle); bool RemoveSharedMem (Bitu handle); // special msdos api stuff Bitu GetSegmentFromSelector (Bitu selector); bool GetMsdosSelector (Bitu realseg, Bitu realoff, Bitu &protsel, Bitu &protoff); void API_Init_MSDOS (void); Bitu API_Entry_MSDOS (void); Bitu API_Int21_MSDOS (void); private: Bitu Mask (Bitu value); Bitu saveStack[DPMI_SAVESTACK_MAX]; Bitu savePtr; Bitu rm_ss, rm_sp; struct { struct { bool have; bool bit32; Bitu psp; } client; Bit16u mem_handle; /* Handle for GDT/IDT */ struct { PhysPt base; Bitu limit; } gdt,idt,ldt; struct { bool inCall; bool inUse; bool stop; Bitu callCount; Bitu id; Bitu dataSelector,dataOffset; Bitu codeSelector,codeOffset; Bitu realSegment ,realOffset; } rmCallback[DPMI_REALMODE_CALLBACK_MAX]; RealPt realModeVec [DPMI_REALVEC_MAX]; Bitu oldRealVec [DPMI_REALVEC_MAX]; Bitu defaultHWIntFromProtMode[DPMI_REALVEC_MAX]; PhysPt ptorint_base; /* Base of pmode int handlers that reflect to realmode */ Bitu exceptionSelector[DPMI_EXCEPTION_MAX],exceptionOffset[DPMI_EXCEPTION_MAX]; Bitu xmsHandles[DPMI_XMSHANDLES_MAX]; Bitu protStack; Bitu protStackSelector[DPMI_PROTMODE_STACK_MAX]; Bitu realStackSelector[DPMI_PROTMODE_STACK_MAX]; Bitu dataSelector [DPMI_PROTMODE_STACK_MAX]; Bitu protStackCurrent; Bitu vIntFlag; bool pharlap; bool suppressRMCB; } dpmi; Bit32u* modIntTable; DPMI* prevDPMI; Bitu dtaAddress; Bitu save_cs[2],save_ds[2],save_es[2],save_fs[2],save_gs[2],save_ss[2]; Bitu save_eax[2],save_ebx[2],save_ecx[2],save_edx[2],save_esi[2],save_edi[2]; Bitu save_ebp[2],save_esp[2],save_eip[2],save_fl[2]; }; struct { Bitu entry; Bitu ptorint; Bitu ptorintReturn; Bitu int31; Bitu int21; Bitu int21Return; Bitu int2f; Bitu enterpmode; Bitu enterrmode; Bitu protsavestate; Bitu realsavestate; Bitu simint; Bitu simintReturn; Bitu rmIntFrame; Bitu rmIntFrameReturn; Bitu rmCallbackReturn; Bitu exception; Bitu exceptionret; // Special callbacks for special dos extenders Bitu apimsdosentry; Bitu int21msdos; } callback; // ************************************************ // DPMI static functions // ************************************************ static DPMI* activeDPMI = 0; static Bit32u originalIntTable[256]; bool DPMI_IsActive(void) { return (cpu.cr0 & CR0_PROTECTION) && activeDPMI && activeDPMI->HasClient(); } void DPMI_SetVirtualIntFlag(bool on) { if (activeDPMI) activeDPMI->SetVirtualIntFlag(on); } void DPMI_CreateException(Bitu num, Bitu errorCode) { if (activeDPMI) activeDPMI->CreateException(num,errorCode); } // ************************************************ // DPMI Methods // ************************************************ DPMI::DPMI(void) { memset(&dpmi,0,sizeof(dpmi)); savePtr = 0; dtaAddress = 0; rm_ss = rm_sp = 0; modIntTable = 0; prevDPMI = activeDPMI; }; DPMI::~DPMI(void) { MEM_ReleasePages(dpmi.mem_handle); // TODO: Free all memory allocated with DOS_GetMemory // Activate previous dpmi activeDPMI = prevDPMI; }; void DPMI::ClearXMSHandles(void) { for (Bitu i=0; iname = name; smem->handle= handle; smem->pages = pages; g_sharedMemList.push_back(smem); }; bool DPMI::GetSharedMem(const char* name, Bitu& handle, Bitu& pages) { TSharedMem* smem; std::list::iterator i; for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) { smem = static_cast(*i); if (smem->name.compare(name)==0) { handle = smem->handle; pages = smem->pages; return true; } }; return false; }; bool DPMI::IsSharedMem(Bitu handle) { std::list::iterator i; for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) { if ((*i)->handle==handle) return true; }; return false; }; bool DPMI::RemoveSharedMem(Bitu handle) { TSharedMem* smem; std::list::iterator i; for(i = g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) { smem = static_cast(*i); if (smem->handle==handle) { g_sharedMemList.remove(*i); delete smem; return true; } }; return false; }; void DPMI::SetXMSHandle(Bitu handle) { for (Bitu i=0; i=DPMI_SAVESTACK_MAX) E_Exit("DPMI: Stack too small."); saveStack[savePtr++] = SegValue(ds); saveStack[savePtr++] = SegValue(es); saveStack[savePtr++] = SegValue(fs); saveStack[savePtr++] = SegValue(gs); saveStack[savePtr++] = SegValue(ss); } void DPMI::SaveRegister(void) { SaveSegments(); if (savePtr+8>=DPMI_SAVESTACK_MAX) E_Exit("DPMI: Stack too small."); saveStack[savePtr++] = reg_eax; saveStack[savePtr++] = reg_ebx; saveStack[savePtr++] = reg_ecx; saveStack[savePtr++] = reg_edx; saveStack[savePtr++] = reg_esi; saveStack[savePtr++] = reg_edi; saveStack[savePtr++] = reg_ebp; saveStack[savePtr++] = reg_esp; }; void DPMI::RestoreSegments(void) { CPU_SetSegGeneral(ss,saveStack[--savePtr]); CPU_SetSegGeneral(gs,saveStack[--savePtr]); CPU_SetSegGeneral(fs,saveStack[--savePtr]); CPU_SetSegGeneral(es,saveStack[--savePtr]); CPU_SetSegGeneral(ds,saveStack[--savePtr]); }; void DPMI::RestoreRegister(void) { reg_esp = saveStack[--savePtr]; reg_ebp = saveStack[--savePtr]; reg_edi = saveStack[--savePtr]; reg_esi = saveStack[--savePtr]; reg_edx = saveStack[--savePtr]; reg_ecx = saveStack[--savePtr]; reg_ebx = saveStack[--savePtr]; reg_eax = saveStack[--savePtr]; RestoreSegments(); }; void DPMI::CopyRegistersToBuffer(PhysPt data) { // Save Values in structure mem_writed(data+0x00, reg_edi); mem_writed(data+0x04, reg_esi); mem_writed(data+0x08, reg_ebp); mem_writed(data+0x0C, 0x0000); mem_writed(data+0x10, reg_ebx); mem_writed(data+0x14, reg_edx); mem_writed(data+0x18, reg_ecx); mem_writed(data+0x1C, reg_eax); mem_writew(data+0x20, flags.word); mem_writew(data+0x22, SegValue(es)); mem_writew(data+0x24, SegValue(ds)); mem_writew(data+0x26, SegValue(fs)); mem_writew(data+0x28, SegValue(gs)); mem_writew(data+0x2A, reg_ip); mem_writew(data+0x2C, SegValue(cs)); mem_writew(data+0x2E, reg_sp); mem_writew(data+0x30, SegValue(ss)); } void DPMI::LoadRegistersFromBuffer(PhysPt data) { reg_edi = mem_readd(data+0x00); reg_esi = mem_readd(data+0x04); reg_ebp = mem_readd(data+0x08); reg_ebx = mem_readd(data+0x10); reg_edx = mem_readd(data+0x14); reg_ecx = mem_readd(data+0x18); reg_eax = mem_readd(data+0x1C); CPU_SetFlagsw(mem_readw(data+0x20)); SegSet16(es,mem_readw(data+0x22)); SegSet16(ds,mem_readw(data+0x24)); SegSet16(fs,mem_readw(data+0x26)); SegSet16(gs,mem_readw(data+0x28)); reg_esp = mem_readw(data+0x2E); SegSet16(ss,mem_readw(data+0x30)); if (!dpmi.client.bit32) { reg_eax &= 0xFFFF; reg_ebx &= 0xFFFF; reg_ecx &= 0xFFFF; reg_edx &= 0xFFFF; reg_edi &= 0xFFFF; reg_esi &= 0xFFFF; reg_ebp &= 0xFFFF; reg_esp &= 0xFFFF; }; }; void DPMI::ProvideRealModeStack(PhysPt prStack, Bitu toCopy) { // Check stack, if zero provide it if ((SegValue(ss)==0) && (reg_sp==0)) { SegSet16(ss,rm_ss); reg_esp = rm_sp; } else { if (SegValue(ss)==rm_ss) reg_esp = rm_sp; }; // We have to be in realmode here if (toCopy>0) { Bitu numBytes = toCopy*2; if (reg_espDPMI_REALMODE_STACKSIZE) E_Exit("DPMI:Realmode stack out of range: %04X",reg_esp); rm_sp = reg_sp; } }; Bitu DPMI::AllocateLDTDescriptor(Bitu count,Bitu & base) { SetDescriptor test; Bitu i=16, found=0; PhysPt address = dpmi.ldt.base + LDT_FIRSTSELECTOR*8; while (i0;found--) { test.Save(address); address+=8; } return true; } } return false; } Bitu DPMI::AllocateLDTDescriptor2(Bitu count,Bitu & base) { static Bitu allocated = 0; SetDescriptor desc; Bitu nr = LDT_FIRSTSELECTOR + allocated; if (nr+count < LDT_SIZE) { desc.Clear(); desc.SetType(DESC_DATA_EU_RW_NA); desc.saved.seg.p = 1; desc.saved.seg.big = dpmi.client.bit32; desc.saved.seg.dpl = DPMI_DPL; base = (nr << 3)|(4|DPMI_DPL); /* Make it an LDT Entry */ allocated += count; Bitu address = dpmi.ldt.base+(base & ~7); for (;count>0;count--) { desc.Save(address); address+=8; } return true; }; return false; } Bitu DPMI::CreateAlias(Bitu selector, Bit16u& alias) { Descriptor oldDesc; Bitu base; if (!cpu.gdt.GetDescriptor(selector,oldDesc)) { alias = DPMI_ERROR_INVALID_SELECTOR; return false; }; if (!AllocateLDTDescriptor(1,base)) { alias = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; return false; }; SetDescriptor desc; desc.Clear(); desc.SetLimit(oldDesc.GetLimit()); desc.SetBase (oldDesc.GetBase()); desc.SetType (DESC_DATA_ED_RW_A); desc.saved.seg.p=1; desc.saved.seg.dpl = DPMI_DPL; desc.Save (dpmi.ldt.base+(base & ~7)); alias = base; return true; }; void DPMI::ReloadSegments(Bitu selector) { if (SegValue(cs)==selector) CPU_SetSegGeneral(cs,selector); if (SegValue(ds)==selector) CPU_SetSegGeneral(ds,selector); if (SegValue(es)==selector) CPU_SetSegGeneral(es,selector); if (SegValue(fs)==selector) CPU_SetSegGeneral(fs,selector); if (SegValue(gs)==selector) CPU_SetSegGeneral(gs,selector); if (SegValue(ss)==selector) CPU_SetSegGeneral(ss,selector); }; void DPMI::CreateException(Bitu num, Bitu errorCode) { if (dpmi.client.bit32) { CPU_Push32(SegValue(ss)); CPU_Push32(reg_esp); CPU_Push32(flags.word); CPU_Push32(SegValue(cs)); CPU_Push32(reg_eip-2); // FIXME: Fake ! CPU_Push32(errorCode); CPU_Push32(GDT_PROTCODE); // return cs CPU_Push32(DPMI_CB_EXCEPTIONRETURN_OFFSET); // return eip } else { CPU_Push16(SegValue(ss)); CPU_Push16(reg_sp); CPU_Push16(flags.word); CPU_Push16(SegValue(cs)); CPU_Push16(reg_ip-2); // FIXME: Fake ! CPU_Push16(errorCode); CPU_Push16(GDT_PROTCODE); // return cs CPU_Push16(DPMI_CB_EXCEPTIONRETURN_OFFSET); // return eip }; DPMI_LOG("DPMI: Exception occured : %04X (%04X:%08X)",num,dpmi.exceptionSelector[num],dpmi.exceptionOffset[num]); CPU_JMP(dpmi.client.bit32,dpmi.exceptionSelector[num],dpmi.exceptionOffset[num]); }; Bitu DPMI::ExceptionReturn(void) { Bitu error; // Restore Registers Bitu newcs; if (dpmi.client.bit32) { error = CPU_Pop32(); reg_eip = CPU_Pop32(); newcs = CPU_Pop32(); CPU_SetFlagsd(CPU_Pop32()); reg_esp = CPU_Pop32(); CPU_SetSegGeneral(ss,CPU_Pop32()); } else { error = CPU_Pop16(); reg_eip = CPU_Pop16(); newcs = CPU_Pop16(); CPU_SetFlagsw(CPU_Pop16()); reg_esp = CPU_Pop16(); CPU_SetSegGeneral(ss,CPU_Pop16()); }; DPMI_LOG("DPMI: Return from Exception. Jump to %04X:%08X",SegValue(cs),reg_eip); CPU_JMP(dpmi.client.bit32,newcs,reg_eip); return 0; }; void DPMI::RemoveIntCallbacks() // When switching dpmi clients, remove active callbacks from hardware int { Bitu i; modIntTable = new Bit32u[256]; // read and store interrupt table for (i=0; i<256; i++) modIntTable[i] = mem_readd(i*4); // set a clean interrupt table for (i=0; i<256; i++) mem_writed(i*4,originalIntTable[i]); }; void DPMI::RestoreIntCallbacks() { if (modIntTable) { // restore modified interrupt table for (int i=0; i<256; i++) mem_writed(i*4,modIntTable[i]); delete[] modIntTable; modIntTable = 0; } }; bool DPMI::AllocateRealModeCallback(Bitu codeSel,Bitu codeOff,Bitu dataSel, Bitu dataOff, Bitu& segment, Bitu& offset) { Bitu num = 0; for (Bitu i=0; i=DPMI_REALMODE_CALLBACK_MAX) || !dpmi.rmCallback[num].inUse) E_Exit("DPMI: Illegal Realmode callback %02X.",num); if (dpmi.rmCallback[num].inCall) DPMI_LOG("DPMI: Recursive Realmode callback %02X",num); if (dpmi.protStackCurrent>DPMI_PROTMODE_STACK_MAX) E_Exit("DPMI: Too many recursive Realmode callbacks. Stack failure."); PushStack(num); DPMI_LOG("DPMI: Realmode Callback %02X (%04X:%08X) enter",num,dpmi.rmCallback[num].codeSelector,dpmi.rmCallback[num].codeOffset); dpmi.rmCallback[num].inCall= true; dpmi.rmCallback[num].callCount++; // Important! Update realmode stack UpdateRealModeStack(); // Setup stack selector of real mode stack SetDescriptor desc; if (cpu.gdt.GetDescriptor(dpmi.realStackSelector[dpmi.protStackCurrent],desc)) { desc.SetBase (SegValue(ss)<<4); desc.SetLimit(0xFFFF); desc.Save (dpmi.ldt.base+(dpmi.realStackSelector[dpmi.protStackCurrent] & ~7)); } else E_Exit("DPMI: RealmodeCB: Could not provide real mode stack descriptor."); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); // Setup dataSelector Descriptor data; Bitu dataSelector; if (dpmi.rmCallback[num].dataSelector==0x0000) dataSelector = dpmi.dataSelector[dpmi.protStackCurrent]; else dataSelector = dpmi.rmCallback[num].dataSelector; if (!cpu.gdt.GetDescriptor(dataSelector,data)) E_Exit("DPMI: Init RM-Callback failed."); DPMI_LOG("DPMI: CB: Writing RegData at = %04X:%04X",dataSelector,dpmi.rmCallback[num].dataOffset); // Prepare data buffer CopyRegistersToBuffer(PhysPt(data.GetBase()+dpmi.rmCallback[num].dataOffset)); DPMI_LOG("DPMI: CB: Stored cs:ip = %04X:%04X",SegValue(cs),reg_ip); // setup registers for protected mode func CPU_SetSegGeneral(ds,dpmi.realStackSelector[dpmi.protStackCurrent]); // DS:ESI = RM Stack reg_esi = reg_esp; CPU_SetSegGeneral(es,dataSelector); // ES:EDI = RM Register data reg_edi = dpmi.rmCallback[num].dataOffset; // SS:ESP = API stack CPU_SetSegGeneral(ss,dpmi.protStackSelector[dpmi.protStackCurrent++]); reg_esp = DPMI_PROTMODE_STACKSIZE; // prepare stack for iret if (dpmi.client.bit32) CPU_Push32(flags.word); else CPU_Push16(flags.word); // Setup cs:ip to return to DPMI_ReturnFromRealModeCallback CPU_SetSegGeneral(cs,GDT_CODE); reg_eip = RealOff(CALLBACK_RealPointer(callback.rmCallbackReturn)); // call protected mode func SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_Push32(flags.word); CPU_CALL(dpmi.client.bit32,dpmi.rmCallback[num].codeSelector,dpmi.rmCallback[num].codeOffset); return 0; }; Bitu DPMI::RealModeCallbackReturn(void) { // returning from protected mode function, now back to real mode Bitu num = PopStack(); DPMI_LOG("DPMI: Realmode Callback leave %02X",num); dpmi.suppressRMCB = false; dpmi.rmCallback[num].inCall = false; dpmi.rmCallback[num].stop = false; dpmi.rmCallback[num].callCount--; PhysPt data = PhysPt(SegPhys(es)+reg_edi); DPMI_LOG("DPMI: CB: Reading RegData at = %04X:%04X",SegValue(es),reg_edi); /* Swtich to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); dpmi.protStackCurrent--; // Restore Registers LoadRegistersFromBuffer(data); Bitu newCS = mem_readw(data+0x2C); Bitu newIP = mem_readw(data+0x2A); UpdateRealModeStack(); DPMI_LOG("DPMI: CB: Retored cs:ip = %04X:%04X (%d)",newCS,newIP); CPU_JMP(false,newCS,newIP); return 0; }; static Bitu count = 0; Bitu DPMI::CallRealIRETFrame(void) { Bitu calledIP = mem_readd(SegPhys(ss)+reg_esp); Bitu calledCS = mem_readd(SegPhys(ss)+reg_esp+4); DPMI_LOG("DPMI: ENTER REAL PROC IRETF %04X:%08X",calledCS,calledIP); // Save changed registers PushStack(SegValue(cs)); SaveRegister(); Bitu toCopy = reg_cx; // Load Registers PhysPt data = SegPhys(es) + reg_edi; PhysPt prStack = SegPhys(ss) + reg_esp; LoadRegistersFromBuffer(data); PushStack(data); /* Switch to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); // Provide Stack ProvideRealModeStack(prStack,toCopy); // Push flags CPU_Push16(flags.word); // Setup IP Bitu newCS = mem_readw(data+0x2C); Bitu newIP = mem_readw(data+0x2A); // Setup cs:ip to return to DPMI_CallRealIRETFrame callback SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.rmIntFrameReturn))); reg_ip = RealOff(CALLBACK_RealPointer(callback.rmIntFrameReturn)); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_CALL(false,newCS,newIP); return 0; } Bitu DPMI::CallRealIRETFrameReturn(void) { UpdateRealModeStack(); // returning from realmode func DPMI_LOG("DPMI: LEAVE REAL PROC IRETF %d",count); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); // Save registers into real mode structure CopyRegistersToBuffer(PopStack()); // Restore changed Resgisters RestoreRegister(); Bitu newcs = PopStack(); CPU_JMP(dpmi.client.bit32,newcs,reg_eip); DPMI_CALLBACK_SCF(false); return 0; }; Bitu DPMI::SimulateInt(void) { Bitu num = reg_bl; // TEMP if (num==0x5C) { int brk = 0; } DPMI_LOG("DPMI: SIM INT %02X %04X called. cs = %04X",num,reg_ax,SegValue(cs)); // Save changed registers PushStack(SegValue(cs)); SaveRegister(); Bitu toCopy = reg_cx; // Load Registers PhysPt data = SegPhys(es) + reg_edi; PhysPt prStack = SegPhys(ss) + reg_esp; LoadRegistersFromBuffer(data); PushStack(data); /* Switch to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); // Provide Stack ProvideRealModeStack(prStack,toCopy); // prepare for return SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.simintReturn))); reg_ip = RealOff(CALLBACK_RealPointer(callback.simintReturn)); // Push flags from structure on stack DPMI_LOG("DPMI: SimInt1: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB)); flags.word = mem_readw(data+0x20); Interrupt(num); DPMI_LOG("DPMI: SimInt2: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB)); return 0; }; Bitu DPMI::SimulateIntReturn(void) { // returning from realmode func DPMI_LOG("DPMI: SIM INT return"); DPMI_LOG("DPMI: SimIntRet1: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB)); UpdateRealModeStack(); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); // Save registers into real mode structure CopyRegistersToBuffer(PopStack()); // Restore changed Resgisters RestoreRegister(); Bitu newcs = PopStack(); DPMI_LOG("DPMI: SimIntRet: JUMP to %04X:%08X",newcs,reg_eip); CPU_JMP(dpmi.client.bit32,newcs,reg_eip); // Free last realmode stack DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: SimIntRet2: StackInfo %04X:%04X (%02X %02X)",SegValue(ss),reg_esp,mem_readb(0xD0100+0x01FA),mem_readb(0xD0100+0x01FB)); return 0; }; void DPMI::PrepareReflectToReal(Bitu num) { // Save segment and stack register SaveSegments(); PushStack(reg_esp); PushStack(num); PushStack(reg_eip); PushStack(SegValue(cs)); /* Swtich to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); // Setup cs:ip to return to intreturn Bitu retcs = RealSeg(CALLBACK_RealPointer(callback.ptorintReturn)); Bitu retip = RealOff(CALLBACK_RealPointer(callback.ptorintReturn)); SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.ptorintReturn))); reg_ip = RealOff(CALLBACK_RealPointer(callback.ptorintReturn)); // setup stack SegSet16(ss,rm_ss); reg_esp = rm_sp; } Bitu DPMI::ptorHandler(void) { /* Pmode interrupt handler that maps the interrupt to realmode */ Bitu num = reg_eip >> 3; if (!dpmi.vIntFlag) { if ((num>=0x08) && (num<=0x0F)) return 0; if ((num>=0x70) && (num<=0x77)) return 0; }; PrepareReflectToReal(num); // if (num==0x0F) DPMI_LOG("DPMI: INT %02X %04X called.",num,reg_ax); // Prepare flags for real int // CPU_SetFlagsw(flags.word & 0x3ED5); // 0011111011010101b Interrupt(num); return 0; } Bitu DPMI::ptorHandlerReturn(void) // Return from reflected real mode int { UpdateRealModeStack(); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); // Restore Registers Bitu newcs = PopStack(); reg_eip = PopStack(); Bitu num = PopStack(); reg_esp = PopStack(); RestoreSegments(); // if (num==0x0F) DPMI_LOG("DPMI: INT %02X RETURN",num); // hardware ints exit here if (((num>=0x08) && (num<=0x0F)) || ((num>=0x70) && (num<=0x77))) { CPU_JMP(dpmi.client.bit32,newcs,reg_eip); return 0; } // Change flags on stack to reflect possible results from ints if (dpmi.client.bit32) { Bit32u oldFlags = mem_readd(SegPhys(ss)+reg_esp+8) & ~FLAG_MASK;// leave only flags that cannot be changed by int Bit32u userFlags = flags.word & FLAG_MASK; // Mask out illegal flags not to change by int (0011111011010101b) mem_writed(SegPhys(ss)+reg_esp+8,oldFlags|userFlags); } else { Bit16u oldFlags = mem_readw(SegPhys(ss)+reg_sp+4) & ~FLAG_MASK; // leave only flags that cannot be changed by int Bit16u userFlags = flags.word & FLAG_MASK; // Mask out illegal flags not to change by int (0011111011010101b) mem_writew(SegPhys(ss)+reg_sp+4,oldFlags|userFlags); }; CPU_JMP(dpmi.client.bit32,newcs,reg_eip); return 0; } Bitu DPMI::Int21Handler(void) { // Check for exit if (reg_ah==0x4C) { DPMI_LOG("DPMI: INT 21: Terminating."); Terminate(); } // Save segment and stack register PushStack(SegValue(ss)); PushStack(reg_esp); PushStack(SegValue(ds)); PushStack(SegValue(es)); PushStack(SegValue(cs)); /* Swtich to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); // Setup cs:ip to return to intreturn SegSet16(cs,RealSeg(CALLBACK_RealPointer(callback.int21Return))); reg_ip = RealOff(CALLBACK_RealPointer(callback.int21Return)); // setup stack SegSet16(ss,rm_ss); reg_esp = rm_sp; // Call realmode interrupt DPMI_LOG("DPMI: INT 21 %04X called.",reg_ax); Interrupt(0x21); if (reg_ah==0x4C) { // Shut doen dpmi and restore previous one delete this; if (activeDPMI) activeDPMI->Reactivate(); } return 0; }; Bitu DPMI::Int21HandlerReturn(void) { UpdateRealModeStack(); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); // Restore Registers Bitu newcs = PopStack(); CPU_SetSegGeneral(es,PopStack()); CPU_SetSegGeneral(ds,PopStack()); reg_esp = PopStack(); CPU_SetSegGeneral(ss,PopStack()); // Set carry flag DPMI_CALLBACK_SCF(flags.word & 1); DPMI_LOG("DPMI: INT 21 RETURN"); CPU_JMP(dpmi.client.bit32,newcs,reg_eip); return 0; } Bitu DPMI::HWIntDefaultHandler() // Wir sind hier im Protected mode // a) durch einen INTerrupt im protected mode // b) durch einen INTerrupt im real mode (durch RMCB) { Bitu index = mem_readw(PhysPt(SegPhys(cs)+reg_eip-2)) - dpmi.defaultHWIntFromProtMode[0]; if (index>=DPMI_REALVEC_MAX) E_Exit("DPMI: Illegal realmode interrupt callback: %02X",index); Bitu num = rmIndexToInt[index]; RealPt vec = RealGetVec(num); if (dpmi.rmCallback[index].callCount==0) { // INT PROT (Use Handler is already done). // Wenn rmcb noch in Realmode Int table installiert, dann originalMethode aufrufef if (vec==dpmi.realModeVec[index]) { // originalroutine aufrufen dpmi.rmCallback[index].stop = false; PrepareReflectToReal(num); CPU_Push16(flags.word); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index])); } else { // user real mode handler in real mode int table aktiv. // Moeglich, dass dieser den RMCB von Hand noch aufruft // dann wird aber callCount>0 sein (da RMCB aktiv) und // dann die alte Routine aufgerufen... // RMCB sperren, um einen erneuten Aufruf des User Handlers zu vermeiden,.. // This is a hack for cybermage wich wont work otherwise. But why ? if (num==0x0F) { if (dpmi.suppressRMCB) { dpmi.suppressRMCB = false; return 0; } else { dpmi.suppressRMCB = true; } }; PrepareReflectToReal(num); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_Push16(flags.word); CPU_CALL(false,RealSeg(vec),RealOff(vec)); } } else { // INT REAL (vom RMCB aktiviert) // Falls user handler schon aktiv war (int von prot->reflected to real) // rufe original routine auf if (dpmi.rmCallback[index].stop) { dpmi.rmCallback[index].stop = false; PrepareReflectToReal(num); CPU_Push16(flags.word); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index])); } else { // User routine wurde noch nicht aktiviert, callback aber ausgeführt // falls spezieller protected mode handler aktiviert wurde, // wird dieser jetzt aufgerufen (user routine im protected mode) Descriptor gate; gate.Load(dpmi.idt.base+num*8); if ((gate.GetSelector()!=GDT_CODE) || (gate.GetOffset()!=RealOff(dpmi.defaultHWIntFromProtMode[index]))) { dpmi.rmCallback[index].stop = true; // vermeide rekursion CPU_JMP(dpmi.client.bit32,gate.GetSelector(),gate.GetOffset()); } else { // kein spezieller Protmode handler - Rufe originalroutine auf PrepareReflectToReal(num); CPU_Push16(flags.word); SETFLAGBIT(IF,false); SETFLAGBIT(TF,false); CPU_CALL(false,RealSeg(dpmi.oldRealVec[index]),RealOff(dpmi.oldRealVec[index])); }; }; } return 0; }; void DPMI::SaveRegisterState(Bitu num) // Copy Current Registers to structure { save_cs[num] = SegValue(cs); save_ds[num] = SegValue(ds); save_es[num] = SegValue(es); save_fs[num] = SegValue(fs); save_gs[num] = SegValue(gs); save_ss[num] = SegValue(ss); save_eip[num] = reg_eip; save_eax[num] = reg_eax; save_ebx[num] = reg_ebx; save_ecx[num] = reg_ecx; save_edx[num] = reg_edx; save_esi[num] = reg_esi; save_edi[num] = reg_edi; save_ebp[num] = reg_ebp; save_esp[num] = reg_esp; save_fl [num] = flags.word; }; void DPMI::LoadRegisterState(Bitu num) // Copy Current Registers to structure { CPU_SetSegGeneral(fs,save_fs[num]); CPU_SetSegGeneral(gs,save_gs[num]); reg_eax = save_eax[num]; reg_ebx = save_ebx[num]; reg_ecx = save_ecx[num]; reg_edx = save_edx[num]; reg_esi = save_esi[num]; reg_edi = save_edi[num]; flags.word = save_fl [num]; }; Bitu DPMI::EnterProtMode(void) { /* Save real mode register state */ SaveRegisterState(0); /* Switch to protected mode */ CPU_SET_CRX(0,cpu.cr0 | CR0_PROTECTION); CPU_SetSegGeneral(ds,reg_ax); CPU_SetSegGeneral(es,reg_cx); CPU_SetSegGeneral(ss,reg_dx); CPU_SetSegGeneral(ds,reg_ax); if (dpmi.client.bit32) { reg_esp = reg_ebx; CPU_JMP(true,reg_si,reg_edi); } else { reg_sp = reg_bx; CPU_JMP(false,reg_si,reg_di); }; /* Load prot mode register state (all other unchanged registers */ LoadRegisterState(1); DPMI_LOG("DPMI: Switch to protected mode."); return 0; } Bitu DPMI::EnterRealMode(void) { /* Save Prot Mode Registers */ SaveRegisterState(1); /* Swtich to real mode */ CPU_SET_CRX(0,cpu.cr0 & ~CR0_PROTECTION); // (E)BP will be preserved across the mode switch call so it can be used as a pointer. // TODO: If interrupts are disabled when the mode switch procedure is invoked, // they will not be re-enabled by the DPMI host (even temporarily). SegSet16(ds,reg_ax); SegSet16(es,reg_cx); SegSet16(ss,reg_dx); SegSet16(fs,0); SegSet16(gs,0); if (dpmi.client.bit32) { reg_esp = reg_ebx; CPU_JMP(true,reg_si,reg_edi); } else { reg_sp = reg_bx; CPU_JMP(false,reg_si,reg_di); }; /* Load real mode register state (all other unchanged registers) */ LoadRegisterState(0); DPMI_LOG("DPMI: Switch to real mode."); return CBRET_NONE; }; Bitu DPMI::RealSaveState(void) { return CBRET_NONE; /* Save Protected mode state */ if (reg_al==0) { PhysPt data = SegPhys(es) + reg_edi; mem_writew(data+ 0,save_cs[1]); mem_writew(data+ 2,save_ds[1]); mem_writew(data+ 4,save_es[1]); mem_writew(data+ 6,save_fs[1]); mem_writew(data+ 8,save_gs[1]); mem_writew(data+10,save_ss[1]); mem_writed(data+12,save_eax[1]); mem_writed(data+16,save_ebx[1]); mem_writed(data+20,save_ecx[1]); mem_writed(data+24,save_edx[1]); mem_writed(data+28,save_esi[1]); mem_writed(data+32,save_edi[1]); mem_writed(data+36,save_ebp[1]); mem_writed(data+40,save_esp[1]); mem_writed(data+44,save_fl [1]); DPMI_LOG("DPMI: Prot Save State."); } else if (reg_al==1) { /* restore state of prot mode registers */ PhysPt data = SegPhys(es) + reg_edi; save_cs [1] = mem_readw(data+ 0); save_ds [1] = mem_readw(data+ 2); save_es [1] = mem_readw(data+ 4); save_fs [1] = mem_readw(data+ 6); save_gs [1] = mem_readw(data+ 8); save_ss [1] = mem_readw(data+10); save_eax[1] = mem_readd(data+12); save_ebx[1] = mem_readd(data+16); save_ecx[1] = mem_readd(data+20); save_edx[1] = mem_readd(data+24); save_edi[1] = mem_readd(data+28); save_esi[1] = mem_readd(data+32); save_ebp[1] = mem_readd(data+36); save_esp[1] = mem_readd(data+40); // save_eip[1] = mem_readd(data+44); save_fl [1] = mem_readd(data+44); DPMI_LOG("DPMI: Prot Restore State."); }; return CBRET_NONE; }; Bitu DPMI::ProtSaveState(void) { return CBRET_NONE; if (reg_al==0) { /* Save State of real mode registers */ PhysPt data = SegPhys(es) + reg_edi; mem_writew(data+ 0,save_cs[0]); mem_writew(data+ 2,save_ds[0]); mem_writew(data+ 4,save_es[0]); mem_writew(data+ 6,save_fs[0]); mem_writew(data+ 8,save_gs[0]); mem_writew(data+10,save_ss[0]); mem_writed(data+12,save_eax[0]); mem_writed(data+16,save_ebx[0]); mem_writed(data+20,save_ecx[0]); mem_writed(data+24,save_edx[0]); mem_writed(data+28,save_esi[0]); mem_writed(data+32,save_edi[0]); mem_writed(data+36,save_ebp[0]); mem_writed(data+40,save_esp[0]); mem_writed(data+44,save_eip[0]); mem_writed(data+48,save_fl [0]); DPMI_LOG("DPMI: Real Save State."); } else if (reg_al==1) { /* restore state of real mode registers */ PhysPt data = SegPhys(es) + reg_edi; save_cs [0] = mem_readw(data+ 0); save_ds [0] = mem_readw(data+ 2); save_es [0] = mem_readw(data+ 4); save_fs [0] = mem_readw(data+ 6); save_gs [0] = mem_readw(data+ 8); save_ss [0] = mem_readw(data+10); save_eax[0] = mem_readd(data+12); save_ebx[0] = mem_readd(data+16); save_ecx[0] = mem_readd(data+20); save_edx[0] = mem_readd(data+24); save_edi[0] = mem_readd(data+28); save_esi[0] = mem_readd(data+32); save_ebp[0] = mem_readd(data+36); save_esp[0] = mem_readd(data+40); save_eip[0] = mem_readd(data+44); save_fl [0] = mem_readd(data+48); DPMI_LOG("DPMI: Real Restore State."); }; return CBRET_NONE; }; bool DPMI::GetVirtualIntFlag(void) // only to call from int 31 cos it uses the pushed flags on int stack { if (dpmi.client.bit32) return (mem_readd(SegPhys(ss)+reg_esp+8) & FLAG_IF)>0; else return (mem_readd(SegPhys(ss)+reg_sp+4) & FLAG_IF)>0; }; void DPMI::SetVirtualIntFlag(bool on) { dpmi.vIntFlag = on; }; bool DPMI::AllocateMem(Bitu size, Bitu& outHandle, Bitu& linear) { Bitu pages = (size/DPMI_PAGE_SIZE) + ((size%DPMI_PAGE_SIZE)>0); // Convert to 4KB pages outHandle = MEM_AllocatePages(pages,true); linear = outHandle*DPMI_PAGE_SIZE; if (outHandle!=0) SetXMSHandle(outHandle); return (outHandle!=0); }; bool DPMI::SetAccessRights(Bitu selector, SetDescriptor& desc, Bitu rights) { // must equal caller DPL if (((rights & 0x60)>>5)!=DPMI_DPL) { DPMI_LOG("DPMI: Set Rights %04X : %04X failure (dpl=%02X)",selector,rights,(rights & 0x60)>>5); return false; } // must be 1 if ((rights & 0x10)==0) { DPMI_LOG_ERROR("DPMI: Set Rights %04X : %04X failure (must be 1)",selector,rights); return false; }; // must be 0 if (dpmi.client.bit32 && desc.saved.seg.p && (rights & 0x2000)) { DPMI_LOG_ERROR("DPMI: Set Rights %04X : %04X failure (must be 0)",selector,rights); return false; }; // all tests passed, set rights for 16 + 32 Bit desc.SetType (rights&0x1F); desc.saved.seg.dpl = (rights&0x60)>>5; // desc.saved.seg.dpl = DPMI_DPL; desc.saved.seg.p = (rights&0x80)>0; // extended rights for 32 Bit apps if (dpmi.client.bit32) { desc.saved.seg.avl = (rights&0x1000)>0; desc.saved.seg.r = (rights&0x2000)>0; desc.saved.seg.big = (rights&0x4000)>0; desc.saved.seg.g = (rights&0x8000)>0; }; return true; }; Bitu DPMI::Int31Handler(void) { switch (reg_ax) { case 0x0000:{// Allocate LDT Descriptors Bitu base; Descriptor desc; if (AllocateLDTDescriptor(reg_cx,base)) { reg_ax = base; DPMI_LOG("DPMI: 0000: Allocate %d descriptors: %04X",reg_cx,base); DPMI_CALLBACK_SCF(false); } else { DPMI_LOG_ERROR("DPMI: 0000: Allocate %d descriptors failure",reg_cx); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0001:{// Free Descriptor SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { desc.saved.seg.p = 0; desc.Save (dpmi.ldt.base+(reg_bx & ~7)); DPMI_LOG("DPMI: 0001: Free Descriptor: %04X",reg_bx); DPMI_CALLBACK_SCF(false); } else { DPMI_LOG_ERROR("DPMI: 0001: Free Descriptor failure : %04X",reg_bx); reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0002:{// Segment to Descriptor SetDescriptor desc; Bitu base; if (AllocateLDTDescriptor(1,base)) { desc.Load (dpmi.ldt.base+(base & ~7)); desc.SetLimit(0xFFFF); desc.SetBase (reg_bx<<4); desc.saved.seg.dpl=3; desc.Save (dpmi.ldt.base+(base & ~7)); reg_ax = base; DPMI_LOG("DPMI: 0000: Seg %04X to Desc: %04X",reg_bx,base); DPMI_CALLBACK_SCF(false); } else { // No more Descriptors available DPMI_LOG_ERROR("DPMI: 0002: No more Descriptors available."); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0003:// Get Next Selector Increment Value reg_ax = 8; DPMI_LOG("DPMI: 0003: Get Selector Inc Value: %04X",reg_ax); DPMI_CALLBACK_SCF(false); break; case 0x0004:// undocumented (reserved) lock selector case 0x0005:// undocumented (reserved) unlock selector DPMI_LOG("DPMI: 0004: Undoc: (un)lock selector",reg_ax); DPMI_CALLBACK_SCF(true); break; case 0x0006:{ // Get Segment Base Address SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { DPMI_LOG("DPMI: 0006: Get Base %04X : B:%08X",reg_bx,desc.GetBase()); reg_cx = desc.GetBase()>>16; reg_dx = desc.GetBase()&0xFFFF; DPMI_CALLBACK_SCF(false); } else { DPMI_LOG_ERROR("DPMI: 0006: Invalid Selector: %04X",reg_bx); reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0007:{// Set Segment base address SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { Bitu base; if (!dpmi.client.bit32) base = (reg_cl<<16)+reg_dx; else base = (reg_cx<<16)+reg_dx; desc.SetBase(base); desc.Save (dpmi.ldt.base+(reg_bx & ~7)); ReloadSegments(reg_bx); DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: 0007: Set Base %04X : B:%08X",reg_bx,base); } else { DPMI_LOG_ERROR("DPMI: 0007: Invalid Selector: %04X",reg_bx); reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0008:{// Set Segment limit SetDescriptor desc; if ((!dpmi.client.bit32) && (reg_cx!=0)) { // 16-bit DPMI implementations can not set segment limits greater // than 0FFFFh (64K) so CX must be zero when calling DPMI_LOG_ERROR("DPMI: 0008: Set Segment Limit invalid: %04X ",reg_bx); reg_ax = DPMI_ERROR_INVALID_VALUE; DPMI_CALLBACK_SCF(true); } else if (cpu.gdt.GetDescriptor(reg_bx,desc)) { desc.SetLimit((reg_cx<<16)+reg_dx); desc.Save (dpmi.ldt.base+(reg_bx & ~7)); ReloadSegments(reg_bx); DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: 0008: Set Limit %08X",(reg_cx<<16)+reg_dx); } else { DPMI_LOG_ERROR("DPMI: 0008: Invalid Selector: %04X",reg_bx); reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); }; }; break; case 0x0009:{// Set Descriptor Access Rights SetDescriptor desc; Bit8u rcl = reg_cl; Bit8u rch = reg_ch; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { if (!SetAccessRights(reg_bx,desc,reg_cx)) { DPMI_LOG_ERROR("DPMI: 0009: Set Rights %04X : failure",reg_bx); reg_ax = DPMI_ERROR_INVALID_VALUE; DPMI_CALLBACK_SCF(true); break; }; desc.Save(dpmi.ldt.base+(reg_bx & ~7)); ReloadSegments(reg_bx); DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: 0009: Set Rights %04X : %04X",reg_bx,reg_cx); } else { DPMI_LOG_ERROR("DPMI: 0009: Set Rights %04X : invalid selector",reg_bx); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; DPMI_CALLBACK_SCF(true); }; }; break; case 0x000A:{// Create Alias Descriptor Descriptor desc; if (CreateAlias(reg_bx, reg_ax)) { DPMI_LOG("DPMI: 000A: Create Alias : %04X - %04X",reg_bx,reg_ax); DPMI_CALLBACK_SCF(false); } else { DPMI_CALLBACK_SCF(true); DPMI_LOG_ERROR("DPMI: 000A: Invalid Selector: %04X",reg_bx); }; }; break; case 0x000B:{//Get Descriptor SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { desc.Save(SegPhys(es)+reg_edi); DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: 000B: Get Descriptor %04X : B:%08X L:%08X",reg_bx,desc.GetBase(),desc.GetLimit()); } else { DPMI_LOG_ERROR("DPMI: 000B: Get Descriptor %04X : failure",reg_bx); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; DPMI_CALLBACK_SCF(true); }; }; break; case 0x000C:{//Set Descriptor SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { desc.Load (SegPhys(es)+reg_edi); Bitu rights = (mem_readb(SegPhys(es)+reg_edi+6)<<8) + mem_readb(SegPhys(es)+reg_edi+5); if (!SetAccessRights(reg_bx,desc,rights)) { DPMI_LOG_ERROR("DPMI: 000C: Set Rights %04X : failure",reg_bx); reg_ax = DPMI_ERROR_INVALID_VALUE; DPMI_CALLBACK_SCF(true); break; }; desc.Save(dpmi.ldt.base+(reg_bx & ~7)); ReloadSegments(reg_bx); DPMI_LOG("DPMI: 000B: Set Descriptor %04X : B:%08X L:%08X : P %01X",reg_bx,desc.GetBase(),desc.GetLimit(),desc.saved.seg.p); DPMI_CALLBACK_SCF(false); } else { DPMI_LOG_ERROR("DPMI: 000C: Set Descriptor %04X failed",reg_bx); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; DPMI_CALLBACK_SCF(true); }; }; break; case 0x000D:{ // Allocate specific LDT Descriptor : TODO: Support it DPMI_LOG("DPMI: 000D: Alloc Specific LDT Selector: %04X",reg_bx); SetDescriptor desc; if (cpu.gdt.GetDescriptor(reg_bx,desc)) { if (!desc.saved.seg.p) { desc.saved.seg.p = 1; desc.SetLimit(0); desc.SetBase (0); desc.Save (dpmi.ldt.base+(reg_bx & ~7)); DPMI_CALLBACK_SCF(false); break; } else { DPMI_LOG_ERROR("DPMI: 000D: Invalid Selector: %04X",reg_bx); reg_ax = DPMI_ERROR_DESCRIPTOR_UNAVAILABLE; }; } else reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); }; break; case 0x0100:{// Allocate DOS Memory Block Bit16u blocks = reg_bx; DPMI_LOG("DPMI: 0100: Allocate DOS Mem: (%04X Blocks)",blocks); if (DOS_AllocateMemory(®_ax,&blocks)) { // Allocate Selector for block SetDescriptor desc; Bitu base; Bitu numDesc; numDesc = reg_bx/0x1000 + ((reg_bx%0x1000)>0); if (AllocateLDTDescriptor(numDesc,base)) { reg_dx = base; // First selector if (numDesc>1) { Bitu descBase = reg_ax*16; Bitu length = reg_bx*16; desc.Load (dpmi.ldt.base+(base & ~7)); desc.SetBase (descBase); desc.SetLimit(dpmi.client.bit32?length:0xFFFF); desc.Save (dpmi.ldt.base+(base & ~7)); for (Bitu i=1; i>4; DOS_MCB mcb(seg-1); Bitu size = mcb.GetSize()*16; if (DOS_FreeMemory(seg)) { while (size>0) { desc.Load(dpmi.ldt.base+(sel & ~7)); desc.saved.seg.p = 0; desc.Save(dpmi.ldt.base+(sel & ~7)); size -= (size>=0x10000)?0x10000:size; sel+=8; }; DPMI_CALLBACK_SCF(false); DPMI_LOG("DPMI: 0101: Free Dos Mem: %04X",reg_dx); break; } } DPMI_LOG_ERROR("DPMI: 0101: Invalid Selector: %04X",reg_bx); reg_ax = DPMI_ERROR_INVALID_SELECTOR; DPMI_CALLBACK_SCF(true); };break; case 0x0200:{// Get Real Mode Interrupt Vector RealPt vec = RealGetVec(reg_bl); reg_cx = RealSeg(vec); reg_dx = RealOff(vec); DPMI_LOG("DPMI: 0200: Get Real Int Vector %02X (%04X:%04X)",reg_bl,reg_cx,reg_dx); DPMI_CALLBACK_SCF(false); }; break; case 0x0201:{// Set Real Mode Interrupt Vector DPMI_LOG("DPMI: 0201: Set Real Int Vector %02X (%04X:%04X)",reg_bl,reg_cx,reg_dx); RealSetVec(reg_bl,RealMake(reg_cx,reg_dx)); DPMI_CALLBACK_SCF(false); }; break; case 0x0202:// Get Processor Exception Handler Vector if (reg_bl>16; reg_di = handle&0xFFFF; reg_bx = linear>>16; reg_cx = linear&0xFFFF; DPMI_CALLBACK_SCF(false); // TEMP Bitu total = MEM_FreeLargest(); // in KB DPMI_LOG("DPMI: 0501: Allocation success: H:%04X%04X (%d KB) (R:%d KB)",reg_si,reg_di,length/1024 + ((length%1024)>0),total*4); } else { reg_ax = DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE; DPMI_CALLBACK_SCF(true); // TEMP Bitu total = MEM_FreeLargest(); // in KB DPMI_LOG_ERROR("DPMI: 0501: Allocation failure (%d KB) (R:%d KB)",length/1024 + ((length%1024)>0),total*4); }; }; break; case 0x0502://Free Memory Block DPMI_LOG("DPMI: 0502: Free Mem: H:%04X%04X",reg_si,reg_di); MEM_ReleasePages((reg_si<<16)+reg_di); FreeXMSHandle((reg_si<<16)+reg_di); DPMI_CALLBACK_SCF(false); break; case 0x0503:{//Resize Memory Block Bitu linear,newHandle; Bitu newByte = (reg_bx<<16)+reg_cx; Bitu newSize = (newByte/DPMI_PAGE_SIZE)+((newByte & (DPMI_PAGE_SIZE-1))>0); MemHandle handle = (reg_si<<16)+reg_di; DPMI_LOG_ERROR("DPMI: 0503: Resize Memory: H:%08X (%d KB)",handle,newSize*4); if (MEM_ReAllocatePages(handle,newSize,true)) { linear = handle * DPMI_PAGE_SIZE; reg_si = handle>>16; reg_di = handle&0xFFFF; reg_bx = linear>>16; reg_cx = linear&0xFFFF; DPMI_CALLBACK_SCF(false); } else if (AllocateMem(newByte,newHandle,linear)) { // Not possible, try to allocate DPMI_LOG_ERROR("DPMI: 0503: Reallocated Memory: %d KB",newSize*4); reg_si = newHandle>>16; reg_di = newHandle&0xFFFF; reg_bx = linear>>16; reg_cx = linear&0xFFFF; // copy contents Bitu size = MEM_AllocatedPages(handle); if (newSize>16; reg_cx = linear & 0xFFFF; DPMI_LOG_ERROR("DPMI: 0800: Phys-adr-map not supported : %08X (%08X) - %08X.",phys,size,linear); DPMI_CALLBACK_SCF(false); }; break; case 0x0801:// Free physical address mapping DPMI_LOG("DPMI: 0801: Free physical address mapping"); DPMI_CALLBACK_SCF(false); break; case 0x0900://Get and Disable Virtual Interrupt State reg_al = dpmi.vIntFlag; dpmi.vIntFlag = 0; DPMI_LOG("DPMI: 0900: Get and disbale vi : %01X",reg_al); DPMI_CALLBACK_SCF(false); break; case 0x0901://Get and Enable Virtual Interrupt State reg_al = dpmi.vIntFlag; dpmi.vIntFlag = 1; DPMI_LOG("DPMI: 0901: Get and enable vi : %01X",reg_al); DPMI_CALLBACK_SCF(false); break; case 0x0902:{//Get Virtual Interrupt State reg_al = dpmi.vIntFlag; reg_al = 0; // TEMP DPMI_LOG("DPMI: 0900: Get vi : %01X",reg_al); DPMI_CALLBACK_SCF(false); }; break; case 0x0A00:{//Get Vendor Specific API Entry Point char name[256]; MEM_StrCopy(SegPhys(ds)+reg_esi,name,255); LOG(LOG_MISC,LOG_WARN)("DPMI: Get API: %s",name); if (strcmp(name,"MS-DOS")==0) { CPU_SetSegGeneral(es,GDT_PROTCODE); reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET; API_Init_MSDOS(); DPMI_CALLBACK_SCF(false); // } else if (strstr(name,"HWINT")!=0) { // reg_ax = DPMI_ERROR_UNSUPPORTED; // DPMI_CALLBACK_SCF(true); } else if (strstr(name,"PHARLAP")!=0) { CPU_SetSegGeneral(es,GDT_PROTCODE); reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET; API_Init_MSDOS(); DPMI_CALLBACK_SCF(false); dpmi.pharlap = true; } else { reg_ax = DPMI_ERROR_UNSUPPORTED; DPMI_CALLBACK_SCF(true); } }; break; case 0x0D00:{//Allocate Shared Memory char name[256]; PhysPt data = SegPhys(es)+reg_edi; Bitu length = mem_readd(data); Bitu pages = (length/DPMI_PAGE_SIZE)+((length%DPMI_PAGE_SIZE)>0); Bitu handle = mem_readd(data+0x08); Bitu linear = mem_readd(data+0x0C); Bitu strOffset = mem_readd(data+0x10); Bitu strSelect = mem_readw(data+0x14); Descriptor desc; if (!cpu.gdt.GetDescriptor(strSelect,desc)) { DPMI_LOG_ERROR("DPMI: 0D00: shared memory: invalid name selector"); reg_ax = DPMI_ERROR_INVALID_VALUE; DPMI_CALLBACK_SCF(true); return false; }; MEM_StrCopy(desc.GetBase()+strOffset,name,256); // Already allocated ? if (!GetSharedMem(name,handle,pages)) { if (!AllocateMem(length,handle,linear)) { DPMI_LOG_ERROR("DPMI: 0D00: Allocation shared failure %s (%d KB)",name,pages*4); reg_ax = DPMI_ERROR_PHYSICAL_MEMORY_UNAVAILABLE; DPMI_CALLBACK_SCF(true); break; }; // Init first paragraph with zeros for (Bitu i=0; i<16; i++) mem_writeb(linear+i,0); SetSharedMem(name,handle,pages); DPMI_LOG("DPMI: 0D00: Allocate shared memory %s (%d KB) ",name,pages*4); } else { linear = handle*DPMI_PAGE_SIZE; DPMI_LOG("DPMI: 0D00: Reuse shared memory %s (%d KB) ",name,pages*4); }; mem_writed(data+0x04,pages*DPMI_PAGE_SIZE); mem_writed(data+0x08,handle); mem_writed(data+0x0C,linear); DPMI_CALLBACK_SCF(false); }; break; case 0x0B00:// Set debug watchpoint case 0x0B01:// Clear debug watchpoint DPMI_CALLBACK_SCF(true); break; case 0x0E00:// Get Coprocessor Status DPMI_LOG("DPMI: 0E00: Get Coprocessor status"); reg_ax = 0x45; // nope, no coprocessor DPMI_CALLBACK_SCF(false); break; case 0x0E01:// Set Coprocessor Emulation DPMI_LOG("DPMI: 0E01: Set Coprocessor emulation"); DPMI_CALLBACK_SCF(true); // failure break; default :LOG(LOG_MISC,LOG_ERROR)("DPMI: Unsupported func %04X",reg_ax); reg_ax = DPMI_ERROR_UNSUPPORTED; DPMI_CALLBACK_SCF(true); // failure break; }; return 0; } Bitu DPMI::Int2fHandler(void) { // Only available in ProtectedMode // LOG(LOG_MISC,LOG_WARN)("DPMI: 0x2F %04x",reg_ax); switch (reg_ax) { case 0x1686: /* Get CPU Mode */ reg_ax = 0; break; case 0x168A: // Only available in protected mode // Get Vendor-Specific API Entry Point char name[256]; MEM_StrCopy(SegPhys(ds)+reg_esi,name,255); LOG(LOG_MISC,LOG_WARN)("DPMI: 0x2F 0x168A: Get Specific API :%s",name); if (strcmp(name,"MS-DOS")==0) { CPU_SetSegGeneral(es,GDT_PROTCODE); reg_edi = DPMI_CB_APIMSDOSENTRY_OFFSET; reg_al = 0x00; // Success, whatever they want... API_Init_MSDOS(); }; break; default : // reflect to real ptorHandler(); break; } return 0; }; // ********************************************************************* // Callbacks and Callback-Returns // ********************************************************************* static Bitu DPMI_ExceptionReturn(void) { if (activeDPMI) return activeDPMI->ExceptionReturn(); return 0;}; static Bitu DPMI_RealModeCallback(void) { if (activeDPMI) return activeDPMI->RealModeCallback(); return 0;}; static Bitu DPMI_RealModeCallbackReturn(void) { if (activeDPMI) return activeDPMI->RealModeCallbackReturn(); return 0;}; static Bitu DPMI_CallRealIRETFrame(void) { if (activeDPMI) return activeDPMI->CallRealIRETFrame(); return 0;}; static Bitu DPMI_CallRealIRETFrameReturn(void) { if (activeDPMI) return activeDPMI->CallRealIRETFrameReturn(); return 0;}; static Bitu DPMI_SimulateInt(void) { if (activeDPMI) return activeDPMI->SimulateInt(); return 0;}; static Bitu DPMI_SimulateIntReturn(void) { if (activeDPMI) return activeDPMI->SimulateIntReturn(); return 0;}; static Bitu DPMI_ptorHandler(void) { if (activeDPMI) return activeDPMI->ptorHandler(); return 0;}; static Bitu DPMI_ptorHandlerReturn(void) { if (activeDPMI) return activeDPMI->ptorHandlerReturn(); return 0;}; static Bitu DPMI_Int21Handler(void) { if (activeDPMI) return activeDPMI->Int21Handler(); return 0;}; static Bitu DPMI_Int21HandlerReturn(void) { if (activeDPMI) return activeDPMI->Int21HandlerReturn(); return 0;}; static Bitu DPMI_HWIntDefaultHandler(void) { if (activeDPMI) return activeDPMI->HWIntDefaultHandler(); return 0;}; static Bitu DPMI_EnterProtMode(void) { if (activeDPMI) return activeDPMI->EnterProtMode(); return 0;}; static Bitu DPMI_EnterRealMode(void) { if (activeDPMI) return activeDPMI->EnterRealMode(); return 0;}; static Bitu DPMI_RealSaveState(void) { if (activeDPMI) return activeDPMI->RealSaveState(); return 0;}; static Bitu DPMI_ProtSaveState(void) { if (activeDPMI) return activeDPMI->ProtSaveState(); return 0;}; static Bitu DPMI_Int2fHandler(void) { if (activeDPMI) return activeDPMI->Int2fHandler(); return 0;}; static Bitu DPMI_Int31Handler(void) { if (activeDPMI) return activeDPMI->Int31Handler(); return 0;}; static Bitu DPMI_API_Int21_MSDOS(void) { if (activeDPMI) return activeDPMI->API_Int21_MSDOS(); return 0;}; static Bitu DPMI_API_Entry_MSDOS(void) { if (activeDPMI) return activeDPMI->API_Entry_MSDOS(); return 0;}; // **************************************************************** // Setup stuff // **************************************************************** RealPt DPMI::HookInterrupt(Bitu num, Bitu intHandler) { // Setup realmode hook RealPt oldVec; Bitu segment, offset; // Allocate Realmode callback RealPt func = CALLBACK_RealPointer(intHandler); if (AllocateRealModeCallback(GDT_CODE,RealOff(func),0x0000,0x0000,segment,offset)) { oldVec = RealGetVec(num); RealSetVec(num,RealMake(segment,offset)); } else E_Exit("DPMI: Couldnt allocate Realmode-Callback for INT %04X",num); // Setup protmode hook func = CALLBACK_RealPointer(intHandler); SetDescriptor gate; gate.Load (dpmi.idt.base+num*8); gate.SetSelector(GDT_CODE); gate.SetOffset (RealOff(func)); gate.Save (dpmi.idt.base+num*8); return oldVec; } void DPMI::RestoreHookedInterrupt(Bitu num, RealPt oldVec) { //.Restore hooked int RealSetVec(num,oldVec); RealPt func = CALLBACK_RealPointer(callback.ptorint); SetDescriptor gate; gate.Load (dpmi.idt.base+num*8); gate.SetSelector(GDT_CODE); gate.SetOffset (RealOff(func)); gate.Save (dpmi.idt.base+num*8); } void DPMI::Terminate(void) { Bitu i; cpu.cpl = 0; dpmi.client.have = false; // 1. Clear the LDT for (i=0; iRemoveIntCallbacks(); } activeDPMI = new DPMI(); return activeDPMI->Entrypoint(); } Bitu DPMI::Entrypoint(void) { /* This should switch to pmode */ if (dpmi.client.have) E_Exit("DPMI:Already have a client"); LOG(LOG_MISC,LOG_ERROR)("DPMI: Entrypoint (%d Bit)",(reg_ax & 1) ? 32:16); MEM_A20_Enable(true); // Create gdt, ldt, idt and other stuff Setup(); // Save Realmode Registers SaveRegisterState(0); dpmi.client.have = true; dpmi.client.bit32 = reg_ax & 1; // Clear XMS Handles ClearXMSHandles(); /* Clear the LDT */ Bitu i; for (i=0;i::iterator i; for(i=g_sharedMemList.begin(); i != g_sharedMemList.end(); i++) delete static_cast(*i); (g_sharedMemList.clear)(); }; void DPMI_Init(Section* sec) { Section_prop * section=static_cast(sec); if (!section->Get_bool("dpmi")) return; memset(&callback,0,sizeof(callback)); /* setup Real mode Callbacks */ callback.entry=CALLBACK_Allocate(); CALLBACK_Setup(callback.entry,DPMI_EntryPoint,CB_RETF); callback.enterpmode=CALLBACK_Allocate(); CALLBACK_Setup(callback.enterpmode,DPMI_EnterProtMode,CB_RETF); callback.realsavestate=CALLBACK_Allocate(); CALLBACK_Setup(callback.realsavestate,DPMI_RealSaveState,CB_RETF); callback.simint=CALLBACK_Allocate(); CALLBACK_Setup(callback.simint,DPMI_SimulateInt,CB_IRET); callback.simintReturn=CALLBACK_Allocate(); CALLBACK_Setup(callback.simintReturn,DPMI_SimulateIntReturn,CB_IRET); callback.rmIntFrame=CALLBACK_Allocate(); CALLBACK_Setup(callback.rmIntFrame,DPMI_CallRealIRETFrame,CB_IRET); callback.rmIntFrameReturn=CALLBACK_Allocate(); CALLBACK_Setup(callback.rmIntFrameReturn,DPMI_CallRealIRETFrameReturn,CB_IRET); callback.ptorint=CALLBACK_Allocate(); CALLBACK_Setup(callback.ptorint,DPMI_ptorHandler,CB_IRET); callback.ptorintReturn=CALLBACK_Allocate(); CALLBACK_Setup(callback.ptorintReturn,DPMI_ptorHandlerReturn,CB_IRET); callback.int21Return=CALLBACK_Allocate(); CALLBACK_Setup(callback.int21Return,DPMI_Int21HandlerReturn,CB_IRET); callback.rmCallbackReturn=CALLBACK_Allocate(); CALLBACK_Setup(callback.rmCallbackReturn,DPMI_RealModeCallbackReturn,CB_IRET); callback.int21msdos=CALLBACK_Allocate(); CALLBACK_Setup(callback.int21msdos,DPMI_API_Int21_MSDOS,CB_IRET); /* Setup multiplex */ DOS_AddMultiplexHandler(DPMI_Multiplex); /* shutdown function */ sec->AddDestroyFunction(&DPMI_ShutDown); } void DPMI::Reactivate() { /* Load GDT and IDT */ CPU_LIDT(dpmi.idt.limit,dpmi.idt.base); CPU_LGDT(dpmi.gdt.limit,dpmi.gdt.base); CPU_LLDT(GDT_LDT); cpu.cpl = DPMI_DPL; RestoreIntCallbacks(); }; void DPMI::Setup() { Bitu i; Bitu xmssize = (TOTAL_SIZE|(DPMI_PAGE_SIZE-1))+1; Bitu protStackSize = ((DPMI_PROTMODE_STACK_MAX*DPMI_PROTMODE_STACKSIZE)|(DPMI_PAGE_SIZE-1))+1; Bitu numPages = ((xmssize+protStackSize) >> 12); #if DPMI_ALLOC_NEEDEDMEM_HIGH /* Allocate the GDT,LDT,IDT Stack space (High Mem) */ dpmi.mem_handle = MEM_AllocatePages(numPages,true); if (dpmi.mem_handle==0) { LOG_MSG("DPMI:Can't allocate XMS memory, disabling dpmi support."); return; } Bitu address = dpmi.mem_handle*DPMI_PAGE_SIZE;; #else // load LDT and stuff in low mem ( Bit16u segment; Bit16u blocks = numPages*4096/16; if (!DOS_AllocateMemory(&segment,&blocks)) { LOG_MSG("DPMI:Can't allocate XMS memory, disabling dpmi support."); return; }; Bitu address = segment * 16; #endif // Allocate real mode stack space rm_ss = DOS_GetMemory(DPMI_REALMODE_STACKSIZE/16); rm_sp = DPMI_REALMODE_STACKSIZE; // Get Begin of protected mode stack dpmi.protStack = address + xmssize; /* Clear the memory */ PhysPt w; for (w=address;w0xFFFFF) || (base & 0x0F)) E_Exit("DPMI:MSDOS: Invalid Selector (convert to segment not possible)"); base >>= 4; } else E_Exit("DPMI:MSDOS: Invalid Selector (not found)"); return base; }; bool DPMI::GetMsdosSelector(Bitu realseg, Bitu realoff, Bitu &protsel, Bitu &protoff) { if (AllocateLDTDescriptor(1,protsel)) { SetDescriptor desc; desc.Load (dpmi.ldt.base+(protsel & ~7)); desc.SetBase (realseg<<4); desc.SetLimit (0xFFFF); desc.Save (dpmi.ldt.base+(protsel & ~7)); } else E_Exit("DPMI:MSDOS: No more selectors."); protoff = realoff; return true; }; void DPMI::API_Init_MSDOS(void) { // Enable special Int 21 Handler.... RealPt func = CALLBACK_RealPointer(callback.int21msdos); SetDescriptor gate; gate.Load (dpmi.idt.base+0x21*8); gate.SetSelector(GDT_CODE); gate.SetOffset (RealOff(func)); gate.Save (dpmi.idt.base+0x21*8); }; Bitu DPMI::API_Entry_MSDOS(void) { LOG(LOG_MISC,LOG_WARN)("DPMI: MSDOS Extension API Entry."); switch (reg_ax) { case 0x0000:// Get MS-DOS Extension Version reg_ax = 0x0000; SETFLAGBIT(CF,false); break; case 0x0100:// Get Selector to Base of LDT // Note that the DPMI host has the option of either failing // this call, or to return a read-only descriptor (we fail it). SETFLAGBIT(CF,true); break; default: SETFLAGBIT(CF,true); LOG(LOG_MISC,LOG_ERROR)("DPMI:MSDOS-API:Unknown ax on entry point %04X.",reg_ax); break; } return 0; }; Bitu DPMI::Mask(Bitu value) { if (dpmi.client.bit32) return value; else return value & 0xFFFF; }; Bitu DPMI::API_Int21_MSDOS(void) { DPMI_LOG("DPMI:MSDOS-API:INT 21 %04X",reg_ax); Bitu protsel,protoff,seg,off; Bitu sax = reg_ax; switch (reg_ah) { case 0x1a: /* Set Disk Transfer Area Address */ dtaAddress = SegPhys(ds) + Mask(reg_edx); break; case 0x25: { // Set Protected mode Interrupt Vector if (dpmi.pharlap) { //LOG(LOG_MISC,LOG_ERROR) switch (reg_al) { case 0x05: // Set Real mode Int Vector RealSetVec(reg_cl,Mask(reg_ebx)); DPMI_CALLBACK_SCF(false); break; default : E_Exit("DPMI:PHARLAP:System call %04X",reg_ax); }; } else { // ms dos api SetDescriptor gate; gate.Clear(); gate.saved.seg.p=1; gate.SetSelector(SegValue(ds)); gate.SetOffset (Mask(reg_edx)); gate.SetType (dpmi.client.bit32?DESC_386_INT_GATE:DESC_286_INT_GATE); gate.saved.seg.dpl = DPMI_DPL; gate.Save(dpmi.idt.base+reg_al*8); } }; break; case 0x35: { // Get Protected Mode Interrupt Vector SetDescriptor gate; gate.Load(dpmi.idt.base+reg_al*8); CPU_SetSegGeneral(es,gate.GetSelector()); reg_ebx = gate.GetOffset(); DPMI_CALLBACK_SCF(false); }; break; case 0x2f: /* Get Disk Transfer Area */ seg = RealSeg(dos.dta); off = RealOff(dos.dta); GetMsdosSelector(seg,off,protsel,protoff); CPU_SetSegGeneral(es,protsel); reg_ebx = protoff; break; case 0x34 : // Get INDOS Flag address seg = RealSeg(dos.tables.indosflag); off = RealOff(dos.tables.indosflag); GetMsdosSelector(seg,off,protsel,protoff); CPU_SetSegGeneral(es,protsel); reg_bx = protoff; break; case 0x39:{ // MKDIR Create directory char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); if (DOS_MakeDir(name1)) { CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); } }; break; case 0x3c: { /* CREATE Create of truncate file */ char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); if (DOS_CreateFile(name1,reg_cx,®_ax)) { DPMI_CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } }; break; case 0x3d: { /* OPEN Open existing file */ char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); if (DOS_OpenFile(name1,reg_al,®_ax)) { DPMI_LOG("DOS: Open success: %s",name1); DPMI_CALLBACK_SCF(false); } else { DOS_PSP psp(dos.psp); psp.SetNumFiles(40); if (DOS_OpenFile(name1,reg_al,®_ax)) { DPMI_LOG_ERROR("DOS: Open success (Hack): %s",name1); DPMI_CALLBACK_SCF(false); break; }; reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } };break; case 0x3f: /* READ Read from file or device */ { if (reg_ecx>0xFFFF) { E_Exit("DPMI:DOS: Read file size > 0xffff"); }; Bit16u toread = Mask(reg_ecx); dos.echo = true; if (DOS_ReadFile(reg_bx,dos_copybuf,&toread)) { MEM_BlockWrite(SegPhys(ds)+Mask(reg_edx),dos_copybuf,toread); reg_eax=toread; DPMI_CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } dos.echo=false; break; } case 0x40: {/* WRITE Write to file or device */ Bit16u towrite = Mask(reg_ecx); MEM_BlockRead(SegPhys(ds)+Mask(reg_edx),dos_copybuf,towrite); if (reg_bx>=5) LOG(LOG_MISC,LOG_ERROR)("INT 21 40: %s",dos_copybuf); if (DOS_WriteFile(reg_bx,dos_copybuf,&towrite)) { reg_eax=towrite; DPMI_CALLBACK_SCF(false); } else { DPMI_LOG_ERROR("DPMI:MSDOS:Write file failure."); reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } }; break; case 0x41: { /* UNLINK Delete file */ char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); if (DOS_UnlinkFile(name1)) { DPMI_CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } }; break; case 0x42: /* LSEEK Set current file position */ { Bit32u pos=(reg_cx<<16) + reg_dx; if (DOS_SeekFile(reg_bx,&pos,reg_al)) { reg_dx=(Bit16u)(pos >> 16); reg_ax=(Bit16u)(pos & 0xFFFF); DPMI_CALLBACK_SCF(false); } else { reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); } break; } case 0x43: { /* Get/Set file attributes */ char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); switch (reg_al) case 0x00: /* Get */ { if (DOS_GetFileAttr(name1,®_cx)) { DPMI_CALLBACK_SCF(false); } else { DPMI_CALLBACK_SCF(true); reg_ax=dos.errorcode; } break; case 0x01: /* Set */ DPMI_LOG_ERROR("DOS:Set File Attributes for %s not supported",name1); DPMI_CALLBACK_SCF(false); break; default: E_Exit("DOS:0x43:Illegal subfunction %2X",reg_al); } }; break; case 0x4E: {/* Get first dir entry */ char name1[256]; MEM_StrCopy(SegPhys(ds)+Mask(reg_edx),name1,255); if (DOS_FindFirst(name1,reg_cx)) { DPMI_CALLBACK_SCF(false); // Copy result to internal dta if (dtaAddress) MEM_BlockCopy(dtaAddress,PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dpmi.pharlap?43:128); reg_ax=0; /* Undocumented */ } else { reg_ax=dos.errorcode; DPMI_CALLBACK_SCF(true); }; }; break; case 0x4f: /* FINDNEXT Find next matching file */ // Copy data to dos dta if (dtaAddress) MEM_BlockCopy(PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dtaAddress,dpmi.pharlap?43:128); if (DOS_FindNext()) { CALLBACK_SCF(false); // Copy result to internal dta if (dtaAddress) MEM_BlockCopy(dtaAddress,PhysMake(RealSeg(dos.dta),RealOff(dos.dta)),dpmi.pharlap?43:128); reg_ax=0xffff; /* Undocumented */ } else { reg_ax=dos.errorcode; CALLBACK_SCF(true); }; break; case 0x50: /* Set current PSP */ if (dpmi.pharlap) dos.psp = reg_bx; // pharlap uses real mode paragraph address else dos.psp = GetSegmentFromSelector(reg_bx); DPMI_LOG("DPMI:MSDOS:0x50:Set current psp:%04X",reg_bx); break; case 0x51: /* Get current PSP */ if (dpmi.pharlap) reg_bx = dos.psp; // pharlap uses real mode paragraph address else { GetMsdosSelector(dos.psp,0x0000,protsel,protoff); reg_bx = protsel; }; DPMI_LOG("DPMI:MSDOS:0x51:Get current psp:%04X",reg_bx); break; case 0x55 : { // Neuen PSP erstellen Bitu segment = GetSegmentFromSelector(reg_dx); DOS_ChildPSP(segment,reg_si); dos.psp = segment; if (dpmi.pharlap) { DOS_PSP psp(dos.psp); psp.SetNumFiles(40); }; DPMI_LOG("DPMI:MSDOS:0x55:Create new psp:%04X",segment); }; break; case 0x5D : // Get Address of dos swappable area // FIXME: This is totally faked... // FIXME: Add size in bytes (at least pharlap) // FIXME: Depending on al, two functions (pharlap) GetMsdosSelector(0xDEAD,0xDEAD,protsel,protoff); CPU_SetSegGeneral(ds,protsel); reg_si = protoff; DPMI_LOG("DPMI:MSDOS:0x5D:Get Addres of DOS SwapArea:%04X",reg_si); break; case 0x62 : /* Get Current PSP Address */ GetMsdosSelector(dos.psp,0x0000,protsel,protoff); reg_bx = protsel; DPMI_LOG("DPMI:MSDOS:0x62:Get current psp:%04X",reg_bx); break; case 0x68: // Flush file to disc DPMI_CALLBACK_SCF(false); break; case 0x09: case 0x0A: case 0x0C: case 0x1B: case 0x1C: case 0x26: // Pharlap != MS-DOS // case 0x30: // Pharlap extended information case 0x31: case 0x32: case 0x38: case 0x3A: case 0x3B: case 0x47: case 0x48: // Pharlap = 4KB mem pages case 0x49: case 0x4A: // Pharlap = 4KB mem pages case 0x4B: case 0x52: case 0x53: case 0x56: case 0x59: case 0x5A: case 0x5B: case 0x5E: case 0x5F: case 0x60: // case 0x62: case 0x65: case 0x6C: E_Exit("DPMI:MSDOS-API:function %04X not yet supported.",reg_ax); break; // *** PASS THROUGH *** case 0x44: if ((reg_al==0x02) || (reg_al==0x03) || (reg_al==0x04) || (reg_al==0x05) || (reg_al==0x0C) || (reg_al==0x0D)) { E_Exit("DPMI:MSDOS-API:function %04X not yet supported.",reg_ax); }; case 0x0E: case 0x19: case 0x2A: case 0x2C: case 0x2D: case 0x30: case 0x36: case 0x3E: case 0x4C: case 0x58: case 0x67: { // reflect to real mode DPMI_Int21Handler(); }; break; default: E_Exit("DPMI:MSDOS-API:Missing function %04X",reg_ax); }; return 0; };