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