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