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