winedos / Enable DPMI IRQs

Jukka Heinonen jhei at iki.fi
Mon Jun 30 13:09:19 CDT 2003


This patch makes DPMI programs process IRQs and other asynchronous
DOS events. The patch is complete enough for Doom Legacy and hopefully 
for a large amount of other protected mode programs.

Interrupt reflection is still missing and real mode IRQ handling
seems to contain some nasty races.



Changelog:
    Start processing asynchronous DOS events in DPMI mode.





Index: dlls/winedos/himem.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/himem.c,v
retrieving revision 1.3
diff -u -r1.3 himem.c
--- dlls/winedos/himem.c	15 Dec 2002 01:18:40 -0000	1.3
+++ dlls/winedos/himem.c	30 Jun 2003 17:45:39 -0000
@@ -181,7 +181,8 @@
     static const char relay[]=
     {
         0xca, 0x04, 0x00, /* 16-bit far return and pop 4 bytes (relay void* arg) */
-        0xcd, 0x31        /* int 31 */
+        0xcd, 0x31,       /* int 31 */
+        0xfb, 0x66, 0xcb  /* sti and 32-bit far return */
     };
 
     /*
@@ -253,6 +254,7 @@
     /*
      * PM / offset 0: Stub where __wine_call_from_16_regs returns.
      * PM / offset 3: Stub which swaps back to 32-bit application code/stack.
+     * PM / offset 5: Stub which enables interrupts
      */
     ptr = DOSVM_AllocCodeUMB( sizeof(relay), 
                               0,  &DOSVM_dpmi_segments->relay_code_sel);




Index: dlls/winedos/int31.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/int31.c,v
retrieving revision 1.24
diff -u -r1.24 int31.c
--- dlls/winedos/int31.c	27 Jun 2003 19:40:29 -0000	1.24
+++ dlls/winedos/int31.c	30 Jun 2003 17:45:45 -0000
@@ -28,7 +28,9 @@
 #include "msdos.h"
 #include "dosexe.h"
 
+#include "excpt.h"
 #include "wine/debug.h"
+#include "wine/exception.h"
 #include "stackframe.h"
 #include "toolhelp.h"
 
@@ -79,6 +81,29 @@
 
 
 /**********************************************************************
+ *          dpmi_exception_handler
+ *
+ * Handle EXCEPTION_VM86_STI exceptions generated
+ * when there are pending asynchronous events.
+ */
+static WINE_EXCEPTION_FILTER(dpmi_exception_handler)
+{
+    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;
+    }
+
+    return EXCEPTION_CONTINUE_SEARCH;
+}
+
+
+/**********************************************************************
  *	    INT_GetRealModeContext
  */
 static void INT_GetRealModeContext( REALMODECALL *call, CONTEXT86 *context )
@@ -297,10 +322,15 @@
  */
 static void DPMI_CallRMCBProc( CONTEXT86 *context, RMCB *rmcb, WORD flag )
 {
+    DWORD old_vif = NtCurrentTeb()->dpmi_vif;
+
+    /* Disable virtual interrupts. */
+    NtCurrentTeb()->dpmi_vif = 0;
+
     if (IS_SELECTOR_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;
@@ -340,7 +370,10 @@
 #else
         ERR("RMCBs only implemented for i386\n");
 #endif
-    }
+    } __EXCEPT(dpmi_exception_handler) { } __ENDTRY
+        
+    /* Restore virtual interrupt flag. */
+    NtCurrentTeb()->dpmi_vif = old_vif;                                 
 }
 
 
@@ -548,17 +581,36 @@
 
     TRACE("DOS program is now entering %d-bit protected mode\n", 
           DOSVM_IsDos32() ? 32 : 16);
-    wine_call_to_16_regs_short(&pm_ctx, 0);
+
+    /*
+     * Enable interrupts. Note that we also make a dummy 
+     * relay call in order to process all pending events. 
+     * This is needed in order to prevent event handling from
+     * getting stuck.
+     */
+    NtCurrentTeb()->dpmi_vif = 1;
+    DOSVM_BuildCallFrame( context, NULL, NULL );
+
+    __TRY 
+    {
+        wine_call_to_16_regs_short(&pm_ctx, 0);
+    } 
+    __EXCEPT(dpmi_exception_handler) 
+    { 
+    } 
+    __ENDTRY
 
     /* in the current state of affairs, we won't ever actually return here... */
     /* we should have int21/ah=4c do it someday, though... */
 
+#if 0
     FreeSelector16(psp->environment);
     psp->environment = env_seg;
     FreeSelector16(es);
     if (ds != ss) FreeSelector16(ds);
     FreeSelector16(ss);
     FreeSelector16(cs);
+#endif
 }
 
 static RMCB *DPMI_AllocRMCB( void )




Index: dlls/winedos/interrupts.c
===================================================================
RCS file: /home/wine/wine/dlls/winedos/interrupts.c,v
retrieving revision 1.16
diff -u -r1.16 interrupts.c
--- dlls/winedos/interrupts.c	30 Jun 2003 02:03:48 -0000	1.16
+++ dlls/winedos/interrupts.c	30 Jun 2003 17:45:50 -0000
@@ -22,6 +22,8 @@
 #include "wine/debug.h"
 #include "wine/winbase16.h"
 
+#include "thread.h"
+
 #ifdef HAVE_SYS_VM86_H
 # include <sys/vm86.h>
 #endif
@@ -81,6 +83,23 @@
 
 
 /**********************************************************************
+ *         DOSVM_IsIRQ
+ *
+ * Return TRUE if interrupt is an IRQ.
+ */
+static BOOL DOSVM_IsIRQ( BYTE intnum )
+{
+    if (intnum >= 0x08 && intnum <= 0x0f)
+        return TRUE;
+
+    if (intnum >= 0x70 && intnum <= 0x77)
+        return TRUE;
+
+    return FALSE;
+}
+
+
+/**********************************************************************
  *         DOSVM_DefaultHandler
  *
  * Default interrupt handler. This will be used to emulate all
@@ -105,6 +124,10 @@
     }
 
     WARN("int%x not implemented, returning dummy handler\n", intnum );
+
+    if (DOSVM_IsIRQ(intnum))
+        return DOSVM_AcknowledgeIRQ;
+
     return DOSVM_DefaultHandler;
 }
 
@@ -122,6 +145,33 @@
 
 
 /**********************************************************************
+ *          DOSVM_PrepareIRQ
+ *
+ */
+static void DOSVM_PrepareIRQ( CONTEXT86 *context, BOOL isbuiltin )
+{
+    /* Disable virtual interrupts. */
+    NtCurrentTeb()->dpmi_vif = 0;
+
+    if (!isbuiltin)
+    {
+        DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
+                                          context->SegSs,
+                                          context->Esp);
+
+        /* Push return address to stack. */
+        *(--stack) = context->SegCs;
+        *(--stack) = context->Eip;
+        context->Esp += -8;
+
+        /* Jump to enable interrupts stub. */
+        context->SegCs = DOSVM_dpmi_segments->relay_code_sel;
+        context->Eip   = 5;
+    }
+}
+
+
+/**********************************************************************
  *          DOSVM_PushFlags
  *
  * This routine is used to make default int25 and int26 handlers leave the 
@@ -270,6 +320,8 @@
 
             if (intnum == 0x25 || intnum == 0x26)
                 DOSVM_PushFlags( context, TRUE, FALSE );
+            else if (DOSVM_IsIRQ(intnum))
+                DOSVM_PrepareIRQ( context, TRUE );
 
             DOSVM_BuildCallFrame( context,
                                   DOSVM_IntProcRelay,
@@ -278,14 +330,16 @@
         }
         else
         {
-            DWORD *stack = CTX_SEG_OFF_TO_LIN(context, 
-                                              context->SegSs, 
-                                              context->Esp);
+            DWORD *stack;
             
             TRACE( "invoking hooked interrupt %02x at %04x:%08lx\n",
                    intnum, addr.selector, addr.offset );
             
+            if (DOSVM_IsIRQ(intnum))
+                DOSVM_PrepareIRQ( context, FALSE );
+
             /* Push the flags and return address on the stack */
+            stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
             *(--stack) = context->EFlags;
             *(--stack) = context->SegCs;
             *(--stack) = context->Eip;
@@ -308,7 +362,9 @@
 
             if (intnum == 0x25 || intnum == 0x26)
                 DOSVM_PushFlags( context, FALSE, FALSE );
-            
+            else if (DOSVM_IsIRQ(intnum))
+                DOSVM_PrepareIRQ( context, TRUE );
+
             DOSVM_BuildCallFrame( context, 
                                   DOSVM_IntProcRelay,
                                   DOSVM_GetBuiltinHandler(
@@ -316,14 +372,16 @@
         }
         else
         {
-            WORD *stack = CTX_SEG_OFF_TO_LIN(context, 
-                                             context->SegSs, 
-                                             context->Esp);
+            WORD *stack;
             
             TRACE( "invoking hooked interrupt %02x at %04x:%04x\n", 
                    intnum, SELECTOROF(addr), OFFSETOF(addr) );
 
+            if (DOSVM_IsIRQ(intnum))
+                DOSVM_PrepareIRQ( context, FALSE );
+
             /* Push the flags and return address on the stack */
+            stack = CTX_SEG_OFF_TO_LIN(context, context->SegSs, context->Esp);
             *(--stack) = LOWORD(context->EFlags);
             *(--stack) = context->SegCs;
             *(--stack) = LOWORD(context->Eip);
@@ -537,6 +595,12 @@
  */
 void WINAPI DOSVM_CallBuiltinHandler( CONTEXT86 *context, BYTE intnum ) 
 {
+    /*
+     * FIXME: Make all builtin interrupt calls go via this routine.
+     * FIXME: Check for PM->RM interrupt reflection.
+     * FIXME: Check for RM->PM interrupt reflection.
+     */
+
   INTPROC proc = DOSVM_GetBuiltinHandler( intnum );
   proc( context );
 }




Index: memory/instr.c
===================================================================
RCS file: /home/wine/wine/memory/instr.c,v
retrieving revision 1.23
diff -u -r1.23 instr.c
--- memory/instr.c	20 May 2003 17:50:59 -0000	1.23
+++ memory/instr.c	30 Jun 2003 17:45:55 -0000
@@ -26,6 +26,8 @@
 #include "selectors.h"
 #include "wine/debug.h"
 #include "callback.h"
+#include "thread.h"
+#include "wine/exception.h"
 
 WINE_DEFAULT_DEBUG_CHANNEL(int);
 WINE_DECLARE_DEBUG_CHANNEL(io);
@@ -793,12 +795,19 @@
             context->Eip += prefixlen + 1;
             return 0;
 
-        case 0xfa: /* cli, ignored */
+        case 0xfa: /* cli */
+            NtCurrentTeb()->dpmi_vif = 0;
             context->Eip += prefixlen + 1;
             return 0;
 
-        case 0xfb: /* sti, ignored */
+        case 0xfb: /* sti */
+            NtCurrentTeb()->dpmi_vif = 1;
             context->Eip += prefixlen + 1;
+            if (NtCurrentTeb()->vm86_pending)
+            {
+                NtCurrentTeb()->vm86_pending = 0;
+                return EXCEPTION_VM86_STI;
+            }
             return 0;
     }
 




Index: dlls/ntdll/signal_i386.c
===================================================================
RCS file: /home/wine/wine/dlls/ntdll/signal_i386.c,v
retrieving revision 1.64
diff -u -r1.64 signal_i386.c
--- dlls/ntdll/signal_i386.c	16 Jun 2003 01:18:26 -0000	1.64
+++ dlls/ntdll/signal_i386.c	30 Jun 2003 17:46:03 -0000
@@ -943,6 +943,11 @@
             teb->vm86_ptr = NULL;
             rec.ExceptionAddress = (LPVOID)context->Eip;
             EXC_RtlRaiseException( &rec, context );
+            /*
+             * FIXME: EXC_RtlRaiseException has unblocked all signals. 
+             *        If we receive nested SIGUSR2 here, VM86 event 
+             *        handling may lock up!
+             */
             teb->vm86_ptr = vm86;
         }
     }
@@ -965,9 +970,30 @@
             save_vm86_context( &vcontext, vm86 );
             rec.ExceptionAddress = (LPVOID)vcontext.Eip;
             EXC_RtlRaiseException( &rec, &vcontext );
+            /*
+             * FIXME: EXC_RtlRaiseException has unblocked all signals. 
+             *        If we receive nested SIGUSR2 here, VM86 event 
+             *        handling may lock up!
+             */
             teb->vm86_ptr = vm86;
             restore_vm86_context( &vcontext, vm86 );
         }
+    }
+    else if(teb->dpmi_vif && 
+            !IS_SELECTOR_SYSTEM(context->SegCs) &&
+            !IS_SELECTOR_SYSTEM(context->SegSs))
+    {
+        /* Executing DPMI code and virtual interrupts are enabled. */
+        teb->vm86_pending = 0;
+        rec.ExceptionAddress = (LPVOID)context->Eip;
+        EXC_RtlRaiseException( &rec, context );
+        /*
+         * EXC_RtlRaiseException has unblocked all signals and this
+         * signal handler is about to return to either DOS relay or
+         * IRQ handler. Because both of these will check pending
+         * interrupts again, it is not a problem if we receive
+         * a nested SIGUSR2 here and ignore it.
+         */
     }
 }
 



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



More information about the wine-patches mailing list