Release 950727
[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 /***********************************************************************
15  *           INSTR_EmulateInstruction
16  *
17  * Emulate a priviledged instruction. Returns TRUE if emulation successful.
18  */
19 BOOL INSTR_EmulateInstruction( struct sigcontext_struct *context )
20 {
21     int prefix, segprefix, repX, long_op, long_addr;
22     BYTE *instr;
23
24     long_op = long_addr = (GET_SEL_FLAGS(CS) & LDT_FLAGS_32BIT) != 0;
25     instr = (BYTE *) PTR_SEG_OFF_TO_LIN( CS, long_op ? EIP : IP );
26
27     /* First handle any possible prefix */
28
29     segprefix = -1;  /* no prefix */
30     prefix = 1;
31     repX = 0;
32     while(prefix)
33     {
34         switch(*instr)
35         {
36         case 0x2e:
37             segprefix = CS;
38             break;
39         case 0x36:
40             segprefix = SS;
41             break;
42         case 0x3e:
43             segprefix = DS;
44             break;
45         case 0x26:
46             segprefix = ES;
47             break;
48         case 0x64:
49             segprefix = FS;
50             break;
51         case 0x65:
52             segprefix = GS;
53             break;
54         case 0x66:
55             long_op = !long_op;  /* opcode size prefix */
56             break;
57         case 0x67:
58             long_addr = !long_addr;  /* addr size prefix */
59             break;
60         case 0xf0:  /* lock */
61             break;
62         case 0xf2:  /* repne */
63             repX = 1;
64             break;
65         case 0xf3:  /* repe */
66             repX = 2;
67             break;
68         default:
69             prefix = 0;  /* no more prefixes */
70             break;
71         }
72         if (prefix)
73         {
74             instr++;
75             EIP++;
76         }
77     }
78
79     /* Now look at the actual instruction */
80
81     switch(*instr)
82     {
83         case 0xcd: /* int <XX> */
84             if (long_op)
85             {
86                 fprintf(stderr, "int xx from 32-bit code is not supported.\n");
87                 return FALSE;  /* Unable to emulate it */
88             }
89             else
90             {
91                 SEGPTR addr = INT_GetHandler( instr[1] );
92                 /* FIXME: should check the stack 'big' bit */
93                 WORD *stack = (WORD *)PTR_SEG_OFF_TO_LIN( SS, SP );
94                 /* Push the flags and return address on the stack */
95                 *(--stack) = FL;
96                 *(--stack) = CS;
97                 *(--stack) = IP + 2;
98                 SP -= 3 * sizeof(WORD);
99                 /* Jump to the interrupt handler */
100                 CS  = HIWORD(addr);
101                 EIP = LOWORD(addr);
102             }
103             break;
104
105         case 0xcf: /* iret */
106             if (long_op)
107             {
108                 /* FIXME: should check the stack 'big' bit */
109                 DWORD *stack = (DWORD *)PTR_SEG_OFF_TO_LIN( SS, SP );
110                 EIP = *stack++;
111                 CS  = *stack++;
112                 EFL = *stack;
113                 SP += 3*sizeof(DWORD);  /* Pop the return address and flags */
114             }
115             else
116             {
117                 /* FIXME: should check the stack 'big' bit */
118                 WORD *stack = (WORD *)PTR_SEG_OFF_TO_LIN( SS, SP );
119                 EIP = *stack++;
120                 CS  = *stack++;
121                 FL  = *stack;
122                 SP += 3*sizeof(WORD);  /* Pop the return address and flags */
123             }
124             break;
125
126         case 0xe4: /* inb al,XX */
127             AL = inport( instr[1], 1 );
128             EIP += 2;
129             break;
130
131         case 0xe5: /* in (e)ax,XX */
132             if (long_op) EAX = inport( instr[1], 4 );
133             else AX = inport( instr[1], 2 );
134             EIP += 2;
135             break;
136
137         case 0xe6: /* outb XX,al */
138             outport( instr[1], 1, AL );
139             EIP += 2;
140             break;
141
142         case 0xe7: /* out XX,(e)ax */
143             if (long_op) outport( instr[1], 4, EAX );
144             else outport( instr[1], 2, AX );
145             EIP += 2;
146             break;
147
148         case 0xec: /* inb al,dx */
149             AL = inport( DX, 1 );
150             EIP++;
151             break;
152
153         case 0xed: /* in (e)ax,dx */
154             if (long_op) EAX = inport( DX, 4 );
155             else AX = inport( DX, 2 );
156             EIP++;  
157             break;
158
159         case 0xee: /* outb dx,al */
160             outport( DX, 1, AL );
161             EIP++;
162             break;
163       
164         case 0xef: /* out dx,(e)ax */
165             if (long_op) outport( DX, 4, EAX );
166             else outport( DX, 2, AX );
167             EIP++;
168             break;
169
170         case 0xfa: /* cli, ignored */
171             EIP++;
172             break;
173
174         case 0xfb: /* sti, ignored */
175             EIP++;
176             break;
177
178         case 0x6c: /* insb     */
179         case 0x6d: /* insw/d   */
180         case 0x6e: /* outsb    */
181         case 0x6f: /* outsw/d  */
182             {
183               int typ = *instr;  /* Just in case it's overwritten.  */
184               int outp = (typ >= 0x6e);
185               unsigned long count = repX ? (long_addr ? ECX : CX) : 1;
186               int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
187               int step = (EFL & 0x400) ? -opsize : +opsize;
188               /* FIXME: Check this, please.  */
189               int seg = outp ? (segprefix >= 0 ? segprefix : DS) : ES;
190
191               if (outp)
192                 /* FIXME: Check segment readable.  */
193                 ;
194               else
195                 /* FIXME: Check segment writeable.  */
196                 ;
197
198               if (repX)
199                 if (long_addr)
200                   ECX = 0;
201                 else
202                   CX = 0;
203
204               while (count-- > 0)
205                 {
206                   void *data;
207                   if (outp)
208                     {
209                       data = PTR_SEG_OFF_TO_LIN (seg, long_addr ? ESI : SI);
210                       if (long_addr)
211                         ESI += step;
212                       else
213                         SI += step;
214                     }
215                   else
216                     {
217                       data = PTR_SEG_OFF_TO_LIN (seg, long_addr ? EDI : DI);
218                       if (long_addr)
219                         EDI += step;
220                       else
221                         DI += step;
222                     }
223
224                   switch (typ)
225                     {
226                     case 0x6c:
227                       *((BYTE *)data) = inport (DX, 1);
228                       break;
229                     case 0x6d:
230                       if (long_op)
231                         *((DWORD *)data) = inport (DX, 4);
232                       else
233                         *((WORD *)data) = inport (DX, 2);
234                       break;
235                     case 0x6e:
236                       outport (DX, 1, *((BYTE *)data));
237                       break;
238                     case 0x6f:
239                       if (long_op)
240                         outport (DX, 4, *((DWORD *)data));
241                       else
242                         outport (DX, 2, *((WORD *)data));
243                       break;
244                     }
245                 }
246               EIP++;
247               break;
248             }
249
250       default:
251             fprintf(stderr, "Unexpected Windows program segfault"
252                             " - opcode = %x\n", *instr);
253             return FALSE;  /* Unable to emulate it */
254     }
255     return TRUE;
256 }