The presence of FGetComponentPath is used to check if mapi32.dll is
[wine] / dlls / winedos / int31.c
index 9dcc21f..36f9999 100644 (file)
 #include "config.h"
 #include "wine/port.h"
 
+#include <stdarg.h>
+
 #include "windef.h"
+#include "winbase.h"
 #include "wine/winbase16.h"
-#include "miscemu.h"
-#include "task.h"
-#include "msdos.h"
+#include "wownt32.h"
 #include "dosexe.h"
 
+#include "excpt.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
+#include "thread.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(int31);
 
@@ -63,6 +67,72 @@ typedef struct tagRMCB {
 
 static RMCB *FirstRMCB = NULL;
 static WORD dpmi_flag;
+static void* lastvalloced = NULL;
+static BYTE DPMI_retval;
+
+/**********************************************************************
+ *          DOSVM_IsDos32
+ * 
+ * Return TRUE if we are in 32-bit protected mode DOS process.
+ */
+BOOL DOSVM_IsDos32(void)
+{
+  return (dpmi_flag & 1) ? TRUE : FALSE;
+}
+
+
+/**********************************************************************
+ *          alloc_pm_selector
+ *
+ * Allocate a 64k sized selector corresponding to a real mode segment.
+ */
+static WORD alloc_pm_selector( WORD seg, unsigned char flags )
+{
+    WORD sel = wine_ldt_alloc_entries( 1 );
+
+    if (sel)
+    {
+        LDT_ENTRY entry;
+        wine_ldt_set_base( &entry, (void *)(seg << 4) );
+        wine_ldt_set_limit( &entry, 0xffff );
+        wine_ldt_set_flags( &entry, flags );
+        wine_ldt_set_entry( sel, &entry );
+    }
+    return sel;
+}
+
+
+/**********************************************************************
+ *          dpmi_exception_handler
+ *
+ * Handle EXCEPTION_VM86_STI exceptions generated
+ * when there are pending asynchronous events.
+ */
+static WINE_EXCEPTION_FILTER(dpmi_exception_handler)
+{
+#ifdef __i386__
+    EXCEPTION_RECORD *rec = GetExceptionInformation()->ExceptionRecord;
+    CONTEXT *context = GetExceptionInformation()->ContextRecord;
+
+    if (rec->ExceptionCode == EXCEPTION_VM86_STI)
+    {
+        if (ISV86(context))
+            ERR( "Real mode STI caught by protected mode handler!\n" );
+        DOSVM_SendQueuedEvents(context);
+        return EXCEPTION_CONTINUE_EXECUTION;
+    }
+    else if (rec->ExceptionCode == EXCEPTION_VM86_INTx)
+    {
+        if (ISV86(context))
+            ERR( "Real mode INTx caught by protected mode handler!\n" );
+        DPMI_retval = (BYTE)rec->ExceptionInformation[0];
+        return EXCEPTION_EXECUTE_HANDLER;
+    }
+
+#endif
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
 
 /**********************************************************************
  *         INT_GetRealModeContext
@@ -111,6 +181,107 @@ static void INT_SetRealModeContext( REALMODECALL *call, CONTEXT86 *context )
     call->ss  = context->SegSs;
 }
 
+/**********************************************************************
+ *          DPMI_xalloc
+ * special virtualalloc, allocates lineary monoton growing memory.
+ * (the usual VirtualAlloc does not satisfy that restriction)
+ */
+static LPVOID DPMI_xalloc( DWORD len ) 
+{
+    LPVOID  ret;
+    LPVOID  oldlastv = lastvalloced;
+
+    if (lastvalloced) 
+    {
+        int xflag = 0;
+
+        ret = NULL;
+        while (!ret) 
+        {
+            ret = VirtualAlloc( lastvalloced, len,
+                                MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
+            if (!ret)
+                lastvalloced = (char *) lastvalloced + 0x10000;
+
+            /* we failed to allocate one in the first round.
+             * try non-linear
+             */
+            if (!xflag && (lastvalloced<oldlastv)) 
+            { 
+                /* wrapped */
+                FIXME( "failed to allocate linearly growing memory (%ld bytes), "
+                       "using non-linear growing...\n", len );
+                xflag++;
+            }
+
+            /* if we even fail to allocate something in the next
+             * round, return NULL
+             */
+            if ((xflag==1) && (lastvalloced >= oldlastv))
+                xflag++;
+
+            if ((xflag==2) && (lastvalloced < oldlastv)) {
+                FIXME( "failed to allocate any memory of %ld bytes!\n", len );
+                return NULL;
+            }
+        }
+    } 
+    else
+    {
+        ret = VirtualAlloc( NULL, len, 
+                            MEM_COMMIT|MEM_RESERVE, PAGE_EXECUTE_READWRITE );
+    }
+
+    lastvalloced = (LPVOID)(((DWORD)ret+len+0xffff)&~0xffff);
+    return ret;
+}
+
+/**********************************************************************
+ *          DPMI_xfree
+ */
+static void DPMI_xfree( LPVOID ptr ) 
+{
+    VirtualFree( ptr, 0, MEM_RELEASE );
+}
+
+/**********************************************************************
+ *          DPMI_xrealloc
+ *
+ * FIXME: perhaps we could grow this mapped area... 
+ */
+static LPVOID DPMI_xrealloc( LPVOID ptr, DWORD newsize ) 
+{
+    MEMORY_BASIC_INFORMATION        mbi;
+    LPVOID                          newptr;
+
+    newptr = DPMI_xalloc( newsize );
+    if (ptr) 
+    {
+        if (!VirtualQuery(ptr,&mbi,sizeof(mbi))) 
+        {
+            FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
+            return NULL;
+        }
+
+        if (mbi.State == MEM_FREE) 
+        {
+            FIXME( "realloc of DPMI_xallocd region %p?\n", ptr );
+            return NULL;
+        }
+
+        /* We do not shrink allocated memory. most reallocs
+         * only do grows anyway
+         */
+        if (newsize <= mbi.RegionSize)
+            return ptr;
+
+        memcpy( newptr, ptr, mbi.RegionSize );
+        DPMI_xfree( ptr );
+    }
+
+    return newptr;
+}
+
 
 #ifdef __i386__
 
@@ -182,16 +353,21 @@ __ASM_GLOBAL_FUNC(DPMI_CallRMCB32,
  */
 static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
 {
-    if (IS_SELECTOR_SYSTEM( rmcb->proc_sel )) {
+    DWORD old_vif = NtCurrentTeb()->dpmi_vif;
+
+    /* Disable virtual interrupts. */
+    NtCurrentTeb()->dpmi_vif = 0;
+
+    if (wine_ldt_is_system( rmcb->proc_sel )) {
         /* Wine-internal RMCB, call directly */
         ((RMCBPROC)rmcb->proc_ofs)(context);
-    } else {
+    } else __TRY {
 #ifdef __i386__
         UINT16 ss,es;
         DWORD esp,edi;
 
         INT_SetRealModeContext(MapSL(MAKESEGPTR( rmcb->regs_sel, rmcb->regs_ofs )), context);
-        ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, WINE_LDT_FLAGS_DATA );
+        ss = alloc_pm_selector( context->SegSs, WINE_LDT_FLAGS_DATA );
         esp = context->Esp;
 
         FIXME("untested!\n");
@@ -216,16 +392,19 @@ static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
             ctx.SegEs = rmcb->regs_sel;
             ctx.Edi   = rmcb->regs_ofs;
             /* FIXME: I'm pretty sure this isn't right - should push flags first */
-            wine_call_to_16_regs_short(&ctx, 0);
+            WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&ctx );
             es = ctx.SegEs;
             edi = ctx.Edi;
         }
-       FreeSelector16(ss);
+        wine_ldt_free_entries( ss, 1 );
         INT_GetRealModeContext( MapSL( MAKESEGPTR( es, edi )), context);
 #else
         ERR("RMCBs only implemented for i386\n");
 #endif
-    }
+    } __EXCEPT(dpmi_exception_handler) { } __ENDTRY
+        
+    /* Restore virtual interrupt flag. */
+    NtCurrentTeb()->dpmi_vif = old_vif;                                 
 }
 
 
@@ -272,7 +451,7 @@ callrmproc_again:
 /* shortcut for chaining to internal interrupt handlers */
     if ((context->SegCs == 0xF000) && iret)
     {
-        DOSVM_RealModeInterrupt( LOWORD(context->Eip)/4, context);
+        DOSVM_CallBuiltinHandler( context, LOWORD(context->Eip)/4 );
         return 0;
     }
 
@@ -310,7 +489,7 @@ callrmproc_again:
             *stack16 = LOWORD(context->EFlags);
         }
         /* push return address (return to interrupt wrapper) */
-        *(--stack16) = DOSMEM_wrap_seg;
+        *(--stack16) = DOSVM_dpmi_segments->wrap_seg;
         *(--stack16) = 0;
         /* adjust stack */
         context->Esp -= 2*sizeof(WORD);
@@ -321,7 +500,7 @@ callrmproc_again:
         /* RMCB call, invoke protected-mode handler directly */
         DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag);
         /* check if we returned to where we thought we would */
-        if ((context->SegCs != DOSMEM_wrap_seg) ||
+        if ((context->SegCs != DOSVM_dpmi_segments->wrap_seg) ||
             (LOWORD(context->Eip) != 0)) {
             /* we need to continue at different address in real-mode space,
                so we need to set it all up for real mode again */
@@ -344,7 +523,9 @@ void WINAPI DOSVM_CallRMInt( CONTEXT86 *context )
 {
     CONTEXT86 realmode_ctx;
     FARPROC16 rm_int = DOSVM_GetRMHandler( BL_reg(context) );
-    REALMODECALL *call = MapSL( MAKESEGPTR( context->SegEs, DI_reg(context) ));
+    REALMODECALL *call = CTX_SEG_OFF_TO_LIN( context, 
+                                             context->SegEs, 
+                                             context->Edi );
     INT_GetRealModeContext( call, &realmode_ctx );
 
     /* we need to check if a real-mode program has hooked the interrupt */
@@ -358,7 +539,7 @@ void WINAPI DOSVM_CallRMInt( CONTEXT86 *context )
         RESET_CFLAG(context);
         /* use the IP we have instead of BL_reg, in case some apps
            decide to move interrupts around for whatever reason... */
-        DOSVM_RealModeInterrupt( LOWORD(rm_int)/4, &realmode_ctx );
+        DOSVM_CallBuiltinHandler( &realmode_ctx, LOWORD(rm_int)/4 );
     }
     INT_SetRealModeContext( call, &realmode_ctx );
 }
@@ -369,7 +550,9 @@ void WINAPI DOSVM_CallRMInt( CONTEXT86 *context )
  */
 void WINAPI DOSVM_CallRMProc( CONTEXT86 *context, int iret )
 {
-    REALMODECALL *p = MapSL( MAKESEGPTR( context->SegEs, DI_reg(context) ));
+    REALMODECALL *p = CTX_SEG_OFF_TO_LIN( context, 
+                                          context->SegEs, 
+                                          context->Edi );
     CONTEXT86 context16;
 
     TRACE("RealModeCall: EAX=%08lx EBX=%08lx ECX=%08lx EDX=%08lx\n",
@@ -403,42 +586,60 @@ static void StartPM( CONTEXT86 *context )
     RESET_CFLAG(context);
     dpmi_flag = AX_reg(context);
 /* our mode switch wrapper have placed the desired CS into DX */
-    cs = SELECTOR_AllocBlock( (void *)(DX_reg(context)<<4), 0x10000, WINE_LDT_FLAGS_CODE );
+    cs = alloc_pm_selector( context->Edx, WINE_LDT_FLAGS_CODE );
 /* due to a flaw in some CPUs (at least mine), it is best to mark stack segments as 32-bit if they
    can be used in 32-bit code. Otherwise, these CPUs may not set the high word of esp during a
    ring transition (from kernel code) to the 16-bit stack, and this causes trouble if executing
    32-bit code using this stack. */
     if (dpmi_flag & 1) selflags |= WINE_LDT_FLAGS_32BIT;
-    ss = SELECTOR_AllocBlock( (void *)(context->SegSs<<4), 0x10000, selflags );
+    ss = alloc_pm_selector( context->SegSs, selflags );
 /* do the same for the data segments, just in case */
     if (context->SegDs == context->SegSs) ds = ss;
-    else ds = SELECTOR_AllocBlock( (void *)(context->SegDs<<4), 0x10000, selflags );
-    es = SELECTOR_AllocBlock( psp, 0x100, selflags );
+    else ds = alloc_pm_selector( context->SegDs, selflags );
+    es = alloc_pm_selector( DOSVM_psp, selflags );
 /* convert environment pointer, as the spec says, but we're a bit lazy about the size here... */
-    psp->environment = SELECTOR_AllocBlock( (void *)(env_seg<<4), 0x10000, WINE_LDT_FLAGS_DATA );
+    psp->environment = alloc_pm_selector( env_seg, WINE_LDT_FLAGS_DATA );
 
     pm_ctx = *context;
-    pm_ctx.SegCs = DOSMEM_dpmi_sel;
+    pm_ctx.SegCs = DOSVM_dpmi_segments->dpmi_sel;
 /* our mode switch wrapper expects the new CS in DX, and the new SS in AX */
     pm_ctx.Eax   = ss;
     pm_ctx.Edx   = cs;
     pm_ctx.SegDs = ds;
     pm_ctx.SegEs = es;
-    pm_ctx.SegFs = 0;
-    pm_ctx.SegGs = 0;
+    pm_ctx.SegFs = wine_get_fs();
+    pm_ctx.SegGs = wine_get_gs();
+    pm_ctx.EFlags &= ~V86_FLAG;
 
-    TRACE("DOS program is now entering protected mode\n");
-    wine_call_to_16_regs_short(&pm_ctx, 0);
+    TRACE("DOS program is now entering %d-bit protected mode\n", 
+          DOSVM_IsDos32() ? 32 : 16);
 
-    /* in the current state of affairs, we won't ever actually return here... */
-    /* we should have int21/ah=4c do it someday, though... */
+    __TRY 
+    {
+        WOWCallback16Ex( 0, WCB16_REGS, 0, NULL, (DWORD *)&pm_ctx );
+    } 
+    __EXCEPT(dpmi_exception_handler) 
+    { 
+    } 
+    __ENDTRY
+
+    TRACE( "Protected mode DOS program is terminating\n" );
+
+    /*
+     * FIXME: Instead of calling ExitThread, we should release all
+     *        allocated protected mode resources and call MZ_Exit
+     *        using real mode context. See DPMI specification.
+     */
+    ExitThread( DPMI_retval );
 
-    FreeSelector16(psp->environment);
+#if 0
+    wine_ldt_free_entries( psp->environment, 1 );
     psp->environment = env_seg;
-    FreeSelector16(es);
-    if (ds != ss) FreeSelector16(ds);
-    FreeSelector16(ss);
-    FreeSelector16(cs);
+    wine_ldt_free_entries(es,1);
+    if (ds != ss) wine_ldt_free_entries(ds,1);
+    wine_ldt_free_entries(ss,1);
+    wine_ldt_free_entries(cs,1);
+#endif
 }
 
 static RMCB *DPMI_AllocRMCB( void )
@@ -496,7 +697,7 @@ static int DPMI_FreeRMCB( DWORD address )
        PrevRMCB->next = CurrRMCB->next;
            else
        FirstRMCB = CurrRMCB->next;
-       DOSMEM_FreeBlock(DOSMEM_MapRealToLinear(CurrRMCB->address));
+       DOSMEM_FreeBlock(PTR_REAL_TO_LIN(SELECTOROF(CurrRMCB->address),OFFSETOF(CurrRMCB->address)));
        HeapFree(GetProcessHeap(), 0, CurrRMCB);
        return 0;
     }
@@ -510,33 +711,33 @@ void WINAPI DPMI_FreeInternalRMCB( FARPROC16 proc )
 }
 
 
-/* DPMI Raw Mode Switch handler */
-
-#if 0
-void WINAPI DPMI_RawModeSwitch( SIGCONTEXT *context )
+/**********************************************************************
+ *         DOSVM_RawModeSwitchHandler
+ *
+ * DPMI Raw Mode Switch handler
+ */
+void WINAPI DOSVM_RawModeSwitchHandler( CONTEXT86 *context )
 {
-  LPDOSTASK lpDosTask = MZ_Current();
   CONTEXT86 rm_ctx;
   int ret;
 
-  if (!lpDosTask) {
-    /* we could probably start a DPMI-only dosmod task here, but I doubt
-       anything other than real DOS apps want to call raw mode switch */
-    ERR("attempting raw mode switch without DOS task!\n");
-    ExitProcess(1);
-  }
   /* initialize real-mode context as per spec */
   memset(&rm_ctx, 0, sizeof(rm_ctx));
-  rm_ctx.SegDs  = AX_sig(context);
-  rm_ctx.SegEs  = CX_sig(context);
-  rm_ctx.SegSs  = DX_sig(context);
-  rm_ctx.Esp    = EBX_sig(context);
-  rm_ctx.SegCs  = SI_sig(context);
-  rm_ctx.Eip    = EDI_sig(context);
-  rm_ctx.Ebp    = EBP_sig(context);
+  rm_ctx.SegDs  = AX_reg(context);
+  rm_ctx.SegEs  = CX_reg(context);
+  rm_ctx.SegSs  = DX_reg(context);
+  rm_ctx.Esp    = context->Ebx;
+  rm_ctx.SegCs  = SI_reg(context);
+  rm_ctx.Eip    = context->Edi;
+  rm_ctx.Ebp    = context->Ebp;
   rm_ctx.SegFs  = 0;
   rm_ctx.SegGs  = 0;
-  rm_ctx.EFlags = EFL_sig(context); /* at least we need the IF flag */
+
+  /* Copy interrupt state. */
+  if (NtCurrentTeb()->dpmi_vif)
+      rm_ctx.EFlags = V86_FLAG | VIF_MASK;
+  else
+      rm_ctx.EFlags = V86_FLAG;
 
   /* enter real mode again */
   TRACE("re-entering real mode at %04lx:%04lx\n",rm_ctx.SegCs,rm_ctx.Eip);
@@ -545,26 +746,32 @@ void WINAPI DPMI_RawModeSwitch( SIGCONTEXT *context )
      DOSVM_Enter will return and we will continue here */
 
   if (ret<0) {
+    ERR("Sync lost!\n");
     /* if the sync was lost, there's no way to recover */
     ExitProcess(1);
   }
 
   /* alter protected-mode context as per spec */
-  DS_sig(context)  = LOWORD(rm_ctx.Eax);
-  ES_sig(context)  = LOWORD(rm_ctx.Ecx);
-  SS_sig(context)  = LOWORD(rm_ctx.Edx);
-  ESP_sig(context) = rm_ctx.Ebx;
-  CS_sig(context)  = LOWORD(rm_ctx.Esi);
-  EIP_sig(context) = rm_ctx.Edi;
-  EBP_sig(context) = rm_ctx.Ebp;
-  FS_sig(context) = 0;
-  GS_sig(context) = 0;
+  context->SegDs   = LOWORD(rm_ctx.Eax);
+  context->SegEs   = LOWORD(rm_ctx.Ecx);
+  context->SegSs   = LOWORD(rm_ctx.Edx);
+  context->Esp     = rm_ctx.Ebx;
+  context->SegCs   = LOWORD(rm_ctx.Esi);
+  context->Eip     = rm_ctx.Edi;
+  context->Ebp     = rm_ctx.Ebp;
+  context->SegFs   = 0;
+  context->SegGs   = 0;
+
+  /* Copy interrupt state. */
+  if (rm_ctx.EFlags & VIF_MASK)
+      NtCurrentTeb()->dpmi_vif = 1;
+  else
+      NtCurrentTeb()->dpmi_vif = 0;
 
   /* Return to new address and hope that we didn't mess up */
-  TRACE("re-entering protected mode at %04x:%08lx\n",
-       CS_sig(context), EIP_sig(context));
+  TRACE("re-entering protected mode at %04lx:%08lx\n",
+      context->SegCs, context->Eip);
 }
-#endif
 
 
 /**********************************************************************
@@ -578,17 +785,16 @@ void WINAPI DOSVM_AllocRMCB( CONTEXT86 *context )
 
     if (NewRMCB)
     {
-       /* FIXME: if 32-bit DPMI client, use ESI and EDI */
-       NewRMCB->proc_ofs = LOWORD(context->Esi);
+       NewRMCB->proc_ofs = DOSVM_IsDos32() ? context->Esi : LOWORD(context->Esi);
        NewRMCB->proc_sel = context->SegDs;
-       NewRMCB->regs_ofs = LOWORD(context->Edi);
+       NewRMCB->regs_ofs = DOSVM_IsDos32() ? context->Edi : LOWORD(context->Edi);
        NewRMCB->regs_sel = context->SegEs;
-       SET_LOWORD( context->Ecx, HIWORD(NewRMCB->address) );
-       SET_LOWORD( context->Edx, LOWORD(NewRMCB->address) );
+       SET_CX( context, HIWORD(NewRMCB->address) );
+       SET_DX( context, LOWORD(NewRMCB->address) );
     }
     else
     {
-       SET_LOWORD( context->Eax, 0x8015 ); /* callback unavailable */
+       SET_AX( context, 0x8015 ); /* callback unavailable */
        SET_CFLAG(context);
     }
 }
@@ -603,31 +809,29 @@ void WINAPI DOSVM_FreeRMCB( CONTEXT86 *context )
           CX_reg(context), DX_reg(context));
 
     if (DPMI_FreeRMCB(MAKELONG(DX_reg(context), CX_reg(context)))) {
-       SET_LOWORD( context->Eax, 0x8024 ); /* invalid callback address */
+       SET_AX( context, 0x8024 ); /* invalid callback address */
        SET_CFLAG(context);
     }
 }
 
 
 /**********************************************************************
- *         DOSVM_Int31Handler
+ *         DOSVM_CheckWrappers
  *
- * Handler for real-mode int 31h (DPMI).
+ * Check if this was really a wrapper call instead of an interrupt.
  */
-void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
+BOOL DOSVM_CheckWrappers( CONTEXT86 *context )
 {
-    /* check if it's our wrapper */
-    TRACE("called from real mode\n");
-    if (context->SegCs==DOSMEM_dpmi_seg) {
+    if (context->SegCs==DOSVM_dpmi_segments->dpmi_seg) {
         /* This is the protected mode switch */
         StartPM(context);
-        return;
+        return TRUE;
     }
-    else if (context->SegCs==DOSMEM_xms_seg)
+    else if (context->SegCs==DOSVM_dpmi_segments->xms_seg)
     {
         /* This is the XMS driver entry point */
         XMS_Handler(context);
-        return;
+        return TRUE;
     }
     else
     {
@@ -640,10 +844,569 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
         if (CurrRMCB) {
             /* RMCB call, propagate to protected-mode handler */
             DPMI_CallRMCBProc(context, CurrRMCB, dpmi_flag);
-            return;
+            return TRUE;
         }
     }
 
-    /* chain to protected mode handler */
-    INT_Int31Handler( context );
+    return FALSE;
+}
+
+/**********************************************************************
+ *         DOSVM_Int31Handler (WINEDOS16.149)
+ *
+ * Handler for int 31h (DPMI).
+ */
+void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
+{
+    RESET_CFLAG(context);
+    switch(AX_reg(context))
+    {
+    case 0x0000:  /* Allocate LDT descriptors */
+        TRACE( "allocate LDT descriptors (%d)\n", CX_reg(context) );
+        {
+            WORD sel =  AllocSelectorArray16( CX_reg(context) );
+            if(!sel) 
+            {
+               TRACE( "failed\n" );
+               SET_AX( context, 0x8011 ); /* descriptor unavailable */
+               SET_CFLAG( context );
+            } 
+            else 
+            { 
+                TRACE( "success, array starts at 0x%04x\n", sel );
+                SET_AX( context, sel );      
+            }
+        }
+        break;
+
+    case 0x0001:  /* Free LDT descriptor */
+        TRACE( "free LDT descriptor (0x%04x)\n", BX_reg(context) );
+        if (FreeSelector16( BX_reg(context) ))
+        {
+            SET_AX( context, 0x8022 );  /* invalid selector */
+            SET_CFLAG( context );
+        }
+        else
+        {
+            /* If a segment register contains the selector being freed, */
+            /* set it to zero. */
+            if (!((context->SegDs^BX_reg(context)) & ~3)) context->SegDs = 0;
+            if (!((context->SegEs^BX_reg(context)) & ~3)) context->SegEs = 0;
+            if (!((context->SegFs^BX_reg(context)) & ~3)) context->SegFs = 0;
+            if (!((context->SegGs^BX_reg(context)) & ~3)) context->SegGs = 0;
+        }
+        break;
+
+    case 0x0002:  /* Real mode segment to descriptor */
+        TRACE( "real mode segment to descriptor (0x%04x)\n", BX_reg(context) );
+        {
+            WORD entryPoint = 0;  /* KERNEL entry point for descriptor */
+            switch(BX_reg(context))
+            {
+            case 0x0000: entryPoint = 183; break;  /* __0000H */
+            case 0x0040: entryPoint = 193; break;  /* __0040H */
+            case 0xa000: entryPoint = 174; break;  /* __A000H */
+            case 0xb000: entryPoint = 181; break;  /* __B000H */
+            case 0xb800: entryPoint = 182; break;  /* __B800H */
+            case 0xc000: entryPoint = 195; break;  /* __C000H */
+            case 0xd000: entryPoint = 179; break;  /* __D000H */
+            case 0xe000: entryPoint = 190; break;  /* __E000H */
+            case 0xf000: entryPoint = 194; break;  /* __F000H */
+            default:
+                SET_AX( context, DOSMEM_AllocSelector(BX_reg(context)) );
+                break;
+            }
+            if (entryPoint)
+            {
+                FARPROC16 proc = GetProcAddress16( GetModuleHandle16( "KERNEL" ),
+                                                   (LPCSTR)(ULONG_PTR)entryPoint );
+                SET_AX( context, LOWORD(proc) );
+            }
+        }
+        break;
+
+    case 0x0003:  /* Get next selector increment */
+        TRACE("get selector increment (__AHINCR)\n");
+        context->Eax = __AHINCR;
+        break;
+
+    case 0x0004:  /* Lock selector (not supported) */
+        FIXME("lock selector not supported\n");
+        context->Eax = 0;  /* FIXME: is this a correct return value? */
+        break;
+
+    case 0x0005:  /* Unlock selector (not supported) */
+        FIXME("unlock selector not supported\n");
+        context->Eax = 0;  /* FIXME: is this a correct return value? */
+        break;
+
+    case 0x0006:  /* Get selector base address */
+        TRACE( "get selector base address (0x%04x)\n", BX_reg(context) );
+        {
+            LDT_ENTRY entry;
+            WORD sel = BX_reg(context);
+            wine_ldt_get_entry( sel, &entry );
+            if (wine_ldt_is_empty(&entry))
+            {
+                context->Eax = 0x8022;  /* invalid selector */
+                SET_CFLAG(context);
+            }
+            else
+            {
+                void *base = wine_ldt_get_base(&entry);
+                SET_CX( context, HIWORD(base) );
+                SET_DX( context, LOWORD(base) );
+            }
+        }
+        break;
+
+    case 0x0007:  /* Set selector base address */
+        {
+            DWORD base = MAKELONG( DX_reg(context), CX_reg(context) );
+            WORD  sel = BX_reg(context);
+            TRACE( "set selector base address (0x%04x,0x%08lx)\n", sel, base );
+
+            /* check if Win16 app wants to access lower 64K of DOS memory */
+            if (base < 0x10000 && DOSVM_IsWin16())
+                DOSMEM_Init(TRUE);
+
+            SetSelectorBase( sel, base );
+        }
+        break;
+
+    case 0x0008:  /* Set selector limit */
+        {
+            DWORD limit = MAKELONG( DX_reg(context), CX_reg(context) );
+            TRACE( "set selector limit (0x%04x,0x%08lx)\n",
+                   BX_reg(context), limit );
+            SetSelectorLimit16( BX_reg(context), limit );
+        }
+        break;
+
+    case 0x0009:  /* Set selector access rights */
+        TRACE( "set selector access rights(0x%04x,0x%04x)\n",
+               BX_reg(context), CX_reg(context) );
+        SelectorAccessRights16( BX_reg(context), 1, CX_reg(context) );
+        break;
+
+    case 0x000a:  /* Allocate selector alias */
+        TRACE( "allocate selector alias (0x%04x)\n", BX_reg(context) );
+        SET_AX( context, AllocCStoDSAlias16( BX_reg(context) ) );
+        if (!AX_reg(context))
+        {
+            SET_AX( context, 0x8011 );  /* descriptor unavailable */
+            SET_CFLAG(context);
+        }
+        break;
+
+    case 0x000b:  /* Get descriptor */
+        TRACE( "get descriptor (0x%04x)\n", BX_reg(context) );
+        {
+            LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
+                                                               context->SegEs, 
+                                                               context->Edi );
+            wine_ldt_get_entry( BX_reg(context), entry );
+        }
+        break;
+
+    case 0x000c:  /* Set descriptor */
+        TRACE( "set descriptor (0x%04x)\n", BX_reg(context) );
+        {
+            LDT_ENTRY *entry = (LDT_ENTRY*)CTX_SEG_OFF_TO_LIN( context,
+                                                               context->SegEs, 
+                                                               context->Edi );
+            wine_ldt_set_entry( BX_reg(context), entry );
+        }
+        break;
+
+    case 0x000d:  /* Allocate specific LDT descriptor */
+        FIXME( "allocate descriptor (0x%04x), stub!\n", BX_reg(context) );
+        SET_AX( context, 0x8011 ); /* descriptor unavailable */
+        SET_CFLAG( context );
+        break;
+
+    case 0x000e:  /* Get Multiple Descriptors (1.0) */
+        FIXME( "get multiple descriptors - unimplemented\n" );
+        break;
+
+    case 0x000f:  /* Set Multiple Descriptors (1.0) */
+        FIXME( "set multiple descriptors - unimplemented\n" );
+        break;
+
+    case 0x0100:  /* Allocate DOS memory block */
+        TRACE( "allocate DOS memory block (0x%x paragraphs)\n", BX_reg(context) );
+        {
+            DWORD dw = GlobalDOSAlloc16( (DWORD)BX_reg(context) << 4 );
+            if (dw) {
+                SET_AX( context, HIWORD(dw) );
+                SET_DX( context, LOWORD(dw) );
+            } else {
+                SET_AX( context, 0x0008 ); /* insufficient memory */
+                SET_BX( context, DOSMEM_Available() >> 4 );
+                SET_CFLAG(context);
+            }
+            break;
+        }
+
+    case 0x0101:  /* Free DOS memory block */
+        TRACE( "free DOS memory block (0x%04x)\n", DX_reg(context) );
+        {
+            WORD error = GlobalDOSFree16( DX_reg(context) );
+            if (error) {
+                SET_AX( context, 0x0009 ); /* memory block address invalid */
+                SET_CFLAG( context );
+            }
+        }
+        break;
+
+    case 0x0102: /* Resize DOS Memory Block */
+        FIXME( "resize DOS memory block (0x%04x, 0x%x paragraphs) - unimplemented\n", 
+               DX_reg(context), BX_reg(context) );
+        break;
+
+    case 0x0200: /* get real mode interrupt vector */
+        TRACE( "get realmode interupt vector (0x%02x)\n",
+               BL_reg(context) );
+        {
+            FARPROC16 proc = DOSVM_GetRMHandler( BL_reg(context) );
+            SET_CX( context, SELECTOROF(proc) );
+            SET_DX( context, OFFSETOF(proc) );
+        }
+        break;
+
+    case 0x0201: /* set real mode interrupt vector */
+        TRACE( "set realmode interrupt vector (0x%02x, 0x%04x:0x%04x)\n", 
+               BL_reg(context), CX_reg(context), DX_reg(context) );
+        DOSVM_SetRMHandler( BL_reg(context), 
+                            (FARPROC16)MAKESEGPTR(CX_reg(context), DX_reg(context)) );
+        break;
+
+    case 0x0202:  /* Get Processor Exception Handler Vector */
+        FIXME( "Get Processor Exception Handler Vector (0x%02x)\n",
+               BL_reg(context) );
+        if (DOSVM_IsDos32()) 
+        {
+            SET_CX( context, 0 );
+            context->Edx = 0;
+        } 
+        else 
+        {
+            SET_CX( context, 0 );
+            SET_DX( context, 0 );
+        }
+        break;
+
+    case 0x0203:  /* Set Processor Exception Handler Vector */
+         FIXME( "Set Processor Exception Handler Vector (0x%02x)\n",
+                BL_reg(context) );
+         break;
+
+    case 0x0204:  /* Get protected mode interrupt vector */
+        TRACE("get protected mode interrupt handler (0x%02x)\n",
+              BL_reg(context));
+        if (DOSVM_IsDos32()) 
+        {
+            FARPROC48 handler = DOSVM_GetPMHandler48( BL_reg(context) );
+            SET_CX( context, handler.selector );
+            context->Edx = handler.offset;
+        } 
+        else 
+        {
+            FARPROC16 handler = DOSVM_GetPMHandler16( BL_reg(context) );
+            SET_CX( context, SELECTOROF(handler) );
+            SET_DX( context, OFFSETOF(handler) );
+        }
+        break;
+
+    case 0x0205:  /* Set protected mode interrupt vector */
+        TRACE("set protected mode interrupt handler (0x%02x,0x%04x:0x%08lx)\n",
+              BL_reg(context), CX_reg(context), context->Edx);
+        if (DOSVM_IsDos32()) 
+        {
+            FARPROC48 handler;
+            handler.selector = CX_reg(context);
+            handler.offset = context->Edx;
+            DOSVM_SetPMHandler48( BL_reg(context), handler );
+        } 
+        else 
+        {
+            FARPROC16 handler;
+            handler = (FARPROC16)MAKESEGPTR( CX_reg(context), DX_reg(context)); 
+            DOSVM_SetPMHandler16( BL_reg(context), handler );
+        }
+        break;
+
+    case 0x0300:  /* Simulate real mode interrupt */
+        TRACE( "Simulate real mode interrupt %02x.\n", BL_reg(context));
+        DOSVM_CallRMInt( context );
+        break;
+
+    case 0x0301:  /* Call real mode procedure with far return */
+        TRACE( "Call real mode procedure with far return.\n" );
+        DOSVM_CallRMProc( context, FALSE );
+        break;
+
+    case 0x0302:  /* Call real mode procedure with interrupt return */
+        TRACE( "Call real mode procedure with interrupt return.\n" );
+        DOSVM_CallRMProc( context, TRUE );
+        break;
+
+    case 0x0303:  /* Allocate Real Mode Callback Address */
+        TRACE( "Allocate real mode callback address.\n" );
+        DOSVM_AllocRMCB( context );
+        break;
+
+    case 0x0304:  /* Free Real Mode Callback Address */
+        TRACE( "Free real mode callback address.\n" );
+        DOSVM_FreeRMCB( context );
+        break;
+
+    case 0x0305:  /* Get State Save/Restore Addresses */
+        TRACE("get state save/restore addresses\n");
+        /* we probably won't need this kind of state saving */
+        SET_AX( context, 0 );
+
+        /* real mode: just point to the lret */
+        SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
+        SET_CX( context, 2 );
+
+        /* protected mode: don't have any handler yet... */
+        /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
+        FIXME("no protected-mode dummy state save/restore handler yet\n");
+        SET_SI( context, 0 );
+        context->Edi = 0;
+        break;
+
+    case 0x0306:  /* Get Raw Mode Switch Addresses */
+        TRACE("get raw mode switch addresses\n");
+
+        /* real mode, point to standard DPMI return wrapper */
+        SET_BX( context, DOSVM_dpmi_segments->wrap_seg );
+        SET_CX( context, 0 );
+
+        /* protected mode, point to DPMI call wrapper */
+        /* FIXME: Use DI in 16-bit DPMI and EDI in 32-bit DPMI */
+        /* FIXME: Doesn't work in DPMI32... */
+        SET_SI( context, DOSVM_dpmi_segments->dpmi_sel );
+        context->Edi = 8; /* offset of the INT 0x31 call */
+        break;
+
+    case 0x0400:  /* Get DPMI version */
+        TRACE("get DPMI version\n");
+        {
+            SYSTEM_INFO si;
+
+            GetSystemInfo(&si);
+            SET_AX( context, 0x005a );  /* DPMI version 0.90 */
+            SET_BX( context, 0x0005 );  /* Flags: 32-bit, virtual memory */
+            SET_CL( context, si.wProcessorLevel );
+            SET_DX( context, 0x0870 );  /* Master/slave interrupt controller base */
+        }
+        break;
+
+    case 0x0401:  /* Get DPMI Capabilities (1.0) */
+        FIXME( "get dpmi capabilities - unimplemented\n");
+        break;
+
+    case 0x0500:  /* Get free memory information */
+        TRACE("get free memory information\n");
+        {
+            MEMORYSTATUS status;
+
+            /* the layout is just the same as MEMMANINFO, but without
+             * the dwSize entry.
+             */
+            struct
+            {
+                DWORD dwLargestFreeBlock;
+                DWORD dwMaxPagesAvailable;
+                DWORD dwMaxPagesLockable;
+                DWORD dwTotalLinearSpace;
+                DWORD dwTotalUnlockedPages;
+                DWORD dwFreePages;
+                DWORD dwTotalPages;
+                DWORD dwFreeLinearSpace;
+                DWORD dwSwapFilePages;
+                WORD  wPageSize;
+            } *info = CTX_SEG_OFF_TO_LIN( context, context->SegEs, context->Edi );
+
+            GlobalMemoryStatus( &status );
+            info->wPageSize            = getpagesize();
+            info->dwLargestFreeBlock   = status.dwAvailVirtual;
+            info->dwMaxPagesAvailable  = info->dwLargestFreeBlock / info->wPageSize;
+            info->dwMaxPagesLockable   = info->dwMaxPagesAvailable;
+            info->dwTotalLinearSpace   = status.dwTotalVirtual / info->wPageSize;
+            info->dwTotalUnlockedPages = info->dwTotalLinearSpace;
+            info->dwFreePages          = info->dwMaxPagesAvailable;
+            info->dwTotalPages         = info->dwTotalLinearSpace;
+            info->dwFreeLinearSpace    = info->dwMaxPagesAvailable;
+            info->dwSwapFilePages      = status.dwTotalPageFile / info->wPageSize;
+            break;
+        }
+
+    case 0x0501:  /* Allocate memory block */
+        {
+            DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
+            BYTE *ptr;
+
+            TRACE( "allocate memory block (%ld bytes)\n", size );
+
+            ptr = (BYTE *)DPMI_xalloc( size );
+            if (!ptr)
+            {
+                SET_AX( context, 0x8012 );  /* linear memory not available */
+                SET_CFLAG(context);
+            } 
+            else 
+            {
+                SET_BX( context, HIWORD(ptr) );
+                SET_CX( context, LOWORD(ptr) );
+                SET_SI( context, HIWORD(ptr) );
+                SET_DI( context, LOWORD(ptr) );
+            }
+            break;
+        }
+
+    case 0x0502:  /* Free memory block */
+        {
+            DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
+            TRACE( "free memory block (0x%08lx)\n", handle );
+            DPMI_xfree( (void *)handle );
+        }
+        break;
+
+    case 0x0503:  /* Resize memory block */
+        {
+            DWORD size = MAKELONG( CX_reg(context), BX_reg(context) );
+            DWORD handle = MAKELONG( DI_reg(context), SI_reg(context) );
+            BYTE *ptr;
+
+            TRACE( "resize memory block (0x%08lx, %ld bytes)\n", handle, size );
+
+            ptr = (BYTE *)DPMI_xrealloc( (void *)handle, size );
+            if (!ptr)
+            {
+                SET_AX( context, 0x8012 );  /* linear memory not available */
+                SET_CFLAG(context);
+            } else {
+                SET_BX( context, HIWORD(ptr) );
+                SET_CX( context, LOWORD(ptr) );
+                SET_SI( context, HIWORD(ptr) );
+                SET_DI( context, LOWORD(ptr) );
+            }
+        }
+        break;
+
+    case 0x0507:  /* Set page attributes (1.0) */
+        FIXME( "set page attributes - unimplemented\n" );
+        break;  /* Just ignore it */
+
+    case 0x0600:  /* Lock linear region */
+        TRACE( "lock linear region - ignored (no paging)\n" );
+        break;
+
+    case 0x0601:  /* Unlock linear region */
+        TRACE( "unlock linear region - ignored (no paging)\n" );
+        break;
+
+    case 0x0602:  /* Mark real mode region as pageable */
+        TRACE( "mark real mode region as pageable - ignored (no paging)\n" );
+        break;
+
+    case 0x0603:  /* Relock real mode region */
+        TRACE( "relock real mode region - ignored (no paging)\n" );
+        break;
+
+    case 0x0604:  /* Get page size */
+        TRACE("get pagesize\n");
+        SET_BX( context, HIWORD(getpagesize()) );
+        SET_CX( context, LOWORD(getpagesize()) );
+        break;
+
+    case 0x0700: /* Mark pages as paging candidates */
+        TRACE( "mark pages as paging candidates - ignored (no paging)\n" );
+        break;
+
+    case 0x0701: /* Discard pages */
+        TRACE( "discard pages - ignored (no paging)\n" );
+        break;
+
+    case 0x0702:  /* Mark page as demand-paging candidate */
+        TRACE( "mark page as demand-paging candidate - ignored (no paging)\n" );
+        break;
+
+    case 0x0703:  /* Discard page contents */
+        TRACE( "discard page contents - ignored (no paging)\n" );
+        break;
+
+    case 0x0800:  /* Physical address mapping */
+        FIXME( "physical address mapping (0x%08lx) - unimplemented\n", 
+               MAKELONG(CX_reg(context),BX_reg(context)) );
+        break;
+
+    case 0x0900:  /* Get and Disable Virtual Interrupt State */
+        TRACE( "Get and Disable Virtual Interrupt State: %ld\n", 
+               NtCurrentTeb()->dpmi_vif );
+        SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+        NtCurrentTeb()->dpmi_vif = 0;
+        break;
+
+    case 0x0901:  /* Get and Enable Virtual Interrupt State */
+        TRACE( "Get and Enable Virtual Interrupt State: %ld\n", 
+               NtCurrentTeb()->dpmi_vif );
+        SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+        NtCurrentTeb()->dpmi_vif = 1;
+        break;
+
+    case 0x0902:  /* Get Virtual Interrupt State */
+        TRACE( "Get Virtual Interrupt State: %ld\n", 
+               NtCurrentTeb()->dpmi_vif );
+        SET_AL( context, NtCurrentTeb()->dpmi_vif ? 1 : 0 );
+        break;
+
+    case 0x0e00:  /* Get Coprocessor Status (1.0) */
+        /*
+         * Return status in AX bits:
+         * B0    - MPv (MP bit in the virtual MSW/CR0)
+         *         0 = numeric coprocessor is disabled for this client
+         *         1 = numeric coprocessor is enabled for this client
+         * B1    - EMv (EM bit in the virtual MSW/CR0)
+         *         0 = client is not emulating coprocessor instructions
+         *         1 = client is emulating coprocessor instructions
+         * B2    - MPr (MP bit from the actual MSW/CR0)
+         *         0 = numeric coprocessor is not present
+         *         1 = numeric coprocessor is present
+         * B3    - EMr (EM bit from the actual MSW/CR0)
+         *         0 = host is not emulating coprocessor instructions
+         *         1 = host is emulating coprocessor instructions
+         * B4-B7 - coprocessor type
+         *         00H = no coprocessor
+         *         02H = 80287
+         *         03H = 80387
+         *         04H = 80486 with numeric coprocessor
+         *         05H-0FH = reserved for future numeric processors
+         */
+        TRACE( "Get Coprocessor Status\n" );
+        SET_AX( context, 69 ); /* 486, coprocessor present and enabled */ 
+        break;
+
+    case 0x0e01: /* Set Coprocessor Emulation (1.0) */
+        /*
+         * See function 0x0e00.
+         * BX bit B0 is new value for MPv.
+         * BX bit B1 is new value for EMv.
+         */
+        if (BX_reg(context) != 1)
+            FIXME( "Set Coprocessor Emulation to %d - unimplemented\n", 
+                   BX_reg(context) );
+        else
+            TRACE( "Set Coprocessor Emulation - ignored\n" );
+        break;
+
+    default:
+        INT_BARF( context, 0x31 );
+        SET_AX( context, 0x8001 );  /* unsupported function */
+        SET_CFLAG(context);
+        break;
+    }  
 }