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