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