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/>