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