rpcrt4: Fix cfi annotations for the stubless proxy entry point.
[wine] / dlls / krnl386.exe16 / instr.c
1 /*
2  * Emulation of privileged instructions
3  *
4  * Copyright 1995 Alexandre Julliard
5  * Copyright 2005 Ivan Leo Puoti
6  * Copyright 2005 Laurent Pinchart
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
21  */
22
23 #include "config.h"
24 #include "wine/port.h"
25
26 #include <stdarg.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winternl.h"
31 #include "wine/winuser16.h"
32 #include "excpt.h"
33 #include "wine/debug.h"
34 #include "kernel16_private.h"
35 #include "dosexe.h"
36 #include "wine/exception.h"
37
38 WINE_DEFAULT_DEBUG_CHANNEL(int);
39 WINE_DECLARE_DEBUG_CHANNEL(io);
40
41 /* macros to set parts of a DWORD */
42 #define SET_LOWORD(dw,val)  ((dw) = ((dw) & 0xffff0000) | LOWORD(val))
43 #define SET_LOBYTE(dw,val)  ((dw) = ((dw) & 0xffffff00) | LOBYTE(val))
44 #define ADD_LOWORD(dw,val)  ((dw) = ((dw) & 0xffff0000) | LOWORD((DWORD)(dw)+(val)))
45 #define ISV86(context)      ((context)->EFlags & 0x00020000)
46
47 static inline void add_stack( CONTEXT86 *context, int offset )
48 {
49     if (ISV86(context) || !IS_SELECTOR_32BIT(context->SegSs))
50         ADD_LOWORD( context->Esp, offset );
51     else
52         context->Esp += offset;
53 }
54
55 static inline void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long_addr )
56 {
57     if (ISV86(context)) return (void *)((seg << 4) + LOWORD(off));
58     if (wine_ldt_is_system(seg)) return (void *)off;
59     if (!long_addr) off = LOWORD(off);
60     return (char *) MapSL( MAKESEGPTR( seg, 0 ) ) + off;
61 }
62
63 static inline void *get_stack( CONTEXT86 *context )
64 {
65     if (ISV86(context)) return (void *)((context->SegSs << 4) + LOWORD(context->Esp));
66     return wine_ldt_get_ptr( context->SegSs, context->Esp );
67 }
68
69 #include "pshpack1.h"
70 struct idtr
71 {
72     WORD  limit;
73     BYTE *base;
74 };
75 #include "poppack.h"
76
77 static LDT_ENTRY idt[256];
78
79 static inline struct idtr get_idtr(void)
80 {
81     struct idtr ret;
82 #if defined(__i386__) && defined(__GNUC__)
83     __asm__( "sidtl %0" : "=m" (ret) );
84 #else
85     ret.base = (BYTE *)idt;
86     ret.limit = sizeof(idt) - 1;
87 #endif
88     return ret;
89 }
90
91
92 /***********************************************************************
93  *           INSTR_ReplaceSelector
94  *
95  * Try to replace an invalid selector by a valid one.
96  * The only selector where it is allowed to do "mov ax,40;mov es,ax"
97  * is the so called 'bimodal' selector 0x40, which points to the BIOS
98  * data segment. Used by (at least) Borland products (and programs compiled
99  * using Borland products).
100  *
101  * See Undocumented Windows, Chapter 5, __0040.
102  */
103 static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel )
104 {
105     if (*sel == 0x40)
106     {
107         DOSVM_start_bios_timer();
108         *sel = DOSMEM_BiosDataSeg;
109         return TRUE;
110     }
111     return FALSE;  /* Can't replace selector, crashdump */
112 }
113
114
115 /* store an operand into a register */
116 static void store_reg( CONTEXT86 *context, BYTE regmodrm, const BYTE *addr, int long_op )
117 {
118     switch((regmodrm >> 3) & 7)
119     {
120     case 0:
121         if (long_op) context->Eax = *(const DWORD *)addr;
122         else SET_LOWORD(context->Eax, *(const WORD *)addr);
123         break;
124     case 1:
125         if (long_op) context->Ecx = *(const DWORD *)addr;
126         else SET_LOWORD(context->Ecx, *(const WORD *)addr);
127         break;
128     case 2:
129         if (long_op) context->Edx = *(const DWORD *)addr;
130         else SET_LOWORD(context->Edx, *(const WORD *)addr);
131         break;
132     case 3:
133         if (long_op) context->Ebx = *(const DWORD *)addr;
134         else SET_LOWORD(context->Ebx, *(const WORD *)addr);
135         break;
136     case 4:
137         if (long_op) context->Esp = *(const DWORD *)addr;
138         else SET_LOWORD(context->Esp, *(const WORD *)addr);
139         break;
140     case 5:
141         if (long_op) context->Ebp = *(const DWORD *)addr;
142         else SET_LOWORD(context->Ebp, *(const WORD *)addr);
143         break;
144     case 6:
145         if (long_op) context->Esi = *(const DWORD *)addr;
146         else SET_LOWORD(context->Esi, *(const WORD *)addr);
147         break;
148     case 7:
149         if (long_op) context->Edi = *(const DWORD *)addr;
150         else SET_LOWORD(context->Edi, *(const WORD *)addr);
151         break;
152     }
153 }
154
155 /***********************************************************************
156  *           INSTR_GetOperandAddr
157  *
158  * Return the address of an instruction operand (from the mod/rm byte).
159  */
160 static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr,
161                                    int long_addr, int segprefix, int *len )
162 {
163     int mod, rm, base = 0, index = 0, ss = 0, seg = 0, off;
164     LDT_ENTRY entry;
165
166 #define GET_VAL(val,type) \
167     { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
168
169     *len = 0;
170     GET_VAL( &mod, BYTE );
171     rm = mod & 7;
172     mod >>= 6;
173
174     if (mod == 3)
175     {
176         switch(rm)
177         {
178         case 0: return (BYTE *)&context->Eax;
179         case 1: return (BYTE *)&context->Ecx;
180         case 2: return (BYTE *)&context->Edx;
181         case 3: return (BYTE *)&context->Ebx;
182         case 4: return (BYTE *)&context->Esp;
183         case 5: return (BYTE *)&context->Ebp;
184         case 6: return (BYTE *)&context->Esi;
185         case 7: return (BYTE *)&context->Edi;
186         }
187     }
188
189     if (long_addr)
190     {
191         if (rm == 4)
192         {
193             BYTE sib;
194             GET_VAL( &sib, BYTE );
195             rm = sib & 7;
196             ss = sib >> 6;
197             switch(sib >> 3)
198             {
199             case 0: index = context->Eax; break;
200             case 1: index = context->Ecx; break;
201             case 2: index = context->Edx; break;
202             case 3: index = context->Ebx; break;
203             case 4: index = 0; break;
204             case 5: index = context->Ebp; break;
205             case 6: index = context->Esi; break;
206             case 7: index = context->Edi; break;
207             }
208         }
209
210         switch(rm)
211         {
212         case 0: base = context->Eax; seg = context->SegDs; break;
213         case 1: base = context->Ecx; seg = context->SegDs; break;
214         case 2: base = context->Edx; seg = context->SegDs; break;
215         case 3: base = context->Ebx; seg = context->SegDs; break;
216         case 4: base = context->Esp; seg = context->SegSs; break;
217         case 5: base = context->Ebp; seg = context->SegSs; break;
218         case 6: base = context->Esi; seg = context->SegDs; break;
219         case 7: base = context->Edi; seg = context->SegDs; break;
220         }
221         switch (mod)
222         {
223         case 0:
224             if (rm == 5)  /* special case: ds:(disp32) */
225             {
226                 GET_VAL( &base, DWORD );
227                 seg = context->SegDs;
228             }
229             break;
230
231         case 1:  /* 8-bit disp */
232             GET_VAL( &off, BYTE );
233             base += (signed char)off;
234             break;
235
236         case 2:  /* 32-bit disp */
237             GET_VAL( &off, DWORD );
238             base += (signed long)off;
239             break;
240         }
241     }
242     else  /* short address */
243     {
244         switch(rm)
245         {
246         case 0:  /* ds:(bx,si) */
247             base = LOWORD(context->Ebx) + LOWORD(context->Esi);
248             seg  = context->SegDs;
249             break;
250         case 1:  /* ds:(bx,di) */
251             base = LOWORD(context->Ebx) + LOWORD(context->Edi);
252             seg  = context->SegDs;
253             break;
254         case 2:  /* ss:(bp,si) */
255             base = LOWORD(context->Ebp) + LOWORD(context->Esi);
256             seg  = context->SegSs;
257             break;
258         case 3:  /* ss:(bp,di) */
259             base = LOWORD(context->Ebp) + LOWORD(context->Edi);
260             seg  = context->SegSs;
261             break;
262         case 4:  /* ds:(si) */
263             base = LOWORD(context->Esi);
264             seg  = context->SegDs;
265             break;
266         case 5:  /* ds:(di) */
267             base = LOWORD(context->Edi);
268             seg  = context->SegDs;
269             break;
270         case 6:  /* ss:(bp) */
271             base = LOWORD(context->Ebp);
272             seg  = context->SegSs;
273             break;
274         case 7:  /* ds:(bx) */
275             base = LOWORD(context->Ebx);
276             seg  = context->SegDs;
277             break;
278         }
279
280         switch(mod)
281         {
282         case 0:
283             if (rm == 6)  /* special case: ds:(disp16) */
284             {
285                 GET_VAL( &base, WORD );
286                 seg  = context->SegDs;
287             }
288             break;
289
290         case 1:  /* 8-bit disp */
291             GET_VAL( &off, BYTE );
292             base += (signed char)off;
293             break;
294
295         case 2:  /* 16-bit disp */
296             GET_VAL( &off, WORD );
297             base += (signed short)off;
298             break;
299         }
300         base &= 0xffff;
301     }
302     if (segprefix != -1) seg = segprefix;
303
304     /* Make sure the segment and offset are valid */
305     if (wine_ldt_is_system(seg)) return (BYTE *)(base + (index << ss));
306     if ((seg & 7) != 7) return NULL;
307     wine_ldt_get_entry( seg, &entry );
308     if (wine_ldt_is_empty( &entry )) return NULL;
309     if (wine_ldt_get_limit(&entry) < (base + (index << ss))) return NULL;
310     return (BYTE *)wine_ldt_get_base(&entry) + base + (index << ss);
311 #undef GET_VAL
312 }
313
314
315 /***********************************************************************
316  *           INSTR_EmulateLDS
317  *
318  * Emulate the LDS (and LES,LFS,etc.) instruction.
319  */
320 static BOOL INSTR_EmulateLDS( CONTEXT86 *context, BYTE *instr, int long_op,
321                               int long_addr, int segprefix, int *len )
322 {
323     WORD seg;
324     BYTE *regmodrm = instr + 1 + (*instr == 0x0f);
325     BYTE *addr = INSTR_GetOperandAddr( context, regmodrm,
326                                        long_addr, segprefix, len );
327     if (!addr)
328         return FALSE;  /* Unable to emulate it */
329     seg = *(WORD *)(addr + (long_op ? 4 : 2));
330
331     if (!INSTR_ReplaceSelector( context, &seg ))
332         return FALSE;  /* Unable to emulate it */
333
334     /* Now store the offset in the correct register */
335
336     store_reg( context, *regmodrm, addr, long_op );
337
338     /* Store the correct segment in the segment register */
339
340     switch(*instr)
341     {
342     case 0xc4: context->SegEs = seg; break;  /* les */
343     case 0xc5: context->SegDs = seg; break;  /* lds */
344     case 0x0f: switch(instr[1])
345                {
346                case 0xb2: context->SegSs = seg; break;  /* lss */
347                case 0xb4: context->SegFs = seg; break;  /* lfs */
348                case 0xb5: context->SegGs = seg; break;  /* lgs */
349                }
350                break;
351     }
352
353     /* Add the opcode size to the total length */
354
355     *len += 1 + (*instr == 0x0f);
356     return TRUE;
357 }
358
359 /***********************************************************************
360  *           INSTR_inport
361  *
362  * input on an I/O port
363  */
364 static DWORD INSTR_inport( WORD port, int size, CONTEXT86 *context )
365 {
366     DWORD res = DOSVM_inport( port, size );
367
368     if (TRACE_ON(io))
369     {
370         switch(size)
371         {
372         case 1:
373             TRACE_(io)( "0x%x < %02x @ %04x:%04x\n", port, LOBYTE(res),
374                      (WORD)context->SegCs, LOWORD(context->Eip));
375             break;
376         case 2:
377             TRACE_(io)( "0x%x < %04x @ %04x:%04x\n", port, LOWORD(res),
378                      (WORD)context->SegCs, LOWORD(context->Eip));
379             break;
380         case 4:
381             TRACE_(io)( "0x%x < %08x @ %04x:%04x\n", port, res,
382                      (WORD)context->SegCs, LOWORD(context->Eip));
383             break;
384         }
385     }
386     return res;
387 }
388
389
390 /***********************************************************************
391  *           INSTR_outport
392  *
393  * output on an I/O port
394  */
395 static void INSTR_outport( WORD port, int size, DWORD val, CONTEXT86 *context )
396 {
397     DOSVM_outport( port, size, val );
398
399     if (TRACE_ON(io))
400     {
401         switch(size)
402         {
403         case 1:
404             TRACE_(io)("0x%x > %02x @ %04x:%04x\n", port, LOBYTE(val),
405                     (WORD)context->SegCs, LOWORD(context->Eip));
406             break;
407         case 2:
408             TRACE_(io)("0x%x > %04x @ %04x:%04x\n", port, LOWORD(val),
409                     (WORD)context->SegCs, LOWORD(context->Eip));
410             break;
411         case 4:
412             TRACE_(io)("0x%x > %08x @ %04x:%04x\n", port, val,
413                     (WORD)context->SegCs, LOWORD(context->Eip));
414             break;
415         }
416     }
417 }
418
419
420 /***********************************************************************
421  *           __wine_emulate_instruction
422  *
423  * Emulate a privileged instruction.
424  * Returns exception continuation status.
425  */
426 DWORD __wine_emulate_instruction( EXCEPTION_RECORD *rec, CONTEXT86 *context )
427 {
428     int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
429     BYTE *instr;
430
431     long_op = long_addr = (!ISV86(context) && IS_SELECTOR_32BIT(context->SegCs));
432     instr = make_ptr( context, context->SegCs, context->Eip, TRUE );
433     if (!instr) return ExceptionContinueSearch;
434
435     /* First handle any possible prefix */
436
437     segprefix = -1;  /* no prefix */
438     prefix = 1;
439     repX = 0;
440     prefixlen = 0;
441     while(prefix)
442     {
443         switch(*instr)
444         {
445         case 0x2e:
446             segprefix = context->SegCs;
447             break;
448         case 0x36:
449             segprefix = context->SegSs;
450             break;
451         case 0x3e:
452             segprefix = context->SegDs;
453             break;
454         case 0x26:
455             segprefix = context->SegEs;
456             break;
457         case 0x64:
458             segprefix = context->SegFs;
459             break;
460         case 0x65:
461             segprefix = context->SegGs;
462             break;
463         case 0x66:
464             long_op = !long_op;  /* opcode size prefix */
465             break;
466         case 0x67:
467             long_addr = !long_addr;  /* addr size prefix */
468             break;
469         case 0xf0:  /* lock */
470             break;
471         case 0xf2:  /* repne */
472             repX = 1;
473             break;
474         case 0xf3:  /* repe */
475             repX = 2;
476             break;
477         default:
478             prefix = 0;  /* no more prefixes */
479             break;
480         }
481         if (prefix)
482         {
483             instr++;
484             prefixlen++;
485         }
486     }
487
488     /* Now look at the actual instruction */
489
490     switch(*instr)
491     {
492         case 0x07: /* pop es */
493         case 0x17: /* pop ss */
494         case 0x1f: /* pop ds */
495             {
496                 WORD seg = *(WORD *)get_stack( context );
497                 if (INSTR_ReplaceSelector( context, &seg ))
498                 {
499                     switch(*instr)
500                     {
501                     case 0x07: context->SegEs = seg; break;
502                     case 0x17: context->SegSs = seg; break;
503                     case 0x1f: context->SegDs = seg; break;
504                     }
505                     add_stack(context, long_op ? 4 : 2);
506                     context->Eip += prefixlen + 1;
507                     return ExceptionContinueExecution;
508                 }
509             }
510             break;  /* Unable to emulate it */
511
512         case 0x0f: /* extended instruction */
513             switch(instr[1])
514             {
515             case 0x22: /* mov eax, crX */
516                 switch (instr[2])
517                 {
518                 case 0xc0:
519                         ERR("mov eax,cr0 at 0x%08x, EAX=0x%08x\n",
520                             context->Eip,context->Eax );
521                         context->Eip += prefixlen+3;
522                         return ExceptionContinueExecution;
523                 default:
524                         break; /*fallthrough to bad instruction handling */
525                 }
526                 break; /*fallthrough to bad instruction handling */
527             case 0x20: /* mov crX, eax */
528                 switch (instr[2])
529                 {
530                 case 0xe0: /* mov cr4, eax */
531                     /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
532                      * bit 0: VME       Virtual Mode Exception ?
533                      * bit 1: PVI       Protected mode Virtual Interrupt
534                      * bit 2: TSD       Timestamp disable
535                      * bit 3: DE        Debugging extensions
536                      * bit 4: PSE       Page size extensions
537                      * bit 5: PAE   Physical address extension
538                      * bit 6: MCE       Machine check enable
539                      * bit 7: PGE   Enable global pages
540                      * bit 8: PCE       Enable performance counters at IPL3
541                      */
542                     ERR("mov cr4,eax at 0x%08x\n",context->Eip);
543                     context->Eax = 0;
544                     context->Eip += prefixlen+3;
545                     return ExceptionContinueExecution;
546                 case 0xc0: /* mov cr0, eax */
547                     ERR("mov cr0,eax at 0x%08x\n",context->Eip);
548                     context->Eax = 0x10; /* FIXME: set more bits ? */
549                     context->Eip += prefixlen+3;
550                     return ExceptionContinueExecution;
551                 default: /* fallthrough to illegal instruction */
552                     break;
553                 }
554                 /* fallthrough to illegal instruction */
555                 break;
556             case 0x21: /* mov drX, eax */
557                 switch (instr[2])
558                 {
559                 case 0xc8: /* mov dr1, eax */
560                     TRACE("mov dr1,eax at 0x%08x\n",context->Eip);
561                     context->Eax = context->Dr1;
562                     context->Eip += prefixlen+3;
563                     return ExceptionContinueExecution;
564                 case 0xf8: /* mov dr7, eax */
565                     TRACE("mov dr7,eax at 0x%08x\n",context->Eip);
566                     context->Eax = 0x400;
567                     context->Eip += prefixlen+3;
568                     return ExceptionContinueExecution;
569                 }
570                 ERR("Unsupported DR register, eip+2 is %02x\n", instr[2]);
571                 /* fallthrough to illegal instruction */
572                 break;
573             case 0x23: /* mov eax drX */
574                 switch (instr[2])
575                 {
576                 case 0xc8: /* mov eax, dr1 */
577                     context->Dr1 = context->Eax;
578                     context->Eip += prefixlen+3;
579                     return ExceptionContinueExecution;
580                 }
581                 ERR("Unsupported DR register, eip+2 is %02x\n", instr[2]);
582                 /* fallthrough to illegal instruction */
583                 break;
584             case 0xa1: /* pop fs */
585                 {
586                     WORD seg = *(WORD *)get_stack( context );
587                     if (INSTR_ReplaceSelector( context, &seg ))
588                     {
589                         context->SegFs = seg;
590                         add_stack(context, long_op ? 4 : 2);
591                         context->Eip += prefixlen + 2;
592                         return ExceptionContinueExecution;
593                     }
594                 }
595                 break;
596             case 0xa9: /* pop gs */
597                 {
598                     WORD seg = *(WORD *)get_stack( context );
599                     if (INSTR_ReplaceSelector( context, &seg ))
600                     {
601                         context->SegGs = seg;
602                         add_stack(context, long_op ? 4 : 2);
603                         context->Eip += prefixlen + 2;
604                         return ExceptionContinueExecution;
605                     }
606                 }
607                 break;
608             case 0xb2: /* lss addr,reg */
609             case 0xb4: /* lfs addr,reg */
610             case 0xb5: /* lgs addr,reg */
611                 if (INSTR_EmulateLDS( context, instr, long_op,
612                                       long_addr, segprefix, &len ))
613                 {
614                     context->Eip += prefixlen + len;
615                     return ExceptionContinueExecution;
616                 }
617                 break;
618             }
619             break;  /* Unable to emulate it */
620
621         case 0x6c: /* insb     */
622         case 0x6d: /* insw/d   */
623         case 0x6e: /* outsb    */
624         case 0x6f: /* outsw/d  */
625             {
626               int typ = *instr;  /* Just in case it's overwritten.  */
627               int outp = (typ >= 0x6e);
628               unsigned long count = repX ?
629                           (long_addr ? context->Ecx : LOWORD(context->Ecx)) : 1;
630               int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
631               int step = (context->EFlags & 0x400) ? -opsize : +opsize;
632               int seg = outp ? context->SegDs : context->SegEs;  /* FIXME: is this right? */
633
634               if (outp)
635                 /* FIXME: Check segment is readable.  */
636                 ;
637               else
638                 /* FIXME: Check segment is writable.  */
639                 ;
640
641               if (repX)
642               {
643                 if (long_addr) context->Ecx = 0;
644                 else SET_LOWORD(context->Ecx,0);
645               }
646
647               while (count-- > 0)
648                 {
649                   void *data;
650                   WORD dx = LOWORD(context->Edx);
651                   if (outp)
652                   {
653                       data = make_ptr( context, seg, context->Esi, long_addr );
654                       if (long_addr) context->Esi += step;
655                       else ADD_LOWORD(context->Esi,step);
656                   }
657                   else
658                   {
659                       data = make_ptr( context, seg, context->Edi, long_addr );
660                       if (long_addr) context->Edi += step;
661                       else ADD_LOWORD(context->Edi,step);
662                   }
663
664                   switch (typ)
665                   {
666                     case 0x6c:
667                       *(BYTE *)data = INSTR_inport( dx, 1, context );
668                       break;
669                     case 0x6d:
670                       if (long_op)
671                           *(DWORD *)data = INSTR_inport( dx, 4, context );
672                       else
673                           *(WORD *)data = INSTR_inport( dx, 2, context );
674                       break;
675                     case 0x6e:
676                         INSTR_outport( dx, 1, *(BYTE *)data, context );
677                         break;
678                     case 0x6f:
679                         if (long_op)
680                             INSTR_outport( dx, 4, *(DWORD *)data, context );
681                         else
682                             INSTR_outport( dx, 2, *(WORD *)data, context );
683                         break;
684                     }
685                 }
686               context->Eip += prefixlen + 1;
687             }
688             return ExceptionContinueExecution;
689
690         case 0x8b: /* mov Ev, Gv */
691             {
692                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1, long_addr,
693                                                   segprefix, &len);
694                 struct idtr idtr = get_idtr();
695                 unsigned int offset = addr - idtr.base;
696
697                 if (offset <= idtr.limit + 1 - (long_op ? 4 : 2))
698                 {
699                     idt[1].LimitLow = 0x100; /* FIXME */
700                     idt[2].LimitLow = 0x11E; /* FIXME */
701                     idt[3].LimitLow = 0x500; /* FIXME */
702                     store_reg( context, instr[1], (BYTE *)idt + offset, long_op );
703                     context->Eip += prefixlen + len + 1;
704                     return ExceptionContinueExecution;
705                 }
706             }
707             break;  /* Unable to emulate it */
708
709         case 0x8e: /* mov XX,segment_reg */
710             {
711                 WORD seg;
712                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
713                                                   long_addr, segprefix, &len );
714                 if (!addr)
715                     break;  /* Unable to emulate it */
716                 seg = *(WORD *)addr;
717                 if (!INSTR_ReplaceSelector( context, &seg ))
718                     break;  /* Unable to emulate it */
719
720                 switch((instr[1] >> 3) & 7)
721                 {
722                 case 0:
723                     context->SegEs = seg;
724                     context->Eip += prefixlen + len + 1;
725                     return ExceptionContinueExecution;
726                 case 1:  /* cs */
727                     break;
728                 case 2:
729                     context->SegSs = seg;
730                     context->Eip += prefixlen + len + 1;
731                     return ExceptionContinueExecution;
732                 case 3:
733                     context->SegDs = seg;
734                     context->Eip += prefixlen + len + 1;
735                     return ExceptionContinueExecution;
736                 case 4:
737                     context->SegFs = seg;
738                     context->Eip += prefixlen + len + 1;
739                     return ExceptionContinueExecution;
740                 case 5:
741                     context->SegGs = seg;
742                     context->Eip += prefixlen + len + 1;
743                     return ExceptionContinueExecution;
744                 case 6:  /* unused */
745                 case 7:  /* unused */
746                     break;
747                 }
748             }
749             break;  /* Unable to emulate it */
750
751         case 0xc4: /* les addr,reg */
752         case 0xc5: /* lds addr,reg */
753             if (INSTR_EmulateLDS( context, instr, long_op,
754                                   long_addr, segprefix, &len ))
755             {
756                 context->Eip += prefixlen + len;
757                 return ExceptionContinueExecution;
758             }
759             break;  /* Unable to emulate it */
760
761         case 0xcd: /* int <XX> */
762             context->Eip += prefixlen + 2;
763             if (DOSVM_EmulateInterruptPM( context, instr[1] )) return ExceptionContinueExecution;
764             context->Eip -= prefixlen + 2;  /* restore eip */
765             break;  /* Unable to emulate it */
766
767         case 0xcf: /* iret */
768             if (wine_ldt_is_system(context->SegCs)) break;  /* don't emulate it in 32-bit code */
769             if (long_op)
770             {
771                 DWORD *stack = get_stack( context );
772                 context->Eip = *stack++;
773                 context->SegCs  = *stack++;
774                 context->EFlags = *stack;
775                 add_stack(context, 3*sizeof(DWORD));  /* Pop the return address and flags */
776             }
777             else
778             {
779                 WORD *stack = get_stack( context );
780                 context->Eip = *stack++;
781                 context->SegCs  = *stack++;
782                 SET_LOWORD(context->EFlags,*stack);
783                 add_stack(context, 3*sizeof(WORD));  /* Pop the return address and flags */
784             }
785             return ExceptionContinueExecution;
786
787         case 0xe4: /* inb al,XX */
788             SET_LOBYTE(context->Eax,INSTR_inport( instr[1], 1, context ));
789             context->Eip += prefixlen + 2;
790             return ExceptionContinueExecution;
791
792         case 0xe5: /* in (e)ax,XX */
793             if (long_op)
794                 context->Eax = INSTR_inport( instr[1], 4, context );
795             else
796                 SET_LOWORD(context->Eax, INSTR_inport( instr[1], 2, context ));
797             context->Eip += prefixlen + 2;
798             return ExceptionContinueExecution;
799
800         case 0xe6: /* outb XX,al */
801             INSTR_outport( instr[1], 1, LOBYTE(context->Eax), context );
802             context->Eip += prefixlen + 2;
803             return ExceptionContinueExecution;
804
805         case 0xe7: /* out XX,(e)ax */
806             if (long_op)
807                 INSTR_outport( instr[1], 4, context->Eax, context );
808             else
809                 INSTR_outport( instr[1], 2, LOWORD(context->Eax), context );
810             context->Eip += prefixlen + 2;
811             return ExceptionContinueExecution;
812
813         case 0xec: /* inb al,dx */
814             SET_LOBYTE(context->Eax, INSTR_inport( LOWORD(context->Edx), 1, context ) );
815             context->Eip += prefixlen + 1;
816             return ExceptionContinueExecution;
817
818         case 0xed: /* in (e)ax,dx */
819             if (long_op)
820                 context->Eax = INSTR_inport( LOWORD(context->Edx), 4, context );
821             else
822                 SET_LOWORD(context->Eax, INSTR_inport( LOWORD(context->Edx), 2, context ));
823             context->Eip += prefixlen + 1;
824             return ExceptionContinueExecution;
825
826         case 0xee: /* outb dx,al */
827             INSTR_outport( LOWORD(context->Edx), 1, LOBYTE(context->Eax), context );
828             context->Eip += prefixlen + 1;
829             return ExceptionContinueExecution;
830
831         case 0xef: /* out dx,(e)ax */
832             if (long_op)
833                 INSTR_outport( LOWORD(context->Edx), 4, context->Eax, context );
834             else
835                 INSTR_outport( LOWORD(context->Edx), 2, LOWORD(context->Eax), context );
836             context->Eip += prefixlen + 1;
837             return ExceptionContinueExecution;
838
839         case 0xfa: /* cli */
840             get_vm86_teb_info()->dpmi_vif = 0;
841             context->Eip += prefixlen + 1;
842             return ExceptionContinueExecution;
843
844         case 0xfb: /* sti */
845             get_vm86_teb_info()->dpmi_vif = 1;
846             context->Eip += prefixlen + 1;
847             if (get_vm86_teb_info()->vm86_pending)
848             {
849                 get_vm86_teb_info()->vm86_pending = 0;
850                 rec->ExceptionCode = EXCEPTION_VM86_STI;
851                 break; /* Handle the pending event. */
852             }
853             return ExceptionContinueExecution;
854     }
855     return ExceptionContinueSearch;  /* Unable to emulate it */
856 }
857
858
859 /***********************************************************************
860  *           INSTR_vectored_handler
861  *
862  * Vectored exception handler used to emulate protected instructions
863  * from 32-bit code.
864  */
865 LONG CALLBACK INSTR_vectored_handler( EXCEPTION_POINTERS *ptrs )
866 {
867     EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
868     CONTEXT86 *context = ptrs->ContextRecord;
869
870     if (wine_ldt_is_system(context->SegCs) &&
871         (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
872          record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION))
873     {
874         if (__wine_emulate_instruction( record, context ) == ExceptionContinueExecution)
875             return EXCEPTION_CONTINUE_EXECUTION;
876     }
877     return EXCEPTION_CONTINUE_SEARCH;
878 }
879
880
881 /***********************************************************************
882  *           DOS3Call         (KERNEL.102)
883  */
884 void WINAPI DOS3Call( CONTEXT86 *context )
885 {
886     __wine_call_int_handler( context, 0x21 );
887 }
888
889
890 /***********************************************************************
891  *           NetBIOSCall      (KERNEL.103)
892  */
893 void WINAPI NetBIOSCall16( CONTEXT86 *context )
894 {
895     __wine_call_int_handler( context, 0x5c );
896 }
897
898
899 /***********************************************************************
900  *              GetSetKernelDOSProc (KERNEL.311)
901  */
902 FARPROC16 WINAPI GetSetKernelDOSProc16( FARPROC16 DosProc )
903 {
904     FIXME("(DosProc=%p): stub\n", DosProc);
905     return NULL;
906 }