Added support for exception handling while in vm86 mode.
Fixed a couple of bugs in vm86 support.

diff --git a/dlls/ntdll/signal_i386.c b/dlls/ntdll/signal_i386.c
index 2cb33a0..a39f5ad 100644
--- a/dlls/ntdll/signal_i386.c
+++ b/dlls/ntdll/signal_i386.c
@@ -29,6 +29,7 @@
 # include <sys/vm86.h>
 #endif
 
+#include "winnt.h"
 #include "selectors.h"
 
 /***********************************************************************
@@ -108,21 +109,26 @@
 }
 #endif
 
-static inline int wine_vm86plus( int func, struct vm86plus_struct *ptr )
-{
-    int res;
-    __asm__ __volatile__( "pushl %%fs\n\t"
-                          "pushl %%ebx\n\t"
-                          "movl %2,%%ebx\n\t"
-                          "int $0x80\n\t"
-                          "popl %%ebx\n\t"
-                          "popl %%fs"
-                          : "=a" (res)
-                          : "0" (166 /*SYS_vm86*/), "g" (func), "c" (ptr) );
-    if (res >= 0) return res;
-    errno = -res;
-    return -1;
-}
+int vm86_enter( struct vm86plus_struct *ptr );
+void vm86_return();
+__ASM_GLOBAL_FUNC(vm86_enter,
+                  "pushl %ebp\n\t"
+                  "movl %esp, %ebp\n\t"
+                  "movl $166,%eax\n\t"  /*SYS_vm86*/
+                  "pushl %fs\n\t"
+                  "movl 8(%ebp),%ecx\n\t"
+                  "pushl %ebx\n\t"
+                  "movl $1,%ebx\n\t"    /*VM86_ENTER*/
+                  "pushl %ecx\n\t"      /* put vm86plus_struct ptr somewhere we can find it */
+                  "int $0x80\n"
+                  ".globl " __ASM_NAME("vm86_return") "\n\t"
+                  ".type " __ASM_NAME("vm86_return") ",@function\n"
+                  __ASM_NAME("vm86_return") ":\n\t"
+                  "popl %ecx\n\t"
+                  "popl %ebx\n\t"
+                  "popl %fs\n\t"
+                  "popl %ebp\n\t"
+                  "ret" );
 
 #endif  /* linux */
 
@@ -331,34 +337,9 @@
 #include "syslevel.h"
 #include "debugtools.h"
 
-DEFAULT_DEBUG_CHANNEL(seh)
+DEFAULT_DEBUG_CHANNEL(seh);
 
 
-
-/***********************************************************************
- *           handler_init
- *
- * Initialization code for a signal handler.
- * Restores the proper %fs value for the current thread.
- */
-static inline void handler_init( CONTEXT *context, const SIGCONTEXT *sigcontext )
-{
-    WORD fs;
-    /* get %fs at time of the fault */
-#ifdef FS_sig
-    fs = FS_sig(sigcontext);
-#else
-    fs = __get_fs();
-#endif
-    context->SegFs = fs;
-    /* now restore a proper %fs for the fault handler */
-    if ((EFL_sig(sigcontext) & 0x00020000) ||  /* vm86 mode */
-        !IS_SELECTOR_SYSTEM(CS_sig(sigcontext)))  /* 16-bit mode */
-        fs = SYSLEVEL_Win16CurrentTeb;
-    if (!fs) fs = SYSLEVEL_EmergencyTeb;
-    __set_fs(fs);
-}
-
 /***********************************************************************
  *           get_trap_code
  *
@@ -408,6 +389,57 @@
  */
 static void save_context( CONTEXT *context, const SIGCONTEXT *sigcontext )
 {
+    WORD fs;
+    /* get %fs at time of the fault */
+#ifdef FS_sig
+    fs = FS_sig(sigcontext);
+#else
+    fs = __get_fs();
+#endif
+    context->SegFs = fs;
+
+    /* now restore a proper %fs for the fault handler */
+    if (!IS_SELECTOR_SYSTEM(CS_sig(sigcontext)))  /* 16-bit mode */
+    {
+        fs = SYSLEVEL_Win16CurrentTeb;
+    }
+#ifdef linux
+    else if ((void *)EIP_sig(sigcontext) == vm86_return)  /* vm86 mode */
+    {
+        /* retrieve pointer to vm86plus struct that was stored in vm86_enter */
+        struct vm86plus_struct *vm86 = *(struct vm86plus_struct **)ESP_sig(sigcontext);
+        /* fetch the saved %fs on the stack */
+        fs = *((unsigned int *)ESP_sig(sigcontext) + 2);
+        __set_fs(fs);
+        /* get context from vm86 struct */
+        context->Eax    = vm86->regs.eax;
+        context->Ebx    = vm86->regs.ebx;
+        context->Ecx    = vm86->regs.ecx;
+        context->Edx    = vm86->regs.edx;
+        context->Esi    = vm86->regs.esi;
+        context->Edi    = vm86->regs.edi;
+        context->Esp    = vm86->regs.esp;
+        context->Ebp    = vm86->regs.ebp;
+        context->Eip    = vm86->regs.eip;
+        context->SegCs  = vm86->regs.cs;
+        context->SegDs  = vm86->regs.ds;
+        context->SegEs  = vm86->regs.es;
+        context->SegFs  = vm86->regs.fs;
+        context->SegGs  = vm86->regs.gs;
+        context->SegSs  = vm86->regs.ss;
+        context->EFlags = vm86->regs.eflags;
+        return;
+    }
+#endif  /* linux */
+
+    if (!fs)
+    {
+        fs = SYSLEVEL_EmergencyTeb;
+        __set_fs(fs);
+        ERR("fallback to emergency TEB\n");
+    }
+    __set_fs(fs);
+
     context->Eax    = EAX_sig(sigcontext);
     context->Ebx    = EBX_sig(sigcontext);
     context->Ecx    = ECX_sig(sigcontext);
@@ -422,7 +454,6 @@
     context->SegDs  = LOWORD(DS_sig(sigcontext));
     context->SegEs  = LOWORD(ES_sig(sigcontext));
     context->SegSs  = LOWORD(SS_sig(sigcontext));
-    /* %fs already handled in handler_init */
 #ifdef GS_sig
     context->SegGs  = LOWORD(GS_sig(sigcontext));
 #else
@@ -438,6 +469,33 @@
  */
 static void restore_context( const CONTEXT *context, SIGCONTEXT *sigcontext )
 {
+#ifdef linux
+    /* check if exception occurred in vm86 mode */
+    if ((void *)EIP_sig(sigcontext) == vm86_return &&
+        IS_SELECTOR_SYSTEM(CS_sig(sigcontext)))
+    {
+        /* retrieve pointer to vm86plus struct that was stored in vm86_enter */
+        struct vm86plus_struct *vm86 = *(struct vm86plus_struct **)ESP_sig(sigcontext);
+        vm86->regs.eax    = context->Eax;
+        vm86->regs.ebx    = context->Ebx;
+        vm86->regs.ecx    = context->Ecx;
+        vm86->regs.edx    = context->Edx;
+        vm86->regs.esi    = context->Esi;
+        vm86->regs.edi    = context->Edi;
+        vm86->regs.esp    = context->Esp;
+        vm86->regs.ebp    = context->Ebp;
+        vm86->regs.eip    = context->Eip;
+        vm86->regs.cs     = context->SegCs;
+        vm86->regs.ds     = context->SegDs;
+        vm86->regs.es     = context->SegEs;
+        vm86->regs.fs     = context->SegFs;
+        vm86->regs.gs     = context->SegGs;
+        vm86->regs.ss     = context->SegSs;
+        vm86->regs.eflags = context->EFlags;
+        return;
+    }
+#endif /* linux */
+
     EAX_sig(sigcontext) = context->Eax;
     EBX_sig(sigcontext) = context->Ebx;
     ECX_sig(sigcontext) = context->Ecx;
@@ -685,7 +743,6 @@
 static HANDLER_DEF(segv_handler)
 {
     CONTEXT context;
-    handler_init( &context, HANDLER_CONTEXT );
     save_context( &context, HANDLER_CONTEXT );
     do_segv( &context, get_trap_code(HANDLER_CONTEXT),
              get_cr2_value(HANDLER_CONTEXT), get_error_code(HANDLER_CONTEXT) );
@@ -701,7 +758,6 @@
 static HANDLER_DEF(trap_handler)
 {
     CONTEXT context;
-    handler_init( &context, HANDLER_CONTEXT );
     save_context( &context, HANDLER_CONTEXT );
     do_trap( &context, get_trap_code(HANDLER_CONTEXT) );
     restore_context( &context, HANDLER_CONTEXT );
@@ -716,7 +772,6 @@
 static HANDLER_DEF(fpe_handler)
 {
     CONTEXT context;
-    handler_init( &context, HANDLER_CONTEXT );
     save_fpu( &context, HANDLER_CONTEXT );
     save_context( &context, HANDLER_CONTEXT );
     do_fpe( &context, get_trap_code(HANDLER_CONTEXT) );
@@ -735,7 +790,6 @@
     EXCEPTION_RECORD rec;
     CONTEXT context;
 
-    handler_init( &context, HANDLER_CONTEXT );
     save_context( &context, HANDLER_CONTEXT );
     rec.ExceptionCode    = CONTROL_C_EXIT;
     rec.ExceptionFlags   = EXCEPTION_CONTINUABLE;
@@ -842,6 +896,7 @@
     int res;
     struct vm86plus_struct vm86;
 
+    memset( &vm86, 0, sizeof(vm86) );
     for (;;)
     {
         vm86.regs.eax    = context->Eax;
@@ -858,10 +913,18 @@
         vm86.regs.es     = context->SegEs;
         vm86.regs.fs     = context->SegFs;
         vm86.regs.gs     = context->SegGs;
+        vm86.regs.ss     = context->SegSs;
         vm86.regs.eflags = context->EFlags;
-        if (vm86.regs.eflags & IF_MASK) vm86.regs.eflags |= VIF_MASK;
 
-        res = wine_vm86plus( VM86_ENTER, &vm86 );
+        do
+        {
+            res = vm86_enter( &vm86 );
+            if (res < 0)
+            {
+                errno = -res;
+                return;
+            }
+        } while (VM86_TYPE(res) == VM86_SIGNAL);
 
         context->Eax    = vm86.regs.eax;
         context->Ebx    = vm86.regs.ebx;
@@ -877,34 +940,16 @@
         context->SegEs  = vm86.regs.es;
         context->SegFs  = vm86.regs.fs;
         context->SegGs  = vm86.regs.gs;
+        context->SegSs  = vm86.regs.ss;
         context->EFlags = vm86.regs.eflags;
 
         switch(VM86_TYPE(res))
         {
-        case VM86_SIGNAL: /* return due to signal */
-            switch(VM86_ARG(res))
-            {
-            case SIGILL:
-            case SIGSEGV:
-#ifdef SIGBUS
-            case SIGBUS:
-#endif
-                do_segv( context, T_UNKNOWN, 0, 0 );
-                continue;
-            case SIGTRAP:
-                do_trap( context, T_UNKNOWN  );
-                continue;
-            case SIGFPE:
-                do_fpe( context, T_UNKNOWN );
-                continue;
-            }
-            rec.ExceptionCode = EXCEPTION_VM86_SIGNAL;
-            break;
         case VM86_UNKNOWN: /* unhandled GP fault - IO-instruction or similar */
             do_segv( context, T_PROTFLT, 0, 0 );
             continue;
         case VM86_TRAP: /* return due to DOS-debugger request */
-            do_trap( context, T_UNKNOWN  );
+            do_trap( context, VM86_ARG(res)  );
             continue;
         case VM86_INTx: /* int3/int x instruction (ARG = x) */
             rec.ExceptionCode = EXCEPTION_VM86_INTx;
diff --git a/dlls/ntdll/signal_sparc.c b/dlls/ntdll/signal_sparc.c
index b2ebff9..c878a9f 100644
--- a/dlls/ntdll/signal_sparc.c
+++ b/dlls/ntdll/signal_sparc.c
@@ -349,6 +349,14 @@
 }
 
 /**********************************************************************
+ *		__wine_enter_vm86
+ */
+void __wine_enter_vm86( CONTEXT *context )
+{
+    MESSAGE("vm86 mode not supported on this platform\n");
+}
+
+/**********************************************************************
  *              DbgBreakPoint   (NTDLL)
  */
 void WINAPI DbgBreakPoint(void)