Added the actual definitions of the interfaces.
[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, DOSMEM_Tick );
68         *sel = DOSMEM_BiosDataSeg;
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     long_op = long_addr = IS_SEL_32(context,CS_sig(context));
327     instr = (BYTE *)MK_PTR(context,CS_sig(context),EIP_sig(context));
328     if (!instr) return FALSE;
329
330     /* First handle any possible prefix */
331
332     segprefix = -1;  /* no prefix */
333     prefix = 1;
334     repX = 0;
335     prefixlen = 0;
336     while(prefix)
337     {
338         switch(*instr)
339         {
340         case 0x2e:
341             segprefix = CS_sig(context);
342             break;
343         case 0x36:
344             segprefix = SS_sig(context);
345             break;
346         case 0x3e:
347             segprefix = DS_sig(context);
348             break;
349         case 0x26:
350             segprefix = ES_sig(context);
351             break;
352 #ifdef FS_sig
353         case 0x64:
354             segprefix = FS_sig(context);
355             break;
356 #endif
357 #ifdef GS_sig
358         case 0x65:
359             segprefix = GS_sig(context);
360             break;
361 #endif
362         case 0x66:
363             long_op = !long_op;  /* opcode size prefix */
364             break;
365         case 0x67:
366             long_addr = !long_addr;  /* addr size prefix */
367             break;
368         case 0xf0:  /* lock */
369             break;
370         case 0xf2:  /* repne */
371             repX = 1;
372             break;
373         case 0xf3:  /* repe */
374             repX = 2;
375             break;
376         default:
377             prefix = 0;  /* no more prefixes */
378             break;
379         }
380         if (prefix)
381         {
382             instr++;
383             prefixlen++;
384         }
385     }
386
387     /* Now look at the actual instruction */
388
389     switch(*instr)
390     {
391         case 0x07: /* pop es */
392         case 0x17: /* pop ss */
393         case 0x1f: /* pop ds */
394             {
395                 WORD seg = *(WORD *)STACK_PTR( context );
396                 if (INSTR_ReplaceSelector( context, &seg ))
397                 {
398                     switch(*instr)
399                     {
400                     case 0x07: ES_sig(context) = seg; break;
401                     case 0x17: SS_sig(context) = seg; break;
402                     case 0x1f: DS_sig(context) = seg; break;
403                     }
404                     STACK_sig(context) += long_op ? 4 : 2;
405                     EIP_sig(context) += prefixlen + 1;
406                     return TRUE;
407                 }
408             }
409             break;  /* Unable to emulate it */
410
411         case 0x0f: /* extended instruction */
412             switch(instr[1])
413             {
414             case 0x20: /* mov cr4, eax */
415                 if (instr[2]!=0xe0) 
416                         break;
417                 /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs
418                  * bit 0: VME   Virtual Mode Exception ?
419                  * bit 1: PVI   Protected mode Virtual Interrupt
420                  * bit 2: TSD   Timestamp disable
421                  * bit 3: DE    Debugging extensions
422                  * bit 4: PSE   Page size extensions
423                  * bit 5: PAE   Physical address extension
424                  * bit 6: MCE   Machine check enable
425                  * bit 7: PGE   Enable global pages
426                  * bit 8: PCE   Enable performance counters at IPL3
427                  */
428                 fprintf(stderr,"mov cr4,eax at 0x%08lx\n",EIP_sig(context));
429                 EAX_sig(context) = 0;
430                 EIP_sig(context) += prefixlen+3;
431                 return TRUE;
432 #ifdef FS_sig
433             case 0xa1: /* pop fs */
434                 {
435                     WORD seg = *(WORD *)STACK_PTR( context );
436                     if (INSTR_ReplaceSelector( context, &seg ))
437                     {
438                         FS_sig(context) = seg;
439                         STACK_sig(context) += long_op ? 4 : 2;
440                         EIP_sig(context) += prefixlen + 2;
441                         return TRUE;
442                     }
443                 }
444                 break;
445 #endif  /* FS_sig */
446
447 #ifdef GS_sig
448             case 0xa9: /* pop gs */
449                 {
450                     WORD seg = *(WORD *)STACK_PTR( context );
451                     if (INSTR_ReplaceSelector( context, &seg ))
452                     {
453                         GS_sig(context) = seg;
454                         STACK_sig(context) += long_op ? 4 : 2;
455                         EIP_sig(context) += prefixlen + 2;
456                         return TRUE;
457                     }
458                 }
459                 break;
460 #endif  /* GS_sig */
461
462             case 0xb2: /* lss addr,reg */
463 #ifdef FS_sig
464             case 0xb4: /* lfs addr,reg */
465 #endif
466 #ifdef GS_sig
467             case 0xb5: /* lgs addr,reg */
468 #endif
469                 if (INSTR_EmulateLDS( context, instr, long_op,
470                                       long_addr, segprefix, &len ))
471                 {
472                     EIP_sig(context) += prefixlen + len;
473                     return TRUE;
474                 }
475                 break;
476             }
477             break;  /* Unable to emulate it */
478
479         case 0x6c: /* insb     */
480         case 0x6d: /* insw/d   */
481         case 0x6e: /* outsb    */
482         case 0x6f: /* outsw/d  */
483             {
484               int typ = *instr;  /* Just in case it's overwritten.  */
485               int outp = (typ >= 0x6e);
486               unsigned long count = repX ?
487                           (long_addr ? ECX_sig(context) : CX_sig(context)) : 1;
488               int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
489               int step = (EFL_sig(context) & 0x400) ? -opsize : +opsize;
490               int seg = outp ? DS_sig(context) : ES_sig(context);  /* FIXME: is this right? */
491
492               if (outp)
493                 /* FIXME: Check segment readable.  */
494                 (void)0;
495               else
496                 /* FIXME: Check segment writeable.  */
497                 (void)0;
498
499               if (repX)
500               {
501                 if (long_addr)
502                   ECX_sig(context) = 0;
503                 else
504                   CX_sig(context) = 0;
505               }
506
507               while (count-- > 0)
508                 {
509                   void *data;
510                   if (outp)
511                   {
512                       data = MK_PTR(context, seg,
513                                long_addr ? ESI_sig(context) : SI_sig(context));
514                       if (long_addr) ESI_sig(context) += step;
515                       else SI_sig(context) += step;
516                   }
517                   else
518                   {
519                       data = MK_PTR(context, seg,
520                                long_addr ? EDI_sig(context) : DI_sig(context));
521                       if (long_addr) EDI_sig(context) += step;
522                       else DI_sig(context) += step;
523                   }
524                   
525                   switch (typ)
526                   {
527                     case 0x6c:
528                       *((BYTE *)data) = IO_inport( DX_sig(context), 1);
529                       TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
530                         *((BYTE *)data), CS_sig(context), IP_sig(context));
531                       break;
532                     case 0x6d:
533                       if (long_op)
534                       {
535                         *((DWORD *)data) = IO_inport( DX_sig(context), 4);
536                         TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
537                           *((DWORD *)data), CS_sig(context), IP_sig(context));
538                       }
539                       else
540                       {
541                         *((WORD *)data) = IO_inport( DX_sig(context), 2);
542                         TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
543                           *((WORD *)data), CS_sig(context), IP_sig(context));
544                       }
545                       break;
546                     case 0x6e:
547                         IO_outport( DX_sig(context), 1, *((BYTE *)data));
548                         TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
549                           *((BYTE *)data), CS_sig(context), IP_sig(context));
550                         break;
551                     case 0x6f:
552                         if (long_op)
553                         {
554                             IO_outport( DX_sig(context), 4, *((DWORD *)data));
555                             TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
556                               *((DWORD *)data), CS_sig(context), IP_sig(context)); 
557                         }
558                         else
559                         {
560                             IO_outport( DX_sig(context), 2, *((WORD *)data));
561                             TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
562                               *((WORD *)data), CS_sig(context), IP_sig(context));
563                         }
564                         break;
565                     }
566                 }
567               EIP_sig(context) += prefixlen + 1;
568             }
569             return TRUE;
570
571         case 0x8e: /* mov XX,segment_reg */
572             {
573                 WORD seg;
574                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
575                                                   long_addr, segprefix, &len );
576                 if (!addr)
577                     break;  /* Unable to emulate it */
578                 seg = *(WORD *)addr;
579                 if (!INSTR_ReplaceSelector( context, &seg ))
580                     break;  /* Unable to emulate it */
581
582                 switch((instr[1] >> 3) & 7)
583                 {
584                 case 0:
585                     ES_sig(context) = seg;
586                     EIP_sig(context) += prefixlen + len + 1;
587                     return TRUE;
588                 case 1:  /* cs */
589                     break;
590                 case 2:
591                     SS_sig(context) = seg;
592                     EIP_sig(context) += prefixlen + len + 1;
593                     return TRUE;
594                 case 3:
595                     DS_sig(context) = seg;
596                     EIP_sig(context) += prefixlen + len + 1;
597                     return TRUE;
598                 case 4:
599 #ifdef FS_sig
600                     FS_sig(context) = seg;
601                     EIP_sig(context) += prefixlen + len + 1;
602                     return TRUE;
603 #endif
604                 case 5:
605 #ifdef GS_sig
606                     GS_sig(context) = seg;
607                     EIP_sig(context) += prefixlen + len + 1;
608                     return TRUE;
609 #endif
610                 case 6:  /* unused */
611                 case 7:  /* unused */
612                     break;
613                 }
614             }
615             break;  /* Unable to emulate it */
616
617         case 0xc4: /* les addr,reg */
618         case 0xc5: /* lds addr,reg */
619             if (INSTR_EmulateLDS( context, instr, long_op,
620                                   long_addr, segprefix, &len ))
621             {
622                 EIP_sig(context) += prefixlen + len;
623                 return TRUE;
624             }
625             break;  /* Unable to emulate it */
626             
627         case 0xcd: /* int <XX> */
628             if (long_op)
629             {
630                 ERR(int, "int xx from 32-bit code is not supported.\n");
631                 break;  /* Unable to emulate it */
632             }
633             else
634             {
635                 FARPROC16 addr = INT_GetPMHandler( instr[1] );
636                 WORD *stack = (WORD *)STACK_PTR( context );
637                 /* Push the flags and return address on the stack */
638                 *(--stack) = FL_sig(context);
639                 *(--stack) = CS_sig(context);
640                 *(--stack) = IP_sig(context) + prefixlen + 2;
641                 STACK_sig(context) -= 3 * sizeof(WORD);
642                 /* Jump to the interrupt handler */
643                 CS_sig(context)  = HIWORD(addr);
644                 EIP_sig(context) = LOWORD(addr);
645             }
646             return TRUE;
647
648         case 0xcf: /* iret */
649             if (long_op)
650             {
651                 DWORD *stack = (DWORD *)STACK_PTR( context );
652                 EIP_sig(context) = *stack++;
653                 CS_sig(context)  = *stack++;
654                 EFL_sig(context) = *stack;
655                 STACK_sig(context) += 3*sizeof(DWORD);  /* Pop the return address and flags */
656             }
657             else
658             {
659                 WORD *stack = (WORD *)STACK_PTR( context );
660                 EIP_sig(context) = *stack++;
661                 CS_sig(context)  = *stack++;
662                 FL_sig(context)  = *stack;
663                 STACK_sig(context) += 3*sizeof(WORD);  /* Pop the return address and flags */
664             }
665             return TRUE;
666
667         case 0xe4: /* inb al,XX */
668             AL_sig(context) = IO_inport( instr[1], 1 );
669             TRACE(io, "0x%x < %02x @ %04x:%04x\n", instr[1],
670                 AL_sig(context), CS_sig(context), IP_sig(context));
671             EIP_sig(context) += prefixlen + 2;
672             return TRUE;
673
674         case 0xe5: /* in (e)ax,XX */
675             if (long_op)
676             {
677                 EAX_sig(context) = IO_inport( instr[1], 4 );
678                 TRACE(io, "0x%x < %08lx @ %04x:%04x\n", instr[1],
679                     EAX_sig(context), CS_sig(context), IP_sig(context));
680             }
681             else
682             {
683                 AX_sig(context) = IO_inport( instr[1], 2 );
684                 TRACE(io, "0x%x < %04x @ %04x:%04x\n", instr[1],
685                     AX_sig(context), CS_sig(context), IP_sig(context));
686             }
687             EIP_sig(context) += prefixlen + 2;
688             return TRUE;
689
690         case 0xe6: /* outb XX,al */
691             IO_outport( instr[1], 1, AL_sig(context) );
692             TRACE(io, "0x%x > %02x @ %04x:%04x\n", instr[1],
693                 AL_sig(context), CS_sig(context), IP_sig(context));
694             EIP_sig(context) += prefixlen + 2;
695             return TRUE;
696
697         case 0xe7: /* out XX,(e)ax */
698             if (long_op)
699             {
700                 IO_outport( instr[1], 4, EAX_sig(context) );
701                 TRACE(io, "0x%x > %08lx @ %04x:%04x\n", instr[1],
702                     EAX_sig(context), CS_sig(context), IP_sig(context));
703             }
704             else
705             {
706                 IO_outport( instr[1], 2, AX_sig(context) );
707                 TRACE(io, "0x%x > %04x @ %04x:%04x\n", instr[1],
708                     AX_sig(context), CS_sig(context), IP_sig(context));
709             }
710             EIP_sig(context) += prefixlen + 2;
711             return TRUE;
712
713         case 0xec: /* inb al,dx */
714             AL_sig(context) = IO_inport( DX_sig(context), 1 );
715             TRACE(io, "0x%x < %02x @ %04x:%04x\n", DX_sig(context),
716                 AL_sig(context), CS_sig(context), IP_sig(context));
717             EIP_sig(context) += prefixlen + 1;
718             return TRUE;
719
720         case 0xed: /* in (e)ax,dx */
721             if (long_op)
722             {
723                 EAX_sig(context) = IO_inport( DX_sig(context), 4 );
724                 TRACE(io, "0x%x < %08lx @ %04x:%04x\n", DX_sig(context),
725                     EAX_sig(context), CS_sig(context), IP_sig(context));
726             }
727             else
728             {
729                 AX_sig(context) = IO_inport( DX_sig(context), 2 );
730                 TRACE(io, "0x%x < %04x @ %04x:%04x\n", DX_sig(context),
731                     AX_sig(context), CS_sig(context), IP_sig(context));
732             }
733             EIP_sig(context) += prefixlen + 1;
734             return TRUE;
735
736         case 0xee: /* outb dx,al */
737             IO_outport( DX_sig(context), 1, AL_sig(context) );
738             TRACE(io, "0x%x > %02x @ %04x:%04x\n", DX_sig(context),
739                 AL_sig(context), CS_sig(context), IP_sig(context));
740             EIP_sig(context) += prefixlen + 1;
741             return TRUE;
742       
743         case 0xef: /* out dx,(e)ax */
744             if (long_op)
745             {
746                 IO_outport( DX_sig(context), 4, EAX_sig(context) );
747                 TRACE(io, "0x%x > %08lx @ %04x:%04x\n", DX_sig(context),
748                     EAX_sig(context), CS_sig(context), IP_sig(context));
749             }
750             else
751             {
752                 IO_outport( DX_sig(context), 2, AX_sig(context) );
753                 TRACE(io, "0x%x > %04x @ %04x:%04x\n", DX_sig(context),
754                     AX_sig(context), CS_sig(context), IP_sig(context));
755             }
756             EIP_sig(context) += prefixlen + 1;
757             return TRUE;
758
759         case 0xfa: /* cli, ignored */
760             EIP_sig(context) += prefixlen + 1;
761             return TRUE;
762
763         case 0xfb: /* sti, ignored */
764             EIP_sig(context) += prefixlen + 1;
765             return TRUE;
766     }
767
768
769     /* Check for Win16 __GP handler */
770     gpHandler = HasGPHandler( PTR_SEG_OFF_TO_SEGPTR( CS_sig(context),
771                                                      EIP_sig(context) ) );
772     if (gpHandler)
773     {
774         WORD *stack = (WORD *)STACK_PTR( context );
775         *--stack = CS_sig(context);
776         *--stack = EIP_sig(context);
777         STACK_sig(context) -= 2*sizeof(WORD);
778
779         CS_sig(context) = SELECTOROF( gpHandler );
780         EIP_sig(context) = OFFSETOF( gpHandler );
781         return TRUE;
782     }
783
784     MSG("Unexpected Windows program segfault"
785                     " - opcode = %x\n", *instr);
786     return FALSE;  /* Unable to emulate it */
787 }