krnl386.exe16: pSeg might be NULL (Coverity).
[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( CONTEXT *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( CONTEXT *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( CONTEXT *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( CONTEXT *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( CONTEXT *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( CONTEXT *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( CONTEXT *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, CONTEXT *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, CONTEXT *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, CONTEXT *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               {
636                 /* FIXME: Check segment is readable.  */
637               }
638               else
639               {
640                 /* FIXME: Check segment is writable.  */
641               }
642
643               if (repX)
644               {
645                 if (long_addr) context->Ecx = 0;
646                 else SET_LOWORD(context->Ecx,0);
647               }
648
649               while (count-- > 0)
650                 {
651                   void *data;
652                   WORD dx = LOWORD(context->Edx);
653                   if (outp)
654                   {
655                       data = make_ptr( context, seg, context->Esi, long_addr );
656                       if (long_addr) context->Esi += step;
657                       else ADD_LOWORD(context->Esi,step);
658                   }
659                   else
660                   {
661                       data = make_ptr( context, seg, context->Edi, long_addr );
662                       if (long_addr) context->Edi += step;
663                       else ADD_LOWORD(context->Edi,step);
664                   }
665
666                   switch (typ)
667                   {
668                     case 0x6c:
669                       *(BYTE *)data = INSTR_inport( dx, 1, context );
670                       break;
671                     case 0x6d:
672                       if (long_op)
673                           *(DWORD *)data = INSTR_inport( dx, 4, context );
674                       else
675                           *(WORD *)data = INSTR_inport( dx, 2, context );
676                       break;
677                     case 0x6e:
678                         INSTR_outport( dx, 1, *(BYTE *)data, context );
679                         break;
680                     case 0x6f:
681                         if (long_op)
682                             INSTR_outport( dx, 4, *(DWORD *)data, context );
683                         else
684                             INSTR_outport( dx, 2, *(WORD *)data, context );
685                         break;
686                     }
687                 }
688               context->Eip += prefixlen + 1;
689             }
690             return ExceptionContinueExecution;
691
692         case 0x8b: /* mov Ev, Gv */
693             {
694                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1, long_addr,
695                                                   segprefix, &len);
696                 struct idtr idtr = get_idtr();
697                 unsigned int offset = addr - idtr.base;
698
699                 if (offset <= idtr.limit + 1 - (long_op ? 4 : 2))
700                 {
701                     idt[1].LimitLow = 0x100; /* FIXME */
702                     idt[2].LimitLow = 0x11E; /* FIXME */
703                     idt[3].LimitLow = 0x500; /* FIXME */
704                     store_reg( context, instr[1], (BYTE *)idt + offset, long_op );
705                     context->Eip += prefixlen + len + 1;
706                     return ExceptionContinueExecution;
707                 }
708             }
709             break;  /* Unable to emulate it */
710
711         case 0x8e: /* mov XX,segment_reg */
712             {
713                 WORD seg;
714                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
715                                                   long_addr, segprefix, &len );
716                 if (!addr)
717                     break;  /* Unable to emulate it */
718                 seg = *(WORD *)addr;
719                 if (!INSTR_ReplaceSelector( context, &seg ))
720                     break;  /* Unable to emulate it */
721
722                 switch((instr[1] >> 3) & 7)
723                 {
724                 case 0:
725                     context->SegEs = seg;
726                     context->Eip += prefixlen + len + 1;
727                     return ExceptionContinueExecution;
728                 case 1:  /* cs */
729                     break;
730                 case 2:
731                     context->SegSs = seg;
732                     context->Eip += prefixlen + len + 1;
733                     return ExceptionContinueExecution;
734                 case 3:
735                     context->SegDs = seg;
736                     context->Eip += prefixlen + len + 1;
737                     return ExceptionContinueExecution;
738                 case 4:
739                     context->SegFs = seg;
740                     context->Eip += prefixlen + len + 1;
741                     return ExceptionContinueExecution;
742                 case 5:
743                     context->SegGs = seg;
744                     context->Eip += prefixlen + len + 1;
745                     return ExceptionContinueExecution;
746                 case 6:  /* unused */
747                 case 7:  /* unused */
748                     break;
749                 }
750             }
751             break;  /* Unable to emulate it */
752
753         case 0xc4: /* les addr,reg */
754         case 0xc5: /* lds addr,reg */
755             if (INSTR_EmulateLDS( context, instr, long_op,
756                                   long_addr, segprefix, &len ))
757             {
758                 context->Eip += prefixlen + len;
759                 return ExceptionContinueExecution;
760             }
761             break;  /* Unable to emulate it */
762
763         case 0xcd: /* int <XX> */
764             context->Eip += prefixlen + 2;
765             if (DOSVM_EmulateInterruptPM( context, instr[1] )) return ExceptionContinueExecution;
766             context->Eip -= prefixlen + 2;  /* restore eip */
767             break;  /* Unable to emulate it */
768
769         case 0xcf: /* iret */
770             if (wine_ldt_is_system(context->SegCs)) break;  /* don't emulate it in 32-bit code */
771             if (long_op)
772             {
773                 DWORD *stack = get_stack( context );
774                 context->Eip = *stack++;
775                 context->SegCs  = *stack++;
776                 context->EFlags = *stack;
777                 add_stack(context, 3*sizeof(DWORD));  /* Pop the return address and flags */
778             }
779             else
780             {
781                 WORD *stack = get_stack( context );
782                 context->Eip = *stack++;
783                 context->SegCs  = *stack++;
784                 SET_LOWORD(context->EFlags,*stack);
785                 add_stack(context, 3*sizeof(WORD));  /* Pop the return address and flags */
786             }
787             return ExceptionContinueExecution;
788
789         case 0xe4: /* inb al,XX */
790             SET_LOBYTE(context->Eax,INSTR_inport( instr[1], 1, context ));
791             context->Eip += prefixlen + 2;
792             return ExceptionContinueExecution;
793
794         case 0xe5: /* in (e)ax,XX */
795             if (long_op)
796                 context->Eax = INSTR_inport( instr[1], 4, context );
797             else
798                 SET_LOWORD(context->Eax, INSTR_inport( instr[1], 2, context ));
799             context->Eip += prefixlen + 2;
800             return ExceptionContinueExecution;
801
802         case 0xe6: /* outb XX,al */
803             INSTR_outport( instr[1], 1, LOBYTE(context->Eax), context );
804             context->Eip += prefixlen + 2;
805             return ExceptionContinueExecution;
806
807         case 0xe7: /* out XX,(e)ax */
808             if (long_op)
809                 INSTR_outport( instr[1], 4, context->Eax, context );
810             else
811                 INSTR_outport( instr[1], 2, LOWORD(context->Eax), context );
812             context->Eip += prefixlen + 2;
813             return ExceptionContinueExecution;
814
815         case 0xec: /* inb al,dx */
816             SET_LOBYTE(context->Eax, INSTR_inport( LOWORD(context->Edx), 1, context ) );
817             context->Eip += prefixlen + 1;
818             return ExceptionContinueExecution;
819
820         case 0xed: /* in (e)ax,dx */
821             if (long_op)
822                 context->Eax = INSTR_inport( LOWORD(context->Edx), 4, context );
823             else
824                 SET_LOWORD(context->Eax, INSTR_inport( LOWORD(context->Edx), 2, context ));
825             context->Eip += prefixlen + 1;
826             return ExceptionContinueExecution;
827
828         case 0xee: /* outb dx,al */
829             INSTR_outport( LOWORD(context->Edx), 1, LOBYTE(context->Eax), context );
830             context->Eip += prefixlen + 1;
831             return ExceptionContinueExecution;
832
833         case 0xef: /* out dx,(e)ax */
834             if (long_op)
835                 INSTR_outport( LOWORD(context->Edx), 4, context->Eax, context );
836             else
837                 INSTR_outport( LOWORD(context->Edx), 2, LOWORD(context->Eax), context );
838             context->Eip += prefixlen + 1;
839             return ExceptionContinueExecution;
840
841         case 0xfa: /* cli */
842             get_vm86_teb_info()->dpmi_vif = 0;
843             context->Eip += prefixlen + 1;
844             return ExceptionContinueExecution;
845
846         case 0xfb: /* sti */
847             get_vm86_teb_info()->dpmi_vif = 1;
848             context->Eip += prefixlen + 1;
849             if (get_vm86_teb_info()->vm86_pending)
850             {
851                 get_vm86_teb_info()->vm86_pending = 0;
852                 rec->ExceptionCode = EXCEPTION_VM86_STI;
853                 break; /* Handle the pending event. */
854             }
855             return ExceptionContinueExecution;
856     }
857     return ExceptionContinueSearch;  /* Unable to emulate it */
858 }
859
860
861 /***********************************************************************
862  *           INSTR_vectored_handler
863  *
864  * Vectored exception handler used to emulate protected instructions
865  * from 32-bit code.
866  */
867 LONG CALLBACK INSTR_vectored_handler( EXCEPTION_POINTERS *ptrs )
868 {
869     EXCEPTION_RECORD *record = ptrs->ExceptionRecord;
870     CONTEXT *context = ptrs->ContextRecord;
871
872     if (wine_ldt_is_system(context->SegCs) &&
873         (record->ExceptionCode == EXCEPTION_ACCESS_VIOLATION ||
874          record->ExceptionCode == EXCEPTION_PRIV_INSTRUCTION))
875     {
876         if (__wine_emulate_instruction( record, context ) == ExceptionContinueExecution)
877             return EXCEPTION_CONTINUE_EXECUTION;
878     }
879     return EXCEPTION_CONTINUE_SEARCH;
880 }
881
882
883 /***********************************************************************
884  *           DOS3Call         (KERNEL.102)
885  */
886 void WINAPI DOS3Call( CONTEXT *context )
887 {
888     __wine_call_int_handler( context, 0x21 );
889 }
890
891
892 /***********************************************************************
893  *           NetBIOSCall      (KERNEL.103)
894  */
895 void WINAPI NetBIOSCall16( CONTEXT *context )
896 {
897     __wine_call_int_handler( context, 0x5c );
898 }
899
900
901 /***********************************************************************
902  *              GetSetKernelDOSProc (KERNEL.311)
903  */
904 FARPROC16 WINAPI GetSetKernelDOSProc16( FARPROC16 DosProc )
905 {
906     FIXME("(DosProc=%p): stub\n", DosProc);
907     return NULL;
908 }