Fix for DPMI event handling

Jukka Heinonen jhei at iki.fi
Mon Mar 15 17:09:02 CST 2004


This patch fixes pending event handling in DPMI programs.
It seems to work at least with Doom Legacy, but event
handling is still likely to have some minor issues.




Changelog:
  DPMI programs now handle pending events.




Index: tools/winebuild/relay.c
===================================================================
RCS file: /home/wine/wine/tools/winebuild/relay.c,v
retrieving revision 1.32
diff -u -r1.32 relay.c
--- tools/winebuild/relay.c	28 Oct 2003 00:30:55 -0000	1.32
+++ tools/winebuild/relay.c	15 Mar 2004 22:55:40 -0000
@@ -1044,6 +1044,57 @@
 
 
 /*******************************************************************
+ *         BuildPendingEventCheck
+ *
+ * Build a function that checks whether there are any
+ * pending DPMI events.
+ *
+ * Stack layout:
+ *   
+ * (sp+12) long   eflags
+ * (sp+6)  long   cs
+ * (sp+2)  long   ip
+ * (sp)    word   fs
+ *
+ * On entry to function, fs register points to a valid TEB.
+ * On exit from function, stack will be popped.
+ */
+void BuildPendingEventCheck( FILE *outfile )
+{
+    /* Function header */
+
+    function_header( outfile, "DPMI_PendingEventCheck" );
+
+    /* Check for pending events. */
+    
+    fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", 
+             STRUCTOFFSET(TEB,vm86_pending) );
+    fprintf( outfile, "\tje DPMI_PendingEventCheck_Cleanup\n" );
+
+    fprintf( outfile, "\t.byte 0x64\n\ttestl $0xffffffff,(%d)\n", 
+             STRUCTOFFSET(TEB,dpmi_vif) );
+
+    fprintf( outfile, "\tje DPMI_PendingEventCheck_Cleanup\n" );
+
+    /* Process pending events. */
+
+    fprintf( outfile, "\tsti\n" );
+   
+    /* Start cleanup. Restore fs register. */
+
+    fprintf( outfile, ".globl DPMI_PendingEventCheck_Cleanup\n" );
+    fprintf( outfile, "DPMI_PendingEventCheck_Cleanup:\n" );
+    fprintf( outfile, "\tpopw %%fs\n" );
+
+    /* Return from function. */
+
+    fprintf( outfile, ".globl DPMI_PendingEventCheck_Return\n" );
+    fprintf( outfile, "DPMI_PendingEventCheck_Return:\n" );
+    fprintf( outfile, "\tiret\n" );
+}
+
+
+/*******************************************************************
  *         BuildRelays16
  *
  * Build all the 16-bit relay callbacks
@@ -1130,6 +1181,9 @@
 
     /* CBClientThunkSLEx return stub */
     BuildCallTo32CBClientRet( outfile, TRUE  );
+
+    /* Pending DPMI events check stub */
+    BuildPendingEventCheck( outfile );
 
     /* End of Call16_Ret segment */
     fprintf( outfile, "\n\t.globl " __ASM_NAME("Call16_Ret_End") "\n" );




Index: dlls/kernel/wowthunk.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/wowthunk.c,v
retrieving revision 1.49
diff -u -r1.49 wowthunk.c
--- dlls/kernel/wowthunk.c	25 Nov 2003 00:42:27 -0000	1.49
+++ dlls/kernel/wowthunk.c	15 Mar 2004 22:55:45 -0000
@@ -83,6 +83,9 @@
 extern void CallTo16_Ret();
 extern void CALL32_CBClient_Ret();
 extern void CALL32_CBClientEx_Ret();
+extern void DPMI_PendingEventCheck();
+extern void DPMI_PendingEventCheck_Cleanup();
+extern void DPMI_PendingEventCheck_Return();
 extern DWORD CallTo16_DataSelector;
 extern SEGPTR CALL32_CBClient_RetAddr;
 extern SEGPTR CALL32_CBClientEx_RetAddr;
@@ -94,6 +97,11 @@
 static LONG CALLBACK vectored_handler( EXCEPTION_POINTERS *ptrs );
 static SEGPTR call16_ret_addr;  /* segptr to CallTo16_Ret routine */
 
+static WORD  dpmi_checker_selector;
+static DWORD dpmi_checker_offset_call;
+static DWORD dpmi_checker_offset_cleanup;
+static DWORD dpmi_checker_offset_return;
+
 /***********************************************************************
  *           WOWTHUNK_Init
  */
@@ -114,6 +122,15 @@
     CALL32_CBClientEx_RetAddr =
         MAKESEGPTR( codesel, (char*)CALL32_CBClientEx_Ret - (char*)Call16_Ret_Start );
 
+    /* Prepare selector and offsets for DPMI event checking. */
+    dpmi_checker_selector = codesel;
+    dpmi_checker_offset_call = 
+        (char*)DPMI_PendingEventCheck - (char*)Call16_Ret_Start;
+    dpmi_checker_offset_cleanup = 
+        (char*)DPMI_PendingEventCheck_Cleanup - (char*)Call16_Ret_Start;
+    dpmi_checker_offset_return = 
+        (char*)DPMI_PendingEventCheck_Return - (char*)Call16_Ret_Start;
+
     if (TRACE_ON(relay) || TRACE_ON(snoop)) RELAY16_InitDebugLists();
 
     /* setup emulation of protected instructions from 32-bit code (only for Win9x versions) */
@@ -165,6 +182,75 @@
 
 
 /*************************************************************
+ *            insert_event_check
+ *
+ * Make resuming the context check for pending DPMI events
+ * before the original context is restored. This is required
+ * because DPMI events are asynchronous, they are blocked while 
+ * Wine 32-bit code is being executed and we want to prevent 
+ * a race when returning back to 16-bit or 32-bit DPMI context.
+ */
+static void insert_event_check( CONTEXT *context )
+{
+    char *stack = wine_ldt_get_ptr( context->SegSs, context->Esp );
+
+    if(context->SegCs == dpmi_checker_selector &&
+       context->Eip   >= dpmi_checker_offset_call && 
+       context->Eip   <= dpmi_checker_offset_cleanup)
+    {
+        /*
+         * Nested call. Stack will be preserved. 
+         */
+    }
+    else if(context->SegCs == dpmi_checker_selector &&
+            context->Eip   == dpmi_checker_offset_return)
+    {
+        /*
+         * Nested call. We have just finished popping the fs
+         * register, lets put it back into stack.
+         */
+
+        stack -= sizeof(WORD);
+        *(WORD*)stack = context->SegFs;
+
+        context->Esp -= 2;
+    }
+    else
+    {
+        /*
+         * Call is not nested.
+         * Push modified registers into stack.
+         * These will be popped by the assembler stub.
+         */
+
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->EFlags;
+   
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->SegCs;
+
+        stack -= sizeof(DWORD);
+        *(DWORD*)stack = context->Eip;
+
+        stack -= sizeof(WORD);
+        *(WORD*)stack = context->SegFs;
+
+        context->Esp  -= 14;
+    }
+
+    /*
+     * Modify the context so that we jump into assembler stub.
+     * TEB access is made easier by providing the stub
+     * with the correct fs register value.
+     */
+
+    context->SegCs = dpmi_checker_selector;
+    context->Eip   = dpmi_checker_offset_call;
+    context->SegFs = wine_get_fs();
+}
+
+
+/*************************************************************
  *            call16_handler
  *
  * Handler for exceptions occurring in 16-bit code.
@@ -191,6 +277,15 @@
             SEGPTR gpHandler;
             DWORD ret = INSTR_EmulateInstruction( record, context );
 
+            /*
+             * Insert check for pending DPMI events. Note that this 
+             * check must be inserted after instructions have been 
+             * emulated because the instruction emulation requires
+             * original CS:IP and the emulation may change TEB.dpmi_vif.
+             */
+            if(NtCurrentTeb()->dpmi_vif)
+                insert_event_check( context );
+
             if (ret != ExceptionContinueSearch) return ret;
 
             /* check for Win16 __GP handler */
@@ -212,6 +307,10 @@
             }
         }
     }
+    else if (record->ExceptionCode == EXCEPTION_VM86_STI)
+    {
+        insert_event_check( context );
+    }
     return ExceptionContinueSearch;
 }
 
@@ -545,6 +644,19 @@
             {
                 *((SEGPTR *)stack - 1) = call16_ret_addr;
                 cbArgs += sizeof(SEGPTR);
+            }
+
+            /*
+             * Start call by checking for pending events.
+             * Note that wine_call_to_16_regs overwrites context stack
+             * pointer so we may modify it here without a problem.
+             */
+            if (NtCurrentTeb()->dpmi_vif)
+            {
+                context->SegSs = wine_get_ds();
+                context->Esp   = (DWORD)stack;
+                insert_event_check( context );
+                cbArgs += (DWORD)stack - context->Esp;
             }
 
             _EnterWin16Lock();




Index: dlls/kernel/instr.c
===================================================================
RCS file: /home/wine/wine/dlls/kernel/instr.c,v
retrieving revision 1.13
diff -u -r1.13 instr.c
--- dlls/kernel/instr.c	15 Mar 2004 20:09:41 -0000	1.13
+++ dlls/kernel/instr.c	15 Mar 2004 22:55:50 -0000
@@ -824,6 +824,7 @@
             context->Eip += prefixlen + 1;
             if (NtCurrentTeb()->vm86_pending)
             {
+                NtCurrentTeb()->vm86_pending = 0;
                 rec->ExceptionCode = EXCEPTION_VM86_STI;
                 break; /* Handle the pending event. */
             }



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



More information about the wine-patches mailing list