diff --git a/include/dosexe.h b/include/dosexe.h
index 2b317a07356f0f556de0c9e273f48f198cc94378..7d126c7a2c2e275ba2e2e091080a6a427a4f2ec6 100644
--- a/include/dosexe.h
+++ b/include/dosexe.h
@@ -9,8 +9,17 @@
 
 #include <sys/types.h> /* pid_t */
 #include "winbase.h"   /* for LPSTARTUPINFO32A */
+#include "winnt.h"     /* for PCONTEXT */
 #include "sig_context.h"
 
+typedef struct _DOSSYSTEM {
+  int id;
+  void *data;
+  struct _DOSSYSTEM *next;
+} DOSSYSTEM;
+
+struct _DOSEVENT;
+
 typedef struct _DOSTASK {
  LPVOID img;
  unsigned img_ofs;
@@ -18,14 +27,30 @@ typedef struct _DOSTASK {
  WORD init_cs,init_ip,init_ss,init_sp;
  WORD xms_seg;
  WORD dpmi_seg,dpmi_sel,dpmi_flag;
- WORD system_timer;
  HMODULE16 hModule;
  char mm_name[128];
  int mm_fd;
+ HANDLE hReadPipe,hXPipe;
  int read_pipe,write_pipe;
  pid_t task;
+ int sig_sent;
+ struct _DOSEVENT *pending,*current;
+ DOSSYSTEM *sys;
 } DOSTASK, *LPDOSTASK;
 
+typedef struct _DOSEVENT {
+  int irq,priority;
+  void (*relay)(LPDOSTASK,PCONTEXT,void*);
+  void *data;
+  struct _DOSEVENT *next;
+} DOSEVENT, *LPDOSEVENT;
+
+#define DOS_PRIORITY_REALTIME 0  /* IRQ0 */
+#define DOS_PRIORITY_KEYBOARD 1  /* IRQ1 */
+#define DOS_PRIORITY_VGA      2  /* IRQ9 */
+#define DOS_PRIORITY_MOUSE    5  /* IRQ12 */
+#define DOS_PRIORITY_SERIAL   10 /* IRQ4 */
+
 #if defined(linux) && defined(__i386__)
 
 #define MZ_SUPPORTED
@@ -33,19 +58,21 @@ typedef struct _DOSTASK {
 extern BOOL MZ_InitTask( LPDOSTASK lpDosTask );
 extern void MZ_KillModule( LPDOSTASK lpDosTask );
 extern LPDOSTASK MZ_AllocDPMITask( HMODULE16 hModule );
+extern void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,PCONTEXT,void*), void *data );
 
 #endif /* linux-i386 */
 
 #define V86_FLAG 0x00020000
 
-extern void MZ_Tick( WORD handle );
-
 extern BOOL MZ_CreateProcess( HFILE hFile, OFSTRUCT *ofs, LPCSTR cmdline, LPCSTR env, 
                               LPSECURITY_ATTRIBUTES psa, LPSECURITY_ATTRIBUTES tsa,
                               BOOL inherit, LPSTARTUPINFOA startup, 
                               LPPROCESS_INFORMATION info );
 extern int DOSVM_Enter( PCONTEXT context );
+extern void DOSVM_PIC_ioport_out( WORD port, BYTE val );
 extern void DOSVM_SetTimer( unsigned ticks );
 extern unsigned DOSVM_GetTimer( void );
+extern void DOSVM_SetSystemData( int id, void *data );
+extern void* DOSVM_GetSystemData( int id );
 
 #endif /* __WINE_DOSEXE_H */
diff --git a/include/winbase.h b/include/winbase.h
index 5bdb71f47d67f887755d5a0acd7fd87c9484ca08..74a28c709d2d94788148980339eaa722db10e874 100644
--- a/include/winbase.h
+++ b/include/winbase.h
@@ -1354,6 +1354,7 @@ HANDLE    WINAPI CreateFileMappingW(HANDLE,LPSECURITY_ATTRIBUTES,DWORD,
 HANDLE    WINAPI CreateMutexA(LPSECURITY_ATTRIBUTES,BOOL,LPCSTR);
 HANDLE    WINAPI CreateMutexW(LPSECURITY_ATTRIBUTES,BOOL,LPCWSTR);
 #define     CreateMutex WINELIB_NAME_AW(CreateMutex)
+BOOL      WINAPI CreatePipe(PHANDLE,PHANDLE,LPSECURITY_ATTRIBUTES,DWORD);
 BOOL      WINAPI CreateProcessA(LPCSTR,LPSTR,LPSECURITY_ATTRIBUTES,
                                     LPSECURITY_ATTRIBUTES,BOOL,DWORD,LPVOID,LPCSTR,
                                     LPSTARTUPINFOA,LPPROCESS_INFORMATION);
diff --git a/loader/dos/dosvm.c b/loader/dos/dosvm.c
index ce93852dfb92029145688ccc5bb65f4bba879137..2648019ca09c8c4c50dea0a404df00a96ebee8bf 100644
--- a/loader/dos/dosvm.c
+++ b/loader/dos/dosvm.c
@@ -18,6 +18,7 @@
 #include <sys/stat.h>
 
 #include "wine/winbase16.h"
+#include "winuser.h"
 #include "winnt.h"
 #include "sig_context.h"
 #include "msdos.h"
@@ -36,6 +37,14 @@
 #include <sys/mman.h>
 #include <sys/vm86.h>
 
+#define IF_CLR(ctx) EFL_reg(ctx) &= ~VIF_MASK
+#define IF_ENABLED(ctx) (EFL_reg(ctx) & VIF_MASK)
+#define SET_PEND(ctx) EFL_reg(ctx) |= VIP_MASK
+#define CLR_PEND(ctx) EFL_reg(ctx) &= ~VIP_MASK
+#define IS_PEND(ctx) (EFL_reg(ctx) & VIP_MASK)
+
+#undef TRY_PICRETURN
+
 static void DOSVM_Dump( LPDOSTASK lpDosTask, int fn, int sig,
                         struct vm86plus_struct*VM86 )
 {
@@ -90,15 +99,104 @@ static int DOSVM_Int( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
 
 static void DOSVM_SimulateInt( int vect, PCONTEXT context, LPDOSTASK lpDosTask )
 {
- FARPROC16 handler=INT_GetRMHandler(vect);
- WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
-
- *(--stack)=FL_reg(context);
- *(--stack)=CS_reg(context);
- *(--stack)=IP_reg(context);
- SP_reg(context)-=6;
- CS_reg(context)=SELECTOROF(handler);
- IP_reg(context)=OFFSETOF(handler);
+  FARPROC16 handler=INT_GetRMHandler(vect);
+
+  if (SELECTOROF(handler)==0xf000) {
+    /* if internal interrupt, call it directly */
+    INT_RealModeInterrupt(vect,context);
+  } else {
+    WORD*stack=(WORD*)(V86BASE(context)+(((DWORD)SS_reg(context))<<4)+SP_reg(context));
+    WORD flag=FL_reg(context);
+
+    if (IF_ENABLED(context)) flag|=IF_MASK;
+    else flag&=~IF_MASK;
+
+    *(--stack)=flag;
+    *(--stack)=CS_reg(context);
+    *(--stack)=IP_reg(context);
+    SP_reg(context)-=6;
+    CS_reg(context)=SELECTOROF(handler);
+    IP_reg(context)=OFFSETOF(handler);
+    IF_CLR(context);
+  }
+}
+
+#define SHOULD_PEND(x) \
+  (x && ((!lpDosTask->current) || (x->priority < lpDosTask->current->priority)))
+
+static void DOSVM_SendQueuedEvent(PCONTEXT context, LPDOSTASK lpDosTask)
+{
+  LPDOSEVENT event = lpDosTask->pending;
+
+  if (SHOULD_PEND(event)) {
+    /* remove from "pending" list */
+    lpDosTask->pending = event->next;
+    /* process event */
+    if (event->irq>=0) {
+      /* it's an IRQ, move it to "current" list */
+      event->next = lpDosTask->current;
+      lpDosTask->current = event;
+      TRACE(int,"dispatching IRQ %d\n",event->irq);
+      /* note that if DOSVM_SimulateInt calls an internal interrupt directly,
+       * lpDosTask->current might be cleared (and event freed) in this very call! */
+      DOSVM_SimulateInt((event->irq<8)?(event->irq+8):(event->irq-8+0x70),context,lpDosTask);
+    } else {
+      /* callback event */
+      TRACE(int,"dispatching callback event\n");
+      (*event->relay)(lpDosTask,context,event->data);
+      free(event);
+    }
+  }
+  if (!SHOULD_PEND(lpDosTask->pending)) {
+    TRACE(int,"clearing Pending flag\n");
+    CLR_PEND(context);
+  }
+}
+
+static void DOSVM_SendQueuedEvents(PCONTEXT context, LPDOSTASK lpDosTask)
+{
+  /* 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,lpDosTask);
+}
+
+void DOSVM_QueueEvent( int irq, int priority, void (*relay)(LPDOSTASK,PCONTEXT,void*), void *data)
+{
+  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
+  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
+  LPDOSEVENT event, cur, prev;
+
+  GlobalUnlock16( GetCurrentTask() );
+  if (pModule && pModule->lpDosTask) {
+    event = malloc(sizeof(DOSEVENT));
+    if (!event) {
+      ERR(int,"out of memory allocating event entry\n");
+      return;
+    }
+    event->irq = irq; event->priority = priority;
+    event->relay = relay; event->data = data;
+
+    /* insert event into linked list, in order *after*
+     * all earlier events of higher or equal priority */
+    cur = pModule->lpDosTask->pending; prev = NULL;
+    while (cur && cur->priority<=priority) {
+      prev = cur;
+      cur = cur->next;
+    }
+    event->next = cur;
+    if (prev) prev->next = event;
+    else pModule->lpDosTask->pending = event;
+    
+    /* get dosmod's attention to the new event, except for irq==0 where we already have it */
+    if (irq && !pModule->lpDosTask->sig_sent) {
+      TRACE(int,"new event queued, signalling dosmod\n");
+      kill(pModule->lpDosTask->task,SIGUSR2);
+      pModule->lpDosTask->sig_sent++;
+    } else {
+      TRACE(int,"new event queued\n");
+    }
+  }
 }
 
 #define CV CP(eax,EAX); CP(ecx,ECX); CP(edx,EDX); CP(ebx,EBX); \
@@ -130,12 +228,34 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
  CV;
 #undef CP
  (void*)V86BASE(&context)=lpDosTask->img;
+#ifdef TRY_PICRETURN
+ if (VM86->vm86plus.force_return_for_pic) {
+   SET_PEND(&context);
+ }
+#else
+ /* linux doesn't preserve pending flag on return */
+ if (SHOULD_PEND(lpDosTask->pending)) {
+   SET_PEND(&context);
+ }
+#endif
 
  switch (VM86_TYPE(fn)) {
   case VM86_SIGNAL:
    TRACE(int,"DOS module caught signal %d\n",sig);
-   if (sig==SIGALRM) {
-    DOSVM_SimulateInt(8,&context,lpDosTask);
+   if ((sig==SIGALRM) || (sig==SIGUSR2)) {
+     if (sig==SIGALRM) {
+       DOSVM_QueueEvent(0,DOS_PRIORITY_REALTIME,NULL,NULL);
+     }
+     if (lpDosTask->pending) {
+       TRACE(int,"setting Pending flag, interrupts are currently %s\n",
+                 IF_ENABLED(&context) ? "enabled" : "disabled");
+       SET_PEND(&context);
+       DOSVM_SendQueuedEvents(&context,lpDosTask);
+     } else {
+       TRACE(int,"no events are pending, clearing Pending flag\n");
+       CLR_PEND(&context);
+     }
+     if (sig==SIGUSR2) lpDosTask->sig_sent--;
    } else
    if (sig==SIGHUP) {
     if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
@@ -159,9 +279,10 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
     DPRINTF("Ret  DOS int 0x%02x (EAX=%08lx) ret=%04lx:%04lx\n",VM86_ARG(fn),context.Eax,context.SegCs,context.Eip);
    break;
   case VM86_STI:
-   break;
   case VM86_PICRETURN:
-   printf("Trapped due to pending PIC request\n"); break;
+    TRACE(int,"DOS task enabled interrupts with events pending, sending events\n");
+    DOSVM_SendQueuedEvents(&context,lpDosTask);
+    break;
   case VM86_TRAP:
    if (ctx_debug_call) ctx_debug_call(SIGTRAP,&context);
    break;
@@ -173,9 +294,42 @@ static int DOSVM_Process( LPDOSTASK lpDosTask, int fn, int sig,
 #define CP(x,y) VM86->regs.x = y##_reg(&context)
  CV;
 #undef CP
+#ifdef TRY_PICRETURN
+ VM86->vm86plus.force_return_for_pic = IS_PEND(&context) ? 1 : 0;
+ CLR_PEND(&context);
+#endif
  return ret;
 }
 
+void DOSVM_ProcessMessage(LPDOSTASK lpDosTask,MSG *msg)
+{
+  BYTE scan = 0;
+
+  fprintf(stderr,"got message %04x, wparam=%08x, lparam=%08lx\n",msg->message,msg->wParam,msg->lParam);
+  if ((msg->message>=WM_MOUSEFIRST)&&
+      (msg->message<=WM_MOUSELAST)) {
+    INT_Int33Message(msg->message,msg->wParam,msg->lParam);
+  } else {
+    switch (msg->message) {
+    case WM_KEYUP:
+      scan = 0x80;
+    case WM_KEYDOWN:
+      scan |= (msg->lParam >> 16) & 0x7f;
+
+      /* check whether extended bit is set,
+       * and if so, queue the extension prefix */
+      if (msg->lParam & 0x1000000) {
+	/* FIXME: some keys (function keys) have
+	 * extended bit set even when they shouldn't,
+	 * should check for them */
+	INT_Int09SendScan(0xE0);
+      }
+      INT_Int09SendScan(scan);
+      break;
+    }
+  }
+}
+
 int DOSVM_Enter( PCONTEXT context )
 {
  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
@@ -183,7 +337,10 @@ int DOSVM_Enter( PCONTEXT context )
  LPDOSTASK lpDosTask;
  struct vm86plus_struct VM86;
  int stat,len,sig;
- fd_set readfds,exceptfds;
+ DWORD waitret;
+ MSG msg;
+ fd_set readfds;
+ struct timeval timeout={0,0};
 
  GlobalUnlock16( GetCurrentTask() );
  if (!pModule) {
@@ -212,6 +369,7 @@ int DOSVM_Enter( PCONTEXT context )
   VM86.regs.esp=lpDosTask->init_sp;
   VM86.regs.ds=lpDosTask->psp_seg;
   VM86.regs.es=lpDosTask->psp_seg;
+  VM86.regs.eflags=VIF_MASK;
   /* hmm, what else do we need? */
  }
 
@@ -221,57 +379,62 @@ int DOSVM_Enter( PCONTEXT context )
   errno = 0;
   /* transmit VM86 structure to dosmod task */
   if (write(lpDosTask->write_pipe,&stat,sizeof(stat))!=sizeof(stat)) {
-   ERR(module,"dosmod sync lost, errno=%d\n",errno);
+   ERR(module,"dosmod sync lost, errno=%d, fd=%d, pid=%d\n",errno,lpDosTask->write_pipe,getpid());
    return -1;
   }
   if (write(lpDosTask->write_pipe,&VM86,sizeof(VM86))!=sizeof(VM86)) {
    ERR(module,"dosmod sync lost, errno=%d\n",errno);
    return -1;
   }
-  /* wait for response, with async events enabled */
-  FD_ZERO(&readfds);
-  FD_ZERO(&exceptfds);
-  SIGNAL_MaskAsyncEvents(FALSE);
-  do {
-   FD_SET(lpDosTask->read_pipe,&readfds);
-   FD_SET(lpDosTask->read_pipe,&exceptfds);
-   select(lpDosTask->read_pipe+1,&readfds,NULL,&exceptfds,NULL);
-  } while (!(FD_ISSET(lpDosTask->read_pipe,&readfds)||
-             FD_ISSET(lpDosTask->read_pipe,&exceptfds)));
-  SIGNAL_MaskAsyncEvents(TRUE);
-  /* read response (with async events disabled to avoid some strange problems) */
   do {
-   if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))!=sizeof(stat)) {
+    /* check for messages (waste time before the response check below) */
+    while (PeekMessageA(&msg,0,0,0,PM_REMOVE|PM_NOYIELD)) {
+      /* got a message */
+      DOSVM_ProcessMessage(lpDosTask,&msg);
+      /* we don't need a TranslateMessage here */
+      DispatchMessageA(&msg);
+    }
+    /* quick check for response from dosmod
+     * (faster than doing the full blocking wait, if data already available) */
+    FD_ZERO(&readfds); FD_SET(lpDosTask->read_pipe,&readfds);
+    if (select(lpDosTask->read_pipe+1,&readfds,NULL,NULL,&timeout)>0)
+      break;
+    /* nothing yet, block while waiting for something to do */
+    waitret=MsgWaitForMultipleObjects(1,&(lpDosTask->hReadPipe),FALSE,INFINITE,QS_ALLINPUT);
+    if (waitret==(DWORD)-1) {
+      ERR(module,"dosvm wait error=%ld\n",GetLastError());
+    }
+  } while (waitret!=WAIT_OBJECT_0);
+  /* read response */
+  while (1) {
+    if ((len=read(lpDosTask->read_pipe,&stat,sizeof(stat)))==sizeof(stat)) break;
     if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
      WARN(module,"rereading dosmod return code due to errno=%d, result=%d\n",errno,len);
      continue;
     }
     ERR(module,"dosmod sync lost reading return code, errno=%d, result=%d\n",errno,len);
     return -1;
-   }
-  } while (0);
+  }
   TRACE(module,"dosmod return code=%d\n",stat);
-  do {
-   if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))!=sizeof(VM86)) {
+  while (1) {
+    if ((len=read(lpDosTask->read_pipe,&VM86,sizeof(VM86)))==sizeof(VM86)) break;
     if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
      WARN(module,"rereading dosmod VM86 structure due to errno=%d, result=%d\n",errno,len);
      continue;
     }
     ERR(module,"dosmod sync lost reading VM86 structure, errno=%d, result=%d\n",errno,len);
     return -1;
-   }
-  } while (0);
+  }
   if ((stat&0xff)==DOSMOD_SIGNAL) {
-   do {
-    if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))!=sizeof(sig)) {
-     if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
-      WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
-      continue;
-     }
-     ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
-     return -1;
-    }
-   } while (0);
+    while (1) {
+      if ((len=read(lpDosTask->read_pipe,&sig,sizeof(sig)))==sizeof(sig)) break;
+      if (((errno==EINTR)||(errno==EAGAIN))&&(len<=0)) {
+	WARN(module,"rereading dosmod signal due to errno=%d, result=%d\n",errno,len);
+	continue;
+      }
+      ERR(module,"dosmod sync lost reading signal, errno=%d, result=%d\n",errno,len);
+      return -1;
+    } while (0);
   } else sig=0;
   /* got response */
  } while (DOSVM_Process(lpDosTask,stat,sig,&VM86)>=0);
@@ -284,6 +447,41 @@ int DOSVM_Enter( PCONTEXT context )
  return 0;
 }
 
+void DOSVM_PIC_ioport_out( WORD port, BYTE val)
+{
+  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
+  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
+  LPDOSEVENT event;
+
+  GlobalUnlock16( GetCurrentTask() );
+  if (pModule && pModule->lpDosTask) {
+    if ((port==0x20) && (val==0x20)) {
+      if (pModule->lpDosTask->current) {
+	/* EOI (End Of Interrupt) */
+	TRACE(int,"received EOI for current IRQ, clearing\n");
+	event = pModule->lpDosTask->current;
+	pModule->lpDosTask->current = event->next;
+	if (event->relay)
+	(*event->relay)(pModule->lpDosTask,NULL,event->data);
+	free(event);
+
+	if (pModule->lpDosTask->pending &&
+	    !pModule->lpDosTask->sig_sent) {
+	  /* another event is pending, which we should probably
+	   * be able to process now, so tell dosmod about it */
+	  TRACE(int,"another event pending, signalling dosmod\n");
+	  kill(pModule->lpDosTask->task,SIGUSR2);
+	  pModule->lpDosTask->sig_sent++;
+	}
+      } else {
+	WARN(int,"EOI without active IRQ\n");
+      }
+    } else {
+      FIXME(int,"unrecognized PIC command %02x\n",val);
+    }
+  }
+}
+
 void DOSVM_SetTimer( unsigned ticks )
 {
  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
@@ -334,19 +532,49 @@ unsigned DOSVM_GetTimer( void )
  return 0;
 }
 
-void MZ_Tick( WORD handle )
+void DOSVM_SetSystemData( int id, void *data )
 {
- /* find the DOS task that has the right system_timer handle... */
- /* should usually be the current, so let's just be lazy... */
- TDB *pTask = (TDB*)GlobalLock16( GetCurrentTask() );
- NE_MODULE *pModule = pTask ? NE_GetPtr( pTask->hModule ) : NULL;
- LPDOSTASK lpDosTask = pModule ? pModule->lpDosTask : NULL;
+  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
+  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
+  DOSSYSTEM *sys, *prev;
+
+  GlobalUnlock16( GetCurrentTask() );
+  if (pModule && pModule->lpDosTask) {
+    sys = pModule->lpDosTask->sys;
+    prev = NULL;
+    while (sys && (sys->id != id)) {
+      prev = sys;
+      sys = sys->next;
+    }
+    if (sys) {
+      free(sys->data);
+      sys->data = data;
+    } else {
+      sys = malloc(sizeof(DOSSYSTEM));
+      sys->id = id;
+      sys->data = data;
+      sys->next = NULL;
+      if (prev) prev->next = sys;
+      else pModule->lpDosTask->sys = sys;
+    }
+  } else free(data);
+}
 
- GlobalUnlock16( GetCurrentTask() );
- if (lpDosTask&&(lpDosTask->system_timer==handle)) {
-  /* BIOS timer tick */
-  (*((DWORD*)(((BYTE*)(lpDosTask->img))+0x46c)))++;
- }
+void* DOSVM_GetSystemData( int id )
+{
+  TDB *pTask = (TDB *)GlobalLock16( GetCurrentTask() );
+  NE_MODULE *pModule = NE_GetPtr( pTask->hModule );
+  DOSSYSTEM *sys;
+
+  GlobalUnlock16( GetCurrentTask() );
+  if (pModule && pModule->lpDosTask) {
+    sys = pModule->lpDosTask->sys;
+    while (sys && (sys->id != id))
+      sys = sys->next;
+    if (sys)
+      return sys->data;
+  }
+  return NULL;
 }
 
 #else /* !MZ_SUPPORTED */
@@ -357,8 +585,10 @@ int DOSVM_Enter( PCONTEXT context )
  return -1;
 }
 
-void MZ_Tick( WORD handle ) {}
+void DOSVM_PIC_ioport_out( WORD port, BYTE val) {}
 void DOSVM_SetTimer( unsigned ticks ) {}
 unsigned DOSVM_GetTimer( void ) { return 0; }
+void DOSVM_SetSystemData( int id, void *data ) { free(data); }
+void* DOSVM_GetSystemData( int id ) { return NULL; }
 
 #endif
diff --git a/loader/dos/module.c b/loader/dos/module.c
index 9a8b301d0b276fbe75c7d451fffe1a010a62a965..2ac543c2e0018505d3eb2b969e2de4c17a34e491 100644
--- a/loader/dos/module.c
+++ b/loader/dos/module.c
@@ -32,6 +32,7 @@
 #include "dosexe.h"
 #include "dosmod.h"
 #include "options.h"
+#include "server.h"
 
 #ifdef MZ_SUPPORTED
 
@@ -322,11 +323,7 @@ LPDOSTASK MZ_AllocDPMITask( HMODULE16 hModule )
 static void MZ_InitTimer( LPDOSTASK lpDosTask, int ver )
 {
  if (ver<1) {
-#if 0
-  /* start simulated system 55Hz timer */
-  lpDosTask->system_timer = CreateSystemTimer( 55, MZ_Tick );
-  TRACE(module,"created 55Hz timer tick, handle=%d\n",lpDosTask->system_timer);
-#endif
+  /* can't make timer ticks */
  } else {
   int func;
   struct timeval tim;
@@ -341,18 +338,36 @@ static void MZ_InitTimer( LPDOSTASK lpDosTask, int ver )
 
 BOOL MZ_InitTask( LPDOSTASK lpDosTask )
 {
- int read_fd[2],write_fd[2];
- pid_t child;
- char *fname,*farg,arg[16],fproc[64],path[256],*fpath;
-
- if (!lpDosTask) return FALSE;
- /* create read pipe */
- if (pipe(read_fd)<0) return FALSE;
- if (pipe(write_fd)<0) {
-  close(read_fd[0]); close(read_fd[1]); return FALSE;
- }
- lpDosTask->read_pipe=read_fd[0];
- lpDosTask->write_pipe=write_fd[1];
+  int write_fd[2],x_fd;
+  pid_t child;
+  char *fname,*farg,arg[16],fproc[64],path[256],*fpath;
+  SECURITY_ATTRIBUTES attr={sizeof(attr),NULL,TRUE};
+  struct get_read_fd_request r_req;
+  struct get_write_fd_request w_req;
+
+  if (!lpDosTask) return FALSE;
+  /* create pipes */
+  /* this happens in the wrong process context, so we have to let the new process
+     inherit it... (FIXME: call MZ_InitTask in the right process context) */
+  if (!CreatePipe(&(lpDosTask->hReadPipe),&(lpDosTask->hXPipe),&attr,0)) return FALSE;
+  if (pipe(write_fd)<0) {
+    CloseHandle(lpDosTask->hReadPipe);
+    CloseHandle(lpDosTask->hXPipe);
+    return FALSE;
+  }
+  r_req.handle = lpDosTask->hReadPipe;
+  CLIENT_SendRequest( REQ_GET_READ_FD, -1, 1, &r_req, sizeof(r_req) );
+  CLIENT_WaitReply( NULL, &(lpDosTask->read_pipe), 0 );
+  w_req.handle = lpDosTask->hXPipe;
+  CLIENT_SendRequest( REQ_GET_WRITE_FD, -1, 1, &w_req, sizeof(w_req) );
+  CLIENT_WaitReply( NULL, &x_fd, 0 );
+
+  TRACE(module,"win32 pipe: read=%d, write=%d, unix pipe: read=%d, write=%d\n",
+	       lpDosTask->hReadPipe,lpDosTask->hXPipe,lpDosTask->read_pipe,x_fd);
+  TRACE(module,"outbound unix pipe: read=%d, write=%d, pid=%d\n",write_fd[0],write_fd[1],getpid());
+
+  lpDosTask->write_pipe=write_fd[1];
+
  /* if we have a mapping file, use it */
  fname=lpDosTask->mm_name; farg=NULL;
  if (!fname[0]) {
@@ -362,16 +377,22 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
   fname=fproc; farg=arg;
  }
 
- TRACE(module,"Loading DOS VM support module (hmodule=%04x)\n",lpDosTask->hModule);
- if ((child=fork())<0) {
-  close(write_fd[0]); close(write_fd[1]);
-  close(read_fd[0]); close(read_fd[1]); return FALSE;
- }
+  TRACE(module,"Loading DOS VM support module (hmodule=%04x)\n",lpDosTask->hModule);
+  if ((child=fork())<0) {
+    close(write_fd[0]);
+    close(lpDosTask->read_pipe);
+    close(lpDosTask->write_pipe);
+    close(x_fd);
+    CloseHandle(lpDosTask->hReadPipe);
+    CloseHandle(lpDosTask->hXPipe);
+    return FALSE;
+  }
  if (child!=0) {
   /* parent process */
   int ret;
 
-  close(read_fd[1]); close(write_fd[0]);
+  close(write_fd[0]);
+  close(x_fd);
   lpDosTask->task=child;
   /* wait for child process to signal readiness */
   do {
@@ -388,26 +409,36 @@ BOOL MZ_InitTask( LPDOSTASK lpDosTask )
   if (lpDosTask->mm_name[0]!=0) unlink(lpDosTask->mm_name);
   /* start simulated system timer */
   MZ_InitTimer(lpDosTask,ret);
+  if (ret<2) {
+    ERR(module,"dosmod version too old! Please install newer dosmod properly\n");
+    ERR(module,"If you don't, the new dosmod event handling system will not work\n");
+  }
   /* all systems are now go */
  } else {
   /* child process */
-  close(read_fd[0]); close(write_fd[1]);
+  close(lpDosTask->read_pipe);
+  close(lpDosTask->write_pipe);
   /* put our pipes somewhere dosmod can find them */
-  dup2(write_fd[0],0);      /* stdin */
-  dup2(read_fd[1],1);       /* stdout */
+  dup2(write_fd[0],0); /* stdin */
+  dup2(x_fd,1);        /* stdout */
   /* enable signals */
   SIGNAL_MaskAsyncEvents(FALSE);
   /* now load dosmod */
-  execlp("dosmod",fname,farg,NULL);
-  execl("dosmod",fname,farg,NULL);
-  /* hmm, they didn't install properly */
-  execl("loader/dos/dosmod",fname,farg,NULL);
-  /* last resort, try to find it through argv[0] */
+  /* check argv[0]-derived paths first, since the newest dosmod is most likely there
+   * (at least it was once for Andreas Mohr, so I decided to make it easier for him) */
   fpath=strrchr(strcpy(path,Options.argv0),'/');
   if (fpath) {
+   strcpy(fpath,"/dosmod");
+   execl(path,fname,farg,NULL);
    strcpy(fpath,"/loader/dos/dosmod");
    execl(path,fname,farg,NULL);
   }
+  /* okay, it wasn't there, try in the path */
+  execlp("dosmod",fname,farg,NULL);
+  /* last desperate attempts: current directory */
+  execl("dosmod",fname,farg,NULL);
+  /* and, just for completeness... */
+  execl("loader/dos/dosmod",fname,farg,NULL);
   /* if failure, exit */
   ERR(module,"Failed to spawn dosmod, error=%s\n",strerror(errno));
   exit(1);
@@ -464,6 +495,7 @@ BOOL MZ_CreateProcess( HFILE hFile, OFSTRUCT *ofs, LPCSTR cmdline, LPCSTR env,
    SetLastError(ERROR_GEN_FAILURE);
    return FALSE;
   }
+  inherit = TRUE; /* bad hack for inheriting the CreatePipe... */
   if (!PROCESS_Create( pModule, cmdline, env, 0, 0, 
                        psa, tsa, inherit, startup, info ))
    return FALSE;
@@ -473,17 +505,33 @@ BOOL MZ_CreateProcess( HFILE hFile, OFSTRUCT *ofs, LPCSTR cmdline, LPCSTR env,
 
 void MZ_KillModule( LPDOSTASK lpDosTask )
 {
- TRACE(module,"killing DOS task\n");
-#if 0
- SYSTEM_KillSystemTimer(lpDosTask->system_timer);
-#endif
- if (lpDosTask->mm_name[0]!=0) {
-  munmap(lpDosTask->img,0x110000-START_OFFSET);
-  close(lpDosTask->mm_fd);
- } else VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
- close(lpDosTask->read_pipe);
- close(lpDosTask->write_pipe);
- kill(lpDosTask->task,SIGTERM);
+  DOSEVENT *event,*p_event;
+  DOSSYSTEM *sys,*p_sys;
+
+  TRACE(module,"killing DOS task\n");
+  if (lpDosTask->mm_name[0]!=0) {
+    munmap(lpDosTask->img,0x110000-START_OFFSET);
+    close(lpDosTask->mm_fd);
+  } else VirtualFree(lpDosTask->img,0x110000,MEM_RELEASE);
+  close(lpDosTask->read_pipe);
+  close(lpDosTask->write_pipe);
+  CloseHandle(lpDosTask->hReadPipe);
+  CloseHandle(lpDosTask->hXPipe);
+  kill(lpDosTask->task,SIGTERM);
+/* free memory allocated for events and systems */
+#define DFREE(var,pvar,svar) \
+  var = lpDosTask->svar; \
+  while (var) { \
+    if (var->data) free(var->data); \
+    pvar = var->next; free(var); var = pvar; \
+  }
+
+  DFREE(event,p_event,pending)
+  DFREE(event,p_event,current)
+  DFREE(sys,p_sys,sys)
+
+#undef DFREE
+
 #if 0
  /* FIXME: this seems to crash */
  if (lpDosTask->dpmi_sel)