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