Commit | Line | Data |
---|---|---|
bd34d4ff AJ |
1 | /* |
2 | * Emulation of priviledged instructions | |
3 | * | |
4 | * Copyright 1995 Alexandre Julliard | |
5 | */ | |
6 | ||
d3e22d9d JW |
7 | #include "windef.h" |
8 | #include "wingdi.h" | |
04c3e1d4 | 9 | #include "wine/winuser16.h" |
d30dfd24 | 10 | #include "module.h" |
bd34d4ff | 11 | #include "miscemu.h" |
735bec0f | 12 | #include "selectors.h" |
06c275a6 | 13 | #include "debugtools.h" |
bd34d4ff | 14 | |
d3e22d9d JW |
15 | DEFAULT_DEBUG_CHANNEL(int); |
16 | DECLARE_DEBUG_CHANNEL(io); | |
b4b9fae6 | 17 | |
9597e209 | 18 | #ifdef __i386__ |
bd34d4ff | 19 | |
9edc7d05 AJ |
20 | inline static void add_stack( CONTEXT86 *context, int offset ) |
21 | { | |
22 | if (ISV86(context) || !IS_SELECTOR_32BIT(context->SegSs)) | |
23 | ADD_LOWORD( context->Esp, offset ); | |
24 | else | |
25 | context->Esp += offset; | |
26 | } | |
af0bae58 | 27 | |
9edc7d05 AJ |
28 | inline static void *make_ptr( CONTEXT86 *context, DWORD seg, DWORD off, int long_addr ) |
29 | { | |
770eb51e | 30 | if (ISV86(context)) return PTR_REAL_TO_LIN( seg, off ); |
9edc7d05 AJ |
31 | if (IS_SELECTOR_SYSTEM(seg)) return (void *)off; |
32 | if (!long_addr) off = LOWORD(off); | |
982a223f | 33 | return MapSL( MAKESEGPTR( seg, off ) ); |
9edc7d05 | 34 | } |
7f740cbb | 35 | |
9edc7d05 AJ |
36 | inline static void *get_stack( CONTEXT86 *context ) |
37 | { | |
38 | if (ISV86(context)) | |
770eb51e | 39 | return PTR_REAL_TO_LIN( context->SegSs, context->Esp ); |
9edc7d05 AJ |
40 | if (IS_SELECTOR_SYSTEM(context->SegSs)) |
41 | return (void *)context->Esp; | |
42 | if (IS_SELECTOR_32BIT(context->SegSs)) | |
982a223f AJ |
43 | return MapSL( MAKESEGPTR( context->SegSs, context->Esp ) ); |
44 | return MapSL( MAKESEGPTR( context->SegSs, LOWORD(context->Esp) ) ); | |
9edc7d05 | 45 | } |
b7258bef AJ |
46 | |
47 | /*********************************************************************** | |
48 | * INSTR_ReplaceSelector | |
49 | * | |
50 | * Try to replace an invalid selector by a valid one. | |
44ed71f5 AJ |
51 | * The only selector where it is allowed to do "mov ax,40;mov es,ax" |
52 | * is the so called 'bimodal' selector 0x40, which points to the BIOS | |
53 | * data segment. Used by (at least) Borland products (and programs compiled | |
54 | * using Borland products). | |
55 | * | |
56 | * See Undocumented Windows, Chapter 5, __0040. | |
b7258bef | 57 | */ |
b3756782 | 58 | static BOOL INSTR_ReplaceSelector( CONTEXT86 *context, WORD *sel ) |
b7258bef | 59 | { |
4b32fd0d UW |
60 | extern char Call16_Start, Call16_End; |
61 | ||
d8fab2e6 AJ |
62 | if (IS_SELECTOR_SYSTEM(context->SegCs)) |
63 | if ( (char *)context->Eip >= &Call16_Start | |
64 | && (char *)context->Eip < &Call16_End ) | |
a95b4886 UW |
65 | { |
66 | /* Saved selector may have become invalid when the relay code */ | |
67 | /* tries to restore it. We simply clear it. */ | |
68 | *sel = 0; | |
69 | return TRUE; | |
70 | } | |
71 | ||
6da20b9d | 72 | if (*sel == 0x40) |
b7258bef | 73 | { |
641ee76a | 74 | static WORD sys_timer = 0; |
641ee76a | 75 | if (!sys_timer) |
7b57b24f | 76 | sys_timer = CreateSystemTimer( 55, DOSMEM_Tick ); |
a7894d93 | 77 | *sel = DOSMEM_BiosDataSeg; |
6da20b9d | 78 | return TRUE; |
b7258bef | 79 | } |
897fd119 | 80 | if (!IS_SELECTOR_SYSTEM(*sel) && !IS_SELECTOR_FREE(*sel)) |
dd03cc19 | 81 | ERR("Got protection fault on valid selector, maybe your kernel is too old?\n" ); |
6da20b9d | 82 | return FALSE; /* Can't replace selector, crashdump */ |
b7258bef AJ |
83 | } |
84 | ||
85 | ||
86 | /*********************************************************************** | |
87 | * INSTR_GetOperandAddr | |
88 | * | |
89 | * Return the address of an instruction operand (from the mod/rm byte). | |
90 | */ | |
b3756782 | 91 | static BYTE *INSTR_GetOperandAddr( CONTEXT86 *context, BYTE *instr, |
3051b644 | 92 | int long_addr, int segprefix, int *len ) |
b7258bef AJ |
93 | { |
94 | int mod, rm, base, index = 0, ss = 0, seg = 0, off; | |
95 | ||
96 | #define GET_VAL(val,type) \ | |
97 | { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); } | |
98 | ||
99 | *len = 0; | |
100 | GET_VAL( &mod, BYTE ); | |
101 | rm = mod & 7; | |
102 | mod >>= 6; | |
103 | ||
104 | if (mod == 3) | |
105 | { | |
106 | switch(rm) | |
107 | { | |
d8fab2e6 AJ |
108 | case 0: return (BYTE *)&context->Eax; |
109 | case 1: return (BYTE *)&context->Ecx; | |
110 | case 2: return (BYTE *)&context->Edx; | |
111 | case 3: return (BYTE *)&context->Ebx; | |
112 | case 4: return (BYTE *)&context->Esp; | |
113 | case 5: return (BYTE *)&context->Ebp; | |
114 | case 6: return (BYTE *)&context->Esi; | |
115 | case 7: return (BYTE *)&context->Edi; | |
b7258bef AJ |
116 | } |
117 | } | |
118 | ||
119 | if (long_addr) | |
120 | { | |
121 | if (rm == 4) | |
122 | { | |
123 | BYTE sib; | |
124 | GET_VAL( &sib, BYTE ); | |
125 | rm = sib & 7; | |
126 | ss = sib >> 6; | |
127 | switch(sib >> 3) | |
128 | { | |
d8fab2e6 AJ |
129 | case 0: index = context->Eax; break; |
130 | case 1: index = context->Ecx; break; | |
131 | case 2: index = context->Edx; break; | |
132 | case 3: index = context->Ebx; break; | |
b7258bef | 133 | case 4: index = 0; break; |
d8fab2e6 AJ |
134 | case 5: index = context->Ebp; break; |
135 | case 6: index = context->Esi; break; | |
136 | case 7: index = context->Edi; break; | |
b7258bef AJ |
137 | } |
138 | } | |
139 | ||
140 | switch(rm) | |
141 | { | |
d8fab2e6 AJ |
142 | case 0: base = context->Eax; seg = context->SegDs; break; |
143 | case 1: base = context->Ecx; seg = context->SegDs; break; | |
144 | case 2: base = context->Edx; seg = context->SegDs; break; | |
145 | case 3: base = context->Ebx; seg = context->SegDs; break; | |
146 | case 4: base = context->Esp; seg = context->SegSs; break; | |
147 | case 5: base = context->Ebp; seg = context->SegSs; break; | |
148 | case 6: base = context->Esi; seg = context->SegDs; break; | |
149 | case 7: base = context->Edi; seg = context->SegDs; break; | |
b7258bef AJ |
150 | } |
151 | switch (mod) | |
152 | { | |
153 | case 0: | |
154 | if (rm == 5) /* special case: ds:(disp32) */ | |
155 | { | |
156 | GET_VAL( &base, DWORD ); | |
d8fab2e6 | 157 | seg = context->SegDs; |
b7258bef AJ |
158 | } |
159 | break; | |
160 | ||
161 | case 1: /* 8-bit disp */ | |
162 | GET_VAL( &off, BYTE ); | |
163 | base += (signed char)off; | |
164 | break; | |
165 | ||
166 | case 2: /* 32-bit disp */ | |
167 | GET_VAL( &off, DWORD ); | |
168 | base += (signed long)off; | |
169 | break; | |
170 | } | |
171 | } | |
172 | else /* short address */ | |
173 | { | |
174 | switch(rm) | |
175 | { | |
176 | case 0: /* ds:(bx,si) */ | |
d8fab2e6 AJ |
177 | base = LOWORD(context->Ebx) + LOWORD(context->Esi); |
178 | seg = context->SegDs; | |
b7258bef AJ |
179 | break; |
180 | case 1: /* ds:(bx,di) */ | |
d8fab2e6 AJ |
181 | base = LOWORD(context->Ebx) + LOWORD(context->Edi); |
182 | seg = context->SegDs; | |
b7258bef AJ |
183 | break; |
184 | case 2: /* ss:(bp,si) */ | |
d8fab2e6 AJ |
185 | base = LOWORD(context->Ebp) + LOWORD(context->Esi); |
186 | seg = context->SegSs; | |
b7258bef AJ |
187 | break; |
188 | case 3: /* ss:(bp,di) */ | |
d8fab2e6 AJ |
189 | base = LOWORD(context->Ebp) + LOWORD(context->Edi); |
190 | seg = context->SegSs; | |
b7258bef AJ |
191 | break; |
192 | case 4: /* ds:(si) */ | |
d8fab2e6 AJ |
193 | base = LOWORD(context->Esi); |
194 | seg = context->SegDs; | |
b7258bef AJ |
195 | break; |
196 | case 5: /* ds:(di) */ | |
d8fab2e6 AJ |
197 | base = LOWORD(context->Edi); |
198 | seg = context->SegDs; | |
b7258bef AJ |
199 | break; |
200 | case 6: /* ss:(bp) */ | |
d8fab2e6 AJ |
201 | base = LOWORD(context->Ebp); |
202 | seg = context->SegSs; | |
b7258bef AJ |
203 | break; |
204 | case 7: /* ds:(bx) */ | |
d8fab2e6 AJ |
205 | base = LOWORD(context->Ebx); |
206 | seg = context->SegDs; | |
b7258bef AJ |
207 | break; |
208 | } | |
209 | ||
210 | switch(mod) | |
211 | { | |
212 | case 0: | |
213 | if (rm == 6) /* special case: ds:(disp16) */ | |
214 | { | |
215 | GET_VAL( &base, WORD ); | |
d8fab2e6 | 216 | seg = context->SegDs; |
b7258bef AJ |
217 | } |
218 | break; | |
219 | ||
220 | case 1: /* 8-bit disp */ | |
221 | GET_VAL( &off, BYTE ); | |
222 | base += (signed char)off; | |
223 | break; | |
224 | ||
225 | case 2: /* 16-bit disp */ | |
226 | GET_VAL( &off, WORD ); | |
227 | base += (signed short)off; | |
228 | break; | |
229 | } | |
d7d4fdf8 | 230 | base &= 0xffff; |
b7258bef AJ |
231 | } |
232 | if (segprefix != -1) seg = segprefix; | |
233 | ||
77b9918e | 234 | /* Make sure the segment and offset are valid */ |
46ea8b3f | 235 | if (IS_SELECTOR_SYSTEM(seg)) return (BYTE *)(base + (index << ss)); |
77b9918e | 236 | if (((seg & 7) != 7) || IS_SELECTOR_FREE(seg)) return NULL; |
914406f8 | 237 | if (wine_ldt_copy.limit[seg >> 3] < (base + (index << ss))) return NULL; |
982a223f | 238 | return MapSL( MAKESEGPTR( seg, (base + (index << ss))) ); |
9ea19e54 | 239 | #undef GET_VAL |
b7258bef AJ |
240 | } |
241 | ||
242 | ||
243 | /*********************************************************************** | |
244 | * INSTR_EmulateLDS | |
245 | * | |
246 | * Emulate the LDS (and LES,LFS,etc.) instruction. | |
247 | */ | |
b3756782 AJ |
248 | static BOOL INSTR_EmulateLDS( CONTEXT86 *context, BYTE *instr, int long_op, |
249 | int long_addr, int segprefix, int *len ) | |
b7258bef | 250 | { |
3051b644 | 251 | WORD seg; |
b7258bef AJ |
252 | BYTE *regmodrm = instr + 1 + (*instr == 0x0f); |
253 | BYTE *addr = INSTR_GetOperandAddr( context, regmodrm, | |
254 | long_addr, segprefix, len ); | |
3051b644 AJ |
255 | if (!addr) |
256 | return FALSE; /* Unable to emulate it */ | |
257 | seg = *(WORD *)(addr + (long_op ? 4 : 2)); | |
b7258bef | 258 | |
6da20b9d | 259 | if (!INSTR_ReplaceSelector( context, &seg )) |
b7258bef AJ |
260 | return FALSE; /* Unable to emulate it */ |
261 | ||
262 | /* Now store the offset in the correct register */ | |
263 | ||
264 | switch((*regmodrm >> 3) & 7) | |
265 | { | |
266 | case 0: | |
d8fab2e6 AJ |
267 | if (long_op) context->Eax = *(DWORD *)addr; |
268 | else SET_LOWORD(context->Eax,*(WORD *)addr); | |
b7258bef AJ |
269 | break; |
270 | case 1: | |
d8fab2e6 AJ |
271 | if (long_op) context->Ecx = *(DWORD *)addr; |
272 | else SET_LOWORD(context->Ecx,*(WORD *)addr); | |
b7258bef AJ |
273 | break; |
274 | case 2: | |
d8fab2e6 AJ |
275 | if (long_op) context->Edx = *(DWORD *)addr; |
276 | else SET_LOWORD(context->Edx,*(WORD *)addr); | |
b7258bef AJ |
277 | break; |
278 | case 3: | |
d8fab2e6 AJ |
279 | if (long_op) context->Ebx = *(DWORD *)addr; |
280 | else SET_LOWORD(context->Ebx,*(WORD *)addr); | |
b7258bef AJ |
281 | break; |
282 | case 4: | |
d8fab2e6 AJ |
283 | if (long_op) context->Esp = *(DWORD *)addr; |
284 | else SET_LOWORD(context->Esp,*(WORD *)addr); | |
b7258bef AJ |
285 | break; |
286 | case 5: | |
d8fab2e6 AJ |
287 | if (long_op) context->Ebp = *(DWORD *)addr; |
288 | else SET_LOWORD(context->Ebp,*(WORD *)addr); | |
b7258bef AJ |
289 | break; |
290 | case 6: | |
d8fab2e6 AJ |
291 | if (long_op) context->Esi = *(DWORD *)addr; |
292 | else SET_LOWORD(context->Esi,*(WORD *)addr); | |
b7258bef AJ |
293 | break; |
294 | case 7: | |
d8fab2e6 AJ |
295 | if (long_op) context->Edi = *(DWORD *)addr; |
296 | else SET_LOWORD(context->Edi,*(WORD *)addr); | |
b7258bef AJ |
297 | break; |
298 | } | |
299 | ||
300 | /* Store the correct segment in the segment register */ | |
301 | ||
302 | switch(*instr) | |
303 | { | |
d8fab2e6 AJ |
304 | case 0xc4: context->SegEs = seg; break; /* les */ |
305 | case 0xc5: context->SegDs = seg; break; /* lds */ | |
b7258bef AJ |
306 | case 0x0f: switch(instr[1]) |
307 | { | |
d8fab2e6 AJ |
308 | case 0xb2: context->SegSs = seg; break; /* lss */ |
309 | case 0xb4: context->SegFs = seg; break; /* lfs */ | |
310 | case 0xb5: context->SegGs = seg; break; /* lgs */ | |
b7258bef AJ |
311 | } |
312 | break; | |
313 | } | |
314 | ||
315 | /* Add the opcode size to the total length */ | |
316 | ||
317 | *len += 1 + (*instr == 0x0f); | |
318 | return TRUE; | |
319 | } | |
320 | ||
b3756782 AJ |
321 | /*********************************************************************** |
322 | * INSTR_inport | |
323 | * | |
324 | * input on a I/O port | |
325 | */ | |
326 | static DWORD INSTR_inport( WORD port, int size, CONTEXT86 *context ) | |
327 | { | |
328 | DWORD res = IO_inport( port, size ); | |
329 | if (TRACE_ON(io)) | |
330 | { | |
331 | switch(size) | |
332 | { | |
333 | case 1: | |
334 | DPRINTF( "0x%x < %02x @ %04x:%04x\n", port, LOBYTE(res), | |
d8fab2e6 | 335 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
336 | break; |
337 | case 2: | |
338 | DPRINTF( "0x%x < %04x @ %04x:%04x\n", port, LOWORD(res), | |
d8fab2e6 | 339 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
340 | break; |
341 | case 4: | |
342 | DPRINTF( "0x%x < %08lx @ %04x:%04x\n", port, res, | |
d8fab2e6 | 343 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
344 | break; |
345 | } | |
346 | } | |
347 | return res; | |
348 | } | |
349 | ||
350 | ||
351 | /*********************************************************************** | |
352 | * INSTR_outport | |
353 | * | |
354 | * output on a I/O port | |
355 | */ | |
356 | static void INSTR_outport( WORD port, int size, DWORD val, CONTEXT86 *context ) | |
357 | { | |
358 | IO_outport( port, size, val ); | |
359 | if (TRACE_ON(io)) | |
360 | { | |
361 | switch(size) | |
362 | { | |
363 | case 1: | |
364 | DPRINTF("0x%x > %02x @ %04x:%04x\n", port, LOBYTE(val), | |
d8fab2e6 | 365 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
366 | break; |
367 | case 2: | |
368 | DPRINTF("0x%x > %04x @ %04x:%04x\n", port, LOWORD(val), | |
d8fab2e6 | 369 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
370 | break; |
371 | case 4: | |
372 | DPRINTF("0x%x > %08lx @ %04x:%04x\n", port, val, | |
d8fab2e6 | 373 | (WORD)context->SegCs, LOWORD(context->Eip)); |
b3756782 AJ |
374 | break; |
375 | } | |
376 | } | |
377 | } | |
378 | ||
b7258bef | 379 | |
bd34d4ff AJ |
380 | /*********************************************************************** |
381 | * INSTR_EmulateInstruction | |
382 | * | |
383 | * Emulate a priviledged instruction. Returns TRUE if emulation successful. | |
384 | */ | |
b3756782 | 385 | BOOL INSTR_EmulateInstruction( CONTEXT86 *context ) |
bd34d4ff | 386 | { |
b7258bef | 387 | int prefix, segprefix, prefixlen, len, repX, long_op, long_addr; |
d30dfd24 | 388 | SEGPTR gpHandler; |
e2991ea7 AJ |
389 | BYTE *instr; |
390 | ||
9edc7d05 AJ |
391 | long_op = long_addr = (!ISV86(context) && IS_SELECTOR_32BIT(context->SegCs)); |
392 | instr = make_ptr( context, context->SegCs, context->Eip, TRUE ); | |
a845b88e | 393 | if (!instr) return FALSE; |
bd34d4ff AJ |
394 | |
395 | /* First handle any possible prefix */ | |
396 | ||
e2991ea7 | 397 | segprefix = -1; /* no prefix */ |
bd34d4ff | 398 | prefix = 1; |
e2991ea7 | 399 | repX = 0; |
b7258bef | 400 | prefixlen = 0; |
bd34d4ff AJ |
401 | while(prefix) |
402 | { | |
403 | switch(*instr) | |
404 | { | |
405 | case 0x2e: | |
d8fab2e6 | 406 | segprefix = context->SegCs; |
bd34d4ff AJ |
407 | break; |
408 | case 0x36: | |
d8fab2e6 | 409 | segprefix = context->SegSs; |
bd34d4ff AJ |
410 | break; |
411 | case 0x3e: | |
d8fab2e6 | 412 | segprefix = context->SegDs; |
bd34d4ff AJ |
413 | break; |
414 | case 0x26: | |
d8fab2e6 | 415 | segprefix = context->SegEs; |
bd34d4ff AJ |
416 | break; |
417 | case 0x64: | |
d8fab2e6 | 418 | segprefix = context->SegFs; |
bd34d4ff AJ |
419 | break; |
420 | case 0x65: | |
d8fab2e6 | 421 | segprefix = context->SegGs; |
bd34d4ff AJ |
422 | break; |
423 | case 0x66: | |
424 | long_op = !long_op; /* opcode size prefix */ | |
425 | break; | |
426 | case 0x67: | |
427 | long_addr = !long_addr; /* addr size prefix */ | |
428 | break; | |
ded3038c | 429 | case 0xf0: /* lock */ |
e2991ea7 | 430 | break; |
ded3038c | 431 | case 0xf2: /* repne */ |
e2991ea7 AJ |
432 | repX = 1; |
433 | break; | |
ded3038c | 434 | case 0xf3: /* repe */ |
e2991ea7 | 435 | repX = 2; |
ded3038c | 436 | break; |
bd34d4ff AJ |
437 | default: |
438 | prefix = 0; /* no more prefixes */ | |
439 | break; | |
440 | } | |
441 | if (prefix) | |
442 | { | |
443 | instr++; | |
b7258bef | 444 | prefixlen++; |
bd34d4ff AJ |
445 | } |
446 | } | |
447 | ||
448 | /* Now look at the actual instruction */ | |
449 | ||
450 | switch(*instr) | |
451 | { | |
b7258bef AJ |
452 | case 0x07: /* pop es */ |
453 | case 0x17: /* pop ss */ | |
454 | case 0x1f: /* pop ds */ | |
bd34d4ff | 455 | { |
9edc7d05 | 456 | WORD seg = *(WORD *)get_stack( context ); |
6da20b9d AJ |
457 | if (INSTR_ReplaceSelector( context, &seg )) |
458 | { | |
459 | switch(*instr) | |
b7258bef | 460 | { |
d8fab2e6 AJ |
461 | case 0x07: context->SegEs = seg; break; |
462 | case 0x17: context->SegSs = seg; break; | |
463 | case 0x1f: context->SegDs = seg; break; | |
b7258bef | 464 | } |
9edc7d05 | 465 | add_stack(context, long_op ? 4 : 2); |
d8fab2e6 | 466 | context->Eip += prefixlen + 1; |
b7258bef AJ |
467 | return TRUE; |
468 | } | |
e2991ea7 | 469 | } |
b7258bef | 470 | break; /* Unable to emulate it */ |
bd34d4ff | 471 | |
b7258bef AJ |
472 | case 0x0f: /* extended instruction */ |
473 | switch(instr[1]) | |
bd34d4ff | 474 | { |
9d9e0606 MM |
475 | case 0x22: /* mov eax, crX */ |
476 | switch (instr[2]) { | |
477 | case 0xc0: | |
d3e22d9d | 478 | ERR("mov eax,cr0 at 0x%08lx, EAX=0x%08lx\n", |
d8fab2e6 AJ |
479 | context->Eip,context->Eax ); |
480 | context->Eip += prefixlen+3; | |
9d9e0606 MM |
481 | return TRUE; |
482 | default: | |
483 | break; /*fallthrough to bad instruction handling */ | |
484 | } | |
485 | break; /*fallthrough to bad instruction handling */ | |
486 | case 0x20: /* mov crX, eax */ | |
487 | switch (instr[2]) { | |
488 | case 0xe0: /* mov cr4, eax */ | |
489 | /* CR4 register . See linux/arch/i386/mm/init.c, X86_CR4_ defs | |
490 | * bit 0: VME Virtual Mode Exception ? | |
491 | * bit 1: PVI Protected mode Virtual Interrupt | |
492 | * bit 2: TSD Timestamp disable | |
493 | * bit 3: DE Debugging extensions | |
494 | * bit 4: PSE Page size extensions | |
495 | * bit 5: PAE Physical address extension | |
496 | * bit 6: MCE Machine check enable | |
497 | * bit 7: PGE Enable global pages | |
498 | * bit 8: PCE Enable performance counters at IPL3 | |
499 | */ | |
d8fab2e6 AJ |
500 | ERR("mov cr4,eax at 0x%08lx\n",context->Eip); |
501 | context->Eax = 0; | |
502 | context->Eip += prefixlen+3; | |
9d9e0606 MM |
503 | return TRUE; |
504 | case 0xc0: /* mov cr0, eax */ | |
d8fab2e6 AJ |
505 | ERR("mov cr0,eax at 0x%08lx\n",context->Eip); |
506 | context->Eax = 0x10; /* FIXME: set more bits ? */ | |
507 | context->Eip += prefixlen+3; | |
9d9e0606 MM |
508 | return TRUE; |
509 | default: /* fallthrough to illegal instruction */ | |
510 | break; | |
511 | } | |
512 | /* fallthrough to illegal instruction */ | |
513 | break; | |
b7258bef | 514 | case 0xa1: /* pop fs */ |
af0bae58 | 515 | { |
9edc7d05 | 516 | WORD seg = *(WORD *)get_stack( context ); |
6da20b9d | 517 | if (INSTR_ReplaceSelector( context, &seg )) |
af0bae58 | 518 | { |
d8fab2e6 | 519 | context->SegFs = seg; |
9edc7d05 | 520 | add_stack(context, long_op ? 4 : 2); |
d8fab2e6 | 521 | context->Eip += prefixlen + 2; |
af0bae58 AJ |
522 | return TRUE; |
523 | } | |
524 | } | |
525 | break; | |
b7258bef AJ |
526 | case 0xa9: /* pop gs */ |
527 | { | |
9edc7d05 | 528 | WORD seg = *(WORD *)get_stack( context ); |
6da20b9d | 529 | if (INSTR_ReplaceSelector( context, &seg )) |
b7258bef | 530 | { |
d8fab2e6 | 531 | context->SegGs = seg; |
9edc7d05 | 532 | add_stack(context, long_op ? 4 : 2); |
d8fab2e6 | 533 | context->Eip += prefixlen + 2; |
b7258bef AJ |
534 | return TRUE; |
535 | } | |
536 | } | |
537 | break; | |
b7258bef AJ |
538 | case 0xb2: /* lss addr,reg */ |
539 | case 0xb4: /* lfs addr,reg */ | |
540 | case 0xb5: /* lgs addr,reg */ | |
541 | if (INSTR_EmulateLDS( context, instr, long_op, | |
542 | long_addr, segprefix, &len )) | |
543 | { | |
d8fab2e6 | 544 | context->Eip += prefixlen + len; |
b7258bef AJ |
545 | return TRUE; |
546 | } | |
547 | break; | |
548 | } | |
549 | break; /* Unable to emulate it */ | |
bd34d4ff | 550 | |
e2991ea7 AJ |
551 | case 0x6c: /* insb */ |
552 | case 0x6d: /* insw/d */ | |
553 | case 0x6e: /* outsb */ | |
554 | case 0x6f: /* outsw/d */ | |
555 | { | |
556 | int typ = *instr; /* Just in case it's overwritten. */ | |
557 | int outp = (typ >= 0x6e); | |
808cb043 | 558 | unsigned long count = repX ? |
d8fab2e6 | 559 | (long_addr ? context->Ecx : LOWORD(context->Ecx)) : 1; |
e2991ea7 | 560 | int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1; |
d8fab2e6 AJ |
561 | int step = (context->EFlags & 0x400) ? -opsize : +opsize; |
562 | int seg = outp ? context->SegDs : context->SegEs; /* FIXME: is this right? */ | |
e2991ea7 AJ |
563 | |
564 | if (outp) | |
565 | /* FIXME: Check segment readable. */ | |
902da699 | 566 | (void)0; |
e2991ea7 AJ |
567 | else |
568 | /* FIXME: Check segment writeable. */ | |
902da699 | 569 | (void)0; |
e2991ea7 AJ |
570 | |
571 | if (repX) | |
5c3e457b | 572 | { |
d8fab2e6 AJ |
573 | if (long_addr) context->Ecx = 0; |
574 | else SET_LOWORD(context->Ecx,0); | |
5c3e457b | 575 | } |
e2991ea7 AJ |
576 | |
577 | while (count-- > 0) | |
578 | { | |
579 | void *data; | |
d8fab2e6 | 580 | WORD dx = LOWORD(context->Edx); |
e2991ea7 | 581 | if (outp) |
808cb043 | 582 | { |
d8fab2e6 AJ |
583 | data = make_ptr( context, seg, context->Esi, long_addr ); |
584 | if (long_addr) context->Esi += step; | |
585 | else ADD_LOWORD(context->Esi,step); | |
808cb043 | 586 | } |
e2991ea7 | 587 | else |
808cb043 | 588 | { |
d8fab2e6 AJ |
589 | data = make_ptr( context, seg, context->Edi, long_addr ); |
590 | if (long_addr) context->Edi += step; | |
591 | else ADD_LOWORD(context->Edi,step); | |
808cb043 AJ |
592 | } |
593 | ||
e2991ea7 | 594 | switch (typ) |
808cb043 | 595 | { |
e2991ea7 | 596 | case 0x6c: |
b3756782 | 597 | *(BYTE *)data = INSTR_inport( dx, 1, context ); |
e2991ea7 AJ |
598 | break; |
599 | case 0x6d: | |
600 | if (long_op) | |
b3756782 | 601 | *(DWORD *)data = INSTR_inport( dx, 4, context ); |
e2991ea7 | 602 | else |
b3756782 | 603 | *(WORD *)data = INSTR_inport( dx, 2, context ); |
e2991ea7 AJ |
604 | break; |
605 | case 0x6e: | |
b3756782 | 606 | INSTR_outport( dx, 1, *(BYTE *)data, context ); |
9ea19e54 | 607 | break; |
e2991ea7 | 608 | case 0x6f: |
9ea19e54 | 609 | if (long_op) |
b3756782 | 610 | INSTR_outport( dx, 4, *(DWORD *)data, context ); |
9ea19e54 | 611 | else |
b3756782 | 612 | INSTR_outport( dx, 2, *(WORD *)data, context ); |
9ea19e54 | 613 | break; |
e2991ea7 AJ |
614 | } |
615 | } | |
d8fab2e6 | 616 | context->Eip += prefixlen + 1; |
e2991ea7 | 617 | } |
b7258bef AJ |
618 | return TRUE; |
619 | ||
af0bae58 | 620 | case 0x8e: /* mov XX,segment_reg */ |
b7258bef | 621 | { |
3051b644 AJ |
622 | WORD seg; |
623 | BYTE *addr = INSTR_GetOperandAddr(context, instr + 1, | |
b7258bef | 624 | long_addr, segprefix, &len ); |
3051b644 AJ |
625 | if (!addr) |
626 | break; /* Unable to emulate it */ | |
627 | seg = *(WORD *)addr; | |
6da20b9d | 628 | if (!INSTR_ReplaceSelector( context, &seg )) |
b7258bef AJ |
629 | break; /* Unable to emulate it */ |
630 | ||
631 | switch((instr[1] >> 3) & 7) | |
632 | { | |
633 | case 0: | |
d8fab2e6 AJ |
634 | context->SegEs = seg; |
635 | context->Eip += prefixlen + len + 1; | |
b7258bef AJ |
636 | return TRUE; |
637 | case 1: /* cs */ | |
638 | break; | |
639 | case 2: | |
d8fab2e6 AJ |
640 | context->SegSs = seg; |
641 | context->Eip += prefixlen + len + 1; | |
b7258bef AJ |
642 | return TRUE; |
643 | case 3: | |
d8fab2e6 AJ |
644 | context->SegDs = seg; |
645 | context->Eip += prefixlen + len + 1; | |
b7258bef | 646 | return TRUE; |
9597e209 | 647 | case 4: |
d8fab2e6 AJ |
648 | context->SegFs = seg; |
649 | context->Eip += prefixlen + len + 1; | |
b7258bef | 650 | return TRUE; |
9597e209 | 651 | case 5: |
d8fab2e6 AJ |
652 | context->SegGs = seg; |
653 | context->Eip += prefixlen + len + 1; | |
b7258bef AJ |
654 | return TRUE; |
655 | case 6: /* unused */ | |
656 | case 7: /* unused */ | |
657 | break; | |
658 | } | |
659 | } | |
660 | break; /* Unable to emulate it */ | |
661 | ||
662 | case 0xc4: /* les addr,reg */ | |
663 | case 0xc5: /* lds addr,reg */ | |
664 | if (INSTR_EmulateLDS( context, instr, long_op, | |
665 | long_addr, segprefix, &len )) | |
666 | { | |
d8fab2e6 | 667 | context->Eip += prefixlen + len; |
b7258bef AJ |
668 | return TRUE; |
669 | } | |
670 | break; /* Unable to emulate it */ | |
671 | ||
672 | case 0xcd: /* int <XX> */ | |
673 | if (long_op) | |
674 | { | |
dd03cc19 | 675 | ERR("int xx from 32-bit code is not supported.\n"); |
b7258bef AJ |
676 | break; /* Unable to emulate it */ |
677 | } | |
678 | else | |
679 | { | |
d30dfd24 | 680 | FARPROC16 addr = INT_GetPMHandler( instr[1] ); |
9edc7d05 | 681 | WORD *stack = get_stack( context ); |
4324b477 AJ |
682 | if (!addr) |
683 | { | |
684 | FIXME("no handler for interrupt %02x, ignoring it\n", instr[1]); | |
d8fab2e6 | 685 | context->Eip += prefixlen + 2; |
4324b477 AJ |
686 | return TRUE; |
687 | } | |
b7258bef | 688 | /* Push the flags and return address on the stack */ |
d8fab2e6 AJ |
689 | *(--stack) = LOWORD(context->EFlags); |
690 | *(--stack) = context->SegCs; | |
691 | *(--stack) = LOWORD(context->Eip) + prefixlen + 2; | |
9edc7d05 | 692 | add_stack(context, -3 * sizeof(WORD)); |
b7258bef | 693 | /* Jump to the interrupt handler */ |
d8fab2e6 AJ |
694 | context->SegCs = HIWORD(addr); |
695 | context->Eip = LOWORD(addr); | |
b7258bef AJ |
696 | } |
697 | return TRUE; | |
e2991ea7 | 698 | |
b7258bef AJ |
699 | case 0xcf: /* iret */ |
700 | if (long_op) | |
701 | { | |
9edc7d05 | 702 | DWORD *stack = get_stack( context ); |
d8fab2e6 AJ |
703 | context->Eip = *stack++; |
704 | context->SegCs = *stack++; | |
705 | context->EFlags = *stack; | |
9edc7d05 | 706 | add_stack(context, 3*sizeof(DWORD)); /* Pop the return address and flags */ |
b7258bef AJ |
707 | } |
708 | else | |
709 | { | |
9edc7d05 | 710 | WORD *stack = get_stack( context ); |
d8fab2e6 AJ |
711 | context->Eip = *stack++; |
712 | context->SegCs = *stack++; | |
713 | SET_LOWORD(context->EFlags,*stack); | |
9edc7d05 | 714 | add_stack(context, 3*sizeof(WORD)); /* Pop the return address and flags */ |
b7258bef AJ |
715 | } |
716 | return TRUE; | |
717 | ||
718 | case 0xe4: /* inb al,XX */ | |
d8fab2e6 AJ |
719 | SET_LOBYTE(context->Eax,INSTR_inport( instr[1], 1, context )); |
720 | context->Eip += prefixlen + 2; | |
b7258bef AJ |
721 | return TRUE; |
722 | ||
723 | case 0xe5: /* in (e)ax,XX */ | |
bdc48bd1 | 724 | if (long_op) |
d8fab2e6 | 725 | context->Eax = INSTR_inport( instr[1], 4, context ); |
bdc48bd1 | 726 | else |
d8fab2e6 AJ |
727 | SET_LOWORD(context->Eax, INSTR_inport( instr[1], 2, context )); |
728 | context->Eip += prefixlen + 2; | |
b7258bef AJ |
729 | return TRUE; |
730 | ||
731 | case 0xe6: /* outb XX,al */ | |
d8fab2e6 AJ |
732 | INSTR_outport( instr[1], 1, LOBYTE(context->Eax), context ); |
733 | context->Eip += prefixlen + 2; | |
b7258bef AJ |
734 | return TRUE; |
735 | ||
736 | case 0xe7: /* out XX,(e)ax */ | |
bdc48bd1 | 737 | if (long_op) |
d8fab2e6 | 738 | INSTR_outport( instr[1], 4, context->Eax, context ); |
bdc48bd1 | 739 | else |
d8fab2e6 AJ |
740 | INSTR_outport( instr[1], 2, LOWORD(context->Eax), context ); |
741 | context->Eip += prefixlen + 2; | |
b7258bef AJ |
742 | return TRUE; |
743 | ||
744 | case 0xec: /* inb al,dx */ | |
d8fab2e6 AJ |
745 | SET_LOBYTE(context->Eax, INSTR_inport( LOWORD(context->Edx), 1, context ) ); |
746 | context->Eip += prefixlen + 1; | |
b7258bef AJ |
747 | return TRUE; |
748 | ||
749 | case 0xed: /* in (e)ax,dx */ | |
bdc48bd1 | 750 | if (long_op) |
d8fab2e6 | 751 | context->Eax = INSTR_inport( LOWORD(context->Edx), 4, context ); |
bdc48bd1 | 752 | else |
d8fab2e6 AJ |
753 | SET_LOWORD(context->Eax, INSTR_inport( LOWORD(context->Edx), 2, context )); |
754 | context->Eip += prefixlen + 1; | |
b7258bef AJ |
755 | return TRUE; |
756 | ||
757 | case 0xee: /* outb dx,al */ | |
d8fab2e6 AJ |
758 | INSTR_outport( LOWORD(context->Edx), 1, LOBYTE(context->Eax), context ); |
759 | context->Eip += prefixlen + 1; | |
b7258bef | 760 | return TRUE; |
d8fab2e6 | 761 | |
b7258bef | 762 | case 0xef: /* out dx,(e)ax */ |
bdc48bd1 | 763 | if (long_op) |
d8fab2e6 | 764 | INSTR_outport( LOWORD(context->Edx), 4, context->Eax, context ); |
bdc48bd1 | 765 | else |
d8fab2e6 AJ |
766 | INSTR_outport( LOWORD(context->Edx), 2, LOWORD(context->Eax), context ); |
767 | context->Eip += prefixlen + 1; | |
b7258bef AJ |
768 | return TRUE; |
769 | ||
770 | case 0xfa: /* cli, ignored */ | |
d8fab2e6 | 771 | context->Eip += prefixlen + 1; |
b7258bef AJ |
772 | return TRUE; |
773 | ||
774 | case 0xfb: /* sti, ignored */ | |
d8fab2e6 | 775 | context->Eip += prefixlen + 1; |
b7258bef | 776 | return TRUE; |
bd34d4ff | 777 | } |
d30dfd24 AJ |
778 | |
779 | ||
780 | /* Check for Win16 __GP handler */ | |
982a223f | 781 | gpHandler = HasGPHandler16( MAKESEGPTR( context->SegCs, context->Eip ) ); |
d30dfd24 AJ |
782 | if (gpHandler) |
783 | { | |
9edc7d05 | 784 | WORD *stack = get_stack( context ); |
d8fab2e6 AJ |
785 | *--stack = context->SegCs; |
786 | *--stack = context->Eip; | |
9edc7d05 | 787 | add_stack(context, -2*sizeof(WORD)); |
d30dfd24 | 788 | |
d8fab2e6 AJ |
789 | context->SegCs = SELECTOROF( gpHandler ); |
790 | context->Eip = OFFSETOF( gpHandler ); | |
d30dfd24 AJ |
791 | return TRUE; |
792 | } | |
b7258bef | 793 | return FALSE; /* Unable to emulate it */ |
bd34d4ff | 794 | } |
9597e209 AJ |
795 | |
796 | #endif /* __i386__ */ |