/* Operand sizes: 8-bit operands or specified/overridden size. */
#define ByteOp (1<<0) /* 8-bit operands. */
/* Destination operand type. */
-#define DstBitBase (0<<1) /* Memory operand, bit string. */
-#define ImplicitOps (1<<1) /* Implicit in opcode. No generic decode. */
+#define DstNone (0<<1) /* No destination operand. */
+#define DstImplicit (0<<1) /* Destination operand is implicit in the opcode. */
+#define DstBitBase (1<<1) /* Memory operand, bit string. */
#define DstReg (2<<1) /* Register operand. */
#define DstMem (3<<1) /* Memory operand. */
#define DstMask (3<<1)
#define ModRM (1<<6)
/* Destination is only written; never read. */
#define Mov (1<<7)
+/* All operands are implicit in the opcode. */
+#define ImplicitOps (DstImplicit|SrcImplicit)
static uint8_t opcode_table[256] = {
/* 0x00 - 0x07 */
int override_seg = -1, rc = X86EMUL_OKAY;
struct operand src, dst;
- /* Data operand effective address (usually computed from ModRM). */
- struct operand ea;
-
- /* Default is a memory operand relative to segment DS. */
- ea.type = OP_MEM;
- ea.mem.seg = x86_seg_ds;
- ea.mem.off = 0;
+ /*
+ * Data operand effective address (usually computed from ModRM).
+ * Default is a memory operand relative to segment DS.
+ */
+ struct operand ea = { .type = OP_MEM };
+ ea.mem.seg = x86_seg_ds; /* gcc may reject anon union initializer */
ctxt->retire.byte = 0;
if ( override_seg != -1 )
ea.mem.seg = override_seg;
- /* Special instructions do their own operand decoding. */
- if ( (d & DstMask) == ImplicitOps )
- goto special_insn;
-
/* Decode and fetch the source operand: register, memory or immediate. */
switch ( d & SrcMask )
{
- case SrcNone:
+ case SrcNone: /* case SrcImplicit: */
+ src.type = OP_NONE;
break;
case SrcReg:
src.type = OP_REG;
/* Decode and fetch the destination operand: register or memory. */
switch ( d & DstMask )
{
+ case DstNone: /* case DstImplicit: */
+ /*
+ * The only implicit-operands instructions allowed a LOCK prefix are
+ * CMPXCHG{8,16}B, MOV CRn, MOV DRn.
+ */
+ generate_exception_if(
+ lock_prefix &&
+ ((b < 0x20) || (b > 0x23)) && /* MOV CRn/DRn */
+ (b != 0xc7), /* CMPXCHG{8,16}B */
+ EXC_GP, 0);
+ dst.type = OP_NONE;
+ break;
+
case DstReg:
+ generate_exception_if(lock_prefix, EXC_GP, 0);
dst.type = OP_REG;
if ( d & ByteOp )
{
dst = ea;
if ( dst.type == OP_REG )
{
+ generate_exception_if(lock_prefix, EXC_GP, 0);
switch ( dst.bytes )
{
case 1: dst.val = *(uint8_t *)dst.reg; break;
break;
}
- /* LOCK prefix allowed only on instructions with memory destination. */
- generate_exception_if(lock_prefix && (dst.type != OP_MEM), EXC_GP, 0);
-
if ( twobyte )
goto twobyte_insn;
dst.type = OP_NONE;
break;
+ case 0x06: /* push %%es */ {
+ struct segment_register reg;
+ src.val = x86_seg_es;
+ push_seg:
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment(src.val, ®, ctxt)) != 0 )
+ return rc;
+ /* 64-bit mode: PUSH defaults to a 64-bit operand. */
+ if ( mode_64bit() && (op_bytes == 4) )
+ op_bytes = 8;
+ if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ ®.sel, op_bytes, ctxt)) != 0 )
+ goto done;
+ break;
+ }
+
+ case 0x07: /* pop %%es */
+ src.val = x86_seg_es;
+ pop_seg:
+ fail_if(ops->write_segment == NULL);
+ /* 64-bit mode: POP defaults to a 64-bit operand. */
+ if ( mode_64bit() && (op_bytes == 4) )
+ op_bytes = 8;
+ if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
+ &dst.val, op_bytes, ctxt, ops)) != 0 )
+ goto done;
+ if ( (rc = load_seg(src.val, (uint16_t)dst.val, ctxt, ops)) != 0 )
+ return rc;
+ break;
+
+ case 0x0e: /* push %%cs */
+ src.val = x86_seg_cs;
+ goto push_seg;
+
+ case 0x16: /* push %%ss */
+ src.val = x86_seg_ss;
+ goto push_seg;
+
+ case 0x17: /* pop %%ss */
+ src.val = x86_seg_ss;
+ ctxt->retire.flags.mov_ss = 1;
+ goto pop_seg;
+
+ case 0x1e: /* push %%ds */
+ src.val = x86_seg_ds;
+ goto push_seg;
+
+ case 0x1f: /* pop %%ds */
+ src.val = x86_seg_ds;
+ goto pop_seg;
+
+ case 0x27: /* daa */ {
+ uint8_t al = _regs.eax;
+ unsigned long eflags = _regs.eflags;
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ _regs.eflags &= ~(EFLG_CF|EFLG_AF);
+ if ( ((al & 0x0f) > 9) || (eflags & EFLG_AF) )
+ {
+ *(uint8_t *)&_regs.eax += 6;
+ _regs.eflags |= EFLG_AF;
+ }
+ if ( (al > 0x99) || (eflags & EFLG_CF) )
+ {
+ *(uint8_t *)&_regs.eax += 0x60;
+ _regs.eflags |= EFLG_CF;
+ }
+ _regs.eflags &= ~(EFLG_SF|EFLG_ZF|EFLG_PF);
+ _regs.eflags |= ((uint8_t)_regs.eax == 0) ? EFLG_ZF : 0;
+ _regs.eflags |= (( int8_t)_regs.eax < 0) ? EFLG_SF : 0;
+ _regs.eflags |= even_parity(_regs.eax) ? EFLG_PF : 0;
+ break;
+ }
+
+ case 0x2f: /* das */ {
+ uint8_t al = _regs.eax;
+ unsigned long eflags = _regs.eflags;
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ _regs.eflags &= ~(EFLG_CF|EFLG_AF);
+ if ( ((al & 0x0f) > 9) || (eflags & EFLG_AF) )
+ {
+ _regs.eflags |= EFLG_AF;
+ if ( (al < 6) || (eflags & EFLG_CF) )
+ _regs.eflags |= EFLG_CF;
+ *(uint8_t *)&_regs.eax -= 6;
+ }
+ if ( (al > 0x99) || (eflags & EFLG_CF) )
+ {
+ *(uint8_t *)&_regs.eax -= 0x60;
+ _regs.eflags |= EFLG_CF;
+ }
+ _regs.eflags &= ~(EFLG_SF|EFLG_ZF|EFLG_PF);
+ _regs.eflags |= ((uint8_t)_regs.eax == 0) ? EFLG_ZF : 0;
+ _regs.eflags |= (( int8_t)_regs.eax < 0) ? EFLG_SF : 0;
+ _regs.eflags |= even_parity(_regs.eax) ? EFLG_PF : 0;
+ break;
+ }
+
+ case 0x37: /* aaa */
+ case 0x3f: /* aas */
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ _regs.eflags &= ~EFLG_CF;
+ if ( ((uint8_t)_regs.eax > 9) || (_regs.eflags & EFLG_AF) )
+ {
+ ((uint8_t *)&_regs.eax)[0] += (b == 0x37) ? 6 : -6;
+ ((uint8_t *)&_regs.eax)[1] += (b == 0x37) ? 1 : -1;
+ _regs.eflags |= EFLG_CF | EFLG_AF;
+ }
+ ((uint8_t *)&_regs.eax)[0] &= 0x0f;
+ break;
+
+ case 0x40 ... 0x4f: /* inc/dec reg */
+ dst.type = OP_REG;
+ dst.reg = decode_register(b & 7, &_regs, 0);
+ dst.bytes = op_bytes;
+ dst.val = *dst.reg;
+ if ( b & 8 )
+ emulate_1op("dec", dst, _regs.eflags);
+ else
+ emulate_1op("inc", dst, _regs.eflags);
+ break;
+
+ case 0x50 ... 0x57: /* push reg */
+ src.val = *(unsigned long *)decode_register(
+ (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
+ goto push;
+
+ case 0x58 ... 0x5f: /* pop reg */
+ dst.type = OP_REG;
+ dst.reg = decode_register(
+ (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
+ dst.bytes = op_bytes;
+ if ( mode_64bit() && (dst.bytes == 4) )
+ dst.bytes = 8;
+ if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(dst.bytes),
+ &dst.val, dst.bytes, ctxt, ops)) != 0 )
+ goto done;
+ break;
+
+ case 0x60: /* pusha */ {
+ int i;
+ unsigned long regs[] = {
+ _regs.eax, _regs.ecx, _regs.edx, _regs.ebx,
+ _regs.esp, _regs.ebp, _regs.esi, _regs.edi };
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ for ( i = 0; i < 8; i++ )
+ if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ ®s[i], op_bytes, ctxt)) != 0 )
+ goto done;
+ break;
+ }
+
+ case 0x61: /* popa */ {
+ int i;
+ unsigned long dummy_esp, *regs[] = {
+ (unsigned long *)&_regs.edi, (unsigned long *)&_regs.esi,
+ (unsigned long *)&_regs.ebp, (unsigned long *)&dummy_esp,
+ (unsigned long *)&_regs.ebx, (unsigned long *)&_regs.edx,
+ (unsigned long *)&_regs.ecx, (unsigned long *)&_regs.eax };
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ for ( i = 0; i < 8; i++ )
+ {
+ if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
+ &dst.val, op_bytes, ctxt, ops)) != 0 )
+ goto done;
+ switch ( op_bytes )
+ {
+ case 1: *(uint8_t *)regs[i] = (uint8_t)dst.val; break;
+ case 2: *(uint16_t *)regs[i] = (uint16_t)dst.val; break;
+ case 4: *regs[i] = (uint32_t)dst.val; break; /* 64b: zero-ext */
+ case 8: *regs[i] = dst.val; break;
+ }
+ }
+ break;
+ }
+
case 0x62: /* bound */ {
unsigned long src_val2;
int lb, ub, idx;
}
break;
+ case 0x68: /* push imm{16,32,64} */
+ src.val = ((op_bytes == 2)
+ ? (int32_t)insn_fetch_type(int16_t)
+ : insn_fetch_type(int32_t));
+ goto push;
+
case 0x69: /* imul imm16/32 */
case 0x6b: /* imul imm8 */ {
unsigned long src1; /* ModR/M source operand */
break;
}
- case 0x82: /* Grp1 (x86/32 only) */
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- case 0x80: case 0x81: case 0x83: /* Grp1 */
- switch ( modrm_reg & 7 )
- {
- case 0: goto add;
- case 1: goto or;
- case 2: goto adc;
- case 3: goto sbb;
- case 4: goto and;
- case 5: goto sub;
- case 6: goto xor;
- case 7: goto cmp;
- }
- break;
-
- case 0xa8 ... 0xa9: /* test imm,%%eax */
- dst.reg = (unsigned long *)&_regs.eax;
- dst.val = _regs.eax;
- case 0x84 ... 0x85: test: /* test */
- emulate_2op_SrcV("test", src, dst, _regs.eflags);
- dst.type = OP_NONE;
- break;
-
- case 0x86 ... 0x87: xchg: /* xchg */
- /* Write back the register source. */
- switch ( dst.bytes )
- {
- case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break;
- case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break;
- case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */
- case 8: *src.reg = dst.val; break;
- }
- /* Write back the memory destination with implicit LOCK prefix. */
- dst.val = src.val;
- lock_prefix = 1;
- break;
-
- case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */
- generate_exception_if((modrm_reg & 7) != 0, EXC_UD, -1);
- case 0x88 ... 0x8b: /* mov */
+ case 0x6a: /* push imm8 */
+ src.val = insn_fetch_type(int8_t);
+ push:
+ d |= Mov; /* force writeback */
+ dst.type = OP_MEM;
+ dst.bytes = op_bytes;
+ if ( mode_64bit() && (dst.bytes == 4) )
+ dst.bytes = 8;
dst.val = src.val;
+ dst.mem.seg = x86_seg_ss;
+ dst.mem.off = sp_pre_dec(dst.bytes);
break;
- case 0x8c: /* mov Sreg,r/m */ {
- struct segment_register reg;
- enum x86_segment seg = decode_segment(modrm_reg);
- generate_exception_if(seg == decode_segment_failed, EXC_UD, -1);
- fail_if(ops->read_segment == NULL);
- if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 )
+ case 0x6c ... 0x6d: /* ins %dx,%es:%edi */ {
+ unsigned long nr_reps = get_rep_prefix();
+ unsigned int port = (uint16_t)_regs.edx;
+ dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
+ dst.mem.seg = x86_seg_es;
+ dst.mem.off = truncate_ea_and_reps(_regs.edi, nr_reps, dst.bytes);
+ if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
goto done;
- dst.val = reg.sel;
- if ( dst.type == OP_MEM )
- dst.bytes = 2;
+ if ( (nr_reps > 1) && (ops->rep_ins != NULL) &&
+ ((rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
+ &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
+ {
+ if ( rc != 0 )
+ goto done;
+ }
+ else
+ {
+ fail_if(ops->read_io == NULL);
+ if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
+ goto done;
+ dst.type = OP_MEM;
+ nr_reps = 1;
+ }
+ register_address_increment(
+ _regs.edi,
+ nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes));
+ put_rep_prefix(nr_reps);
+ break;
+ }
+
+ case 0x6e ... 0x6f: /* outs %esi,%dx */ {
+ unsigned long nr_reps = get_rep_prefix();
+ unsigned int port = (uint16_t)_regs.edx;
+ dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
+ ea.mem.off = truncate_ea_and_reps(_regs.esi, nr_reps, dst.bytes);
+ if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
+ goto done;
+ if ( (nr_reps > 1) && (ops->rep_outs != NULL) &&
+ ((rc = ops->rep_outs(ea.mem.seg, ea.mem.off, port, dst.bytes,
+ &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
+ {
+ if ( rc != 0 )
+ goto done;
+ }
+ else
+ {
+ if ( (rc = read_ulong(ea.mem.seg, truncate_ea(_regs.esi),
+ &dst.val, dst.bytes, ctxt, ops)) != 0 )
+ goto done;
+ fail_if(ops->write_io == NULL);
+ if ( (rc = ops->write_io(port, dst.bytes, dst.val, ctxt)) != 0 )
+ goto done;
+ nr_reps = 1;
+ }
+ register_address_increment(
+ _regs.esi,
+ nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes));
+ put_rep_prefix(nr_reps);
+ break;
+ }
+
+ case 0x70 ... 0x7f: /* jcc (short) */ {
+ int rel = insn_fetch_type(int8_t);
+ if ( test_cc(b, _regs.eflags) )
+ jmp_rel(rel);
+ break;
+ }
+
+ case 0x82: /* Grp1 (x86/32 only) */
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ case 0x80: case 0x81: case 0x83: /* Grp1 */
+ switch ( modrm_reg & 7 )
+ {
+ case 0: goto add;
+ case 1: goto or;
+ case 2: goto adc;
+ case 3: goto sbb;
+ case 4: goto and;
+ case 5: goto sub;
+ case 6: goto xor;
+ case 7: goto cmp;
+ }
+ break;
+
+ case 0xa8 ... 0xa9: /* test imm,%%eax */
+ dst.reg = (unsigned long *)&_regs.eax;
+ dst.val = _regs.eax;
+ case 0x84 ... 0x85: test: /* test */
+ emulate_2op_SrcV("test", src, dst, _regs.eflags);
+ dst.type = OP_NONE;
+ break;
+
+ case 0x86 ... 0x87: xchg: /* xchg */
+ /* Write back the register source. */
+ switch ( dst.bytes )
+ {
+ case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break;
+ case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break;
+ case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */
+ case 8: *src.reg = dst.val; break;
+ }
+ /* Write back the memory destination with implicit LOCK prefix. */
+ dst.val = src.val;
+ lock_prefix = 1;
+ break;
+
+ case 0xc6 ... 0xc7: /* mov (sole member of Grp11) */
+ generate_exception_if((modrm_reg & 7) != 0, EXC_UD, -1);
+ case 0x88 ... 0x8b: /* mov */
+ dst.val = src.val;
+ break;
+
+ case 0x8c: /* mov Sreg,r/m */ {
+ struct segment_register reg;
+ enum x86_segment seg = decode_segment(modrm_reg);
+ generate_exception_if(seg == decode_segment_failed, EXC_UD, -1);
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment(seg, ®, ctxt)) != 0 )
+ goto done;
+ dst.val = reg.sel;
+ if ( dst.type == OP_MEM )
+ dst.bytes = 2;
break;
}
goto done;
break;
- case 0xb0 ... 0xb7: /* mov imm8,r8 */
- dst.reg = decode_register(
- (b & 7) | ((rex_prefix & 1) << 3), &_regs, (rex_prefix == 0));
- dst.val = src.val;
- break;
+ case 0x90: /* nop / xchg %%r8,%%rax */
+ if ( !(rex_prefix & 1) )
+ break; /* nop */
- case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
- if ( dst.bytes == 8 ) /* Fetch more bytes to obtain imm64 */
- src.val = ((uint32_t)src.val |
- ((uint64_t)insn_fetch_type(uint32_t) << 32));
- dst.reg = decode_register(
+ case 0x91 ... 0x97: /* xchg reg,%%rax */
+ src.type = dst.type = OP_REG;
+ src.bytes = dst.bytes = op_bytes;
+ src.reg = (unsigned long *)&_regs.eax;
+ src.val = *src.reg;
+ dst.reg = decode_register(
(b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
- dst.val = src.val;
+ dst.val = *dst.reg;
+ goto xchg;
+
+ case 0x98: /* cbw/cwde/cdqe */
+ switch ( op_bytes )
+ {
+ case 2: *(int16_t *)&_regs.eax = (int8_t)_regs.eax; break; /* cbw */
+ case 4: _regs.eax = (uint32_t)(int16_t)_regs.eax; break; /* cwde */
+ case 8: _regs.eax = (int32_t)_regs.eax; break; /* cdqe */
+ }
break;
- case 0xc0 ... 0xc1: grp2: /* Grp2 */
- switch ( modrm_reg & 7 )
+ case 0x99: /* cwd/cdq/cqo */
+ switch ( op_bytes )
{
- case 0: /* rol */
- emulate_2op_SrcB("rol", src, dst, _regs.eflags);
- break;
- case 1: /* ror */
- emulate_2op_SrcB("ror", src, dst, _regs.eflags);
- break;
- case 2: /* rcl */
- emulate_2op_SrcB("rcl", src, dst, _regs.eflags);
- break;
- case 3: /* rcr */
- emulate_2op_SrcB("rcr", src, dst, _regs.eflags);
- break;
- case 4: /* sal/shl */
- case 6: /* sal/shl */
- emulate_2op_SrcB("sal", src, dst, _regs.eflags);
+ case 2:
+ *(int16_t *)&_regs.edx = ((int16_t)_regs.eax < 0) ? -1 : 0;
break;
- case 5: /* shr */
- emulate_2op_SrcB("shr", src, dst, _regs.eflags);
+ case 4:
+ _regs.edx = (uint32_t)(((int32_t)_regs.eax < 0) ? -1 : 0);
break;
- case 7: /* sar */
- emulate_2op_SrcB("sar", src, dst, _regs.eflags);
+ case 8:
+ _regs.edx = (_regs.eax < 0) ? -1 : 0;
break;
}
break;
- case 0xc4: /* les */ {
- unsigned long sel;
- dst.val = x86_seg_es;
- les: /* dst.val identifies the segment */
- generate_exception_if(src.type != OP_MEM, EXC_UD, -1);
- if ( (rc = read_ulong(src.mem.seg, src.mem.off + src.bytes,
- &sel, 2, ctxt, ops)) != 0 )
+ case 0x9a: /* call (far, absolute) */ {
+ struct segment_register reg;
+ uint16_t sel;
+ uint32_t eip;
+
+ fail_if(ops->read_segment == NULL);
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+
+ eip = insn_fetch_bytes(op_bytes);
+ sel = insn_fetch_type(uint16_t);
+
+ if ( (rc = ops->read_segment(x86_seg_cs, ®, ctxt)) ||
+ (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ ®.sel, op_bytes, ctxt)) ||
+ (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ &_regs.eip, op_bytes, ctxt)) )
goto done;
- if ( (rc = load_seg(dst.val, (uint16_t)sel, ctxt, ops)) != 0 )
+
+ if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
goto done;
- dst.val = src.val;
+ _regs.eip = eip;
break;
}
- case 0xc5: /* lds */
- dst.val = x86_seg_ds;
- goto les;
+ case 0x9b: /* wait/fwait */
+ emulate_fpu_insn("fwait");
+ break;
- case 0xd0 ... 0xd1: /* Grp2 */
- src.val = 1;
- goto grp2;
+ case 0x9c: /* pushf */
+ src.val = _regs.eflags;
+ goto push;
- case 0xd2 ... 0xd3: /* Grp2 */
- src.val = _regs.ecx;
- goto grp2;
-
- case 0xf6 ... 0xf7: /* Grp3 */
- switch ( modrm_reg & 7 )
- {
- case 0 ... 1: /* test */
- /* Special case in Grp3: test has an immediate source operand. */
- src.type = OP_IMM;
- src.bytes = (d & ByteOp) ? 1 : op_bytes;
- if ( src.bytes == 8 ) src.bytes = 4;
- switch ( src.bytes )
- {
- case 1: src.val = insn_fetch_type(int8_t); break;
- case 2: src.val = insn_fetch_type(int16_t); break;
- case 4: src.val = insn_fetch_type(int32_t); break;
- }
- goto test;
- case 2: /* not */
- dst.val = ~dst.val;
- break;
- case 3: /* neg */
- emulate_1op("neg", dst, _regs.eflags);
- break;
- case 4: /* mul */
- src = dst;
- dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- dst.val = *dst.reg;
- _regs.eflags &= ~(EFLG_OF|EFLG_CF);
- switch ( src.bytes )
- {
- case 1:
- dst.val = (uint8_t)dst.val;
- dst.val *= src.val;
- if ( (uint8_t)dst.val != (uint16_t)dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- dst.bytes = 2;
- break;
- case 2:
- dst.val = (uint16_t)dst.val;
- dst.val *= src.val;
- if ( (uint16_t)dst.val != (uint32_t)dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- *(uint16_t *)&_regs.edx = dst.val >> 16;
- break;
-#ifdef __x86_64__
- case 4:
- dst.val = (uint32_t)dst.val;
- dst.val *= src.val;
- if ( (uint32_t)dst.val != dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- _regs.edx = (uint32_t)(dst.val >> 32);
- break;
-#endif
- default: {
- unsigned long m[2] = { src.val, dst.val };
- if ( mul_dbl(m) )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- _regs.edx = m[1];
- dst.val = m[0];
- break;
- }
- }
- break;
- case 5: /* imul */
- src = dst;
- dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- dst.val = *dst.reg;
- _regs.eflags &= ~(EFLG_OF|EFLG_CF);
- switch ( src.bytes )
- {
- case 1:
- dst.val = ((uint16_t)(int8_t)src.val *
- (uint16_t)(int8_t)dst.val);
- if ( (int8_t)dst.val != (uint16_t)dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- dst.bytes = 2;
- break;
- case 2:
- dst.val = ((uint32_t)(int16_t)src.val *
- (uint32_t)(int16_t)dst.val);
- if ( (int16_t)dst.val != (uint32_t)dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- *(uint16_t *)&_regs.edx = dst.val >> 16;
- break;
-#ifdef __x86_64__
- case 4:
- dst.val = ((uint64_t)(int32_t)src.val *
- (uint64_t)(int32_t)dst.val);
- if ( (int32_t)dst.val != dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- _regs.edx = (uint32_t)(dst.val >> 32);
- break;
-#endif
- default: {
- unsigned long m[2] = { src.val, dst.val };
- if ( imul_dbl(m) )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- _regs.edx = m[1];
- dst.val = m[0];
- break;
- }
- }
- break;
- case 6: /* div */ {
- unsigned long u[2], v;
- src = dst;
- dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- switch ( src.bytes )
- {
- case 1:
- u[0] = (uint16_t)_regs.eax;
- u[1] = 0;
- v = (uint8_t)src.val;
- generate_exception_if(
- div_dbl(u, v) || ((uint8_t)u[0] != (uint16_t)u[0]),
- EXC_DE, -1);
- dst.val = (uint8_t)u[0];
- ((uint8_t *)&_regs.eax)[1] = u[1];
- break;
- case 2:
- u[0] = ((uint32_t)_regs.edx << 16) | (uint16_t)_regs.eax;
- u[1] = 0;
- v = (uint16_t)src.val;
- generate_exception_if(
- div_dbl(u, v) || ((uint16_t)u[0] != (uint32_t)u[0]),
- EXC_DE, -1);
- dst.val = (uint16_t)u[0];
- *(uint16_t *)&_regs.edx = u[1];
- break;
-#ifdef __x86_64__
- case 4:
- u[0] = (_regs.edx << 32) | (uint32_t)_regs.eax;
- u[1] = 0;
- v = (uint32_t)src.val;
- generate_exception_if(
- div_dbl(u, v) || ((uint32_t)u[0] != u[0]),
- EXC_DE, -1);
- dst.val = (uint32_t)u[0];
- _regs.edx = (uint32_t)u[1];
- break;
-#endif
- default:
- u[0] = _regs.eax;
- u[1] = _regs.edx;
- v = src.val;
- generate_exception_if(div_dbl(u, v), EXC_DE, -1);
- dst.val = u[0];
- _regs.edx = u[1];
- break;
- }
- break;
- }
- case 7: /* idiv */ {
- unsigned long u[2], v;
- src = dst;
- dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- switch ( src.bytes )
- {
- case 1:
- u[0] = (int16_t)_regs.eax;
- u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
- v = (int8_t)src.val;
- generate_exception_if(
- idiv_dbl(u, v) || ((int8_t)u[0] != (int16_t)u[0]),
- EXC_DE, -1);
- dst.val = (int8_t)u[0];
- ((int8_t *)&_regs.eax)[1] = u[1];
- break;
- case 2:
- u[0] = (int32_t)((_regs.edx << 16) | (uint16_t)_regs.eax);
- u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
- v = (int16_t)src.val;
- generate_exception_if(
- idiv_dbl(u, v) || ((int16_t)u[0] != (int32_t)u[0]),
- EXC_DE, -1);
- dst.val = (int16_t)u[0];
- *(int16_t *)&_regs.edx = u[1];
- break;
-#ifdef __x86_64__
- case 4:
- u[0] = (_regs.edx << 32) | (uint32_t)_regs.eax;
- u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
- v = (int32_t)src.val;
- generate_exception_if(
- idiv_dbl(u, v) || ((int32_t)u[0] != u[0]),
- EXC_DE, -1);
- dst.val = (int32_t)u[0];
- _regs.edx = (uint32_t)u[1];
- break;
-#endif
- default:
- u[0] = _regs.eax;
- u[1] = _regs.edx;
- v = src.val;
- generate_exception_if(idiv_dbl(u, v), EXC_DE, -1);
- dst.val = u[0];
- _regs.edx = u[1];
- break;
- }
- break;
- }
- default:
- goto cannot_emulate;
- }
- break;
-
- case 0xfe: /* Grp4 */
- generate_exception_if((modrm_reg & 7) >= 2, EXC_UD, -1);
- case 0xff: /* Grp5 */
- switch ( modrm_reg & 7 )
- {
- case 0: /* inc */
- emulate_1op("inc", dst, _regs.eflags);
- break;
- case 1: /* dec */
- emulate_1op("dec", dst, _regs.eflags);
- break;
- case 2: /* call (near) */
- case 4: /* jmp (near) */
- if ( (dst.bytes != 8) && mode_64bit() )
- {
- dst.bytes = op_bytes = 8;
- if ( dst.type == OP_REG )
- dst.val = *dst.reg;
- else if ( (rc = read_ulong(dst.mem.seg, dst.mem.off,
- &dst.val, 8, ctxt, ops)) != 0 )
- goto done;
- }
- src.val = _regs.eip;
- _regs.eip = dst.val;
- if ( (modrm_reg & 7) == 2 )
- goto push; /* call */
- dst.type = OP_NONE;
- break;
- case 3: /* call (far, absolute indirect) */
- case 5: /* jmp (far, absolute indirect) */ {
- unsigned long sel;
-
- generate_exception_if(dst.type != OP_MEM, EXC_UD, -1);
-
- if ( (rc = read_ulong(dst.mem.seg, dst.mem.off+dst.bytes,
- &sel, 2, ctxt, ops)) )
- goto done;
-
- if ( (modrm_reg & 7) == 3 ) /* call */
- {
- struct segment_register reg;
- fail_if(ops->read_segment == NULL);
- if ( (rc = ops->read_segment(x86_seg_cs, ®, ctxt)) ||
- (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- ®.sel, op_bytes, ctxt)) ||
- (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- &_regs.eip, op_bytes, ctxt)) )
- goto done;
- }
-
- if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
- goto done;
- _regs.eip = dst.val;
-
- dst.type = OP_NONE;
- break;
- }
- case 6: /* push */
- /* 64-bit mode: PUSH defaults to a 64-bit operand. */
- if ( mode_64bit() && (dst.bytes == 4) )
- {
- dst.bytes = 8;
- if ( dst.type == OP_REG )
- dst.val = *dst.reg;
- else if ( (rc = read_ulong(dst.mem.seg, dst.mem.off,
- &dst.val, 8, ctxt, ops)) != 0 )
- goto done;
- }
- if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(dst.bytes),
- &dst.val, dst.bytes, ctxt)) != 0 )
- goto done;
- dst.type = OP_NONE;
- break;
- case 7:
- generate_exception_if(1, EXC_UD, -1);
- default:
- goto cannot_emulate;
- }
- break;
- }
-
- writeback:
- switch ( dst.type )
- {
- case OP_REG:
- /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
- switch ( dst.bytes )
- {
- case 1: *(uint8_t *)dst.reg = (uint8_t)dst.val; break;
- case 2: *(uint16_t *)dst.reg = (uint16_t)dst.val; break;
- case 4: *dst.reg = (uint32_t)dst.val; break; /* 64b: zero-ext */
- case 8: *dst.reg = dst.val; break;
- }
- break;
- case OP_MEM:
- if ( !(d & Mov) && (dst.orig_val == dst.val) &&
- !ctxt->force_writeback )
- /* nothing to do */;
- else if ( lock_prefix )
- rc = ops->cmpxchg(
- dst.mem.seg, dst.mem.off, &dst.orig_val,
- &dst.val, dst.bytes, ctxt);
- else
- rc = ops->write(
- dst.mem.seg, dst.mem.off, &dst.val, dst.bytes, ctxt);
- if ( rc != 0 )
- goto done;
- default:
- break;
- }
-
- /* Inject #DB if single-step tracing was enabled at instruction start. */
- if ( (ctxt->regs->eflags & EFLG_TF) && (rc == X86EMUL_OKAY) &&
- (ops->inject_hw_exception != NULL) )
- rc = ops->inject_hw_exception(EXC_DB, -1, ctxt) ? : X86EMUL_EXCEPTION;
-
- /* Commit shadow register state. */
- _regs.eflags &= ~EFLG_RF;
- *ctxt->regs = _regs;
-
- done:
- return rc;
-
- special_insn:
- dst.type = OP_NONE;
-
- /*
- * The only implicit-operands instructions allowed a LOCK prefix are
- * CMPXCHG{8,16}B, MOV CRn, MOV DRn.
- */
- generate_exception_if(lock_prefix &&
- ((b < 0x20) || (b > 0x23)) && /* MOV CRn/DRn */
- (b != 0xc7), /* CMPXCHG{8,16}B */
- EXC_GP, 0);
-
- if ( twobyte )
- goto twobyte_special_insn;
-
- switch ( b )
- {
- case 0x06: /* push %%es */ {
- struct segment_register reg;
- src.val = x86_seg_es;
- push_seg:
- fail_if(ops->read_segment == NULL);
- if ( (rc = ops->read_segment(src.val, ®, ctxt)) != 0 )
- return rc;
- /* 64-bit mode: PUSH defaults to a 64-bit operand. */
- if ( mode_64bit() && (op_bytes == 4) )
- op_bytes = 8;
- if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- ®.sel, op_bytes, ctxt)) != 0 )
- goto done;
- break;
- }
-
- case 0x07: /* pop %%es */
- src.val = x86_seg_es;
- pop_seg:
- fail_if(ops->write_segment == NULL);
- /* 64-bit mode: POP defaults to a 64-bit operand. */
- if ( mode_64bit() && (op_bytes == 4) )
- op_bytes = 8;
- if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
- &dst.val, op_bytes, ctxt, ops)) != 0 )
- goto done;
- if ( (rc = load_seg(src.val, (uint16_t)dst.val, ctxt, ops)) != 0 )
- return rc;
- break;
-
- case 0x0e: /* push %%cs */
- src.val = x86_seg_cs;
- goto push_seg;
-
- case 0x16: /* push %%ss */
- src.val = x86_seg_ss;
- goto push_seg;
-
- case 0x17: /* pop %%ss */
- src.val = x86_seg_ss;
- ctxt->retire.flags.mov_ss = 1;
- goto pop_seg;
-
- case 0x1e: /* push %%ds */
- src.val = x86_seg_ds;
- goto push_seg;
-
- case 0x1f: /* pop %%ds */
- src.val = x86_seg_ds;
- goto pop_seg;
-
- case 0x27: /* daa */ {
- uint8_t al = _regs.eax;
- unsigned long eflags = _regs.eflags;
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- _regs.eflags &= ~(EFLG_CF|EFLG_AF);
- if ( ((al & 0x0f) > 9) || (eflags & EFLG_AF) )
- {
- *(uint8_t *)&_regs.eax += 6;
- _regs.eflags |= EFLG_AF;
- }
- if ( (al > 0x99) || (eflags & EFLG_CF) )
- {
- *(uint8_t *)&_regs.eax += 0x60;
- _regs.eflags |= EFLG_CF;
- }
- _regs.eflags &= ~(EFLG_SF|EFLG_ZF|EFLG_PF);
- _regs.eflags |= ((uint8_t)_regs.eax == 0) ? EFLG_ZF : 0;
- _regs.eflags |= (( int8_t)_regs.eax < 0) ? EFLG_SF : 0;
- _regs.eflags |= even_parity(_regs.eax) ? EFLG_PF : 0;
- break;
- }
-
- case 0x2f: /* das */ {
- uint8_t al = _regs.eax;
- unsigned long eflags = _regs.eflags;
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- _regs.eflags &= ~(EFLG_CF|EFLG_AF);
- if ( ((al & 0x0f) > 9) || (eflags & EFLG_AF) )
- {
- _regs.eflags |= EFLG_AF;
- if ( (al < 6) || (eflags & EFLG_CF) )
- _regs.eflags |= EFLG_CF;
- *(uint8_t *)&_regs.eax -= 6;
- }
- if ( (al > 0x99) || (eflags & EFLG_CF) )
- {
- *(uint8_t *)&_regs.eax -= 0x60;
- _regs.eflags |= EFLG_CF;
- }
- _regs.eflags &= ~(EFLG_SF|EFLG_ZF|EFLG_PF);
- _regs.eflags |= ((uint8_t)_regs.eax == 0) ? EFLG_ZF : 0;
- _regs.eflags |= (( int8_t)_regs.eax < 0) ? EFLG_SF : 0;
- _regs.eflags |= even_parity(_regs.eax) ? EFLG_PF : 0;
- break;
- }
-
- case 0x37: /* aaa */
- case 0x3f: /* aas */
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- _regs.eflags &= ~EFLG_CF;
- if ( ((uint8_t)_regs.eax > 9) || (_regs.eflags & EFLG_AF) )
- {
- ((uint8_t *)&_regs.eax)[0] += (b == 0x37) ? 6 : -6;
- ((uint8_t *)&_regs.eax)[1] += (b == 0x37) ? 1 : -1;
- _regs.eflags |= EFLG_CF | EFLG_AF;
- }
- ((uint8_t *)&_regs.eax)[0] &= 0x0f;
- break;
-
- case 0x40 ... 0x4f: /* inc/dec reg */
- dst.type = OP_REG;
- dst.reg = decode_register(b & 7, &_regs, 0);
- dst.bytes = op_bytes;
- dst.val = *dst.reg;
- if ( b & 8 )
- emulate_1op("dec", dst, _regs.eflags);
- else
- emulate_1op("inc", dst, _regs.eflags);
- break;
-
- case 0x50 ... 0x57: /* push reg */
- src.val = *(unsigned long *)decode_register(
- (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
- goto push;
-
- case 0x58 ... 0x5f: /* pop reg */
- dst.type = OP_REG;
- dst.reg = decode_register(
- (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
- dst.bytes = op_bytes;
- if ( mode_64bit() && (dst.bytes == 4) )
- dst.bytes = 8;
- if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(dst.bytes),
- &dst.val, dst.bytes, ctxt, ops)) != 0 )
- goto done;
- break;
-
- case 0x60: /* pusha */ {
- int i;
- unsigned long regs[] = {
- _regs.eax, _regs.ecx, _regs.edx, _regs.ebx,
- _regs.esp, _regs.ebp, _regs.esi, _regs.edi };
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- for ( i = 0; i < 8; i++ )
- if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- ®s[i], op_bytes, ctxt)) != 0 )
- goto done;
- break;
- }
-
- case 0x61: /* popa */ {
- int i;
- unsigned long dummy_esp, *regs[] = {
- (unsigned long *)&_regs.edi, (unsigned long *)&_regs.esi,
- (unsigned long *)&_regs.ebp, (unsigned long *)&dummy_esp,
- (unsigned long *)&_regs.ebx, (unsigned long *)&_regs.edx,
- (unsigned long *)&_regs.ecx, (unsigned long *)&_regs.eax };
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- for ( i = 0; i < 8; i++ )
- {
- if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
- &dst.val, op_bytes, ctxt, ops)) != 0 )
- goto done;
- switch ( op_bytes )
- {
- case 1: *(uint8_t *)regs[i] = (uint8_t)dst.val; break;
- case 2: *(uint16_t *)regs[i] = (uint16_t)dst.val; break;
- case 4: *regs[i] = (uint32_t)dst.val; break; /* 64b: zero-ext */
- case 8: *regs[i] = dst.val; break;
- }
- }
- break;
- }
-
- case 0x68: /* push imm{16,32,64} */
- src.val = ((op_bytes == 2)
- ? (int32_t)insn_fetch_type(int16_t)
- : insn_fetch_type(int32_t));
- goto push;
-
- case 0x6a: /* push imm8 */
- src.val = insn_fetch_type(int8_t);
- push:
- d |= Mov; /* force writeback */
- dst.type = OP_MEM;
- dst.bytes = op_bytes;
- if ( mode_64bit() && (dst.bytes == 4) )
- dst.bytes = 8;
- dst.val = src.val;
- dst.mem.seg = x86_seg_ss;
- dst.mem.off = sp_pre_dec(dst.bytes);
- break;
-
- case 0x6c ... 0x6d: /* ins %dx,%es:%edi */ {
- unsigned long nr_reps = get_rep_prefix();
- unsigned int port = (uint16_t)_regs.edx;
- dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
- dst.mem.seg = x86_seg_es;
- dst.mem.off = truncate_ea_and_reps(_regs.edi, nr_reps, dst.bytes);
- if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
- goto done;
- if ( (nr_reps > 1) && (ops->rep_ins != NULL) &&
- ((rc = ops->rep_ins(port, dst.mem.seg, dst.mem.off, dst.bytes,
- &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
- {
- if ( rc != 0 )
- goto done;
- }
- else
- {
- fail_if(ops->read_io == NULL);
- if ( (rc = ops->read_io(port, dst.bytes, &dst.val, ctxt)) != 0 )
- goto done;
- dst.type = OP_MEM;
- nr_reps = 1;
- }
- register_address_increment(
- _regs.edi,
- nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes));
- put_rep_prefix(nr_reps);
- break;
- }
-
- case 0x6e ... 0x6f: /* outs %esi,%dx */ {
- unsigned long nr_reps = get_rep_prefix();
- unsigned int port = (uint16_t)_regs.edx;
- dst.bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
- ea.mem.off = truncate_ea_and_reps(_regs.esi, nr_reps, dst.bytes);
- if ( (rc = ioport_access_check(port, dst.bytes, ctxt, ops)) != 0 )
- goto done;
- if ( (nr_reps > 1) && (ops->rep_outs != NULL) &&
- ((rc = ops->rep_outs(ea.mem.seg, ea.mem.off, port, dst.bytes,
- &nr_reps, ctxt)) != X86EMUL_UNHANDLEABLE) )
- {
- if ( rc != 0 )
- goto done;
- }
- else
- {
- if ( (rc = read_ulong(ea.mem.seg, truncate_ea(_regs.esi),
- &dst.val, dst.bytes, ctxt, ops)) != 0 )
- goto done;
- fail_if(ops->write_io == NULL);
- if ( (rc = ops->write_io(port, dst.bytes, dst.val, ctxt)) != 0 )
- goto done;
- nr_reps = 1;
- }
- register_address_increment(
- _regs.esi,
- nr_reps * ((_regs.eflags & EFLG_DF) ? -dst.bytes : dst.bytes));
- put_rep_prefix(nr_reps);
- break;
- }
-
- case 0x70 ... 0x7f: /* jcc (short) */ {
- int rel = insn_fetch_type(int8_t);
- if ( test_cc(b, _regs.eflags) )
- jmp_rel(rel);
- break;
- }
-
- case 0x90: /* nop / xchg %%r8,%%rax */
- if ( !(rex_prefix & 1) )
- break; /* nop */
-
- case 0x91 ... 0x97: /* xchg reg,%%rax */
- src.type = dst.type = OP_REG;
- src.bytes = dst.bytes = op_bytes;
- src.reg = (unsigned long *)&_regs.eax;
- src.val = *src.reg;
- dst.reg = decode_register(
- (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
- dst.val = *dst.reg;
- goto xchg;
-
- case 0x98: /* cbw/cwde/cdqe */
- switch ( op_bytes )
- {
- case 2: *(int16_t *)&_regs.eax = (int8_t)_regs.eax; break; /* cbw */
- case 4: _regs.eax = (uint32_t)(int16_t)_regs.eax; break; /* cwde */
- case 8: _regs.eax = (int32_t)_regs.eax; break; /* cdqe */
- }
- break;
-
- case 0x99: /* cwd/cdq/cqo */
- switch ( op_bytes )
- {
- case 2:
- *(int16_t *)&_regs.edx = ((int16_t)_regs.eax < 0) ? -1 : 0;
- break;
- case 4:
- _regs.edx = (uint32_t)(((int32_t)_regs.eax < 0) ? -1 : 0);
- break;
- case 8:
- _regs.edx = (_regs.eax < 0) ? -1 : 0;
- break;
- }
- break;
-
- case 0x9a: /* call (far, absolute) */ {
- struct segment_register reg;
- uint16_t sel;
- uint32_t eip;
-
- fail_if(ops->read_segment == NULL);
- generate_exception_if(mode_64bit(), EXC_UD, -1);
-
- eip = insn_fetch_bytes(op_bytes);
- sel = insn_fetch_type(uint16_t);
-
- if ( (rc = ops->read_segment(x86_seg_cs, ®, ctxt)) ||
- (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- ®.sel, op_bytes, ctxt)) ||
- (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
- &_regs.eip, op_bytes, ctxt)) )
- goto done;
-
- if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
- goto done;
- _regs.eip = eip;
- break;
- }
-
- case 0x9b: /* wait/fwait */
- emulate_fpu_insn("fwait");
- break;
-
- case 0x9c: /* pushf */
- src.val = _regs.eflags;
- goto push;
-
- case 0x9d: /* popf */ {
- uint32_t mask = EFLG_VIP | EFLG_VIF | EFLG_VM;
- if ( !mode_ring0() )
- mask |= EFLG_IOPL;
- if ( !mode_iopl() )
- mask |= EFLG_IF;
- /* 64-bit mode: POP defaults to a 64-bit operand. */
- if ( mode_64bit() && (op_bytes == 4) )
- op_bytes = 8;
- if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
- &dst.val, op_bytes, ctxt, ops)) != 0 )
- goto done;
- if ( op_bytes == 2 )
- dst.val = (uint16_t)dst.val | (_regs.eflags & 0xffff0000u);
- dst.val &= 0x257fd5;
- _regs.eflags &= mask;
- _regs.eflags |= (uint32_t)(dst.val & ~mask) | 0x02;
- break;
- }
+ case 0x9d: /* popf */ {
+ uint32_t mask = EFLG_VIP | EFLG_VIF | EFLG_VM;
+ if ( !mode_ring0() )
+ mask |= EFLG_IOPL;
+ if ( !mode_iopl() )
+ mask |= EFLG_IF;
+ /* 64-bit mode: POP defaults to a 64-bit operand. */
+ if ( mode_64bit() && (op_bytes == 4) )
+ op_bytes = 8;
+ if ( (rc = read_ulong(x86_seg_ss, sp_post_inc(op_bytes),
+ &dst.val, op_bytes, ctxt, ops)) != 0 )
+ goto done;
+ if ( op_bytes == 2 )
+ dst.val = (uint16_t)dst.val | (_regs.eflags & 0xffff0000u);
+ dst.val &= 0x257fd5;
+ _regs.eflags &= mask;
+ _regs.eflags |= (uint32_t)(dst.val & ~mask) | 0x02;
+ break;
+ }
case 0x9e: /* sahf */
*(uint8_t *)&_regs.eflags = (((uint8_t *)&_regs.eax)[1] & 0xd7) | 0x02;
break;
}
+ case 0xb0 ... 0xb7: /* mov imm8,r8 */
+ dst.reg = decode_register(
+ (b & 7) | ((rex_prefix & 1) << 3), &_regs, (rex_prefix == 0));
+ dst.val = src.val;
+ break;
+
+ case 0xb8 ... 0xbf: /* mov imm{16,32,64},r{16,32,64} */
+ if ( dst.bytes == 8 ) /* Fetch more bytes to obtain imm64 */
+ src.val = ((uint32_t)src.val |
+ ((uint64_t)insn_fetch_type(uint32_t) << 32));
+ dst.reg = decode_register(
+ (b & 7) | ((rex_prefix & 1) << 3), &_regs, 0);
+ dst.val = src.val;
+ break;
+
+ case 0xc0 ... 0xc1: grp2: /* Grp2 */
+ switch ( modrm_reg & 7 )
+ {
+ case 0: /* rol */
+ emulate_2op_SrcB("rol", src, dst, _regs.eflags);
+ break;
+ case 1: /* ror */
+ emulate_2op_SrcB("ror", src, dst, _regs.eflags);
+ break;
+ case 2: /* rcl */
+ emulate_2op_SrcB("rcl", src, dst, _regs.eflags);
+ break;
+ case 3: /* rcr */
+ emulate_2op_SrcB("rcr", src, dst, _regs.eflags);
+ break;
+ case 4: /* sal/shl */
+ case 6: /* sal/shl */
+ emulate_2op_SrcB("sal", src, dst, _regs.eflags);
+ break;
+ case 5: /* shr */
+ emulate_2op_SrcB("shr", src, dst, _regs.eflags);
+ break;
+ case 7: /* sar */
+ emulate_2op_SrcB("sar", src, dst, _regs.eflags);
+ break;
+ }
+ break;
+
case 0xc2: /* ret imm16 (near) */
case 0xc3: /* ret (near) */ {
int offset = (b == 0xc2) ? insn_fetch_type(uint16_t) : 0;
break;
}
+ case 0xc4: /* les */ {
+ unsigned long sel;
+ dst.val = x86_seg_es;
+ les: /* dst.val identifies the segment */
+ generate_exception_if(src.type != OP_MEM, EXC_UD, -1);
+ if ( (rc = read_ulong(src.mem.seg, src.mem.off + src.bytes,
+ &sel, 2, ctxt, ops)) != 0 )
+ goto done;
+ if ( (rc = load_seg(dst.val, (uint16_t)sel, ctxt, ops)) != 0 )
+ goto done;
+ dst.val = src.val;
+ break;
+ }
+
+ case 0xc5: /* lds */
+ dst.val = x86_seg_ds;
+ goto les;
+
case 0xc8: /* enter imm16,imm8 */ {
uint16_t size = insn_fetch_type(uint16_t);
uint8_t depth = insn_fetch_type(uint8_t) & 31;
break;
}
+ case 0xd0 ... 0xd1: /* Grp2 */
+ src.val = 1;
+ goto grp2;
+
+ case 0xd2 ... 0xd3: /* Grp2 */
+ src.val = _regs.ecx;
+ goto grp2;
+
case 0xd4: /* aam */ {
unsigned int base = insn_fetch_type(uint8_t);
uint8_t al = _regs.eax;
if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
src.bytes, ctxt)) != 0 )
goto done;
- emulate_fpu_insn_memsrc("fldl", src.val);
+ emulate_fpu_insn_memsrc("fldl", src.val);
+ break;
+ case 1: /* fisttp m64i */
+ ea.bytes = 8;
+ dst = ea;
+ dst.type = OP_MEM;
+ emulate_fpu_insn_memdst("fisttpll", dst.val);
+ break;
+ case 2: /* fst m64fp */
+ ea.bytes = 8;
+ dst = ea;
+ dst.type = OP_MEM;
+ emulate_fpu_insn_memsrc("fstl", dst.val);
+ break;
+ case 3: /* fstp m64fp */
+ ea.bytes = 8;
+ dst = ea;
+ dst.type = OP_MEM;
+ emulate_fpu_insn_memdst("fstpl", dst.val);
+ break;
+ case 7: /* fnstsw m2byte */
+ ea.bytes = 2;
+ dst = ea;
+ dst.type = OP_MEM;
+ emulate_fpu_insn_memdst("fnstsw", dst.val);
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ }
+ break;
+
+ case 0xde: /* FPU 0xde */
+ switch ( modrm )
+ {
+ case 0xc0 ... 0xc7: /* faddp %stN */
+ case 0xc8 ... 0xcf: /* fmulp %stN */
+ case 0xd9: /* fcompp */
+ case 0xe0 ... 0xe7: /* fsubrp %stN */
+ case 0xe8 ... 0xef: /* fsubp %stN */
+ case 0xf0 ... 0xf7: /* fdivrp %stN */
+ case 0xf8 ... 0xff: /* fdivp %stN */
+ emulate_fpu_insn_stub(0xde, modrm);
+ break;
+ default:
+ fail_if(modrm >= 0xc0);
+ ea.bytes = 2;
+ src = ea;
+ if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
+ src.bytes, ctxt)) != 0 )
+ goto done;
+ switch ( modrm_reg & 7 )
+ {
+ case 0: /* fiadd m16i */
+ emulate_fpu_insn_memsrc("fiadd", src.val);
+ break;
+ case 1: /* fimul m16i */
+ emulate_fpu_insn_memsrc("fimul", src.val);
+ break;
+ case 2: /* ficom m16i */
+ emulate_fpu_insn_memsrc("ficom", src.val);
+ break;
+ case 3: /* ficomp m16i */
+ emulate_fpu_insn_memsrc("ficomp", src.val);
+ break;
+ case 4: /* fisub m16i */
+ emulate_fpu_insn_memsrc("fisub", src.val);
+ break;
+ case 5: /* fisubr m16i */
+ emulate_fpu_insn_memsrc("fisubr", src.val);
+ break;
+ case 6: /* fidiv m16i */
+ emulate_fpu_insn_memsrc("fidiv", src.val);
+ break;
+ case 7: /* fidivr m16i */
+ emulate_fpu_insn_memsrc("fidivr", src.val);
+ break;
+ default:
+ goto cannot_emulate;
+ }
+ }
+ break;
+
+ case 0xdf: /* FPU 0xdf */
+ switch ( modrm )
+ {
+ case 0xe0:
+ /* fnstsw %ax */
+ dst.bytes = 2;
+ dst.type = OP_REG;
+ dst.reg = (unsigned long *)&_regs.eax;
+ emulate_fpu_insn_memdst("fnstsw", dst.val);
+ break;
+ case 0xf0 ... 0xf7: /* fcomip %stN */
+ case 0xf8 ... 0xff: /* fucomip %stN */
+ emulate_fpu_insn_stub(0xdf, modrm);
+ break;
+ default:
+ fail_if(modrm >= 0xc0);
+ switch ( modrm_reg & 7 )
+ {
+ case 0: /* fild m16i */
+ ea.bytes = 2;
+ src = ea;
+ if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
+ src.bytes, ctxt)) != 0 )
+ goto done;
+ emulate_fpu_insn_memsrc("fild", src.val);
break;
- case 1: /* fisttp m64i */
- ea.bytes = 8;
+ case 1: /* fisttp m16i */
+ ea.bytes = 2;
dst = ea;
dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fisttpll", dst.val);
+ emulate_fpu_insn_memdst("fisttp", dst.val);
break;
- case 2: /* fst m64fp */
- ea.bytes = 8;
+ case 2: /* fist m16i */
+ ea.bytes = 2;
dst = ea;
dst.type = OP_MEM;
- emulate_fpu_insn_memsrc("fstl", dst.val);
+ emulate_fpu_insn_memdst("fist", dst.val);
break;
- case 3: /* fstp m64fp */
+ case 3: /* fistp m16i */
+ ea.bytes = 2;
+ dst = ea;
+ dst.type = OP_MEM;
+ emulate_fpu_insn_memdst("fistp", dst.val);
+ break;
+ case 4: /* fbld m80dec */
+ ea.bytes = 10;
+ dst = ea;
+ if ( (rc = ops->read(src.mem.seg, src.mem.off,
+ &src.val, src.bytes, ctxt)) != 0 )
+ goto done;
+ emulate_fpu_insn_memdst("fbld", src.val);
+ break;
+ case 5: /* fild m64i */
ea.bytes = 8;
+ src = ea;
+ if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
+ src.bytes, ctxt)) != 0 )
+ goto done;
+ emulate_fpu_insn_memsrc("fildll", src.val);
+ break;
+ case 6: /* fbstp packed bcd */
+ ea.bytes = 10;
dst = ea;
dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fstpl", dst.val);
+ emulate_fpu_insn_memdst("fbstp", dst.val);
break;
- case 7: /* fnstsw m2byte */
- ea.bytes = 2;
+ case 7: /* fistp m64i */
+ ea.bytes = 8;
dst = ea;
dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fnstsw", dst.val);
+ emulate_fpu_insn_memdst("fistpll", dst.val);
break;
default:
goto cannot_emulate;
}
break;
- case 0xde: /* FPU 0xde */
- switch ( modrm )
+ case 0xe0 ... 0xe2: /* loop{,z,nz} */ {
+ int rel = insn_fetch_type(int8_t);
+ int do_jmp = !(_regs.eflags & EFLG_ZF); /* loopnz */
+ if ( b == 0xe1 )
+ do_jmp = !do_jmp; /* loopz */
+ else if ( b == 0xe2 )
+ do_jmp = 1; /* loop */
+ switch ( ad_bytes )
{
- case 0xc0 ... 0xc7: /* faddp %stN */
- case 0xc8 ... 0xcf: /* fmulp %stN */
- case 0xd9: /* fcompp */
- case 0xe0 ... 0xe7: /* fsubrp %stN */
- case 0xe8 ... 0xef: /* fsubp %stN */
- case 0xf0 ... 0xf7: /* fdivrp %stN */
- case 0xf8 ... 0xff: /* fdivp %stN */
- emulate_fpu_insn_stub(0xde, modrm);
+ case 2:
+ do_jmp &= --(*(uint16_t *)&_regs.ecx) != 0;
break;
- default:
- fail_if(modrm >= 0xc0);
- ea.bytes = 2;
- src = ea;
- if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
- src.bytes, ctxt)) != 0 )
- goto done;
- switch ( modrm_reg & 7 )
+ case 4:
+ do_jmp &= --(*(uint32_t *)&_regs.ecx) != 0;
+ _regs.ecx = (uint32_t)_regs.ecx; /* zero extend in x86/64 mode */
+ break;
+ default: /* case 8: */
+ do_jmp &= --_regs.ecx != 0;
+ break;
+ }
+ if ( do_jmp )
+ jmp_rel(rel);
+ break;
+ }
+
+ case 0xe3: /* jcxz/jecxz (short) */ {
+ int rel = insn_fetch_type(int8_t);
+ if ( (ad_bytes == 2) ? !(uint16_t)_regs.ecx :
+ (ad_bytes == 4) ? !(uint32_t)_regs.ecx : !_regs.ecx )
+ jmp_rel(rel);
+ break;
+ }
+
+ case 0xe4: /* in imm8,%al */
+ case 0xe5: /* in imm8,%eax */
+ case 0xe6: /* out %al,imm8 */
+ case 0xe7: /* out %eax,imm8 */
+ case 0xec: /* in %dx,%al */
+ case 0xed: /* in %dx,%eax */
+ case 0xee: /* out %al,%dx */
+ case 0xef: /* out %eax,%dx */ {
+ unsigned int port = ((b < 0xe8)
+ ? insn_fetch_type(uint8_t)
+ : (uint16_t)_regs.edx);
+ op_bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
+ if ( (rc = ioport_access_check(port, op_bytes, ctxt, ops)) != 0 )
+ goto done;
+ if ( b & 2 )
+ {
+ /* out */
+ fail_if(ops->write_io == NULL);
+ rc = ops->write_io(port, op_bytes, _regs.eax, ctxt);
+ }
+ else
+ {
+ /* in */
+ dst.type = OP_REG;
+ dst.bytes = op_bytes;
+ dst.reg = (unsigned long *)&_regs.eax;
+ fail_if(ops->read_io == NULL);
+ rc = ops->read_io(port, dst.bytes, &dst.val, ctxt);
+ }
+ if ( rc != 0 )
+ goto done;
+ break;
+ }
+
+ case 0xe8: /* call (near) */ {
+ int rel = (((op_bytes == 2) && !mode_64bit())
+ ? (int32_t)insn_fetch_type(int16_t)
+ : insn_fetch_type(int32_t));
+ op_bytes = mode_64bit() ? 8 : op_bytes;
+ src.val = _regs.eip;
+ jmp_rel(rel);
+ goto push;
+ }
+
+ case 0xe9: /* jmp (near) */ {
+ int rel = (((op_bytes == 2) && !mode_64bit())
+ ? (int32_t)insn_fetch_type(int16_t)
+ : insn_fetch_type(int32_t));
+ jmp_rel(rel);
+ break;
+ }
+
+ case 0xea: /* jmp (far, absolute) */ {
+ uint16_t sel;
+ uint32_t eip;
+ generate_exception_if(mode_64bit(), EXC_UD, -1);
+ eip = insn_fetch_bytes(op_bytes);
+ sel = insn_fetch_type(uint16_t);
+ if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
+ goto done;
+ _regs.eip = eip;
+ break;
+ }
+
+ case 0xeb: /* jmp (short) */ {
+ int rel = insn_fetch_type(int8_t);
+ jmp_rel(rel);
+ break;
+ }
+
+ case 0xf1: /* int1 (icebp) */
+ src.val = EXC_DB;
+ goto swint;
+
+ case 0xf4: /* hlt */
+ ctxt->retire.flags.hlt = 1;
+ break;
+
+ case 0xf5: /* cmc */
+ _regs.eflags ^= EFLG_CF;
+ break;
+
+ case 0xf6 ... 0xf7: /* Grp3 */
+ switch ( modrm_reg & 7 )
+ {
+ case 0 ... 1: /* test */
+ /* Special case in Grp3: test has an immediate source operand. */
+ src.type = OP_IMM;
+ src.bytes = (d & ByteOp) ? 1 : op_bytes;
+ if ( src.bytes == 8 ) src.bytes = 4;
+ switch ( src.bytes )
{
- case 0: /* fiadd m16i */
- emulate_fpu_insn_memsrc("fiadd", src.val);
+ case 1: src.val = insn_fetch_type(int8_t); break;
+ case 2: src.val = insn_fetch_type(int16_t); break;
+ case 4: src.val = insn_fetch_type(int32_t); break;
+ }
+ goto test;
+ case 2: /* not */
+ dst.val = ~dst.val;
+ break;
+ case 3: /* neg */
+ emulate_1op("neg", dst, _regs.eflags);
+ break;
+ case 4: /* mul */
+ src = dst;
+ dst.type = OP_REG;
+ dst.reg = (unsigned long *)&_regs.eax;
+ dst.val = *dst.reg;
+ _regs.eflags &= ~(EFLG_OF|EFLG_CF);
+ switch ( src.bytes )
+ {
+ case 1:
+ dst.val = (uint8_t)dst.val;
+ dst.val *= src.val;
+ if ( (uint8_t)dst.val != (uint16_t)dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ dst.bytes = 2;
break;
- case 1: /* fimul m16i */
- emulate_fpu_insn_memsrc("fimul", src.val);
+ case 2:
+ dst.val = (uint16_t)dst.val;
+ dst.val *= src.val;
+ if ( (uint16_t)dst.val != (uint32_t)dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ *(uint16_t *)&_regs.edx = dst.val >> 16;
+ break;
+#ifdef __x86_64__
+ case 4:
+ dst.val = (uint32_t)dst.val;
+ dst.val *= src.val;
+ if ( (uint32_t)dst.val != dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ _regs.edx = (uint32_t)(dst.val >> 32);
+ break;
+#endif
+ default: {
+ unsigned long m[2] = { src.val, dst.val };
+ if ( mul_dbl(m) )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ _regs.edx = m[1];
+ dst.val = m[0];
+ break;
+ }
+ }
+ break;
+ case 5: /* imul */
+ src = dst;
+ dst.type = OP_REG;
+ dst.reg = (unsigned long *)&_regs.eax;
+ dst.val = *dst.reg;
+ _regs.eflags &= ~(EFLG_OF|EFLG_CF);
+ switch ( src.bytes )
+ {
+ case 1:
+ dst.val = ((uint16_t)(int8_t)src.val *
+ (uint16_t)(int8_t)dst.val);
+ if ( (int8_t)dst.val != (uint16_t)dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ dst.bytes = 2;
break;
- case 2: /* ficom m16i */
- emulate_fpu_insn_memsrc("ficom", src.val);
+ case 2:
+ dst.val = ((uint32_t)(int16_t)src.val *
+ (uint32_t)(int16_t)dst.val);
+ if ( (int16_t)dst.val != (uint32_t)dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ *(uint16_t *)&_regs.edx = dst.val >> 16;
break;
- case 3: /* ficomp m16i */
- emulate_fpu_insn_memsrc("ficomp", src.val);
+#ifdef __x86_64__
+ case 4:
+ dst.val = ((uint64_t)(int32_t)src.val *
+ (uint64_t)(int32_t)dst.val);
+ if ( (int32_t)dst.val != dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ _regs.edx = (uint32_t)(dst.val >> 32);
break;
- case 4: /* fisub m16i */
- emulate_fpu_insn_memsrc("fisub", src.val);
+#endif
+ default: {
+ unsigned long m[2] = { src.val, dst.val };
+ if ( imul_dbl(m) )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ _regs.edx = m[1];
+ dst.val = m[0];
break;
- case 5: /* fisubr m16i */
- emulate_fpu_insn_memsrc("fisubr", src.val);
+ }
+ }
+ break;
+ case 6: /* div */ {
+ unsigned long u[2], v;
+ src = dst;
+ dst.type = OP_REG;
+ dst.reg = (unsigned long *)&_regs.eax;
+ switch ( src.bytes )
+ {
+ case 1:
+ u[0] = (uint16_t)_regs.eax;
+ u[1] = 0;
+ v = (uint8_t)src.val;
+ generate_exception_if(
+ div_dbl(u, v) || ((uint8_t)u[0] != (uint16_t)u[0]),
+ EXC_DE, -1);
+ dst.val = (uint8_t)u[0];
+ ((uint8_t *)&_regs.eax)[1] = u[1];
break;
- case 6: /* fidiv m16i */
- emulate_fpu_insn_memsrc("fidiv", src.val);
+ case 2:
+ u[0] = ((uint32_t)_regs.edx << 16) | (uint16_t)_regs.eax;
+ u[1] = 0;
+ v = (uint16_t)src.val;
+ generate_exception_if(
+ div_dbl(u, v) || ((uint16_t)u[0] != (uint32_t)u[0]),
+ EXC_DE, -1);
+ dst.val = (uint16_t)u[0];
+ *(uint16_t *)&_regs.edx = u[1];
break;
- case 7: /* fidivr m16i */
- emulate_fpu_insn_memsrc("fidivr", src.val);
+#ifdef __x86_64__
+ case 4:
+ u[0] = (_regs.edx << 32) | (uint32_t)_regs.eax;
+ u[1] = 0;
+ v = (uint32_t)src.val;
+ generate_exception_if(
+ div_dbl(u, v) || ((uint32_t)u[0] != u[0]),
+ EXC_DE, -1);
+ dst.val = (uint32_t)u[0];
+ _regs.edx = (uint32_t)u[1];
break;
+#endif
default:
- goto cannot_emulate;
+ u[0] = _regs.eax;
+ u[1] = _regs.edx;
+ v = src.val;
+ generate_exception_if(div_dbl(u, v), EXC_DE, -1);
+ dst.val = u[0];
+ _regs.edx = u[1];
+ break;
}
+ break;
}
- break;
-
- case 0xdf: /* FPU 0xdf */
- switch ( modrm )
- {
- case 0xe0:
- /* fnstsw %ax */
- dst.bytes = 2;
+ case 7: /* idiv */ {
+ unsigned long u[2], v;
+ src = dst;
dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- emulate_fpu_insn_memdst("fnstsw", dst.val);
- break;
- case 0xf0 ... 0xf7: /* fcomip %stN */
- case 0xf8 ... 0xff: /* fucomip %stN */
- emulate_fpu_insn_stub(0xdf, modrm);
- break;
- default:
- fail_if(modrm >= 0xc0);
- switch ( modrm_reg & 7 )
+ dst.reg = (unsigned long *)&_regs.eax;
+ switch ( src.bytes )
{
- case 0: /* fild m16i */
- ea.bytes = 2;
- src = ea;
- if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
- src.bytes, ctxt)) != 0 )
- goto done;
- emulate_fpu_insn_memsrc("fild", src.val);
- break;
- case 1: /* fisttp m16i */
- ea.bytes = 2;
- dst = ea;
- dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fisttp", dst.val);
- break;
- case 2: /* fist m16i */
- ea.bytes = 2;
- dst = ea;
- dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fist", dst.val);
- break;
- case 3: /* fistp m16i */
- ea.bytes = 2;
- dst = ea;
- dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fistp", dst.val);
- break;
- case 4: /* fbld m80dec */
- ea.bytes = 10;
- dst = ea;
- if ( (rc = ops->read(src.mem.seg, src.mem.off,
- &src.val, src.bytes, ctxt)) != 0 )
- goto done;
- emulate_fpu_insn_memdst("fbld", src.val);
- break;
- case 5: /* fild m64i */
- ea.bytes = 8;
- src = ea;
- if ( (rc = ops->read(src.mem.seg, src.mem.off, &src.val,
- src.bytes, ctxt)) != 0 )
- goto done;
- emulate_fpu_insn_memsrc("fildll", src.val);
+ case 1:
+ u[0] = (int16_t)_regs.eax;
+ u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
+ v = (int8_t)src.val;
+ generate_exception_if(
+ idiv_dbl(u, v) || ((int8_t)u[0] != (int16_t)u[0]),
+ EXC_DE, -1);
+ dst.val = (int8_t)u[0];
+ ((int8_t *)&_regs.eax)[1] = u[1];
break;
- case 6: /* fbstp packed bcd */
- ea.bytes = 10;
- dst = ea;
- dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fbstp", dst.val);
+ case 2:
+ u[0] = (int32_t)((_regs.edx << 16) | (uint16_t)_regs.eax);
+ u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
+ v = (int16_t)src.val;
+ generate_exception_if(
+ idiv_dbl(u, v) || ((int16_t)u[0] != (int32_t)u[0]),
+ EXC_DE, -1);
+ dst.val = (int16_t)u[0];
+ *(int16_t *)&_regs.edx = u[1];
break;
- case 7: /* fistp m64i */
- ea.bytes = 8;
- dst = ea;
- dst.type = OP_MEM;
- emulate_fpu_insn_memdst("fistpll", dst.val);
+#ifdef __x86_64__
+ case 4:
+ u[0] = (_regs.edx << 32) | (uint32_t)_regs.eax;
+ u[1] = ((long)u[0] < 0) ? ~0UL : 0UL;
+ v = (int32_t)src.val;
+ generate_exception_if(
+ idiv_dbl(u, v) || ((int32_t)u[0] != u[0]),
+ EXC_DE, -1);
+ dst.val = (int32_t)u[0];
+ _regs.edx = (uint32_t)u[1];
break;
+#endif
default:
- goto cannot_emulate;
+ u[0] = _regs.eax;
+ u[1] = _regs.edx;
+ v = src.val;
+ generate_exception_if(idiv_dbl(u, v), EXC_DE, -1);
+ dst.val = u[0];
+ _regs.edx = u[1];
+ break;
}
- }
- break;
-
- case 0xe0 ... 0xe2: /* loop{,z,nz} */ {
- int rel = insn_fetch_type(int8_t);
- int do_jmp = !(_regs.eflags & EFLG_ZF); /* loopnz */
- if ( b == 0xe1 )
- do_jmp = !do_jmp; /* loopz */
- else if ( b == 0xe2 )
- do_jmp = 1; /* loop */
- switch ( ad_bytes )
- {
- case 2:
- do_jmp &= --(*(uint16_t *)&_regs.ecx) != 0;
- break;
- case 4:
- do_jmp &= --(*(uint32_t *)&_regs.ecx) != 0;
- _regs.ecx = (uint32_t)_regs.ecx; /* zero extend in x86/64 mode */
- break;
- default: /* case 8: */
- do_jmp &= --_regs.ecx != 0;
break;
}
- if ( do_jmp )
- jmp_rel(rel);
- break;
- }
-
- case 0xe3: /* jcxz/jecxz (short) */ {
- int rel = insn_fetch_type(int8_t);
- if ( (ad_bytes == 2) ? !(uint16_t)_regs.ecx :
- (ad_bytes == 4) ? !(uint32_t)_regs.ecx : !_regs.ecx )
- jmp_rel(rel);
- break;
- }
-
- case 0xe4: /* in imm8,%al */
- case 0xe5: /* in imm8,%eax */
- case 0xe6: /* out %al,imm8 */
- case 0xe7: /* out %eax,imm8 */
- case 0xec: /* in %dx,%al */
- case 0xed: /* in %dx,%eax */
- case 0xee: /* out %al,%dx */
- case 0xef: /* out %eax,%dx */ {
- unsigned int port = ((b < 0xe8)
- ? insn_fetch_type(uint8_t)
- : (uint16_t)_regs.edx);
- op_bytes = !(b & 1) ? 1 : (op_bytes == 8) ? 4 : op_bytes;
- if ( (rc = ioport_access_check(port, op_bytes, ctxt, ops)) != 0 )
- goto done;
- if ( b & 2 )
- {
- /* out */
- fail_if(ops->write_io == NULL);
- rc = ops->write_io(port, op_bytes, _regs.eax, ctxt);
- }
- else
- {
- /* in */
- dst.type = OP_REG;
- dst.bytes = op_bytes;
- dst.reg = (unsigned long *)&_regs.eax;
- fail_if(ops->read_io == NULL);
- rc = ops->read_io(port, dst.bytes, &dst.val, ctxt);
+ default:
+ goto cannot_emulate;
}
- if ( rc != 0 )
- goto done;
- break;
- }
-
- case 0xe8: /* call (near) */ {
- int rel = (((op_bytes == 2) && !mode_64bit())
- ? (int32_t)insn_fetch_type(int16_t)
- : insn_fetch_type(int32_t));
- op_bytes = mode_64bit() ? 8 : op_bytes;
- src.val = _regs.eip;
- jmp_rel(rel);
- goto push;
- }
-
- case 0xe9: /* jmp (near) */ {
- int rel = (((op_bytes == 2) && !mode_64bit())
- ? (int32_t)insn_fetch_type(int16_t)
- : insn_fetch_type(int32_t));
- jmp_rel(rel);
- break;
- }
-
- case 0xea: /* jmp (far, absolute) */ {
- uint16_t sel;
- uint32_t eip;
- generate_exception_if(mode_64bit(), EXC_UD, -1);
- eip = insn_fetch_bytes(op_bytes);
- sel = insn_fetch_type(uint16_t);
- if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
- goto done;
- _regs.eip = eip;
- break;
- }
-
- case 0xeb: /* jmp (short) */ {
- int rel = insn_fetch_type(int8_t);
- jmp_rel(rel);
- break;
- }
-
- case 0xf1: /* int1 (icebp) */
- src.val = EXC_DB;
- goto swint;
-
- case 0xf4: /* hlt */
- ctxt->retire.flags.hlt = 1;
- break;
-
- case 0xf5: /* cmc */
- _regs.eflags ^= EFLG_CF;
break;
case 0xf8: /* clc */
case 0xfd: /* std */
_regs.eflags |= EFLG_DF;
break;
- }
- goto writeback;
-
- twobyte_insn:
- switch ( b )
- {
- case 0x40 ... 0x4f: /* cmovcc */
- dst.val = src.val;
- if ( !test_cc(b, _regs.eflags) )
- dst.type = OP_NONE;
- break;
-
- case 0x90 ... 0x9f: /* setcc */
- dst.val = test_cc(b, _regs.eflags);
- break;
-
- case 0xb0 ... 0xb1: /* cmpxchg */
- /* Save real source value, then compare EAX against destination. */
- src.orig_val = src.val;
- src.val = _regs.eax;
- emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
- if ( _regs.eflags & EFLG_ZF )
- {
- /* Success: write back to memory. */
- dst.val = src.orig_val;
- }
- else
- {
- /* Failure: write the value we saw to EAX. */
- dst.type = OP_REG;
- dst.reg = (unsigned long *)&_regs.eax;
- }
- break;
-
- case 0xa3: bt: /* bt */
- emulate_2op_SrcV_nobyte("bt", src, dst, _regs.eflags);
- dst.type = OP_NONE;
- break;
-
- case 0xa4: /* shld imm8,r,r/m */
- case 0xa5: /* shld %%cl,r,r/m */
- case 0xac: /* shrd imm8,r,r/m */
- case 0xad: /* shrd %%cl,r,r/m */ {
- uint8_t shift, width = dst.bytes << 3;
- shift = (b & 1) ? (uint8_t)_regs.ecx : insn_fetch_type(uint8_t);
- if ( (shift &= width - 1) == 0 )
- break;
- dst.orig_val = truncate_word(dst.val, dst.bytes);
- dst.val = ((shift == width) ? src.val :
- (b & 8) ?
- /* shrd */
- ((dst.orig_val >> shift) |
- truncate_word(src.val << (width - shift), dst.bytes)) :
- /* shld */
- ((dst.orig_val << shift) |
- ((src.val >> (width - shift)) & ((1ull << shift) - 1))));
- dst.val = truncate_word(dst.val, dst.bytes);
- _regs.eflags &= ~(EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_PF|EFLG_CF);
- if ( (dst.val >> ((b & 8) ? (shift - 1) : (width - shift))) & 1 )
- _regs.eflags |= EFLG_CF;
- if ( ((dst.val ^ dst.orig_val) >> (width - 1)) & 1 )
- _regs.eflags |= EFLG_OF;
- _regs.eflags |= ((dst.val >> (width - 1)) & 1) ? EFLG_SF : 0;
- _regs.eflags |= (dst.val == 0) ? EFLG_ZF : 0;
- _regs.eflags |= even_parity(dst.val) ? EFLG_PF : 0;
- break;
- }
-
- case 0xb3: btr: /* btr */
- emulate_2op_SrcV_nobyte("btr", src, dst, _regs.eflags);
- break;
-
- case 0xab: bts: /* bts */
- emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags);
- break;
- case 0xaf: /* imul */
- _regs.eflags &= ~(EFLG_OF|EFLG_CF);
- switch ( dst.bytes )
+ case 0xfe: /* Grp4 */
+ generate_exception_if((modrm_reg & 7) >= 2, EXC_UD, -1);
+ case 0xff: /* Grp5 */
+ switch ( modrm_reg & 7 )
{
- case 2:
- dst.val = ((uint32_t)(int16_t)src.val *
- (uint32_t)(int16_t)dst.val);
- if ( (int16_t)dst.val != (uint32_t)dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
+ case 0: /* inc */
+ emulate_1op("inc", dst, _regs.eflags);
break;
-#ifdef __x86_64__
- case 4:
- dst.val = ((uint64_t)(int32_t)src.val *
- (uint64_t)(int32_t)dst.val);
- if ( (int32_t)dst.val != dst.val )
- _regs.eflags |= EFLG_OF|EFLG_CF;
+ case 1: /* dec */
+ emulate_1op("dec", dst, _regs.eflags);
break;
-#endif
- default: {
- unsigned long m[2] = { src.val, dst.val };
- if ( imul_dbl(m) )
- _regs.eflags |= EFLG_OF|EFLG_CF;
- dst.val = m[0];
+ case 2: /* call (near) */
+ case 4: /* jmp (near) */
+ if ( (dst.bytes != 8) && mode_64bit() )
+ {
+ dst.bytes = op_bytes = 8;
+ if ( dst.type == OP_REG )
+ dst.val = *dst.reg;
+ else if ( (rc = read_ulong(dst.mem.seg, dst.mem.off,
+ &dst.val, 8, ctxt, ops)) != 0 )
+ goto done;
+ }
+ src.val = _regs.eip;
+ _regs.eip = dst.val;
+ if ( (modrm_reg & 7) == 2 )
+ goto push; /* call */
+ dst.type = OP_NONE;
break;
- }
- }
- break;
+ case 3: /* call (far, absolute indirect) */
+ case 5: /* jmp (far, absolute indirect) */ {
+ unsigned long sel;
- case 0xb2: /* lss */
- dst.val = x86_seg_ss;
- goto les;
+ generate_exception_if(dst.type != OP_MEM, EXC_UD, -1);
- case 0xb4: /* lfs */
- dst.val = x86_seg_fs;
- goto les;
+ if ( (rc = read_ulong(dst.mem.seg, dst.mem.off+dst.bytes,
+ &sel, 2, ctxt, ops)) )
+ goto done;
- case 0xb5: /* lgs */
- dst.val = x86_seg_gs;
- goto les;
+ if ( (modrm_reg & 7) == 3 ) /* call */
+ {
+ struct segment_register reg;
+ fail_if(ops->read_segment == NULL);
+ if ( (rc = ops->read_segment(x86_seg_cs, ®, ctxt)) ||
+ (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ ®.sel, op_bytes, ctxt)) ||
+ (rc = ops->write(x86_seg_ss, sp_pre_dec(op_bytes),
+ &_regs.eip, op_bytes, ctxt)) )
+ goto done;
+ }
- case 0xb6: /* movzx rm8,r{16,32,64} */
- /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
- dst.reg = decode_register(modrm_reg, &_regs, 0);
- dst.bytes = op_bytes;
- dst.val = (uint8_t)src.val;
- break;
+ if ( (rc = load_seg(x86_seg_cs, sel, ctxt, ops)) != 0 )
+ goto done;
+ _regs.eip = dst.val;
- case 0xbc: /* bsf */ {
- int zf;
- asm ( "bsf %2,%0; setz %b1"
- : "=r" (dst.val), "=q" (zf)
- : "r" (src.val), "1" (0) );
- _regs.eflags &= ~EFLG_ZF;
- if ( zf )
- {
- _regs.eflags |= EFLG_ZF;
dst.type = OP_NONE;
+ break;
}
- break;
- }
-
- case 0xbd: /* bsr */ {
- int zf;
- asm ( "bsr %2,%0; setz %b1"
- : "=r" (dst.val), "=q" (zf)
- : "r" (src.val), "1" (0) );
- _regs.eflags &= ~EFLG_ZF;
- if ( zf )
- {
- _regs.eflags |= EFLG_ZF;
+ case 6: /* push */
+ /* 64-bit mode: PUSH defaults to a 64-bit operand. */
+ if ( mode_64bit() && (dst.bytes == 4) )
+ {
+ dst.bytes = 8;
+ if ( dst.type == OP_REG )
+ dst.val = *dst.reg;
+ else if ( (rc = read_ulong(dst.mem.seg, dst.mem.off,
+ &dst.val, 8, ctxt, ops)) != 0 )
+ goto done;
+ }
+ if ( (rc = ops->write(x86_seg_ss, sp_pre_dec(dst.bytes),
+ &dst.val, dst.bytes, ctxt)) != 0 )
+ goto done;
dst.type = OP_NONE;
- }
- break;
- }
-
- case 0xb7: /* movzx rm16,r{16,32,64} */
- dst.val = (uint16_t)src.val;
- break;
-
- case 0xbb: btc: /* btc */
- emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags);
+ break;
+ case 7:
+ generate_exception_if(1, EXC_UD, -1);
+ default:
+ goto cannot_emulate;
+ }
break;
+ }
- case 0xba: /* Grp8 */
- switch ( modrm_reg & 7 )
+ writeback:
+ switch ( dst.type )
+ {
+ case OP_REG:
+ /* The 4-byte case *is* correct: in 64-bit mode we zero-extend. */
+ switch ( dst.bytes )
{
- case 4: goto bt;
- case 5: goto bts;
- case 6: goto btr;
- case 7: goto btc;
- default: generate_exception_if(1, EXC_UD, -1);
+ case 1: *(uint8_t *)dst.reg = (uint8_t)dst.val; break;
+ case 2: *(uint16_t *)dst.reg = (uint16_t)dst.val; break;
+ case 4: *dst.reg = (uint32_t)dst.val; break; /* 64b: zero-ext */
+ case 8: *dst.reg = dst.val; break;
}
break;
-
- case 0xbe: /* movsx rm8,r{16,32,64} */
- /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
- dst.reg = decode_register(modrm_reg, &_regs, 0);
- dst.bytes = op_bytes;
- dst.val = (int8_t)src.val;
+ case OP_MEM:
+ if ( !(d & Mov) && (dst.orig_val == dst.val) &&
+ !ctxt->force_writeback )
+ /* nothing to do */;
+ else if ( lock_prefix )
+ rc = ops->cmpxchg(
+ dst.mem.seg, dst.mem.off, &dst.orig_val,
+ &dst.val, dst.bytes, ctxt);
+ else
+ rc = ops->write(
+ dst.mem.seg, dst.mem.off, &dst.val, dst.bytes, ctxt);
+ if ( rc != 0 )
+ goto done;
+ default:
break;
+ }
- case 0xbf: /* movsx rm16,r{16,32,64} */
- dst.val = (int16_t)src.val;
- break;
+ /* Inject #DB if single-step tracing was enabled at instruction start. */
+ if ( (ctxt->regs->eflags & EFLG_TF) && (rc == X86EMUL_OKAY) &&
+ (ops->inject_hw_exception != NULL) )
+ rc = ops->inject_hw_exception(EXC_DB, -1, ctxt) ? : X86EMUL_EXCEPTION;
- case 0xc0 ... 0xc1: /* xadd */
- /* Write back the register source. */
- switch ( dst.bytes )
- {
- case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break;
- case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break;
- case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */
- case 8: *src.reg = dst.val; break;
- }
- goto add;
- }
- goto writeback;
+ /* Commit shadow register state. */
+ _regs.eflags &= ~EFLG_RF;
+ *ctxt->regs = _regs;
+
+ done:
+ return rc;
- twobyte_special_insn:
+ twobyte_insn:
switch ( b )
{
case 0x01: /* Grp7 */ {
break;
}
+ case 0x40 ... 0x4f: /* cmovcc */
+ dst.val = src.val;
+ if ( !test_cc(b, _regs.eflags) )
+ dst.type = OP_NONE;
+ break;
+
case 0x6f: /* movq mm/m64,mm */ {
uint8_t stub[] = { 0x0f, 0x6f, modrm, 0xc3 };
struct fpu_insn_ctxt fic = { .insn_bytes = sizeof(stub)-1 };
break;
}
+ case 0x90 ... 0x9f: /* setcc */
+ dst.val = test_cc(b, _regs.eflags);
+ break;
+
case 0xa0: /* push %%fs */
src.val = x86_seg_fs;
goto push_seg;
src.val = x86_seg_gs;
goto pop_seg;
+ case 0xb0 ... 0xb1: /* cmpxchg */
+ /* Save real source value, then compare EAX against destination. */
+ src.orig_val = src.val;
+ src.val = _regs.eax;
+ emulate_2op_SrcV("cmp", src, dst, _regs.eflags);
+ if ( _regs.eflags & EFLG_ZF )
+ {
+ /* Success: write back to memory. */
+ dst.val = src.orig_val;
+ }
+ else
+ {
+ /* Failure: write the value we saw to EAX. */
+ dst.type = OP_REG;
+ dst.reg = (unsigned long *)&_regs.eax;
+ }
+ break;
+
+ case 0xa3: bt: /* bt */
+ emulate_2op_SrcV_nobyte("bt", src, dst, _regs.eflags);
+ dst.type = OP_NONE;
+ break;
+
+ case 0xa4: /* shld imm8,r,r/m */
+ case 0xa5: /* shld %%cl,r,r/m */
+ case 0xac: /* shrd imm8,r,r/m */
+ case 0xad: /* shrd %%cl,r,r/m */ {
+ uint8_t shift, width = dst.bytes << 3;
+ shift = (b & 1) ? (uint8_t)_regs.ecx : insn_fetch_type(uint8_t);
+ if ( (shift &= width - 1) == 0 )
+ break;
+ dst.orig_val = truncate_word(dst.val, dst.bytes);
+ dst.val = ((shift == width) ? src.val :
+ (b & 8) ?
+ /* shrd */
+ ((dst.orig_val >> shift) |
+ truncate_word(src.val << (width - shift), dst.bytes)) :
+ /* shld */
+ ((dst.orig_val << shift) |
+ ((src.val >> (width - shift)) & ((1ull << shift) - 1))));
+ dst.val = truncate_word(dst.val, dst.bytes);
+ _regs.eflags &= ~(EFLG_OF|EFLG_SF|EFLG_ZF|EFLG_PF|EFLG_CF);
+ if ( (dst.val >> ((b & 8) ? (shift - 1) : (width - shift))) & 1 )
+ _regs.eflags |= EFLG_CF;
+ if ( ((dst.val ^ dst.orig_val) >> (width - 1)) & 1 )
+ _regs.eflags |= EFLG_OF;
+ _regs.eflags |= ((dst.val >> (width - 1)) & 1) ? EFLG_SF : 0;
+ _regs.eflags |= (dst.val == 0) ? EFLG_ZF : 0;
+ _regs.eflags |= even_parity(dst.val) ? EFLG_PF : 0;
+ break;
+ }
+
+ case 0xb3: btr: /* btr */
+ emulate_2op_SrcV_nobyte("btr", src, dst, _regs.eflags);
+ break;
+
+ case 0xab: bts: /* bts */
+ emulate_2op_SrcV_nobyte("bts", src, dst, _regs.eflags);
+ break;
+
+ case 0xaf: /* imul */
+ _regs.eflags &= ~(EFLG_OF|EFLG_CF);
+ switch ( dst.bytes )
+ {
+ case 2:
+ dst.val = ((uint32_t)(int16_t)src.val *
+ (uint32_t)(int16_t)dst.val);
+ if ( (int16_t)dst.val != (uint32_t)dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ break;
+#ifdef __x86_64__
+ case 4:
+ dst.val = ((uint64_t)(int32_t)src.val *
+ (uint64_t)(int32_t)dst.val);
+ if ( (int32_t)dst.val != dst.val )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ break;
+#endif
+ default: {
+ unsigned long m[2] = { src.val, dst.val };
+ if ( imul_dbl(m) )
+ _regs.eflags |= EFLG_OF|EFLG_CF;
+ dst.val = m[0];
+ break;
+ }
+ }
+ break;
+
+ case 0xb2: /* lss */
+ dst.val = x86_seg_ss;
+ goto les;
+
+ case 0xb4: /* lfs */
+ dst.val = x86_seg_fs;
+ goto les;
+
+ case 0xb5: /* lgs */
+ dst.val = x86_seg_gs;
+ goto les;
+
+ case 0xb6: /* movzx rm8,r{16,32,64} */
+ /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
+ dst.reg = decode_register(modrm_reg, &_regs, 0);
+ dst.bytes = op_bytes;
+ dst.val = (uint8_t)src.val;
+ break;
+
+ case 0xbc: /* bsf */ {
+ int zf;
+ asm ( "bsf %2,%0; setz %b1"
+ : "=r" (dst.val), "=q" (zf)
+ : "r" (src.val), "1" (0) );
+ _regs.eflags &= ~EFLG_ZF;
+ if ( zf )
+ {
+ _regs.eflags |= EFLG_ZF;
+ dst.type = OP_NONE;
+ }
+ break;
+ }
+
+ case 0xbd: /* bsr */ {
+ int zf;
+ asm ( "bsr %2,%0; setz %b1"
+ : "=r" (dst.val), "=q" (zf)
+ : "r" (src.val), "1" (0) );
+ _regs.eflags &= ~EFLG_ZF;
+ if ( zf )
+ {
+ _regs.eflags |= EFLG_ZF;
+ dst.type = OP_NONE;
+ }
+ break;
+ }
+
+ case 0xb7: /* movzx rm16,r{16,32,64} */
+ dst.val = (uint16_t)src.val;
+ break;
+
+ case 0xbb: btc: /* btc */
+ emulate_2op_SrcV_nobyte("btc", src, dst, _regs.eflags);
+ break;
+
+ case 0xba: /* Grp8 */
+ switch ( modrm_reg & 7 )
+ {
+ case 4: goto bt;
+ case 5: goto bts;
+ case 6: goto btr;
+ case 7: goto btc;
+ default: generate_exception_if(1, EXC_UD, -1);
+ }
+ break;
+
+ case 0xbe: /* movsx rm8,r{16,32,64} */
+ /* Recompute DstReg as we may have decoded AH/BH/CH/DH. */
+ dst.reg = decode_register(modrm_reg, &_regs, 0);
+ dst.bytes = op_bytes;
+ dst.val = (int8_t)src.val;
+ break;
+
+ case 0xbf: /* movsx rm16,r{16,32,64} */
+ dst.val = (int16_t)src.val;
+ break;
+
+ case 0xc0 ... 0xc1: /* xadd */
+ /* Write back the register source. */
+ switch ( dst.bytes )
+ {
+ case 1: *(uint8_t *)src.reg = (uint8_t)dst.val; break;
+ case 2: *(uint16_t *)src.reg = (uint16_t)dst.val; break;
+ case 4: *src.reg = (uint32_t)dst.val; break; /* 64b reg: zero-extend */
+ case 8: *src.reg = dst.val; break;
+ }
+ goto add;
+
case 0xc7: /* Grp9 (cmpxchg8b/cmpxchg16b) */ {
unsigned long old[2], exp[2], new[2];
unsigned int i;