This patch adds some preliminary stuff needed in order to support DPMI IRQs. Changelog: Add int31 VIF manipulation functions. Fix asynchronous event locking. Make asynchronous event handling support DPMI. Index: dlls/winedos/dosvm.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/dosvm.c,v retrieving revision 1.39 diff -u -r1.39 dosvm.c --- dlls/winedos/dosvm.c 16 Jun 2003 01:17:30 -0000 1.39 +++ dlls/winedos/dosvm.c 19 Jun 2003 14:50:51 -0000 @@ -70,14 +70,6 @@ # include <sys/mman.h> #endif -#define IF_CLR(ctx) ((ctx)->EFlags &= ~VIF_MASK) -#define IF_SET(ctx) ((ctx)->EFlags |= VIF_MASK) -#define IF_ENABLED(ctx) ((ctx)->EFlags & VIF_MASK) -#define SET_PEND(ctx) ((ctx)->EFlags |= VIP_MASK) -#define CLR_PEND(ctx) ((ctx)->EFlags &= ~VIP_MASK) -#define IS_PEND(ctx) ((ctx)->EFlags & VIP_MASK) - -#undef TRY_PICRETURN typedef struct _DOSEVENT { int irq,priority; @@ -88,7 +80,6 @@ static CRITICAL_SECTION qcrit = CRITICAL_SECTION_INIT("DOSVM"); static struct _DOSEVENT *pending_event, *current_event; -static int sig_sent; static HANDLE event_notifier; @@ -113,50 +104,122 @@ } -static void DOSVM_SendQueuedEvent(CONTEXT86 *context) +/*********************************************************************** + * DOSVM_SendOneEvent + * + * Process single pending event. + * + * This function should be called with queue critical section locked. + * The function temporarily releases the critical section if it is + * possible that internal interrupt handler or user procedure will + * be called. This is because we may otherwise get a deadlock if + * another thread is waiting for the same critical section. + */ +static void DOSVM_SendOneEvent( CONTEXT86 *context ) { - LPDOSEVENT event = pending_event; + LPDOSEVENT event = pending_event; - if (DOSVM_HasPendingEvents()) { - /* remove from "pending" list */ + /* Remove from pending events list. */ pending_event = event->next; - /* process event */ - if (event->irq>=0) { - /* it's an IRQ, move it to "current" list */ - event->next = current_event; - current_event = event; - TRACE("dispatching IRQ %d\n",event->irq); - /* note that if DOSVM_SimulateInt calls an internal interrupt directly, - * current_event might be cleared (and event freed) in this very call! */ - DOSVM_HardwareInterruptRM( context, (event->irq < 8) ? - (event->irq + 8) : (event->irq - 8 + 0x70) ); - } else { - /* callback event */ - TRACE("dispatching callback event\n"); - (*event->relay)(context,event->data); - free(event); + + /* Process active event. */ + if (event->irq >= 0) + { + BYTE intnum = (event->irq < 8) ? + (event->irq + 8) : (event->irq - 8 + 0x70); + + /* Event is an IRQ, move it to current events list. */ + event->next = current_event; + current_event = event; + + TRACE( "Dispatching IRQ %d.\n", event->irq ); + + if (ISV86(context)) + { + /* + * Note that if DOSVM_HardwareInterruptRM calls an internal + * interrupt directly, current_event might be cleared + * (and event freed) in this call. + */ + LeaveCriticalSection(&qcrit); + DOSVM_HardwareInterruptRM( context, intnum ); + EnterCriticalSection(&qcrit); + } + else + { + /* + * This routine only modifies current context so it is + * not necessary to release critical section. + */ + DOSVM_HardwareInterruptPM( context, intnum ); + } + } + else + { + /* Callback event. */ + TRACE( "Dispatching callback event.\n" ); + + LeaveCriticalSection(&qcrit); + (*event->relay)( context, event->data ); + EnterCriticalSection(&qcrit); + + free(event); } - } - if (!DOSVM_HasPendingEvents()) { - TRACE("clearing Pending flag\n"); - CLR_PEND(context); - } } -static void DOSVM_SendQueuedEvents(CONTEXT86 *context) + +/*********************************************************************** + * DOSVM_SendQueuedEvents + * + * As long as interrupts are enabled, process all pending events + * that are not blocked by currently active event. + */ +static void DOSVM_SendQueuedEvents( CONTEXT86 *context ) { - /* we will send all queued events as long as interrupts are enabled, - * but IRQ events will disable interrupts again */ - while (IS_PEND(context) && IF_ENABLED(context)) - DOSVM_SendQueuedEvent(context); + EnterCriticalSection(&qcrit); + + TRACE( "Called in %s mode %s events pending (time=%ld)\n", + ISV86(context) ? "real" : "protected", + DOSVM_HasPendingEvents() ? "with" : "without", + GetTickCount() ); + TRACE( "cs:ip=%04lx:%08lx, ss:sp=%04lx:%08lx\n", + context->SegCs, context->Eip, context->SegSs, context->Esp); + + while (DOSVM_HasPendingEvents() && + (ISV86(context) ? + (context->EFlags & VIF_MASK) : NtCurrentTeb()->dpmi_vif)) + { + DOSVM_SendOneEvent(context); + + /* + * Event handling may have turned pending events flag on. + * We disable it here because this prevents some + * unnecessary calls to this function. + */ + NtCurrentTeb()->vm86_pending = 0; + } + + if (DOSVM_HasPendingEvents()) + { + /* + * Interrupts disabled, but there are still + * pending events, make sure that pending flag is turned on. + */ + TRACE( "Another event is pending, setting VIP flag.\n" ); + NtCurrentTeb()->vm86_pending |= VIP_MASK; + } + + LeaveCriticalSection(&qcrit); } + /*********************************************************************** * QueueEvent (WINEDOS.@) */ void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data) { LPDOSEVENT event, cur, prev; + BOOL old_pending; if (MZ_Current()) { event = malloc(sizeof(DOSEVENT)); @@ -168,6 +231,8 @@ event->relay = relay; event->data = data; EnterCriticalSection(&qcrit); + old_pending = DOSVM_HasPendingEvents(); + /* insert event into linked list, in order *after* * all earlier events of higher or equal priority */ cur = pending_event; prev = NULL; @@ -179,18 +244,18 @@ if (prev) prev->next = event; else pending_event = event; - /* alert the vm86 about the new event */ - if (!sig_sent) { + if (!old_pending && DOSVM_HasPendingEvents()) { TRACE("new event queued, signalling (time=%ld)\n", GetTickCount()); + + /* Alert VM86 thread about the new event. */ kill(dosvm_pid,SIGUSR2); - sig_sent++; + + /* Wake up DOSVM_Wait so that it can serve pending events. */ + SetEvent(event_notifier); } else { TRACE("new event queued (time=%ld)\n", GetTickCount()); } - /* Wake up DOSVM_Wait so that it can serve pending events. */ - SetEvent(event_notifier); - LeaveCriticalSection(&qcrit); } else { /* DOS subsystem not running */ @@ -289,9 +354,6 @@ { if (DOSVM_HasPendingEvents()) { - /* - * FIXME: Critical section locking is broken. - */ CONTEXT86 context = *waitctx; /* @@ -312,8 +374,7 @@ context.Esp = 0; } - IF_SET(&context); - SET_PEND(&context); + context.EFlags |= VIF_MASK; context.SegCs = 0; context.Eip = 0; @@ -459,18 +520,12 @@ case EXCEPTION_VM86_STI: /* case EXCEPTION_VM86_PICRETURN: */ - IF_SET(context); - EnterCriticalSection(&qcrit); - sig_sent++; - TRACE_(int)("context=%p\n", context); - TRACE_(int)("cs:ip=%04lx:%04lx, ss:sp=%04lx:%04lx\n", context->SegCs, context->Eip, context->SegSs, context->Esp); - if (!ISV86(context)) { - ERR_(int)("@#&*%%, winedos signal handling is *still* messed up\n"); - } - TRACE_(int)("DOS task enabled interrupts %s events pending, sending events (time=%ld)\n", IS_PEND(context)?"with":"without", GetTickCount()); + if (!ISV86(context)) + ERR( "Protected mode STI caught by real mode handler!\n" ); + + context->EFlags |= VIF_MASK; + context->EFlags &= ~VIP_MASK; DOSVM_SendQueuedEvents(context); - sig_sent=0; - LeaveCriticalSection(&qcrit); return EXCEPTION_CONTINUE_EXECUTION; } return EXCEPTION_CONTINUE_SEARCH; Index: dlls/winedos/int31.c =================================================================== RCS file: /home/wine/wine/dlls/winedos/int31.c,v retrieving revision 1.22 diff -u -r1.22 int31.c --- dlls/winedos/int31.c 13 Jun 2003 23:17:34 -0000 1.22 +++ dlls/winedos/int31.c 19 Jun 2003 14:50:58 -0000 @@ -1062,7 +1062,6 @@ break; case 0x0300: /* Simulate real mode interrupt */ - TRACE( "Simulate real mode interrupt %d.\n", BL_reg(context)); DOSVM_CallRMInt( context ); break; @@ -1250,6 +1249,26 @@ 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) */ -- Jukka Heinonen <http://www.iki.fi/jhei/>