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