DOS / Add support for DPMI32

Jukka Heinonen jhei at iki.fi
Wed Dec 11 14:51:37 CST 2002


This patch adds support for DPMI32. It makes protected mode nethack
work and those DOS/Win16 applications that worked before this patch
still seem to work.

The patch also makes winedos16.dll unnecessary (this dll is not 
removed in this patch because the patch is already quite complicated). 
Directive "-interrupt" in spec files is not needed any more, either.

The patch may look too complicated but we probably don't want to call 
interrupt handlers directly from signal handlers and we really need relay 
wrapper around interrupt handlers in order to be able to add support 
for interrupt reflection and for properly managing IRQs even in 
protected mode DOS applications.




Changelog:
  Add support for DPMI32. Make winedos16.dll unnecessary.




Index: dlls/winedos/Makefile.in
===================================================================
RCS file: /home/wine/wine/dlls/winedos/Makefile.in,v
retrieving revision 1.21
diff -u -r1.21 Makefile.in
--- dlls/winedos/Makefile.in	2 Dec 2002 21:39:58 -0000	1.21
+++ dlls/winedos/Makefile.in	11 Dec 2002 20:29:54 -0000
@@ -43,6 +43,7 @@
 	interrupts.c \
 	ioports.c \
 	module.c \
+	relay.c \
 	soundblaster.c \
 	vga.c \
 	xms.c




Index: dlls/winedos/dosexe.h
===================================================================
RCS file: /home/wine/wine/dlls/winedos/dosexe.h,v
retrieving revision 1.20
diff -u -r1.20 dosexe.h
--- dlls/winedos/dosexe.h	7 Dec 2002 23:46:41 -0000	1.20
+++ dlls/winedos/dosexe.h	11 Dec 2002 20:29:59 -0000
@@ -28,6 +28,10 @@
 #include "miscemu.h"
 
 struct _DOSEVENT;
+struct _STACK16FRAME;
+
+/* amount of space reserved for relay stack */
+#define DOSVM_RELAY_DATA_SIZE 4096
 
 /* various real-mode code stubs */
 struct DPMI_segments
@@ -37,6 +41,9 @@
     WORD dpmi_seg;
     WORD dpmi_sel;
     WORD int48_sel;
+    WORD int16_sel;
+    WORD relay_code_sel;
+    WORD relay_data_sel;
 };
 
 /* 48-bit segmented pointers for DOS DPMI32 */
@@ -181,6 +188,7 @@
 
 /* int31.c */
 extern void WINAPI DOSVM_Int31Handler(CONTEXT86*);
+extern void WINAPI DOSVM_RawModeSwitchHandler(CONTEXT86*);
 extern BOOL DOSVM_IsDos32(void);
 extern FARPROC16 WINAPI DPMI_AllocInternalRMCB(RMCBPROC);
 extern void WINAPI DPMI_FreeInternalRMCB(FARPROC16);
@@ -212,6 +220,12 @@
 extern FARPROC48 DOSVM_GetPMHandler48( BYTE intnum );
 extern void DOSVM_SetPMHandler48( BYTE intnum, FARPROC48 handler );
 extern INTPROC DOSVM_GetBuiltinHandler( BYTE intnum );
+
+/* relay.c */
+void DOSVM_RelayHandler( CONTEXT86 * );
+void DOSVM_SaveCallFrame( CONTEXT86 *, struct _STACK16FRAME * );
+void DOSVM_RestoreCallFrame( CONTEXT86 *, struct _STACK16FRAME * );
+void DOSVM_BuildCallFrame( CONTEXT86 *, DOSRELAY, LPVOID );
 
 /* soundblaster.c */
 extern void SB_ioport_out( WORD port, BYTE val );





Index: dlls/winedos/himem.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/himem.c,v
retrieving revision 1.2
diff -u -r1.2 himem.c
--- dlls/winedos/himem.c	7 Dec 2002 23:46:41 -0000	1.2
+++ dlls/winedos/himem.c	11 Dec 2002 20:31:47 -0000
@@ -178,6 +178,12 @@
         0xCB                 /* lret */
     };
 
+    static const char relay[]=
+    {
+        0xca, 0x04, 0x00, /* 16-bit far return and pop 4 bytes (relay void* arg) */
+        0xcd, 0x31        /* int 31 */
+    };
+
     /*
      * Allocate pointer array.
      */
@@ -216,7 +222,7 @@
         /*
          * Each 32-bit interrupt handler is 6 bytes:
          * 0xCD,<i>            = int <i> (nested 16-bit interrupt)
-         * 0x66,0xCA,0x04,0x00 = ret 4   (32-bit far return and pop 4 bytes)
+         * 0x66,0xCA,0x04,0x00 = ret 4   (32-bit far return and pop 4 bytes / eflags)
          */
         ptr[i * 6 + 0] = 0xCD;
         ptr[i * 6 + 1] = i;
@@ -225,4 +231,37 @@
         ptr[i * 6 + 4] = 0x04;
         ptr[i * 6 + 5] = 0x00;
     }
+
+    /*
+     * PM / offset N*5: Interrupt N in 16-bit protected mode.
+     */
+    ptr = DOSVM_AllocCodeUMB( 5 * 256,
+                              0, &DOSVM_dpmi_segments->int16_sel );
+    for(i=0; i<256; i++) {
+        /*
+         * Each 16-bit interrupt handler is 5 bytes:
+         * 0xCD,<i>       = int <i> (interrupt)
+         * 0xCA,0x02,0x00 = ret 2   (16-bit far return and pop 2 bytes / eflags)
+         */
+        ptr[i * 5 + 0] = 0xCD;
+        ptr[i * 5 + 1] = i;
+        ptr[i * 5 + 2] = 0xCA;
+        ptr[i * 5 + 3] = 0x02;
+        ptr[i * 5 + 4] = 0x00;
+    }
+
+    /*
+     * PM / offset 0: Stub where __wine_call_from_16_regs returns.
+     * PM / offset 3: Stub which swaps back to 32-bit application code/stack.
+     */
+    ptr = DOSVM_AllocCodeUMB( sizeof(relay), 
+                              0,  &DOSVM_dpmi_segments->relay_code_sel);
+    memcpy( ptr, relay, sizeof(relay) );
+
+    /*
+     * Space for 16-bit stack used by relay code.
+     */
+    ptr = DOSVM_AllocDataUMB( DOSVM_RELAY_DATA_SIZE, 
+                              0, &DOSVM_dpmi_segments->relay_data_sel);
+    memset( ptr, 0, DOSVM_RELAY_DATA_SIZE );
 }





Index: dlls/winedos/int31.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/int31.c,v
retrieving revision 1.15
diff -u -r1.15 int31.c
--- dlls/winedos/int31.c	10 Dec 2002 22:56:45 -0000	1.15
+++ dlls/winedos/int31.c	11 Dec 2002 20:30:09 -0000
@@ -722,52 +722,21 @@
     }
 }
 
+
 /**********************************************************************
- *          DOSVM_RawModeSwitchWrapper
+ *          DOSVM_RawModeSwitchHandler
  *
- * DPMI Raw Mode Switch wrapper.
+ * DPMI Raw Mode Switch handler.
  * This routine does all the stack manipulation tricks needed
  * to return from protected mode interrupt using modified 
  * code and stack pointers.
  */
-static void DOSVM_RawModeSwitchWrapper( CONTEXT86 *context )
+void WINAPI DOSVM_RawModeSwitchHandler( CONTEXT86 *context )
 {
-    /*
-     * FIXME: This routine will not work if it is called
-     *        from 32 bit DPMI program and the program returns
-     *        to protected mode while ESP or EIP is over 0xffff.
-     * FIXME: This routine will not work if it is not called
-     *        using 16-bit-to-Wine callback glue function.
-     */
-    STACK16FRAME frame = *CURRENT_STACK16;
-  
+    STACK16FRAME frame;
+    DOSVM_SaveCallFrame( context, &frame );
     DOSVM_RawModeSwitch( context );
-
-    /*
-     * After this function returns to relay code, protected mode
-     * 16 bit stack will contain STACK16FRAME and single WORD
-     * (EFlags, see next comment).
-     */
-    NtCurrentTeb()->cur_stack =
-        MAKESEGPTR( context->SegSs,
-                    context->Esp - sizeof(STACK16FRAME) - sizeof(WORD) );
-  
-    /*
-     * After relay code returns to glue function, protected
-     * mode 16 bit stack will contain interrupt return record:
-     * IP, CS and EFlags. Since EFlags is ignored, it won't
-     * need to be initialized.
-     */
-    context->Esp -= 3 * sizeof(WORD);
-
-    /*
-     * Restore stack frame so that relay code won't be confused.
-     * It should be noted that relay code overwrites IP and CS
-     * in STACK16FRAME with values taken from current CONTEXT86.
-     * These values are what is returned to glue function
-     * (see previous comment).
-     */
-    *CURRENT_STACK16 = frame;
+    DOSVM_RestoreCallFrame( context, &frame );
 }
 
 
@@ -781,15 +750,6 @@
  */
 static BOOL DOSVM_CheckWrappers( CONTEXT86 *context )
 {
-    /* Handle protected mode interrupts. */
-    if (!ISV86(context)) {
-        if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel) {
-            DOSVM_RawModeSwitchWrapper( context );
-            return TRUE;
-        }
-        return FALSE;
-    }
-
     /* check if it's our wrapper */
     TRACE("called from real mode\n");
     if (context->SegCs==DOSVM_dpmi_segments->dpmi_seg) {
@@ -828,7 +788,7 @@
  */
 void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
 {
-    if (DOSVM_CheckWrappers(context))
+    if (ISV86(context) && DOSVM_CheckWrappers(context))
         return;
 
     RESET_CFLAG(context);





Index: dlls/winedos/interrupts.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/interrupts.c,v
retrieving revision 1.12
diff -u -r1.12 interrupts.c
--- dlls/winedos/interrupts.c	10 Dec 2002 22:56:45 -0000	1.12
+++ dlls/winedos/interrupts.c	11 Dec 2002 20:30:14 -0000
@@ -56,11 +56,9 @@
   /* 64 */ 0,                  0,                  0,                  DOSVM_Int67Handler
 };
 
-/* Ordinal number for interrupt 0 handler in winedos16.dll */
-#define FIRST_INTERRUPT 100
 
 /**********************************************************************
- *         DOSVM_DefaultHandler (WINEDOS16.356)
+ *         DOSVM_DefaultHandler
  *
  * Default interrupt handler. This will be used to emulate all
  * interrupts that don't have their own interrupt handler.
@@ -69,6 +67,70 @@
 {
 }
 
+
+/**********************************************************************
+ *          DOSVM_IntProcRelay
+ *
+ * Simple DOSRELAY that interprets its argument as INTPROC and calls it.
+ */
+static void DOSVM_IntProcRelay( CONTEXT86 *context, LPVOID data )
+{
+    INTPROC proc = (INTPROC)data;
+    proc(context);
+}
+
+
+/**********************************************************************
+ *          DOSVM_PushFlags
+ *
+ * This routine is used to make default int25 and int26 handlers leave the 
+ * original eflags into stack. In order to do this, stack is manipulated
+ * so that it actually contains two copies of eflags, one of which is
+ * popped during return from interrupt handler.
+ */
+static void DOSVM_PushFlags( CONTEXT86 *context, BOOL islong, BOOL isstub )
+{
+    if (islong)
+    {
+        DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
+                                          context->SegSs, 
+                                          context->Esp);
+        context->Esp += -4; /* One item will be added to stack. */
+
+        if (isstub)
+        {
+            DWORD ip = stack[0];
+            DWORD cs = stack[1];
+            stack += 2; /* Pop ip and cs. */
+            *(--stack) = context->EFlags;
+            *(--stack) = cs;
+            *(--stack) = ip;
+        }
+        else
+            *(--stack) = context->EFlags;            
+    }
+    else
+    {
+        WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
+                                         context->SegSs, 
+                                         context->Esp);
+        ADD_LOWORD( context->Esp, -2 ); /* One item will be added to stack. */
+
+        if (isstub)
+        {
+            WORD ip = stack[0];
+            WORD cs = stack[1];
+            stack += 2; /* Pop ip and cs. */
+            *(--stack) = LOWORD(context->EFlags);
+            *(--stack) = cs;
+            *(--stack) = ip;
+        }
+        else
+            *(--stack) = LOWORD(context->EFlags);
+    }
+}
+
+
 /**********************************************************************
  *         DOSVM_EmulateInterruptPM
  *
@@ -80,49 +142,97 @@
  */
 void WINAPI DOSVM_EmulateInterruptPM( CONTEXT86 *context, BYTE intnum ) 
 {
-  BOOL islong;
+    if (context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
+    {
+        DOSVM_BuildCallFrame( context, 
+                              DOSVM_IntProcRelay,
+                              DOSVM_RawModeSwitchHandler );
+    }
+    else if (context->SegCs == DOSVM_dpmi_segments->relay_code_sel)
+    {
+        /*
+         * This must not be called using DOSVM_BuildCallFrame.
+         */
+        DOSVM_RelayHandler( context );
+    }
+    else if (context->SegCs == DOSVM_dpmi_segments->int48_sel)
+    {
+        if (intnum == 0x25 || intnum == 0x26)
+            DOSVM_PushFlags( context, TRUE, TRUE );
+
+        DOSVM_BuildCallFrame( context, 
+                              DOSVM_IntProcRelay,
+                              DOSVM_GetBuiltinHandler(intnum) );
+    }
+    else if (context->SegCs == DOSVM_dpmi_segments->int16_sel)
+    {
+        if (intnum == 0x25 || intnum == 0x26)
+            DOSVM_PushFlags( context, FALSE, TRUE );
+
+        DOSVM_BuildCallFrame( context, 
+                              DOSVM_IntProcRelay, 
+                              DOSVM_GetBuiltinHandler(intnum) );
+    }
+    else if(DOSVM_IsDos32())
+    {
+        FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
+        
+        if (addr.selector == DOSVM_dpmi_segments->int48_sel)
+        {
+            if (intnum == 0x25 || intnum == 0x26)
+                DOSVM_PushFlags( context, TRUE, FALSE );
+
+             DOSVM_BuildCallFrame( context, 
+                                   DOSVM_IntProcRelay,
+                                   DOSVM_GetBuiltinHandler(intnum) );
+        }
+        else
+        {
+            DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
+                                              context->SegSs, 
+                                              context->Esp);
+            
+            /* Push the flags and return address on the stack */
+            *(--stack) = context->EFlags;
+            *(--stack) = context->SegCs;
+            *(--stack) = context->Eip;
+            context->Esp += -12;
+
+            /* Jump to the interrupt handler */
+            context->SegCs  = addr.selector;
+            context->Eip = addr.offset;
+        }
+    }
+    else
+    {
+        FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
 
-  if(context->SegCs == DOSVM_dpmi_segments->int48_sel) 
-    islong = FALSE;
-  else if(context->SegCs == DOSVM_dpmi_segments->dpmi_sel)
-    islong = FALSE;
-  else if(DOSVM_IsDos32())
-    islong = TRUE;
-  else if(IS_SELECTOR_32BIT(context->SegCs)) {
-    WARN("Interrupt in 32-bit code and mode is not DPMI32\n");
-    islong = TRUE;
-  } else
-    islong = FALSE;
-
-  if(islong)
-  {
-    FARPROC48 addr = DOSVM_GetPMHandler48( intnum );
-    DWORD *stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
-    /* Push the flags and return address on the stack */
-    *(--stack) = context->EFlags;
-    *(--stack) = context->SegCs;
-    *(--stack) = context->Eip;
-    /* Jump to the interrupt handler */
-    context->SegCs  = addr.selector;
-    context->Eip = addr.offset;
-  }
-  else
-  {
-    FARPROC16 addr = DOSVM_GetPMHandler16( intnum );
-    WORD *stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
-    /* Push the flags and return address on the stack */
-    *(--stack) = LOWORD(context->EFlags);
-    *(--stack) = context->SegCs;
-    *(--stack) = LOWORD(context->Eip);
-    /* Jump to the interrupt handler */
-    context->SegCs  = HIWORD(addr);
-    context->Eip = LOWORD(addr);
-  }
-
-  if (IS_SELECTOR_32BIT(context->SegSs))
-    context->Esp += islong ? -12 : -6;
-  else
-    context->Esp = (context->Esp & ~0xffff) | (WORD)((context->Esp & 0xffff) + (islong ? -12 : -6));
+        if (SELECTOROF(addr) == DOSVM_dpmi_segments->int16_sel)
+        {
+            if (intnum == 0x25 || intnum == 0x26)
+                DOSVM_PushFlags( context, FALSE, FALSE );
+
+            DOSVM_BuildCallFrame( context, 
+                                  DOSVM_IntProcRelay,
+                                  DOSVM_GetBuiltinHandler(intnum) );
+        }
+        else
+        {
+            WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
+                                             context->SegSs, 
+                                             context->Esp);
+            
+            /* Push the flags and return address on the stack */
+            *(--stack) = LOWORD(context->EFlags);
+            *(--stack) = context->SegCs;
+            *(--stack) = LOWORD(context->Eip);
+            ADD_LOWORD( context->Esp, -6 );
+
+            /* Jump to the interrupt handler */
+            context->SegCs  = HIWORD(addr);
+            context->Eip = LOWORD(addr);
+        }
+    }
 }
 
 /**********************************************************************
@@ -147,6 +257,7 @@
   ((FARPROC16*)0)[intnum] = handler;
 }
 
+
 /**********************************************************************
  *          DOSVM_GetPMHandler16
  *
@@ -154,31 +265,13 @@
  */
 FARPROC16 DOSVM_GetPMHandler16( BYTE intnum )
 {
-  static HMODULE16 procs;
-  FARPROC16 handler = DOSVM_Vectors16[intnum];
-
-  if (!handler)
-  {
-    if (!procs &&
-        (procs = GetModuleHandle16( "winedos16" )) < 32 &&
-        (procs = LoadLibrary16( "winedos16" )) < 32)
+    if (!DOSVM_Vectors16[intnum])
     {
-      ERR("could not load winedos16.dll\n");
-      procs = 0;
-      return 0;
+        FARPROC16 proc = (FARPROC16)MAKESEGPTR( DOSVM_dpmi_segments->int16_sel,
+                                                5 * intnum );
+        DOSVM_Vectors16[intnum] = proc;
     }
-
-    handler = GetProcAddress16( procs, (LPCSTR)(FIRST_INTERRUPT + intnum));
-    if (!handler) 
-    {
-      WARN("int%x not implemented, returning dummy handler\n", intnum );
-      handler = GetProcAddress16( procs, (LPCSTR)(FIRST_INTERRUPT + 256));
-    }
-
-    DOSVM_Vectors16[intnum] = handler;
-  }
-
-  return handler;
+    return DOSVM_Vectors16[intnum];
 }
 



 
--- /dev/null	Thu Jan  1 02:00:00 1970
+++ dlls/winedos/relay.c	Wed Dec 11 22:24:16 2002
@@ -0,0 +1,262 @@
+/*
+ * Routines for dynamically building calls to Wine from 
+ * protected mode applications.
+ *
+ * Copyright 2002 Jukka Heinonen
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2.1 of the License, or (at your option) any later version.
+ *
+ * This library 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this library; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+#include "dosexe.h"
+#include "stackframe.h"
+#include "wine/debug.h"
+#include "builtin16.h"
+
+WINE_DEFAULT_DEBUG_CHANNEL(int);
+
+/*
+ * Magic DWORD used to check stack integrity.
+ */
+#define RELAY_MAGIC 0xabcdef00
+
+/*
+ * Memory block for temporary 16-bit stacks used when
+ * 32-bit code calls relay.
+ */
+typedef struct {
+    DWORD inuse;          /* non-zero if stack block is in use */
+    DWORD eip;            /* saved ip */
+    DWORD seg_cs;         /* saved cs */
+    DWORD esp;            /* saved sp */
+    DWORD seg_ss;         /* saved ss */
+    DWORD stack_bottom;   /* guard dword */
+    BYTE  stack[256-7*4]; /* 16-bit stack */
+    DWORD stack_top;      /* guard dword */
+} RELAY_Stack16;
+
+
+/**********************************************************************
+ *          RELAY_GetPointer
+ *
+ * Get pointer to stack block when given esp pointing to 16-bit stack
+ * inside relay data segment.
+ */
+static RELAY_Stack16 *RELAY_GetPointer( DWORD offset )
+{
+    offset = offset / sizeof(RELAY_Stack16) * sizeof(RELAY_Stack16);
+    return MapSL(MAKESEGPTR(DOSVM_dpmi_segments->relay_data_sel, offset));
+}
+
+
+/**********************************************************************
+ *          RELAY_MakeShortContext
+ *
+ * If context is using 32-bit stack or code segment, allocate
+ * 16-bit stack, make stack pointer point to this stack and
+ * make code pointer point to stub that restores everything.
+ * So, after this routine, SS and CS are guaranteed to be 16-bit.
+ *
+ * Note: This might be called from signal handler, so the stack
+ *       allocation algorithm must be signal safe.
+ */
+static void RELAY_MakeShortContext( CONTEXT86 *context )
+{
+    if (IS_SELECTOR_32BIT(context->SegCs) || IS_SELECTOR_32BIT(context->SegSs))
+    {
+        DWORD offset = offsetof(RELAY_Stack16, stack_top);
+        RELAY_Stack16 *stack = RELAY_GetPointer( 0 );
+
+        while (stack->inuse && offset < DOSVM_RELAY_DATA_SIZE) {
+            stack++;
+            offset += sizeof(RELAY_Stack16);
+        }
+
+        if (offset >= DOSVM_RELAY_DATA_SIZE)
+            ERR( "Too many nested interrupts!\n" );
+        
+        stack->inuse = 1;
+        stack->eip = context->Eip;
+        stack->seg_cs = context->SegCs;
+        stack->esp = context->Esp;
+        stack->seg_ss = context->SegSs;
+
+        stack->stack_bottom = RELAY_MAGIC;
+        stack->stack_top = RELAY_MAGIC;
+
+        context->SegSs = DOSVM_dpmi_segments->relay_data_sel;
+        context->Esp = offset;
+        context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
+        context->Eip = 3;
+    }
+}
+
+
+/**********************************************************************
+ *          RELAY_RelayStub
+ *
+ * This stub is called by __wine_call_from_16_regs in order to marshall
+ * relay parameters.
+ */
+static void __stdcall RELAY_RelayStub( DOSRELAY proc, 
+                                       unsigned char *args, 
+                                       void *context )
+{
+    proc( (CONTEXT86*)context, *(LPVOID *)args );
+}
+
+
+/**********************************************************************
+ *          DOSVM_RelayHandler
+ *
+ * Restore saved code and stack pointers and release stack block.
+ */
+void DOSVM_RelayHandler( CONTEXT86 *context )
+{
+    RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
+
+    context->SegSs = stack->seg_ss;
+    context->Esp = stack->esp;
+    context->SegCs = stack->seg_cs;
+    context->Eip = stack->eip;
+
+    if (!stack->inuse || 
+        stack->stack_bottom != RELAY_MAGIC ||
+        stack->stack_top != RELAY_MAGIC)
+        ERR( "Stack corrupted!\n" );
+
+    stack->inuse = 0;
+}
+
+
+/**********************************************************************
+ *          DOSVM_SaveCallFrame
+ *
+ * Save current call frame. This routine must be called from DOSRELAY
+ * called using DOSVM_BuildCallFrame before the relay modifies stack 
+ * pointer. This routine makes sure that the relay can return safely
+ * to application context and that no memory is leaked.
+ *
+ * Note: If DOSVM_BuildCallFrame was called using 32-bit CS or SS,
+ *       old values of CS and SS will be lost. This does not matter
+ *       since this routine is only used by Raw Mode Switch.
+ */
+void DOSVM_SaveCallFrame( CONTEXT86 *context, STACK16FRAME *frame )
+{
+    *frame = *CURRENT_STACK16;
+
+    /*
+     * If context is using allocated stack, release it.
+     */
+    if (context->SegSs == DOSVM_dpmi_segments->relay_data_sel)
+    {
+        RELAY_Stack16 *stack = RELAY_GetPointer( context->Esp );
+
+        if (!stack->inuse || 
+            stack->stack_bottom != RELAY_MAGIC ||
+            stack->stack_top != RELAY_MAGIC)
+            ERR( "Stack corrupted!\n" );
+
+        stack->inuse = 0;
+    }
+}
+
+
+/**********************************************************************
+ *          DOSVM_RestoreCallFrame
+ *
+ * Restore saved call frame to currect stack. This routine must always
+ * be called after DOSVM_SaveCallFrame has been called and before returning
+ * from DOSRELAY.
+ */
+void DOSVM_RestoreCallFrame( CONTEXT86 *context, STACK16FRAME *frame )
+{
+    /*
+     * Make sure that CS and SS are 16-bit.
+     */
+    RELAY_MakeShortContext( context );
+
+    /*
+     * After this function returns to relay code, protected mode
+     * 16 bit stack will contain STACK16FRAME and single WORD
+     * (EFlags, see next comment).
+     */
+    NtCurrentTeb()->cur_stack =
+        MAKESEGPTR( context->SegSs,
+                    context->Esp - sizeof(STACK16FRAME) - sizeof(WORD) );
+    
+    /*
+     * After relay code returns to glue function, protected
+     * mode 16 bit stack will contain interrupt return record:
+     * IP, CS and EFlags. Since EFlags is ignored, it won't
+     * need to be initialized.
+     */
+    context->Esp -= 3 * sizeof(WORD);
+
+    /*
+     * Restore stack frame so that relay code won't be confused.
+     * It should be noted that relay code overwrites IP and CS
+     * in STACK16FRAME with values taken from current CONTEXT86.
+     * These values are what is returned to glue function
+     * (see previous comment).
+     */
+    *CURRENT_STACK16 = *frame;
+}
+
+
+/**********************************************************************
+ *          DOSVM_BuildCallFrame
+ *
+ * Modifies the context so that return to context calls DOSRELAY and 
+ * only after return from DOSRELAY the original context will be returned to.
+ */
+void DOSVM_BuildCallFrame( CONTEXT86 *context, DOSRELAY relay, LPVOID data )
+{
+    WORD *stack;
+    WORD  code_sel = DOSVM_dpmi_segments->relay_code_sel;
+
+    /*
+     * Make sure that CS and SS are 16-bit.
+     */
+    RELAY_MakeShortContext( context );
+
+    /*
+     * Get stack pointer after RELAY_MakeShortContext.
+     */
+    stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
+
+    /*
+     * Build call frame.
+     */
+    *(--stack) = HIWORD(data);            /* argument.hiword */ 
+    *(--stack) = LOWORD(data);            /* argument.loword */
+    *(--stack) = context->SegCs;          /* STACK16FRAME.cs */
+    *(--stack) = LOWORD(context->Eip);    /* STACK16FRAME.ip */
+    *(--stack) = LOWORD(context->Ebp);    /* STACK16FRAME.bp */
+    *(--stack) = HIWORD(relay);           /* STACK16FRAME.entry_point.hiword */
+    *(--stack) = LOWORD(relay);           /* STACK16FRAME.entry_point.loword */
+    *(--stack) = 0;                       /* STACK16FRAME.entry_ip */
+    *(--stack) = HIWORD(RELAY_RelayStub); /* STACK16FRAME.relay.hiword */
+    *(--stack) = LOWORD(RELAY_RelayStub); /* STACK16FRAME.relay.loword */
+    *(--stack) = 0;                       /* STACK16FRAME.module_cs.hiword */
+    *(--stack) = code_sel;                /* STACK16FRAME.module_cs.loword */
+    *(--stack) = 0;                       /* STACK16FRAME.callfrom_ip.hiword */
+    *(--stack) = 0;                       /* STACK16FRAME.callfrom_ip.loword */
+
+    /*
+     * Adjust stack and code pointers.
+     */
+    ADD_LOWORD( context->Esp, -28 );
+    context->SegCs = wine_get_cs();
+    context->Eip = (DWORD)__wine_call_from_16_regs;
+}






-- 
Jukka Heinonen <http://www.iki.fi/jhei/>



More information about the wine-patches mailing list