Added a default program option in wine.conf in section [programs] key
[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                       break;
493                     case 0x6d:
494                       if (long_op)
495                         *((DWORD *)data) = IO_inport( DX_sig(context), 4);
496                       else
497                         *((WORD *)data) = IO_inport( DX_sig(context), 2);
498                       break;
499                     case 0x6e:
500                         IO_outport( DX_sig(context), 1, *((BYTE *)data));
501                         break;
502                     case 0x6f:
503                         if (long_op)
504                             IO_outport( DX_sig(context), 4, *((DWORD *)data));
505                         else
506                             IO_outport( DX_sig(context), 2, *((WORD *)data));
507                         break;
508                     }
509                 }
510               EIP_sig(context) += prefixlen + 1;
511             }
512             return TRUE;
513
514         case 0x8e: /* mov XX,segment_reg */
515             {
516                 WORD seg;
517                 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
518                                                   long_addr, segprefix, &len );
519                 if (!addr)
520                     break;  /* Unable to emulate it */
521                 seg = *(WORD *)addr;
522                 if (!(seg = INSTR_ReplaceSelector( context, seg )))
523                     break;  /* Unable to emulate it */
524
525                 switch((instr[1] >> 3) & 7)
526                 {
527                 case 0:
528                     ES_sig(context) = seg;
529                     EIP_sig(context) += prefixlen + len + 1;
530                     return TRUE;
531                 case 1:  /* cs */
532                     break;
533                 case 2:
534                     SS_sig(context) = seg;
535                     EIP_sig(context) += prefixlen + len + 1;
536                     return TRUE;
537                 case 3:
538                     DS_sig(context) = seg;
539                     EIP_sig(context) += prefixlen + len + 1;
540                     return TRUE;
541                 case 4:
542 #ifdef FS_sig
543                     FS_sig(context) = seg;
544                     EIP_sig(context) += prefixlen + len + 1;
545                     return TRUE;
546 #endif
547                 case 5:
548 #ifdef GS_sig
549                     GS_sig(context) = seg;
550                     EIP_sig(context) += prefixlen + len + 1;
551                     return TRUE;
552 #endif
553                 case 6:  /* unused */
554                 case 7:  /* unused */
555                     break;
556                 }
557             }
558             break;  /* Unable to emulate it */
559
560         case 0xc4: /* les addr,reg */
561         case 0xc5: /* lds addr,reg */
562             if (INSTR_EmulateLDS( context, instr, long_op,
563                                   long_addr, segprefix, &len ))
564             {
565                 EIP_sig(context) += prefixlen + len;
566                 return TRUE;
567             }
568             break;  /* Unable to emulate it */
569             
570         case 0xcd: /* int <XX> */
571             if (long_op)
572             {
573                 ERR(int, "int xx from 32-bit code is not supported.\n");
574                 break;  /* Unable to emulate it */
575             }
576             else
577             {
578                 FARPROC16 addr = INT_GetPMHandler( instr[1] );
579                 WORD *stack = (WORD *)STACK_PTR( context );
580                 /* Push the flags and return address on the stack */
581                 *(--stack) = FL_sig(context);
582                 *(--stack) = CS_sig(context);
583                 *(--stack) = IP_sig(context) + prefixlen + 2;
584                 STACK_sig(context) -= 3 * sizeof(WORD);
585                 /* Jump to the interrupt handler */
586                 CS_sig(context)  = HIWORD(addr);
587                 EIP_sig(context) = LOWORD(addr);
588             }
589             return TRUE;
590
591         case 0xcf: /* iret */
592             if (long_op)
593             {
594                 DWORD *stack = (DWORD *)STACK_PTR( context );
595                 EIP_sig(context) = *stack++;
596                 CS_sig(context)  = *stack++;
597                 EFL_sig(context) = *stack;
598                 STACK_sig(context) += 3*sizeof(DWORD);  /* Pop the return address and flags */
599             }
600             else
601             {
602                 WORD *stack = (WORD *)STACK_PTR( context );
603                 EIP_sig(context) = *stack++;
604                 CS_sig(context)  = *stack++;
605                 FL_sig(context)  = *stack;
606                 STACK_sig(context) += 3*sizeof(WORD);  /* Pop the return address and flags */
607             }
608             return TRUE;
609
610         case 0xe4: /* inb al,XX */
611             AL_sig(context) = IO_inport( instr[1], 1 );
612             EIP_sig(context) += prefixlen + 2;
613             return TRUE;
614
615         case 0xe5: /* in (e)ax,XX */
616             if (long_op) EAX_sig(context) = IO_inport( instr[1], 4 );
617             else AX_sig(context) = IO_inport( instr[1], 2 );
618             EIP_sig(context) += prefixlen + 2;
619             return TRUE;
620
621         case 0xe6: /* outb XX,al */
622             IO_outport( instr[1], 1, AL_sig(context) );
623             EIP_sig(context) += prefixlen + 2;
624             return TRUE;
625
626         case 0xe7: /* out XX,(e)ax */
627             if (long_op) IO_outport( instr[1], 4, EAX_sig(context) );
628             else IO_outport( instr[1], 2, AX_sig(context) );
629             EIP_sig(context) += prefixlen + 2;
630             return TRUE;
631
632         case 0xec: /* inb al,dx */
633             AL_sig(context) = IO_inport( DX_sig(context), 1 );
634             EIP_sig(context) += prefixlen + 1;
635             return TRUE;
636
637         case 0xed: /* in (e)ax,dx */
638             if (long_op) EAX_sig(context) = IO_inport( DX_sig(context), 4 );
639             else AX_sig(context) = IO_inport( DX_sig(context), 2 );
640             EIP_sig(context) += prefixlen + 1;
641             return TRUE;
642
643         case 0xee: /* outb dx,al */
644             IO_outport( DX_sig(context), 1, AL_sig(context) );
645             EIP_sig(context) += prefixlen + 1;
646             return TRUE;
647       
648         case 0xef: /* out dx,(e)ax */
649             if (long_op) IO_outport( DX_sig(context), 4, EAX_sig(context) );
650             else IO_outport( DX_sig(context), 2, AX_sig(context) );
651             EIP_sig(context) += prefixlen + 1;
652             return TRUE;
653
654         case 0xfa: /* cli, ignored */
655             EIP_sig(context) += prefixlen + 1;
656             return TRUE;
657
658         case 0xfb: /* sti, ignored */
659             EIP_sig(context) += prefixlen + 1;
660             return TRUE;
661     }
662
663
664     /* Check for Win16 __GP handler */
665     gpHandler = HasGPHandler( PTR_SEG_OFF_TO_SEGPTR( CS_sig(context),
666                                                      EIP_sig(context) ) );
667     if (gpHandler)
668     {
669         WORD *stack = (WORD *)STACK_PTR( context );
670         *--stack = CS_sig(context);
671         *--stack = EIP_sig(context);
672         STACK_sig(context) -= 2*sizeof(WORD);
673
674         CS_sig(context) = SELECTOROF( gpHandler );
675         EIP_sig(context) = OFFSETOF( gpHandler );
676         return TRUE;
677     }
678
679     MSG("Unexpected Windows program segfault"
680                     " - opcode = %x\n", *instr);
681     return FALSE;  /* Unable to emulate it */
682 }