Add int31 VIF manipulation functions.

Fix asynchronous event locking.
Make asynchronous event handling support DPMI.
This commit is contained in:
Jukka Heinonen 2003-06-20 21:27:49 +00:00 committed by Alexandre Julliard
parent 0a970ce0e2
commit 75e87b4457
2 changed files with 135 additions and 61 deletions

View File

@ -70,14 +70,6 @@ WORD DOSVM_retval = 0;
# 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 @@ typedef struct _DOSEVENT {
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 BOOL DOSVM_HasPendingEvents( void )
}
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 @@ void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data
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 @@ void WINAPI DOSVM_QueueEvent( INT irq, INT priority, DOSRELAY relay, LPVOID data
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 @@ void WINAPI DOSVM_Wait( CONTEXT86 *waitctx )
{
if (DOSVM_HasPendingEvents())
{
/*
* FIXME: Critical section locking is broken.
*/
CONTEXT86 context = *waitctx;
/*
@ -312,8 +374,7 @@ void WINAPI DOSVM_Wait( CONTEXT86 *waitctx )
context.Esp = 0;
}
IF_SET(&context);
SET_PEND(&context);
context.EFlags |= VIF_MASK;
context.SegCs = 0;
context.Eip = 0;
@ -459,18 +520,12 @@ static WINE_EXCEPTION_FILTER(exception_handler)
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;

View File

@ -1062,7 +1062,6 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
break;
case 0x0300: /* Simulate real mode interrupt */
TRACE( "Simulate real mode interrupt %d.\n", BL_reg(context));
DOSVM_CallRMInt( context );
break;
@ -1252,6 +1251,26 @@ void WINAPI DOSVM_Int31Handler( CONTEXT86 *context )
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) */
/*
* Return status in AX bits: