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