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