Release 960114
[wine] / miscemu / instr.c
1 /*
2  * Emulation of priviledged instructions
3  *
4  * Copyright 1995 Alexandre Julliard
5  */
6
7 #include <stdio.h>
8 #include "windows.h"
9 #include "ldt.h"
10 #include "miscemu.h"
11 #include "registers.h"
12
13
14 #define STACK_reg(context) \
15    ((GET_SEL_FLAGS(SS_reg(context)) & LDT_FLAGS_32BIT) ? \
16                    ESP_reg(context) : SP_reg(context))
17
18 #define STACK_PTR(context) \
19     (PTR_SEG_OFF_TO_LIN(SS_reg(context),STACK_reg(context)))
20
21 /***********************************************************************
22  *           INSTR_ReplaceSelector
23  *
24  * Try to replace an invalid selector by a valid one.
25  * For now, only selector 0x40 is handled here.
26  */
27 static WORD INSTR_ReplaceSelector( struct sigcontext_struct *context, WORD sel)
28 {
29     if (sel == 0x40)
30     {
31         fprintf( stderr, "Direct access to segment 0x40 (cs:ip=%04x:%04lx).\n",
32                  CS_reg(context), EIP_reg(context) );
33         return DOSMEM_BiosSeg;
34     }
35     return 0;  /* Can't replace selector */
36 }
37
38
39 /***********************************************************************
40  *           INSTR_GetOperandAddr
41  *
42  * Return the address of an instruction operand (from the mod/rm byte).
43  */
44 static BYTE *INSTR_GetOperandAddr( struct sigcontext_struct *context,
45                                    BYTE *instr, int long_addr,
46                                    int segprefix, int *len )
47 {
48     int mod, rm, base, index = 0, ss = 0, seg = 0, off;
49
50 #define GET_VAL(val,type) \
51     { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
52
53     *len = 0;
54     GET_VAL( &mod, BYTE );
55     rm = mod & 7;
56     mod >>= 6;
57
58     if (mod == 3)
59     {
60         switch(rm)
61         {
62         case 0: return (BYTE *)&EAX_reg(context);
63         case 1: return (BYTE *)&ECX_reg(context);
64         case 2: return (BYTE *)&EDX_reg(context);
65         case 3: return (BYTE *)&EBX_reg(context);
66         case 4: return (BYTE *)&ESP_reg(context);
67         case 5: return (BYTE *)&EBP_reg(context);
68         case 6: return (BYTE *)&ESI_reg(context);
69         case 7: return (BYTE *)&EDI_reg(context);
70         }
71     }
72
73     if (long_addr)
74     {
75         if (rm == 4)
76         {
77             BYTE sib;
78             GET_VAL( &sib, BYTE );
79             rm = sib & 7;
80             ss = sib >> 6;
81             switch(sib >> 3)
82             {
83             case 0: index = EAX_reg(context); break;
84             case 1: index = ECX_reg(context); break;
85             case 2: index = EDX_reg(context); break;
86             case 3: index = EBX_reg(context); break;
87             case 4: index = 0; break;
88             case 5: index = EBP_reg(context); break;
89             case 6: index = ESI_reg(context); break;
90             case 7: index = EDI_reg(context); break;
91             }
92         }
93
94         switch(rm)
95         {
96         case 0: base = EAX_reg(context); seg = DS_reg(context); break;
97         case 1: base = ECX_reg(context); seg = DS_reg(context); break;
98         case 2: base = EDX_reg(context); seg = DS_reg(context); break;
99         case 3: base = EBX_reg(context); seg = DS_reg(context); break;
100         case 4: base = ESP_reg(context); seg = SS_reg(context); break;
101         case 5: base = EBP_reg(context); seg = SS_reg(context); break;
102         case 6: base = ESI_reg(context); seg = DS_reg(context); break;
103         case 7: base = EDI_reg(context); seg = DS_reg(context); break;
104         }
105         switch (mod)
106         {
107         case 0:
108             if (rm == 5)  /* special case: ds:(disp32) */
109             {
110                 GET_VAL( &base, DWORD );
111                 seg = DS_reg(context);
112             }
113             break;
114
115         case 1:  /* 8-bit disp */
116             GET_VAL( &off, BYTE );
117             base += (signed char)off;
118             break;
119
120         case 2:  /* 32-bit disp */
121             GET_VAL( &off, DWORD );
122             base += (signed long)off;
123             break;
124         }
125     }
126     else  /* short address */
127     {
128         switch(rm)
129         {
130         case 0:  /* ds:(bx,si) */
131             base = BX_reg(context) + SI_reg(context);
132             seg  = DS_reg(context);
133             break;
134         case 1:  /* ds:(bx,di) */
135             base = BX_reg(context) + DI_reg(context);
136             seg  = DS_reg(context);
137             break;
138         case 2:  /* ss:(bp,si) */
139             base = BP_reg(context) + SI_reg(context);
140             seg  = SS_reg(context);
141             break;
142         case 3:  /* ss:(bp,di) */
143             base = BP_reg(context) + DI_reg(context);
144             seg  = SS_reg(context);
145             break;
146         case 4:  /* ds:(si) */
147             base = SI_reg(context);
148             seg  = DS_reg(context);
149             break;
150         case 5:  /* ds:(di) */
151             base = DI_reg(context);
152             seg  = DS_reg(context);
153             break;
154         case 6:  /* ss:(bp) */
155             base = BP_reg(context);
156             seg  = SS_reg(context);
157             break;
158         case 7:  /* ds:(bx) */
159             base = BX_reg(context);
160             seg  = DS_reg(context);
161             break;
162         }
163
164         switch(mod)
165         {
166         case 0:
167             if (rm == 6)  /* special case: ds:(disp16) */
168             {
169                 GET_VAL( &base, WORD );
170                 seg  = DS_reg(context);
171             }
172             break;
173
174         case 1:  /* 8-bit disp */
175             GET_VAL( &off, BYTE );
176             base += (signed char)off;
177             break;
178
179         case 2:  /* 16-bit disp */
180             GET_VAL( &off, WORD );
181             base += (signed short)off;
182             break;
183         }
184         base &= 0xffff;
185     }
186     if (segprefix != -1) seg = segprefix;
187
188     /* FIXME: should check limit of the segment here */
189     return (BYTE *)PTR_SEG_OFF_TO_LIN( seg, (base + (index << ss)) );
190 }
191
192
193 /***********************************************************************
194  *           INSTR_EmulateLDS
195  *
196  * Emulate the LDS (and LES,LFS,etc.) instruction.
197  */
198 static BOOL INSTR_EmulateLDS( struct sigcontext_struct *context,
199                               BYTE *instr, int long_op, int long_addr,
200                               int segprefix, int *len )
201 {
202     BYTE *regmodrm = instr + 1 + (*instr == 0x0f);
203     BYTE *addr = INSTR_GetOperandAddr( context, regmodrm,
204                                        long_addr, segprefix, len );
205     WORD seg = *(WORD *)(addr + (long_op ? 4 : 2));
206
207     if (!(seg = INSTR_ReplaceSelector( context, seg )))
208         return FALSE;  /* Unable to emulate it */
209
210     /* Now store the offset in the correct register */
211
212     switch((*regmodrm >> 3) & 7)
213     {
214     case 0:
215         if (long_op) EAX_reg(context) = *(DWORD *)addr;
216         else AX_reg(context) = *(WORD *)addr;
217         break;
218     case 1:
219         if (long_op) ECX_reg(context) = *(DWORD *)addr;
220         else CX_reg(context) = *(WORD *)addr;
221         break;
222     case 2:
223         if (long_op) EDX_reg(context) = *(DWORD *)addr;
224         else DX_reg(context) = *(WORD *)addr;
225         break;
226     case 3:
227         if (long_op) EBX_reg(context) = *(DWORD *)addr;
228         else BX_reg(context) = *(WORD *)addr;
229         break;
230     case 4:
231         if (long_op) ESP_reg(context) = *(DWORD *)addr;
232         else SP_reg(context) = *(WORD *)addr;
233         break;
234     case 5:
235         if (long_op) EBP_reg(context) = *(DWORD *)addr;
236         else BP_reg(context) = *(WORD *)addr;
237         break;
238     case 6:
239         if (long_op) ESI_reg(context) = *(DWORD *)addr;
240         else SI_reg(context) = *(WORD *)addr;
241         break;
242     case 7:
243         if (long_op) EDI_reg(context) = *(DWORD *)addr;
244         else DI_reg(context) = *(WORD *)addr;
245         break;
246     }
247
248     /* Store the correct segment in the segment register */
249
250     switch(*instr)
251     {
252     case 0xc4: ES_reg(context) = seg; break;  /* les */
253     case 0xc5: DS_reg(context) = seg; break;  /* lds */
254     case 0x0f: switch(instr[1])
255                {
256                case 0xb2: SS_reg(context) = seg; break;  /* lss */
257 #ifdef FS_reg
258                case 0xb4: FS_reg(context) = seg; break;  /* lfs */
259 #endif
260 #ifdef GS_reg
261                case 0xb5: GS_reg(context) = seg; break;  /* lgs */
262 #endif
263                }
264                break;
265     }
266
267     /* Add the opcode size to the total length */
268
269     *len += 1 + (*instr == 0x0f);
270     return TRUE;
271 }
272
273
274 /***********************************************************************
275  *           INSTR_EmulateInstruction
276  *
277  * Emulate a priviledged instruction. Returns TRUE if emulation successful.
278  */
279 BOOL INSTR_EmulateInstruction( struct sigcontext_struct *context )
280 {
281     int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
282     BYTE *instr;
283
284     long_op = long_addr = (GET_SEL_FLAGS(CS_reg(context)) & LDT_FLAGS_32BIT) != 0;
285     instr = (BYTE *) PTR_SEG_OFF_TO_LIN( CS_reg(context), EIP_reg(context) );
286
287     /* First handle any possible prefix */
288
289     segprefix = -1;  /* no prefix */
290     prefix = 1;
291     repX = 0;
292     prefixlen = 0;
293     while(prefix)
294     {
295         switch(*instr)
296         {
297         case 0x2e:
298             segprefix = CS_reg(context);
299             break;
300         case 0x36:
301             segprefix = SS_reg(context);
302             break;
303         case 0x3e:
304             segprefix = DS_reg(context);
305             break;
306         case 0x26:
307             segprefix = ES_reg(context);
308             break;
309 #ifdef FS_reg
310         case 0x64:
311             segprefix = FS_reg(context);
312             break;
313 #endif
314 #ifdef GS_reg
315         case 0x65:
316             segprefix = GS_reg(context);
317             break;
318 #endif
319         case 0x66:
320             long_op = !long_op;  /* opcode size prefix */
321             break;
322         case 0x67:
323             long_addr = !long_addr;  /* addr size prefix */
324             break;
325         case 0xf0:  /* lock */
326             break;
327         case 0xf2:  /* repne */
328             repX = 1;
329             break;
330         case 0xf3:  /* repe */
331             repX = 2;
332             break;
333         default:
334             prefix = 0;  /* no more prefixes */
335             break;
336         }
337         if (prefix)
338         {
339             instr++;
340             prefixlen++;
341         }
342     }
343
344     /* Now look at the actual instruction */
345
346     switch(*instr)
347     {
348         case 0x07: /* pop es */
349         case 0x17: /* pop ss */
350         case 0x1f: /* pop ds */
351             {
352                 WORD seg = *(WORD *)STACK_PTR( context );
353                 if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
354                 {
355                     switch(*instr)
356                     {
357                     case 0x07: ES_reg(context) = seg; break;
358                     case 0x17: SS_reg(context) = seg; break;
359                     case 0x1f: DS_reg(context) = seg; break;
360                     }
361                     STACK_reg(context) += long_op ? 4 : 2;
362                     EIP_reg(context) += prefixlen + 1;
363                     return TRUE;
364                 }
365             }
366             break;  /* Unable to emulate it */
367
368         case 0x0f: /* extended instruction */
369             switch(instr[1])
370             {
371 #ifdef FS_reg
372             case 0xa1: /* pop fs */
373                 {
374                     WORD seg = *(WORD *)STACK_PTR( context );
375                     if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
376                     {
377                         FS_reg(context) = seg;
378                         STACK_reg(context) += long_op ? 4 : 2;
379                         EIP_reg(context) += prefixlen + 2;
380                         return TRUE;
381                     }
382                 }
383                 break;
384 #endif  /* FS_reg */
385
386 #ifdef GS_reg
387             case 0xa9: /* pop gs */
388                 {
389                     WORD seg = *(WORD *)STACK_PTR( context );
390                     if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
391                     {
392                         GS_reg(context) = seg;
393                         STACK_reg(context) += long_op ? 4 : 2;
394                         EIP_reg(context) += prefixlen + 2;
395                         return TRUE;
396                     }
397                 }
398                 break;
399 #endif  /* GS_reg */
400
401             case 0xb2: /* lss addr,reg */
402 #ifdef FS_reg
403             case 0xb4: /* lfs addr,reg */
404 #endif
405 #ifdef GS_reg
406             case 0xb5: /* lgs addr,reg */
407 #endif
408                 if (INSTR_EmulateLDS( context, instr, long_op,
409                                       long_addr, segprefix, &len ))
410                 {
411                     EIP_reg(context) += prefixlen + len;
412                     return TRUE;
413                 }
414                 break;
415             }
416             break;  /* Unable to emulate it */
417
418         case 0x6c: /* insb     */
419         case 0x6d: /* insw/d   */
420         case 0x6e: /* outsb    */
421         case 0x6f: /* outsw/d  */
422             {
423               int typ = *instr;  /* Just in case it's overwritten.  */
424               int outp = (typ >= 0x6e);
425               unsigned long count = repX ?
426                           (long_addr ? ECX_reg(context) : CX_reg(context)) : 1;
427               int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
428               int step = (EFL_reg(context) & 0x400) ? -opsize : +opsize;
429               int seg = outp ? DS_reg(context) : ES_reg(context);  /* FIXME: is this right? */
430
431               if (outp)
432                 /* FIXME: Check segment readable.  */
433                 (void)0;
434               else
435                 /* FIXME: Check segment writeable.  */
436                 (void)0;
437
438               if (repX)
439                 if (long_addr)
440                   ECX_reg(context) = 0;
441                 else
442                   CX_reg(context) = 0;
443
444               while (count-- > 0)
445                 {
446                   void *data;
447                   if (outp)
448                   {
449                       data = PTR_SEG_OFF_TO_LIN (seg,
450                                long_addr ? ESI_reg(context) : SI_reg(context));
451                       if (long_addr) ESI_reg(context) += step;
452                       else SI_reg(context) += step;
453                   }
454                   else
455                   {
456                       data = PTR_SEG_OFF_TO_LIN (seg,
457                                long_addr ? EDI_reg(context) : DI_reg(context));
458                       if (long_addr) EDI_reg(context) += step;
459                       else DI_reg(context) += step;
460                   }
461                   
462                   switch (typ)
463                   {
464                     case 0x6c:
465                       *((BYTE *)data) = inport( DX_reg(context), 1);
466                       break;
467                     case 0x6d:
468                       if (long_op)
469                         *((DWORD *)data) = inport( DX_reg(context), 4);
470                       else
471                         *((WORD *)data) = inport( DX_reg(context), 2);
472                       break;
473                     case 0x6e:
474                       outport( DX_reg(context), 1, *((BYTE *)data));
475                       break;
476                     case 0x6f:
477                       if (long_op)
478                         outport( DX_reg(context), 4, *((DWORD *)data));
479                       else
480                         outport( DX_reg(context), 2, *((WORD *)data));
481                       break;
482                     }
483                 }
484               EIP_reg(context) += prefixlen + 1;
485             }
486             return TRUE;
487
488         case 0x8e: /* mov XX,segment_reg */
489             {
490                 WORD seg = *(WORD *)INSTR_GetOperandAddr( context, instr + 1,
491                                                   long_addr, segprefix, &len );
492                 if (!(seg = INSTR_ReplaceSelector( context, seg )))
493                     break;  /* Unable to emulate it */
494
495                 switch((instr[1] >> 3) & 7)
496                 {
497                 case 0:
498                     ES_reg(context) = seg;
499                     EIP_reg(context) += prefixlen + len + 1;
500                     return TRUE;
501                 case 1:  /* cs */
502                     break;
503                 case 2:
504                     SS_reg(context) = seg;
505                     EIP_reg(context) += prefixlen + len + 1;
506                     return TRUE;
507                 case 3:
508                     DS_reg(context) = seg;
509                     EIP_reg(context) += prefixlen + len + 1;
510                     return TRUE;
511                 case 4:
512 #ifdef FS_reg
513                     FS_reg(context) = seg;
514                     EIP_reg(context) += prefixlen + len + 1;
515                     return TRUE;
516 #endif
517                 case 5:
518 #ifdef GS_reg
519                     GS_reg(context) = seg;
520                     EIP_reg(context) += prefixlen + len + 1;
521                     return TRUE;
522 #endif
523                 case 6:  /* unused */
524                 case 7:  /* unused */
525                     break;
526                 }
527             }
528             break;  /* Unable to emulate it */
529
530         case 0xc4: /* les addr,reg */
531         case 0xc5: /* lds addr,reg */
532             if (INSTR_EmulateLDS( context, instr, long_op,
533                                   long_addr, segprefix, &len ))
534             {
535                 EIP_reg(context) += prefixlen + len;
536                 return TRUE;
537             }
538             break;  /* Unable to emulate it */
539             
540         case 0xcd: /* int <XX> */
541             if (long_op)
542             {
543                 fprintf(stderr, "int xx from 32-bit code is not supported.\n");
544                 break;  /* Unable to emulate it */
545             }
546             else
547             {
548                 SEGPTR addr = INT_GetHandler( instr[1] );
549                 WORD *stack = (WORD *)STACK_PTR( context );
550                 /* Push the flags and return address on the stack */
551                 *(--stack) = FL_reg(context);
552                 *(--stack) = CS_reg(context);
553                 *(--stack) = IP_reg(context) + prefixlen + 2;
554                 STACK_reg(context) -= 3 * sizeof(WORD);
555                 /* Jump to the interrupt handler */
556                 CS_reg(context)  = HIWORD(addr);
557                 EIP_reg(context) = LOWORD(addr);
558             }
559             return TRUE;
560
561         case 0xcf: /* iret */
562             if (long_op)
563             {
564                 DWORD *stack = (DWORD *)STACK_PTR( context );
565                 EIP_reg(context) = *stack++;
566                 CS_reg(context)  = *stack++;
567                 EFL_reg(context) = *stack;
568                 STACK_reg(context) += 3*sizeof(DWORD);  /* Pop the return address and flags */
569             }
570             else
571             {
572                 WORD *stack = (WORD *)STACK_PTR( context );
573                 EIP_reg(context) = *stack++;
574                 CS_reg(context)  = *stack++;
575                 FL_reg(context)  = *stack;
576                 STACK_reg(context) += 3*sizeof(WORD);  /* Pop the return address and flags */
577             }
578             return TRUE;
579
580         case 0xe4: /* inb al,XX */
581             AL_reg(context) = inport( instr[1], 1 );
582             EIP_reg(context) += prefixlen + 2;
583             return TRUE;
584
585         case 0xe5: /* in (e)ax,XX */
586             if (long_op) EAX_reg(context) = inport( instr[1], 4 );
587             else AX_reg(context) = inport( instr[1], 2 );
588             EIP_reg(context) += prefixlen + 2;
589             return TRUE;
590
591         case 0xe6: /* outb XX,al */
592             outport( instr[1], 1, AL_reg(context) );
593             EIP_reg(context) += prefixlen + 2;
594             return TRUE;
595
596         case 0xe7: /* out XX,(e)ax */
597             if (long_op) outport( instr[1], 4, EAX_reg(context) );
598             else outport( instr[1], 2, AX_reg(context) );
599             EIP_reg(context) += prefixlen + 2;
600             return TRUE;
601
602         case 0xec: /* inb al,dx */
603             AL_reg(context) = inport( DX_reg(context), 1 );
604             EIP_reg(context) += prefixlen + 1;
605             return TRUE;
606
607         case 0xed: /* in (e)ax,dx */
608             if (long_op) EAX_reg(context) = inport( DX_reg(context), 4 );
609             else AX_reg(context) = inport( DX_reg(context), 2 );
610             EIP_reg(context) += prefixlen + 1;
611             return TRUE;
612
613         case 0xee: /* outb dx,al */
614             outport( DX_reg(context), 1, AL_reg(context) );
615             EIP_reg(context) += prefixlen + 1;
616             return TRUE;
617       
618         case 0xef: /* out dx,(e)ax */
619             if (long_op) outport( DX_reg(context), 4, EAX_reg(context) );
620             else outport( DX_reg(context), 2, AX_reg(context) );
621             EIP_reg(context) += prefixlen + 1;
622             return TRUE;
623
624         case 0xfa: /* cli, ignored */
625             EIP_reg(context) += prefixlen + 1;
626             return TRUE;
627
628         case 0xfb: /* sti, ignored */
629             EIP_reg(context) += prefixlen + 1;
630             return TRUE;
631     }
632     fprintf(stderr, "Unexpected Windows program segfault"
633                     " - opcode = %x\n", *instr);
634     return FALSE;  /* Unable to emulate it */
635 }