Release 980301
[wine.git] / miscemu / instr.c
blob46cd421bc0a3a667b547a1714139a1e54a1f0509
1 /*
2 * Emulation of priviledged instructions
4 * Copyright 1995 Alexandre Julliard
5 */
7 #include <stdio.h>
8 #include "windows.h"
9 #include "ldt.h"
10 #include "miscemu.h"
11 #include "sig_context.h"
14 #define STACK_sig(context) \
15 ((GET_SEL_FLAGS(SS_sig(context)) & LDT_FLAGS_32BIT) ? \
16 ESP_sig(context) : SP_sig(context))
18 #define STACK_PTR(context) \
19 (PTR_SEG_OFF_TO_LIN(SS_sig(context),STACK_sig(context)))
21 /***********************************************************************
22 * INSTR_ReplaceSelector
24 * Try to replace an invalid selector by a valid one.
25 * The only selector where it is allowed to do "mov ax,40;mov es,ax"
26 * is the so called 'bimodal' selector 0x40, which points to the BIOS
27 * data segment. Used by (at least) Borland products (and programs compiled
28 * using Borland products).
30 * See Undocumented Windows, Chapter 5, __0040.
32 static WORD INSTR_ReplaceSelector( SIGCONTEXT *context, WORD sel)
34 if (sel == 0x40)
36 static WORD sys_timer = 0;
37 if (!sys_timer)
38 sys_timer = CreateSystemTimer( 55, (FARPROC16)DOSMEM_Tick );
39 return DOSMEM_BiosSeg;
41 return 0; /* Can't replace selector, crashdump */
45 /***********************************************************************
46 * INSTR_GetOperandAddr
48 * Return the address of an instruction operand (from the mod/rm byte).
50 static BYTE *INSTR_GetOperandAddr( SIGCONTEXT *context, BYTE *instr,
51 int long_addr, int segprefix, int *len )
53 int mod, rm, base, index = 0, ss = 0, seg = 0, off;
55 #define GET_VAL(val,type) \
56 { *val = *(type *)instr; instr += sizeof(type); *len += sizeof(type); }
58 *len = 0;
59 GET_VAL( &mod, BYTE );
60 rm = mod & 7;
61 mod >>= 6;
63 if (mod == 3)
65 switch(rm)
67 case 0: return (BYTE *)&EAX_sig(context);
68 case 1: return (BYTE *)&ECX_sig(context);
69 case 2: return (BYTE *)&EDX_sig(context);
70 case 3: return (BYTE *)&EBX_sig(context);
71 case 4: return (BYTE *)&ESP_sig(context);
72 case 5: return (BYTE *)&EBP_sig(context);
73 case 6: return (BYTE *)&ESI_sig(context);
74 case 7: return (BYTE *)&EDI_sig(context);
78 if (long_addr)
80 if (rm == 4)
82 BYTE sib;
83 GET_VAL( &sib, BYTE );
84 rm = sib & 7;
85 ss = sib >> 6;
86 switch(sib >> 3)
88 case 0: index = EAX_sig(context); break;
89 case 1: index = ECX_sig(context); break;
90 case 2: index = EDX_sig(context); break;
91 case 3: index = EBX_sig(context); break;
92 case 4: index = 0; break;
93 case 5: index = EBP_sig(context); break;
94 case 6: index = ESI_sig(context); break;
95 case 7: index = EDI_sig(context); break;
99 switch(rm)
101 case 0: base = EAX_sig(context); seg = DS_sig(context); break;
102 case 1: base = ECX_sig(context); seg = DS_sig(context); break;
103 case 2: base = EDX_sig(context); seg = DS_sig(context); break;
104 case 3: base = EBX_sig(context); seg = DS_sig(context); break;
105 case 4: base = ESP_sig(context); seg = SS_sig(context); break;
106 case 5: base = EBP_sig(context); seg = SS_sig(context); break;
107 case 6: base = ESI_sig(context); seg = DS_sig(context); break;
108 case 7: base = EDI_sig(context); seg = DS_sig(context); break;
110 switch (mod)
112 case 0:
113 if (rm == 5) /* special case: ds:(disp32) */
115 GET_VAL( &base, DWORD );
116 seg = DS_sig(context);
118 break;
120 case 1: /* 8-bit disp */
121 GET_VAL( &off, BYTE );
122 base += (signed char)off;
123 break;
125 case 2: /* 32-bit disp */
126 GET_VAL( &off, DWORD );
127 base += (signed long)off;
128 break;
131 else /* short address */
133 switch(rm)
135 case 0: /* ds:(bx,si) */
136 base = BX_sig(context) + SI_sig(context);
137 seg = DS_sig(context);
138 break;
139 case 1: /* ds:(bx,di) */
140 base = BX_sig(context) + DI_sig(context);
141 seg = DS_sig(context);
142 break;
143 case 2: /* ss:(bp,si) */
144 base = BP_sig(context) + SI_sig(context);
145 seg = SS_sig(context);
146 break;
147 case 3: /* ss:(bp,di) */
148 base = BP_sig(context) + DI_sig(context);
149 seg = SS_sig(context);
150 break;
151 case 4: /* ds:(si) */
152 base = SI_sig(context);
153 seg = DS_sig(context);
154 break;
155 case 5: /* ds:(di) */
156 base = DI_sig(context);
157 seg = DS_sig(context);
158 break;
159 case 6: /* ss:(bp) */
160 base = BP_sig(context);
161 seg = SS_sig(context);
162 break;
163 case 7: /* ds:(bx) */
164 base = BX_sig(context);
165 seg = DS_sig(context);
166 break;
169 switch(mod)
171 case 0:
172 if (rm == 6) /* special case: ds:(disp16) */
174 GET_VAL( &base, WORD );
175 seg = DS_sig(context);
177 break;
179 case 1: /* 8-bit disp */
180 GET_VAL( &off, BYTE );
181 base += (signed char)off;
182 break;
184 case 2: /* 16-bit disp */
185 GET_VAL( &off, WORD );
186 base += (signed short)off;
187 break;
189 base &= 0xffff;
191 if (segprefix != -1) seg = segprefix;
193 /* Make sure the segment and offset are valid */
194 if (((seg & 7) != 7) || IS_SELECTOR_FREE(seg)) return NULL;
195 if (GET_SEL_LIMIT(seg) < (base + (index << ss))) return NULL;
196 return (BYTE *)PTR_SEG_OFF_TO_LIN( seg, (base + (index << ss)) );
197 #undef GET_VAL
201 /***********************************************************************
202 * INSTR_EmulateLDS
204 * Emulate the LDS (and LES,LFS,etc.) instruction.
206 static BOOL32 INSTR_EmulateLDS( SIGCONTEXT *context, BYTE *instr, int long_op,
207 int long_addr, int segprefix, int *len )
209 WORD seg;
210 BYTE *regmodrm = instr + 1 + (*instr == 0x0f);
211 BYTE *addr = INSTR_GetOperandAddr( context, regmodrm,
212 long_addr, segprefix, len );
213 if (!addr)
214 return FALSE; /* Unable to emulate it */
215 seg = *(WORD *)(addr + (long_op ? 4 : 2));
217 if (!(seg = INSTR_ReplaceSelector( context, seg )))
218 return FALSE; /* Unable to emulate it */
220 /* Now store the offset in the correct register */
222 switch((*regmodrm >> 3) & 7)
224 case 0:
225 if (long_op) EAX_sig(context) = *(DWORD *)addr;
226 else AX_sig(context) = *(WORD *)addr;
227 break;
228 case 1:
229 if (long_op) ECX_sig(context) = *(DWORD *)addr;
230 else CX_sig(context) = *(WORD *)addr;
231 break;
232 case 2:
233 if (long_op) EDX_sig(context) = *(DWORD *)addr;
234 else DX_sig(context) = *(WORD *)addr;
235 break;
236 case 3:
237 if (long_op) EBX_sig(context) = *(DWORD *)addr;
238 else BX_sig(context) = *(WORD *)addr;
239 break;
240 case 4:
241 if (long_op) ESP_sig(context) = *(DWORD *)addr;
242 else SP_sig(context) = *(WORD *)addr;
243 break;
244 case 5:
245 if (long_op) EBP_sig(context) = *(DWORD *)addr;
246 else BP_sig(context) = *(WORD *)addr;
247 break;
248 case 6:
249 if (long_op) ESI_sig(context) = *(DWORD *)addr;
250 else SI_sig(context) = *(WORD *)addr;
251 break;
252 case 7:
253 if (long_op) EDI_sig(context) = *(DWORD *)addr;
254 else DI_sig(context) = *(WORD *)addr;
255 break;
258 /* Store the correct segment in the segment register */
260 switch(*instr)
262 case 0xc4: ES_sig(context) = seg; break; /* les */
263 case 0xc5: DS_sig(context) = seg; break; /* lds */
264 case 0x0f: switch(instr[1])
266 case 0xb2: SS_sig(context) = seg; break; /* lss */
267 #ifdef FS_sig
268 case 0xb4: FS_sig(context) = seg; break; /* lfs */
269 #endif
270 #ifdef GS_sig
271 case 0xb5: GS_sig(context) = seg; break; /* lgs */
272 #endif
274 break;
277 /* Add the opcode size to the total length */
279 *len += 1 + (*instr == 0x0f);
280 return TRUE;
284 /***********************************************************************
285 * INSTR_EmulateInstruction
287 * Emulate a priviledged instruction. Returns TRUE if emulation successful.
289 BOOL32 INSTR_EmulateInstruction( SIGCONTEXT *context )
291 int prefix, segprefix, prefixlen, len, repX, long_op, long_addr;
292 BYTE *instr;
294 long_op = long_addr = (GET_SEL_FLAGS(CS_sig(context)) & LDT_FLAGS_32BIT) != 0;
295 instr = (BYTE *) PTR_SEG_OFF_TO_LIN( CS_sig(context), EIP_sig(context) );
297 /* First handle any possible prefix */
299 segprefix = -1; /* no prefix */
300 prefix = 1;
301 repX = 0;
302 prefixlen = 0;
303 while(prefix)
305 switch(*instr)
307 case 0x2e:
308 segprefix = CS_sig(context);
309 break;
310 case 0x36:
311 segprefix = SS_sig(context);
312 break;
313 case 0x3e:
314 segprefix = DS_sig(context);
315 break;
316 case 0x26:
317 segprefix = ES_sig(context);
318 break;
319 #ifdef FS_sig
320 case 0x64:
321 segprefix = FS_sig(context);
322 break;
323 #endif
324 #ifdef GS_sig
325 case 0x65:
326 segprefix = GS_sig(context);
327 break;
328 #endif
329 case 0x66:
330 long_op = !long_op; /* opcode size prefix */
331 break;
332 case 0x67:
333 long_addr = !long_addr; /* addr size prefix */
334 break;
335 case 0xf0: /* lock */
336 break;
337 case 0xf2: /* repne */
338 repX = 1;
339 break;
340 case 0xf3: /* repe */
341 repX = 2;
342 break;
343 default:
344 prefix = 0; /* no more prefixes */
345 break;
347 if (prefix)
349 instr++;
350 prefixlen++;
354 /* Now look at the actual instruction */
356 switch(*instr)
358 case 0x07: /* pop es */
359 case 0x17: /* pop ss */
360 case 0x1f: /* pop ds */
362 WORD seg = *(WORD *)STACK_PTR( context );
363 if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
365 switch(*instr)
367 case 0x07: ES_sig(context) = seg; break;
368 case 0x17: SS_sig(context) = seg; break;
369 case 0x1f: DS_sig(context) = seg; break;
371 STACK_sig(context) += long_op ? 4 : 2;
372 EIP_sig(context) += prefixlen + 1;
373 return TRUE;
376 break; /* Unable to emulate it */
378 case 0x0f: /* extended instruction */
379 switch(instr[1])
381 #ifdef FS_sig
382 case 0xa1: /* pop fs */
384 WORD seg = *(WORD *)STACK_PTR( context );
385 if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
387 FS_sig(context) = seg;
388 STACK_sig(context) += long_op ? 4 : 2;
389 EIP_sig(context) += prefixlen + 2;
390 return TRUE;
393 break;
394 #endif /* FS_sig */
396 #ifdef GS_sig
397 case 0xa9: /* pop gs */
399 WORD seg = *(WORD *)STACK_PTR( context );
400 if ((seg = INSTR_ReplaceSelector( context, seg )) != 0)
402 GS_sig(context) = seg;
403 STACK_sig(context) += long_op ? 4 : 2;
404 EIP_sig(context) += prefixlen + 2;
405 return TRUE;
408 break;
409 #endif /* GS_sig */
411 case 0xb2: /* lss addr,reg */
412 #ifdef FS_sig
413 case 0xb4: /* lfs addr,reg */
414 #endif
415 #ifdef GS_sig
416 case 0xb5: /* lgs addr,reg */
417 #endif
418 if (INSTR_EmulateLDS( context, instr, long_op,
419 long_addr, segprefix, &len ))
421 EIP_sig(context) += prefixlen + len;
422 return TRUE;
424 break;
426 break; /* Unable to emulate it */
428 case 0x6c: /* insb */
429 case 0x6d: /* insw/d */
430 case 0x6e: /* outsb */
431 case 0x6f: /* outsw/d */
433 int typ = *instr; /* Just in case it's overwritten. */
434 int outp = (typ >= 0x6e);
435 unsigned long count = repX ?
436 (long_addr ? ECX_sig(context) : CX_sig(context)) : 1;
437 int opsize = (typ & 1) ? (long_op ? 4 : 2) : 1;
438 int step = (EFL_sig(context) & 0x400) ? -opsize : +opsize;
439 int seg = outp ? DS_sig(context) : ES_sig(context); /* FIXME: is this right? */
441 if (outp)
442 /* FIXME: Check segment readable. */
443 (void)0;
444 else
445 /* FIXME: Check segment writeable. */
446 (void)0;
448 if (repX)
449 if (long_addr)
450 ECX_sig(context) = 0;
451 else
452 CX_sig(context) = 0;
454 while (count-- > 0)
456 void *data;
457 if (outp)
459 data = PTR_SEG_OFF_TO_LIN (seg,
460 long_addr ? ESI_sig(context) : SI_sig(context));
461 if (long_addr) ESI_sig(context) += step;
462 else SI_sig(context) += step;
464 else
466 data = PTR_SEG_OFF_TO_LIN (seg,
467 long_addr ? EDI_sig(context) : DI_sig(context));
468 if (long_addr) EDI_sig(context) += step;
469 else DI_sig(context) += step;
472 switch (typ)
474 case 0x6c:
475 *((BYTE *)data) = IO_inport( DX_sig(context), 1);
476 break;
477 case 0x6d:
478 if (long_op)
479 *((DWORD *)data) = IO_inport( DX_sig(context), 4);
480 else
481 *((WORD *)data) = IO_inport( DX_sig(context), 2);
482 break;
483 case 0x6e:
484 IO_outport( DX_sig(context), 1, *((BYTE *)data));
485 break;
486 case 0x6f:
487 if (long_op)
488 IO_outport( DX_sig(context), 4, *((DWORD *)data));
489 else
490 IO_outport( DX_sig(context), 2, *((WORD *)data));
491 break;
494 EIP_sig(context) += prefixlen + 1;
496 return TRUE;
498 case 0x8e: /* mov XX,segment_reg */
500 WORD seg;
501 BYTE *addr = INSTR_GetOperandAddr(context, instr + 1,
502 long_addr, segprefix, &len );
503 if (!addr)
504 break; /* Unable to emulate it */
505 seg = *(WORD *)addr;
506 if (!(seg = INSTR_ReplaceSelector( context, seg )))
507 break; /* Unable to emulate it */
509 switch((instr[1] >> 3) & 7)
511 case 0:
512 ES_sig(context) = seg;
513 EIP_sig(context) += prefixlen + len + 1;
514 return TRUE;
515 case 1: /* cs */
516 break;
517 case 2:
518 SS_sig(context) = seg;
519 EIP_sig(context) += prefixlen + len + 1;
520 return TRUE;
521 case 3:
522 DS_sig(context) = seg;
523 EIP_sig(context) += prefixlen + len + 1;
524 return TRUE;
525 case 4:
526 #ifdef FS_sig
527 FS_sig(context) = seg;
528 EIP_sig(context) += prefixlen + len + 1;
529 return TRUE;
530 #endif
531 case 5:
532 #ifdef GS_sig
533 GS_sig(context) = seg;
534 EIP_sig(context) += prefixlen + len + 1;
535 return TRUE;
536 #endif
537 case 6: /* unused */
538 case 7: /* unused */
539 break;
542 break; /* Unable to emulate it */
544 case 0xc4: /* les addr,reg */
545 case 0xc5: /* lds addr,reg */
546 if (INSTR_EmulateLDS( context, instr, long_op,
547 long_addr, segprefix, &len ))
549 EIP_sig(context) += prefixlen + len;
550 return TRUE;
552 break; /* Unable to emulate it */
554 case 0xcd: /* int <XX> */
555 if (long_op)
557 fprintf(stderr, "int xx from 32-bit code is not supported.\n");
558 break; /* Unable to emulate it */
560 else
562 FARPROC16 addr = INT_GetHandler( instr[1] );
563 WORD *stack = (WORD *)STACK_PTR( context );
564 /* Push the flags and return address on the stack */
565 *(--stack) = FL_sig(context);
566 *(--stack) = CS_sig(context);
567 *(--stack) = IP_sig(context) + prefixlen + 2;
568 STACK_sig(context) -= 3 * sizeof(WORD);
569 /* Jump to the interrupt handler */
570 CS_sig(context) = HIWORD(addr);
571 EIP_sig(context) = LOWORD(addr);
573 return TRUE;
575 case 0xcf: /* iret */
576 if (long_op)
578 DWORD *stack = (DWORD *)STACK_PTR( context );
579 EIP_sig(context) = *stack++;
580 CS_sig(context) = *stack++;
581 EFL_sig(context) = *stack;
582 STACK_sig(context) += 3*sizeof(DWORD); /* Pop the return address and flags */
584 else
586 WORD *stack = (WORD *)STACK_PTR( context );
587 EIP_sig(context) = *stack++;
588 CS_sig(context) = *stack++;
589 FL_sig(context) = *stack;
590 STACK_sig(context) += 3*sizeof(WORD); /* Pop the return address and flags */
592 return TRUE;
594 case 0xe4: /* inb al,XX */
595 AL_sig(context) = IO_inport( instr[1], 1 );
596 EIP_sig(context) += prefixlen + 2;
597 return TRUE;
599 case 0xe5: /* in (e)ax,XX */
600 if (long_op) EAX_sig(context) = IO_inport( instr[1], 4 );
601 else AX_sig(context) = IO_inport( instr[1], 2 );
602 EIP_sig(context) += prefixlen + 2;
603 return TRUE;
605 case 0xe6: /* outb XX,al */
606 IO_outport( instr[1], 1, AL_sig(context) );
607 EIP_sig(context) += prefixlen + 2;
608 return TRUE;
610 case 0xe7: /* out XX,(e)ax */
611 if (long_op) IO_outport( instr[1], 4, EAX_sig(context) );
612 else IO_outport( instr[1], 2, AX_sig(context) );
613 EIP_sig(context) += prefixlen + 2;
614 return TRUE;
616 case 0xec: /* inb al,dx */
617 AL_sig(context) = IO_inport( DX_sig(context), 1 );
618 EIP_sig(context) += prefixlen + 1;
619 return TRUE;
621 case 0xed: /* in (e)ax,dx */
622 if (long_op) EAX_sig(context) = IO_inport( DX_sig(context), 4 );
623 else AX_sig(context) = IO_inport( DX_sig(context), 2 );
624 EIP_sig(context) += prefixlen + 1;
625 return TRUE;
627 case 0xee: /* outb dx,al */
628 IO_outport( DX_sig(context), 1, AL_sig(context) );
629 EIP_sig(context) += prefixlen + 1;
630 return TRUE;
632 case 0xef: /* out dx,(e)ax */
633 if (long_op) IO_outport( DX_sig(context), 4, EAX_sig(context) );
634 else IO_outport( DX_sig(context), 2, AX_sig(context) );
635 EIP_sig(context) += prefixlen + 1;
636 return TRUE;
638 case 0xfa: /* cli, ignored */
639 EIP_sig(context) += prefixlen + 1;
640 return TRUE;
642 case 0xfb: /* sti, ignored */
643 EIP_sig(context) += prefixlen + 1;
644 return TRUE;
646 fprintf(stderr, "Unexpected Windows program segfault"
647 " - opcode = %x\n", *instr);
648 return FALSE; /* Unable to emulate it */