#include #include #include #include #include #include #include "cpu-i386.h" static uint8_t *gen_code_ptr; int __op_param1, __op_param2, __op_param3; /* supress that */ static void error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vfprintf(stderr, fmt, ap); va_end(ap); exit(1); } #define PREFIX_REPZ 1 #define PREFIX_REPNZ 2 #define PREFIX_LOCK 4 #define PREFIX_CS 8 #define PREFIX_SS 0x10 #define PREFIX_DS 0x20 #define PREFIX_ES 0x40 #define PREFIX_FS 0x80 #define PREFIX_GS 0x100 #define PREFIX_DATA 0x200 #define PREFIX_ADR 0x400 #define PREFIX_FWAIT 0x800 typedef struct DisasContext { /* current insn context */ int prefix; int aflag, dflag; uint8_t *pc; /* current pc */ uint8_t *runtime_pc; /* current pc in the runtime generated code */ int cc_op; /* current CC operation */ int f_st; } DisasContext; /* i386 arith/logic operations */ enum { OP_ADDL, OP_ORL, OP_ADCL, OP_SBBL, OP_ANDL, OP_SUBL, OP_XORL, OP_CMPL, }; /* i386 shift ops */ enum { OP_ROL, OP_ROR, OP_RCL, OP_RCR, OP_SHL, OP_SHR, OP_SHL1, /* undocumented */ OP_SAR = 7, }; static const int fp_ops[8] = { #if 0 OP_FADDQ, OP_FMULQ, OP_CMP, OP_CMP, OP_FSUBQ, OP_FSUBQ, OP_FDIVQ, OP_FDIVQ #endif }; extern char cc_table, rclw_table, rclb_table; extern char helper_rcll_T0_T1_cc; extern char __udivdi3, __umoddi3; #include "op-i386.h" /* operand size */ enum { OT_BYTE = 0, OT_WORD, OT_LONG, OT_QUAD, }; enum { /* I386 int registers */ OR_EAX, /* MUST be even numbered */ OR_ECX, OR_EDX, OR_EBX, OR_ESP, OR_EBP, OR_ESI, OR_EDI, /* I386 float registers */ OR_ST0, OR_ST1, OR_ST2, OR_ST3, OR_ST4, OR_ST5, OR_ST6, OR_ST7, OR_TMP0, /* temporary operand register */ OR_TMP1, OR_A0, /* temporary register used when doing address evaluation */ OR_EFLAGS, /* cpu flags */ OR_ITMP0, /* used for byte/word insertion */ OR_ITMP1, /* used for byte/word insertion */ OR_ITMP2, /* used for byte/word insertion */ OR_FTMP0, /* float temporary */ OR_DF, /* D flag, for string ops */ OR_ZERO, /* fixed zero register */ OR_IM, /* dummy immediate value register */ NB_OREGS, }; #if 0 static const double tab_const[7] = { 1.0, 3.32192809488736234789, /* log2(10) */ M_LOG2E, M_PI, 0.30102999566398119521, /* log10(2) */ M_LN2, 0.0 }; #endif typedef void (GenOpFunc)(void); typedef void (GenOpFunc1)(long); typedef void (GenOpFunc2)(long, long); static GenOpFunc *gen_op_mov_reg_T0[3][8] = { [OT_BYTE] = { gen_op_movb_EAX_T0, gen_op_movb_ECX_T0, gen_op_movb_EDX_T0, gen_op_movb_EBX_T0, gen_op_movh_EAX_T0, gen_op_movh_ECX_T0, gen_op_movh_EDX_T0, gen_op_movh_EBX_T0, }, [OT_WORD] = { gen_op_movw_EAX_T0, gen_op_movw_ECX_T0, gen_op_movw_EDX_T0, gen_op_movw_EBX_T0, gen_op_movw_ESP_T0, gen_op_movw_EBP_T0, gen_op_movw_ESI_T0, gen_op_movw_EDI_T0, }, [OT_LONG] = { gen_op_movl_EAX_T0, gen_op_movl_ECX_T0, gen_op_movl_EDX_T0, gen_op_movl_EBX_T0, gen_op_movl_ESP_T0, gen_op_movl_EBP_T0, gen_op_movl_ESI_T0, gen_op_movl_EDI_T0, }, }; static GenOpFunc *gen_op_mov_reg_T1[3][8] = { [OT_BYTE] = { gen_op_movb_EAX_T1, gen_op_movb_ECX_T1, gen_op_movb_EDX_T1, gen_op_movb_EBX_T1, gen_op_movh_EAX_T1, gen_op_movh_ECX_T1, gen_op_movh_EDX_T1, gen_op_movh_EBX_T1, }, [OT_WORD] = { gen_op_movw_EAX_T1, gen_op_movw_ECX_T1, gen_op_movw_EDX_T1, gen_op_movw_EBX_T1, gen_op_movw_ESP_T1, gen_op_movw_EBP_T1, gen_op_movw_ESI_T1, gen_op_movw_EDI_T1, }, [OT_LONG] = { gen_op_movl_EAX_T1, gen_op_movl_ECX_T1, gen_op_movl_EDX_T1, gen_op_movl_EBX_T1, gen_op_movl_ESP_T1, gen_op_movl_EBP_T1, gen_op_movl_ESI_T1, gen_op_movl_EDI_T1, }, }; static GenOpFunc *gen_op_mov_reg_A0[2][8] = { [0] = { gen_op_movw_EAX_A0, gen_op_movw_ECX_A0, gen_op_movw_EDX_A0, gen_op_movw_EBX_A0, gen_op_movw_ESP_A0, gen_op_movw_EBP_A0, gen_op_movw_ESI_A0, gen_op_movw_EDI_A0, }, [1] = { gen_op_movl_EAX_A0, gen_op_movl_ECX_A0, gen_op_movl_EDX_A0, gen_op_movl_EBX_A0, gen_op_movl_ESP_A0, gen_op_movl_EBP_A0, gen_op_movl_ESI_A0, gen_op_movl_EDI_A0, }, }; static GenOpFunc *gen_op_mov_TN_reg[3][2][8] = { [OT_BYTE] = { { gen_op_movl_T0_EAX, gen_op_movl_T0_ECX, gen_op_movl_T0_EDX, gen_op_movl_T0_EBX, gen_op_movh_T0_EAX, gen_op_movh_T0_ECX, gen_op_movh_T0_EDX, gen_op_movh_T0_EBX, }, { gen_op_movl_T1_EAX, gen_op_movl_T1_ECX, gen_op_movl_T1_EDX, gen_op_movl_T1_EBX, gen_op_movh_T1_EAX, gen_op_movh_T1_ECX, gen_op_movh_T1_EDX, gen_op_movh_T1_EBX, }, }, [OT_WORD] = { { gen_op_movl_T0_EAX, gen_op_movl_T0_ECX, gen_op_movl_T0_EDX, gen_op_movl_T0_EBX, gen_op_movl_T0_ESP, gen_op_movl_T0_EBP, gen_op_movl_T0_ESI, gen_op_movl_T0_EDI, }, { gen_op_movl_T1_EAX, gen_op_movl_T1_ECX, gen_op_movl_T1_EDX, gen_op_movl_T1_EBX, gen_op_movl_T1_ESP, gen_op_movl_T1_EBP, gen_op_movl_T1_ESI, gen_op_movl_T1_EDI, }, }, [OT_LONG] = { { gen_op_movl_T0_EAX, gen_op_movl_T0_ECX, gen_op_movl_T0_EDX, gen_op_movl_T0_EBX, gen_op_movl_T0_ESP, gen_op_movl_T0_EBP, gen_op_movl_T0_ESI, gen_op_movl_T0_EDI, }, { gen_op_movl_T1_EAX, gen_op_movl_T1_ECX, gen_op_movl_T1_EDX, gen_op_movl_T1_EBX, gen_op_movl_T1_ESP, gen_op_movl_T1_EBP, gen_op_movl_T1_ESI, gen_op_movl_T1_EDI, }, }, }; static GenOpFunc *gen_op_movl_A0_reg[8] = { gen_op_movl_A0_EAX, gen_op_movl_A0_ECX, gen_op_movl_A0_EDX, gen_op_movl_A0_EBX, gen_op_movl_A0_ESP, gen_op_movl_A0_EBP, gen_op_movl_A0_ESI, gen_op_movl_A0_EDI, }; static GenOpFunc *gen_op_addl_A0_reg_sN[4][8] = { [0] = { gen_op_addl_A0_EAX, gen_op_addl_A0_ECX, gen_op_addl_A0_EDX, gen_op_addl_A0_EBX, gen_op_addl_A0_ESP, gen_op_addl_A0_EBP, gen_op_addl_A0_ESI, gen_op_addl_A0_EDI, }, [1] = { gen_op_addl_A0_EAX_s1, gen_op_addl_A0_ECX_s1, gen_op_addl_A0_EDX_s1, gen_op_addl_A0_EBX_s1, gen_op_addl_A0_ESP_s1, gen_op_addl_A0_EBP_s1, gen_op_addl_A0_ESI_s1, gen_op_addl_A0_EDI_s1, }, [2] = { gen_op_addl_A0_EAX_s2, gen_op_addl_A0_ECX_s2, gen_op_addl_A0_EDX_s2, gen_op_addl_A0_EBX_s2, gen_op_addl_A0_ESP_s2, gen_op_addl_A0_EBP_s2, gen_op_addl_A0_ESI_s2, gen_op_addl_A0_EDI_s2, }, [3] = { gen_op_addl_A0_EAX_s3, gen_op_addl_A0_ECX_s3, gen_op_addl_A0_EDX_s3, gen_op_addl_A0_EBX_s3, gen_op_addl_A0_ESP_s3, gen_op_addl_A0_EBP_s3, gen_op_addl_A0_ESI_s3, gen_op_addl_A0_EDI_s3, }, }; static GenOpFunc *gen_op_arith_T0_T1_cc[8] = { gen_op_addl_T0_T1_cc, gen_op_orl_T0_T1_cc, gen_op_adcl_T0_T1_cc, gen_op_sbbl_T0_T1_cc, gen_op_andl_T0_T1_cc, gen_op_subl_T0_T1_cc, gen_op_xorl_T0_T1_cc, gen_op_cmpl_T0_T1_cc, }; static const int cc_op_arithb[8] = { CC_OP_ADDB, CC_OP_LOGICB, CC_OP_ADDB, CC_OP_SUBB, CC_OP_LOGICB, CC_OP_SUBB, CC_OP_LOGICB, CC_OP_SUBB, }; static GenOpFunc *gen_op_shift_T0_T1_cc[3][8] = { [OT_BYTE] = { gen_op_rolb_T0_T1_cc, gen_op_rorb_T0_T1_cc, gen_op_rclb_T0_T1_cc, gen_op_rcrb_T0_T1_cc, gen_op_shlb_T0_T1_cc, gen_op_shrb_T0_T1_cc, gen_op_shlb_T0_T1_cc, gen_op_sarb_T0_T1_cc, }, [OT_WORD] = { gen_op_rolw_T0_T1_cc, gen_op_rorw_T0_T1_cc, gen_op_rclw_T0_T1_cc, gen_op_rcrw_T0_T1_cc, gen_op_shlw_T0_T1_cc, gen_op_shrw_T0_T1_cc, gen_op_shlw_T0_T1_cc, gen_op_sarw_T0_T1_cc, }, [OT_LONG] = { gen_op_roll_T0_T1_cc, gen_op_rorl_T0_T1_cc, gen_op_rcll_T0_T1_cc, gen_op_rcrl_T0_T1_cc, gen_op_shll_T0_T1_cc, gen_op_shrl_T0_T1_cc, gen_op_shll_T0_T1_cc, gen_op_sarl_T0_T1_cc, }, }; static GenOpFunc *gen_op_lds_T0_A0[3] = { gen_op_ldsb_T0_A0, gen_op_ldsw_T0_A0, }; static GenOpFunc *gen_op_ldu_T0_A0[3] = { gen_op_ldub_T0_A0, gen_op_lduw_T0_A0, }; /* sign does not matter */ static GenOpFunc *gen_op_ld_T0_A0[3] = { gen_op_ldub_T0_A0, gen_op_lduw_T0_A0, gen_op_ldl_T0_A0, }; static GenOpFunc *gen_op_ld_T1_A0[3] = { gen_op_ldub_T1_A0, gen_op_lduw_T1_A0, gen_op_ldl_T1_A0, }; static GenOpFunc *gen_op_st_T0_A0[3] = { gen_op_stb_T0_A0, gen_op_stw_T0_A0, gen_op_stl_T0_A0, }; static GenOpFunc *gen_op_movs[6] = { gen_op_movsb, gen_op_movsw, gen_op_movsl, gen_op_rep_movsb, gen_op_rep_movsw, gen_op_rep_movsl, }; static GenOpFunc *gen_op_stos[6] = { gen_op_stosb, gen_op_stosw, gen_op_stosl, gen_op_rep_stosb, gen_op_rep_stosw, gen_op_rep_stosl, }; static GenOpFunc *gen_op_lods[6] = { gen_op_lodsb, gen_op_lodsw, gen_op_lodsl, gen_op_rep_lodsb, gen_op_rep_lodsw, gen_op_rep_lodsl, }; static GenOpFunc *gen_op_scas[9] = { gen_op_scasb, gen_op_scasw, gen_op_scasl, gen_op_repz_scasb, gen_op_repz_scasw, gen_op_repz_scasl, gen_op_repnz_scasb, gen_op_repnz_scasw, gen_op_repnz_scasl, }; static GenOpFunc *gen_op_cmps[9] = { gen_op_cmpsb, gen_op_cmpsw, gen_op_cmpsl, gen_op_repz_cmpsb, gen_op_repz_cmpsw, gen_op_repz_cmpsl, gen_op_repnz_cmpsb, gen_op_repnz_cmpsw, gen_op_repnz_cmpsl, }; static GenOpFunc *gen_op_ins[6] = { gen_op_insb, gen_op_insw, gen_op_insl, gen_op_rep_insb, gen_op_rep_insw, gen_op_rep_insl, }; static GenOpFunc *gen_op_outs[6] = { gen_op_outsb, gen_op_outsw, gen_op_outsl, gen_op_rep_outsb, gen_op_rep_outsw, gen_op_rep_outsl, }; enum { JCC_O, JCC_B, JCC_Z, JCC_BE, JCC_S, JCC_P, JCC_L, JCC_LE, }; static GenOpFunc2 *gen_jcc_slow[8] = { gen_op_jo_cc, gen_op_jb_cc, gen_op_jz_cc, gen_op_jbe_cc, gen_op_js_cc, gen_op_jp_cc, gen_op_jl_cc, gen_op_jle_cc, }; static GenOpFunc2 *gen_jcc_sub[3][8] = { [OT_BYTE] = { NULL, gen_op_jb_subb, gen_op_jz_subb, gen_op_jbe_subb, gen_op_js_subb, NULL, gen_op_jl_subb, gen_op_jle_subb, }, [OT_WORD] = { NULL, gen_op_jb_subw, gen_op_jz_subw, gen_op_jbe_subw, gen_op_js_subw, NULL, gen_op_jl_subw, gen_op_jle_subw, }, [OT_LONG] = { NULL, gen_op_jb_subl, gen_op_jz_subl, gen_op_jbe_subl, gen_op_js_subl, NULL, gen_op_jl_subl, gen_op_jle_subl, }, }; static GenOpFunc *gen_setcc_slow[8] = { gen_op_seto_T0_cc, gen_op_setb_T0_cc, gen_op_setz_T0_cc, gen_op_setbe_T0_cc, gen_op_sets_T0_cc, gen_op_setp_T0_cc, gen_op_setl_T0_cc, gen_op_setle_T0_cc, }; static GenOpFunc *gen_setcc_sub[3][8] = { [OT_BYTE] = { NULL, gen_op_setb_T0_subb, gen_op_setz_T0_subb, gen_op_setbe_T0_subb, gen_op_sets_T0_subb, NULL, gen_op_setl_T0_subb, gen_op_setle_T0_subb, }, [OT_WORD] = { NULL, gen_op_setb_T0_subw, gen_op_setz_T0_subw, gen_op_setbe_T0_subw, gen_op_sets_T0_subw, NULL, gen_op_setl_T0_subw, gen_op_setle_T0_subw, }, [OT_LONG] = { NULL, gen_op_setb_T0_subl, gen_op_setz_T0_subl, gen_op_setbe_T0_subl, gen_op_sets_T0_subl, NULL, gen_op_setl_T0_subl, gen_op_setle_T0_subl, }, }; static void gen_op(DisasContext *s1, int op, int ot, int d, int s) { if (d != OR_TMP0) gen_op_mov_TN_reg[ot][0][d](); if (s != OR_TMP1) gen_op_mov_TN_reg[ot][1][s](); if ((op == OP_ADCL || op == OP_SBBL) && s1->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s1->cc_op); gen_op_arith_T0_T1_cc[op](); if (d != OR_TMP0 && op != OP_CMPL) gen_op_mov_reg_T0[ot][d](); s1->cc_op = cc_op_arithb[op] + ot; } static void gen_opi(DisasContext *s1, int op, int ot, int d, int c) { gen_op1_movl_T1_im(c); gen_op(s1, op, ot, d, OR_TMP0); } static void gen_inc(DisasContext *s1, int ot, int d, int c) { if (d != OR_TMP0) gen_op_mov_TN_reg[ot][0][d](); if (s1->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s1->cc_op); if (c > 0) gen_op_incl_T0_cc(); else gen_op_decl_T0_cc(); if (d != OR_TMP0) gen_op_mov_reg_T0[ot][d](); } static void gen_shift(DisasContext *s1, int op, int ot, int d, int s) { if (d != OR_TMP0) gen_op_mov_TN_reg[ot][0][d](); if (s != OR_TMP1) gen_op_mov_TN_reg[ot][1][s](); switch(op) { case OP_ROL: case OP_ROR: case OP_RCL: case OP_RCR: /* only C and O are modified, so we must update flags dynamically */ if (s1->cc_op != CC_OP_DYNAMIC) gen_op_set_cc_op(s1->cc_op); gen_op_shift_T0_T1_cc[ot][op](); break; default: gen_op_shift_T0_T1_cc[ot][op](); break; } if (d != OR_TMP0) gen_op_mov_reg_T0[ot][d](); s1->cc_op = CC_OP_DYNAMIC; /* cannot predict flags after */ } static void gen_shifti(DisasContext *s1, int op, int ot, int d, int c) { /* currently not optimized */ gen_op1_movl_T1_im(c); gen_shift(s1, op, ot, d, OR_TMP1); } static void gen_lea_modrm(DisasContext *s, int modrm, int *reg_ptr, int *offset_ptr) { int havesib; int havebase; int base, disp; int index = 0; int scale = 0; int reg1, reg2, opreg; int mod, rm, code; mod = (modrm >> 6) & 3; rm = modrm & 7; if (s->aflag) { havesib = 0; havebase = 1; base = rm; if (base == 4) { havesib = 1; code = ldub(s->pc++); scale = (code >> 6) & 3; index = (code >> 3) & 7; base = code & 7; } switch (mod) { case 0: if (base == 5) { havebase = 0; disp = ldl(s->pc); s->pc += 4; } else { disp = 0; } break; case 1: disp = (int8_t)ldub(s->pc++); break; default: case 2: disp = ldl(s->pc); s->pc += 4; break; } reg1 = OR_ZERO; reg2 = OR_ZERO; if (havebase || (havesib && (index != 4 || scale != 0))) { if (havebase) reg1 = OR_EAX + base; if (havesib && index != 4) { if (havebase) reg2 = index + OR_EAX; else reg1 = index + OR_EAX; } } /* XXX: disp only ? */ if (reg2 == OR_ZERO) { /* op: disp + (reg1 << scale) */ if (reg1 == OR_ZERO) { gen_op1_movl_A0_im(disp); } else if (scale == 0 && disp == 0) { gen_op_movl_A0_reg[reg1](); } else { gen_op_addl_A0_reg_sN[scale][reg1](); } } else { /* op: disp + reg1 + (reg2 << scale) */ if (disp != 0) { gen_op1_movl_A0_im(disp); gen_op_addl_A0_reg_sN[0][reg1](); } else { gen_op_movl_A0_reg[reg1](); } gen_op_addl_A0_reg_sN[scale][reg2](); } opreg = OR_A0; } else { fprintf(stderr, "16 bit addressing not supported\n"); disp = 0; opreg = 0; } *reg_ptr = opreg; *offset_ptr = disp; } /* generate modrm memory load or store of 'reg'. TMP0 is used if reg != OR_TMP0 */ static void gen_ldst_modrm(DisasContext *s, int modrm, int ot, int reg, int is_store) { int mod, rm, opreg, disp; mod = (modrm >> 6) & 3; rm = modrm & 7; if (mod == 3) { if (is_store) { if (reg != OR_TMP0) gen_op_mov_TN_reg[ot][0][reg](); gen_op_mov_reg_T0[ot][rm](); } else { gen_op_mov_TN_reg[ot][0][rm](); if (reg != OR_TMP0) gen_op_mov_reg_T0[ot][reg](); } } else { gen_lea_modrm(s, modrm, &opreg, &disp); if (is_store) { if (reg != OR_TMP0) gen_op_mov_TN_reg[ot][0][reg](); gen_op_st_T0_A0[ot](); } else { gen_op_ld_T0_A0[ot](); if (reg != OR_TMP0) gen_op_mov_reg_T0[ot][reg](); } } } static inline uint32_t insn_get(DisasContext *s, int ot) { uint32_t ret; switch(ot) { case OT_BYTE: ret = ldub(s->pc); s->pc++; break; case OT_WORD: ret = lduw(s->pc); s->pc += 2; break; default: case OT_LONG: ret = ldl(s->pc); s->pc += 4; break; } return ret; } static void gen_jcc(DisasContext *s, int b, int val) { int inv, jcc_op; GenOpFunc2 *func; inv = b & 1; jcc_op = (b >> 1) & 7; switch(s->cc_op) { /* we optimize the cmp/jcc case */ case CC_OP_SUBB: case CC_OP_SUBW: case CC_OP_SUBL: func = gen_jcc_sub[s->cc_op - CC_OP_SUBB][jcc_op]; if (!func) goto slow_jcc; break; /* some jumps are easy to compute */ case CC_OP_ADDB: case CC_OP_ADDW: case CC_OP_ADDL: case CC_OP_LOGICB: case CC_OP_LOGICW: case CC_OP_LOGICL: case CC_OP_INCB: case CC_OP_INCW: case CC_OP_INCL: case CC_OP_DECB: case CC_OP_DECW: case CC_OP_DECL: case CC_OP_SHLB: case CC_OP_SHLW: case CC_OP_SHLL: switch(jcc_op) { case JCC_Z: func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 3][jcc_op]; break; case JCC_S: func = gen_jcc_sub[(s->cc_op - CC_OP_ADDB) % 3][jcc_op]; break; default: goto slow_jcc; } break; default: slow_jcc: if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); func = gen_jcc_slow[jcc_op]; break; } if (!inv) { func(val, (long)s->pc); } else { func((long)s->pc, val); } } static void gen_setcc(DisasContext *s, int b) { int inv, jcc_op; GenOpFunc *func; inv = b & 1; jcc_op = (b >> 1) & 7; switch(s->cc_op) { /* we optimize the cmp/jcc case */ case CC_OP_SUBB: case CC_OP_SUBW: case CC_OP_SUBL: func = gen_setcc_sub[s->cc_op - CC_OP_SUBB][jcc_op]; if (!func) goto slow_jcc; break; /* some jumps are easy to compute */ case CC_OP_ADDB: case CC_OP_ADDW: case CC_OP_ADDL: case CC_OP_LOGICB: case CC_OP_LOGICW: case CC_OP_LOGICL: case CC_OP_INCB: case CC_OP_INCW: case CC_OP_INCL: case CC_OP_DECB: case CC_OP_DECW: case CC_OP_DECL: case CC_OP_SHLB: case CC_OP_SHLW: case CC_OP_SHLL: switch(jcc_op) { case JCC_Z: func = gen_setcc_sub[s->cc_op - CC_OP_ADDB][jcc_op]; break; case JCC_S: func = gen_setcc_sub[s->cc_op - CC_OP_ADDB][jcc_op]; break; default: goto slow_jcc; } break; default: slow_jcc: if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); func = gen_setcc_slow[jcc_op]; break; } func(); if (inv) { gen_op_xor_T0_1(); } } /* return the size of the intruction. Return -1 if no insn found */ int disas_insn(DisasContext *s, uint8_t *pc_start) { int b, prefixes, aflag, dflag; int shift, ot; int modrm, reg, rm, mod, reg_addr, op, opreg, offset_addr, val; s->pc = pc_start; prefixes = 0; aflag = 1; dflag = 1; // cur_pc = s->pc; /* for insn generation */ next_byte: b = ldub(s->pc); if (b < 0) return -1; s->pc++; /* check prefixes */ switch (b) { case 0xf3: prefixes |= PREFIX_REPZ; goto next_byte; case 0xf2: prefixes |= PREFIX_REPNZ; goto next_byte; case 0xf0: prefixes |= PREFIX_LOCK; goto next_byte; case 0x2e: prefixes |= PREFIX_CS; goto next_byte; case 0x36: prefixes |= PREFIX_SS; goto next_byte; case 0x3e: prefixes |= PREFIX_DS; goto next_byte; case 0x26: prefixes |= PREFIX_ES; goto next_byte; case 0x64: prefixes |= PREFIX_FS; goto next_byte; case 0x65: prefixes |= PREFIX_GS; goto next_byte; case 0x66: prefixes |= PREFIX_DATA; goto next_byte; case 0x67: prefixes |= PREFIX_ADR; goto next_byte; case 0x9b: prefixes |= PREFIX_FWAIT; goto next_byte; } if (prefixes & PREFIX_DATA) dflag ^= 1; if (prefixes & PREFIX_ADR) aflag ^= 1; s->prefix = prefixes; s->aflag = aflag; s->dflag = dflag; /* now check op code */ reswitch: switch(b) { case 0x0f: /**************************/ /* extended op code */ b = ldub(s->pc++) | 0x100; goto reswitch; /**************************/ /* arith & logic */ case 0x00 ... 0x05: case 0x08 ... 0x0d: case 0x10 ... 0x15: case 0x18 ... 0x1d: case 0x20 ... 0x25: case 0x28 ... 0x2d: case 0x30 ... 0x35: case 0x38 ... 0x3d: { int op, f, val; op = (b >> 3) & 7; f = (b >> 1) & 3; if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; switch(f) { case 0: /* OP Ev, Gv */ modrm = ldub(s->pc++); reg = ((modrm >> 3) & 7) + OR_EAX; mod = (modrm >> 6) & 3; rm = modrm & 7; if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T0_A0[ot](); opreg = OR_TMP0; } else { opreg = OR_EAX + rm; } gen_op(s, op, ot, opreg, reg); if (mod != 3 && op != 7) { gen_op_st_T0_A0[ot](); } break; case 1: /* OP Gv, Ev */ modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; reg = ((modrm >> 3) & 7) + OR_EAX; rm = modrm & 7; if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T1_A0[ot](); opreg = OR_TMP1; } else { opreg = OR_EAX + rm; } gen_op(s, op, ot, reg, opreg); break; case 2: /* OP A, Iv */ val = insn_get(s, ot); gen_opi(s, op, ot, OR_EAX, val); break; } } break; case 0x80: /* GRP1 */ case 0x81: case 0x83: { int val; if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; op = (modrm >> 3) & 7; if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T0_A0[ot](); opreg = OR_TMP0; } else { opreg = rm + OR_EAX; } switch(b) { default: case 0x80: case 0x81: val = insn_get(s, ot); break; case 0x83: val = (int8_t)insn_get(s, OT_BYTE); break; } gen_opi(s, op, ot, opreg, val); if (op != 7 && mod != 3) { gen_op_st_T0_A0[ot](); } } break; /**************************/ /* inc, dec, and other misc arith */ case 0x40 ... 0x47: /* inc Gv */ ot = dflag ? OT_LONG : OT_WORD; gen_inc(s, ot, OR_EAX + (b & 7), 1); break; case 0x48 ... 0x4f: /* dec Gv */ ot = dflag ? OT_LONG : OT_WORD; gen_inc(s, ot, OR_EAX + (b & 7), -1); break; case 0xf6: /* GRP3 */ case 0xf7: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; op = (modrm >> 3) & 7; if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T0_A0[ot](); } else { gen_op_mov_TN_reg[ot][0][rm](); } switch(op) { case 0: /* test */ val = insn_get(s, ot); gen_op1_movl_T1_im(val); gen_op_testl_T0_T1_cc(); s->cc_op = CC_OP_LOGICB + ot; break; case 2: /* not */ gen_op_notl_T0(); if (mod != 3) { gen_op_st_T0_A0[ot](); } else { gen_op_mov_reg_T0[ot][rm](); } break; case 3: /* neg */ gen_op_negl_T0_cc(); if (mod != 3) { gen_op_st_T0_A0[ot](); } else { gen_op_mov_reg_T0[ot][rm](); } s->cc_op = CC_OP_SUBB + ot; break; case 4: /* mul */ switch(ot) { case OT_BYTE: gen_op_mulb_AL_T0(); break; case OT_WORD: gen_op_mulw_AX_T0(); break; default: case OT_LONG: gen_op_mull_EAX_T0(); break; } break; case 5: /* imul */ switch(ot) { case OT_BYTE: gen_op_imulb_AL_T0(); break; case OT_WORD: gen_op_imulw_AX_T0(); break; default: case OT_LONG: gen_op_imull_EAX_T0(); break; } break; case 6: /* div */ switch(ot) { case OT_BYTE: gen_op_divb_AL_T0(); break; case OT_WORD: gen_op_divw_AX_T0(); break; default: case OT_LONG: gen_op_divl_EAX_T0(); break; } break; case 7: /* idiv */ switch(ot) { case OT_BYTE: gen_op_idivb_AL_T0(); break; case OT_WORD: gen_op_idivw_AX_T0(); break; default: case OT_LONG: gen_op_idivl_EAX_T0(); break; } break; default: error("GRP3: bad instruction"); return -1; } break; case 0xfe: /* GRP4 */ case 0xff: /* GRP5 */ if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; op = (modrm >> 3) & 7; if (op >= 2 && b == 0xfe) { error("GRP4: bad instruction"); return -1; } if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T0_A0[ot](); } else { gen_op_mov_TN_reg[ot][0][rm](); } switch(op) { case 0: /* inc Ev */ gen_inc(s, ot, OR_TMP0, 1); if (mod != 3) gen_op_st_T0_A0[ot](); break; case 1: /* dec Ev */ gen_inc(s, ot, OR_TMP0, -1); if (mod != 3) gen_op_st_T0_A0[ot](); break; case 2: /* call Ev */ gen_op1_movl_T1_im((long)s->pc); gen_op_pushl_T1(); gen_op_jmp_T0(); break; case 4: /* jmp Ev */ gen_op_jmp_T0(); break; case 6: /* push Ev */ gen_op_pushl_T0(); break; default: error("GRP5: bad instruction"); return -1; } break; case 0x84: /* test Ev, Gv */ case 0x85: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; reg = (modrm >> 3) & 7; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); gen_op_mov_TN_reg[ot][1][reg + OR_EAX](); gen_op_testl_T0_T1_cc(); s->cc_op = CC_OP_LOGICB + ot; break; case 0xa8: /* test eAX, Iv */ case 0xa9: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; val = insn_get(s, ot); gen_op_mov_TN_reg[ot][0][OR_EAX](); gen_op1_movl_T1_im(val); gen_op_testl_T0_T1_cc(); s->cc_op = CC_OP_LOGICB + ot; break; case 0x98: /* CWDE/CBW */ if (dflag) gen_op_movswl_EAX_AX(); else gen_op_movsbw_AX_AL(); break; case 0x99: /* CDQ/CWD */ if (dflag) gen_op_movslq_EDX_EAX(); else gen_op_movswl_DX_AX(); break; case 0x1af: /* imul Gv, Ev */ case 0x69: /* imul Gv, Ev, I */ case 0x6b: ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = ((modrm >> 3) & 7) + OR_EAX; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); if (b == 0x69) { val = insn_get(s, ot); gen_op1_movl_T1_im(val); } else if (b == 0x6b) { val = insn_get(s, OT_BYTE); gen_op1_movl_T1_im(val); } else { gen_op_mov_TN_reg[ot][1][reg](); } if (ot == OT_LONG) { op_imull_T0_T1(); } else { op_imulw_T0_T1(); } gen_op_mov_reg_T0[ot][reg](); break; /**************************/ /* push/pop */ case 0x50 ... 0x57: /* push */ gen_op_mov_TN_reg[OT_LONG][0][(b & 7)](); gen_op_pushl_T0(); break; case 0x58 ... 0x5f: /* pop */ gen_op_popl_T0(); gen_op_mov_reg_T0[OT_LONG][reg](); break; case 0x68: /* push Iv */ case 0x6a: ot = dflag ? OT_LONG : OT_WORD; if (b == 0x68) val = insn_get(s, ot); else val = (int8_t)insn_get(s, OT_BYTE); gen_op1_movl_T0_im(val); gen_op_pushl_T0(); break; case 0x8f: /* pop Ev */ ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); gen_op_popl_T0(); gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); break; case 0xc9: /* leave */ gen_op_mov_TN_reg[OT_LONG][0][R_EBP](); gen_op_mov_reg_T0[OT_LONG][R_ESP](); gen_op_popl_T0(); gen_op_mov_reg_T0[OT_LONG][R_EBP](); break; /**************************/ /* mov */ case 0x88: case 0x89: /* mov Gv, Ev */ if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; /* generate a generic store */ gen_ldst_modrm(s, modrm, ot, OR_EAX + reg, 1); break; case 0xc6: case 0xc7: /* mov Ev, Iv */ if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; val = insn_get(s, ot); gen_op1_movl_T0_im(val); gen_ldst_modrm(s, modrm, ot, OR_TMP0, 1); break; case 0x8a: case 0x8b: /* mov Ev, Gv */ if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; gen_ldst_modrm(s, modrm, ot, OR_TMP0, 0); gen_op_mov_reg_T0[ot][reg](); break; case 0x1b6: /* movzbS Gv, Eb */ case 0x1b7: /* movzwS Gv, Eb */ case 0x1be: /* movsbS Gv, Eb */ case 0x1bf: /* movswS Gv, Eb */ { int d_ot; /* d_ot is the size of destination */ d_ot = dflag + OT_WORD; /* ot is the size of source */ ot = (b & 1) + OT_BYTE; modrm = ldub(s->pc++); reg = ((modrm >> 3) & 7) + OR_EAX; mod = (modrm >> 6) & 3; rm = modrm & 7; if (mod == 3) { gen_op_mov_TN_reg[ot][0][rm](); switch(ot | (b & 8)) { case OT_BYTE: gen_op_movzbl_T0_T0(); break; case OT_BYTE | 8: gen_op_movsbl_T0_T0(); break; case OT_WORD: gen_op_movzwl_T0_T0(); break; default: case OT_WORD | 8: gen_op_movswl_T0_T0(); break; } gen_op_mov_reg_T0[d_ot][reg](); } else { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); if (b & 8) { gen_op_lds_T0_A0[ot](); } else { gen_op_ldu_T0_A0[ot](); } gen_op_mov_reg_T0[d_ot][reg](); } } break; case 0x8d: /* lea */ ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_mov_reg_A0[ot - OT_WORD][reg](); break; case 0xa0: /* mov EAX, Ov */ case 0xa1: case 0xa2: /* mov Ov, EAX */ case 0xa3: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (s->aflag) offset_addr = insn_get(s, OT_LONG); else offset_addr = insn_get(s, OT_WORD); if ((b & 2) == 0) { gen_op_ld_T0_A0[ot](); gen_op_mov_reg_T0[ot][R_EAX](); } else { gen_op_mov_TN_reg[ot][0][R_EAX](); gen_op_st_T0_A0[ot](); } break; case 0xb0 ... 0xb7: /* mov R, Ib */ val = insn_get(s, OT_BYTE); gen_op1_movl_T0_im(val); gen_op_mov_reg_T0[OT_BYTE][b & 7](); break; case 0xb8 ... 0xbf: /* mov R, Iv */ ot = dflag ? OT_LONG : OT_WORD; val = insn_get(s, ot); reg = OR_EAX + (b & 7); gen_op1_movl_T0_im(val); gen_op_mov_reg_T0[ot][reg](); break; case 0x91 ... 0x97: /* xchg R, EAX */ ot = dflag ? OT_LONG : OT_WORD; reg = b & 7; gen_op_mov_TN_reg[ot][0][reg](); gen_op_mov_TN_reg[ot][1][R_EAX](); gen_op_mov_reg_T0[ot][R_EAX](); gen_op_mov_reg_T1[ot][reg](); break; case 0x86: case 0x87: /* xchg Ev, Gv */ if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); reg = (modrm >> 3) & 7; gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_mov_TN_reg[ot][0][reg](); gen_op_ld_T1_A0[ot](); gen_op_st_T0_A0[ot](); gen_op_mov_reg_T1[ot][reg](); break; /************************/ /* shifts */ case 0xc0: case 0xc1: /* shift Ev,Ib */ shift = 2; grp2: { if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; op = (modrm >> 3) & 7; if (mod != 3) { gen_lea_modrm(s, modrm, ®_addr, &offset_addr); gen_op_ld_T0_A0[ot](); opreg = OR_TMP0; } else { opreg = rm + OR_EAX; } /* simpler op */ if (shift == 0) { gen_shift(s, op, ot, opreg, OR_ECX); } else { if (shift == 2) { shift = ldub(s->pc++); } gen_shifti(s, op, ot, opreg, shift); } if (mod != 3) { gen_op_st_T0_A0[ot](); } } break; case 0xd0: case 0xd1: /* shift Ev,1 */ shift = 1; goto grp2; case 0xd2: case 0xd3: /* shift Ev,cl */ shift = 0; goto grp2; /************************/ /* floats */ #if 0 case 0xd8 ... 0xdf: modrm = ldub(s->pc++); mod = (modrm >> 6) & 3; rm = modrm & 7; op = ((b & 7) << 3) | ((modrm >> 3) & 7); if (mod != 3) { /* memory op */ gen_lea_modrm(s, modrm, ®_addr, &offset_addr); switch(op) { case 0x00 ... 0x07: /* fxxxs */ case 0x10 ... 0x17: /* fixxxl */ case 0x20 ... 0x27: /* fxxxl */ case 0x30 ... 0x37: /* fixxx */ { int op1, swap; op1 = fp_ops[op & 7]; swap = 0; if ((op & 7) == 5 || (op & 7) == 7) swap = 1; switch(op >> 4) { case 0: ot = OT_LONG; is_int = 0; break; case 1: ot = OT_LONG; is_int = 1; break; case 2: ot = OT_QUAD; is_int = 0; break; case 3: default: ot = OT_WORD; is_int = 1; break; } /* if integer, needs to convert to float */ if (is_int) { /* XXX: potential loss of precision if large integer */ gen_ld(OP_LDUB + ot, OR_TMP0, reg_addr, offset_addr); gen_insn2(OP_I2FL, OR_FTMP0, OR_TMP0); } else { gen_ld(OP_LDUB + ot, OR_FTMP0, reg_addr, offset_addr); } if (ot != OT_QUAD) op1 += OP_FADDL - OP_FADDQ; if (!swap) gen_insn3(op1, OR_ST0, OR_ST0, OR_FTMP0); else gen_insn3(op1, OR_ST0, OR_FTMP0, OR_ST0); if ((op & 7) == 3) { /* fcomp needs pop */ gen_insn0(OP_FPOP); } } break; case 0x08: /* flds */ case 0x0a: /* fsts */ case 0x0b: /* fstps */ case 0x18: /* fildl */ case 0x1a: /* fistl */ case 0x1b: /* fistpl */ case 0x28: /* fldl */ case 0x2a: /* fstl */ case 0x2b: /* fstpl */ case 0x38: /* filds */ case 0x3a: /* fists */ case 0x3b: /* fistps */ switch(op >> 4) { case 0: ot = OT_LONG; is_int = 0; break; case 1: ot = OT_LONG; is_int = 1; break; case 2: ot = OT_QUAD; is_int = 0; break; case 3: default: ot = OT_WORD; is_int = 1; break; } switch(op & 7) { case 0: gen_insn0(OP_FPUSH); if (is_int) { /* XXX: potential loss of precision */ gen_ld(OP_LDUB + ot, OR_TMP0, reg_addr, offset_addr); gen_insn2(OP_I2FL, OR_ST0, OR_TMP0); } else { gen_ld(OP_LDUB + ot, OR_ST0, reg_addr, offset_addr); } break; default: if (is_int) { gen_insn2(OP_F2IL, OR_TMP0, OR_ST0); gen_st(OP_STB + ot, OR_TMP0, reg_addr, offset_addr); } else { gen_st(OP_STB + ot, OR_ST0, reg_addr, offset_addr); } if ((op & 7) == 3) gen_insn0(OP_FPOP); break; } break; case 0x2f: /* fnstsw mem */ gen_insn3(OP_FNSTS, OR_TMP0, OR_ZERO, OR_ZERO); gen_st(OP_STW, OR_TMP0, reg_addr, offset_addr); break; case 0x3c: /* fbld */ case 0x3e: /* fbstp */ error("float BCD not hanlded"); return -1; case 0x3d: /* fildll */ gen_insn0(OP_FPUSH); gen_ld(OP_LDQ, OR_TMP0, reg_addr, offset_addr); gen_insn2(OP_I2FQ, OR_ST0, OR_TMP0); break; case 0x3f: /* fistpll */ gen_insn2(OP_F2IQ, OR_TMP0, OR_ST0); gen_st(OP_STQ, OR_TMP0, reg_addr, offset_addr); gen_insn0(OP_FPOP); break; default: error("unhandled memory FP\n"); return -1; } } else { /* register float ops */ opreg = rm + OR_ST0; switch(op) { case 0x08: /* fld sti */ gen_insn0(OP_FPUSH); gen_mov(OR_ST0, OR_ST0 + ((rm + 1) & 7)); break; case 0x09: /* fxchg sti */ gen_mov(OR_TMP0, OR_ST0); gen_mov(OR_ST0, opreg); gen_mov(opreg, OR_TMP0); break; case 0x0a: /* grp d9/2 */ switch(rm) { case 0: /* fnop */ gen_insn0(OP_NOP); break; default: error("unhandled FP GRP d9/2\n"); return -1; } break; case 0x0c: /* grp d9/4 */ switch(rm) { case 0: /* fchs */ gen_insn3(OP_FSUBQ, OR_ST0, OR_ZERO, OR_ST0); break; case 1: /* fabs */ gen_insn2(OP_FABSQ, OR_ST0, OR_ST0); break; case 4: /* ftst */ gen_insn3(OP_CMP, OR_ZERO, OR_ST0, OR_ZERO); break; case 5: /* fxam */ gen_insn3(OP_FSPECIAL, OR_ZERO, OR_ST0, OR_ZERO); break; default: return -1; } break; case 0x0d: /* grp d9/5 */ { if (rm == 7) { error("bad GRP d9/5"); return -1; } /* XXX: needs constant load or symbol table */ gen_insn0(OP_FPUSH); gen_ld(OP_LDQ, OR_ST0, OR_ZERO, (rm * 8) + FLOAT_CONST_ADDR); } break; case 0x0e: /* grp d9/6 */ switch(rm) { case 0: /* f2xm1 */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ZERO); break; case 1: /* fyl2x */ gen_insn3(OP_FSPECIAL, OR_ST1, OR_ST0, OR_ST1); gen_insn0(OP_FPOP); break; case 2: /* fptan */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ZERO); gen_insn0(OP_FPUSH); /* load one */ gen_ld(OP_LDQ, OR_ST0, OR_ZERO, (0 * 8) + FLOAT_CONST_ADDR); break; case 3: /* fpatan */ gen_insn3(OP_FSPECIAL, OR_ST1, OR_ST0, OR_ST1); gen_insn0(OP_FPOP); break; case 4: /* fxtract */ gen_insn0(OP_FPUSH); gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST1, OR_ZERO); gen_insn3(OP_FSPECIAL, OR_ST1, OR_ST1, OR_ZERO); break; case 5: /* fprem1 */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ST1); break; case 6: /* fdecstp */ gen_insn0(OP_FPUSH); break; default: case 7: /* fdecstp */ gen_insn0(OP_FPOP); break; } break; case 0x0f: /* grp d9/7 */ switch(rm) { case 0: /* fprem */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ST1); break; case 1: /* fyl2xp1 */ gen_insn3(OP_FSPECIAL, OR_ST1, OR_ST0, OR_ST1); gen_insn0(OP_FPOP); break; case 3: /* fsincos */ gen_insn0(OP_FPUSH); gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST1, OR_ZERO); gen_insn3(OP_FSPECIAL, OR_ST1, OR_ST1, OR_ZERO); break; case 5: /* fscale */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ST1); break; case 2: /* fsqrt */ case 4: /* frndint */ case 6: /* fsin */ default: case 7: /* fcos */ gen_insn3(OP_FSPECIAL, OR_ST0, OR_ST0, OR_ZERO); break; } break; case 0x00: case 0x01: case 0x04 ... 0x07: /* fxxx st, sti */ case 0x20: case 0x21: case 0x24 ... 0x27: /* fxxx sti, st */ case 0x30: case 0x31: case 0x34 ... 0x37: /* fxxxp sti, st */ { int op1, swap; op1 = fp_ops[op & 7]; swap = 0; if ((op & 7) == 5 || (op & 7) == 7) swap = 1; if (op >= 0x20) { if (swap) gen_insn3(op1, opreg, OR_ST0, opreg); else gen_insn3(op1, opreg, opreg, OR_ST0); } else { if (swap) gen_insn3(op1, OR_ST0, opreg, OR_ST0); else gen_insn3(op1, OR_ST0, OR_ST0, opreg); } if (op >= 0x30) gen_insn0(OP_FPOP); } break; case 0x02: /* fcom */ gen_insn3(OP_CMP, OR_ZERO, OR_ST0, opreg); break; case 0x03: /* fcomp */ gen_insn3(OP_CMP, OR_ZERO, OR_ST0, opreg); gen_insn0(OP_FPOP); break; case 0x15: /* da/5 */ switch(rm) { case 1: /* fucompp */ gen_insn3(OP_CMP, OR_ZERO, OR_ST0, opreg); gen_insn0(OP_FPOP); gen_insn0(OP_FPOP); break; default: return -1; } break; case 0x2a: /* fst sti */ gen_mov(opreg, OR_ST0); break; case 0x2b: /* fstp sti */ gen_mov(opreg, OR_ST0); gen_insn0(OP_FPOP); break; case 0x33: /* de/3 */ switch(rm) { case 1: /* fcompp */ gen_insn3(OP_CMP, OR_ZERO, OR_ST0, opreg); gen_insn0(OP_FPOP); gen_insn0(OP_FPOP); break; default: return -1; } break; case 0x3c: /* df/4 */ switch(rm) { case 0: gen_insn3(OP_FNSTS, OR_EAX, OR_ZERO, OR_ZERO); break; default: return -1; } break; default: error("unhandled FP\n"); return -1; } } break; #endif /************************/ /* string ops */ case 0xa4: /* movsS */ case 0xa5: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPZ) { gen_op_movs[3 + ot](); } else { gen_op_movs[ot](); } break; case 0xaa: /* stosS */ case 0xab: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPZ) { gen_op_stos[3 + ot](); } else { gen_op_stos[ot](); } break; case 0xac: /* lodsS */ case 0xad: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPZ) { gen_op_lods[3 + ot](); } else { gen_op_lods[ot](); } break; case 0xae: /* scasS */ case 0xaf: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPNZ) { gen_op_scas[6 + ot](); } else if (prefixes & PREFIX_REPZ) { gen_op_scas[3 + ot](); } else { gen_op_scas[ot](); } break; case 0xa6: /* cmpsS */ case 0xa7: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPNZ) { gen_op_cmps[6 + ot](); } else if (prefixes & PREFIX_REPZ) { gen_op_cmps[3 + ot](); } else { gen_op_cmps[ot](); } break; case 0x6c: /* insS */ case 0x6d: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPZ) { gen_op_ins[3 + ot](); } else { gen_op_ins[ot](); } break; case 0x6e: /* outsS */ case 0x6f: if ((b & 1) == 0) ot = OT_BYTE; else ot = dflag ? OT_LONG : OT_WORD; if (prefixes & PREFIX_REPZ) { gen_op_outs[3 + ot](); } else { gen_op_outs[ot](); } break; /************************/ /* control */ case 0xc2: /* ret im */ /* XXX: handle stack pop ? */ val = ldsw(s->pc); s->pc += 2; gen_op_popl_T0(); gen_op_addl_ESP_im(val); gen_op_jmp_T0(); break; case 0xc3: /* ret */ gen_op_popl_T0(); gen_op_jmp_T0(); break; case 0xe8: /* call */ val = insn_get(s, OT_LONG); val += (long)s->pc; gen_op1_movl_T1_im((long)s->pc); gen_op_pushl_T1(); gen_op_jmp_im(val); break; case 0xe9: /* jmp */ val = insn_get(s, OT_LONG); val += (long)s->pc; gen_op_jmp_im(val); break; case 0xeb: /* jmp Jb */ val = (int8_t)insn_get(s, OT_BYTE); val += (long)s->pc; gen_op_jmp_im(val); break; case 0x70 ... 0x7f: /* jcc Jb */ val = (int8_t)insn_get(s, OT_BYTE); val += (long)s->pc; goto do_jcc; case 0x180 ... 0x18f: /* jcc Jv */ if (dflag) { val = insn_get(s, OT_LONG); } else { val = (int16_t)insn_get(s, OT_WORD); } val += (long)s->pc; /* XXX: fix 16 bit wrap */ do_jcc: gen_jcc(s, b, val); break; case 0x190 ... 0x19f: modrm = ldub(s->pc++); gen_setcc(s, b); gen_ldst_modrm(s, modrm, OT_BYTE, OR_TMP0, 1); break; /************************/ /* flags */ case 0x9c: /* pushf */ gen_op_movl_T0_eflags(); gen_op_pushl_T0(); break; case 0x9d: /* popf */ gen_op_popl_T0(); gen_op_movl_eflags_T0(); s->cc_op = CC_OP_EFLAGS; break; case 0x9e: /* sahf */ gen_op_mov_TN_reg[OT_BYTE][0][R_AH](); if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); gen_op_movb_eflags_T0(); s->cc_op = CC_OP_EFLAGS; break; case 0x9f: /* lahf */ if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); gen_op_movl_T0_eflags(); gen_op_mov_reg_T0[OT_BYTE][R_AH](); break; case 0xf5: /* cmc */ if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); gen_op_cmc(); s->cc_op = CC_OP_EFLAGS; break; case 0xf8: /* clc */ if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); gen_op_clc(); s->cc_op = CC_OP_EFLAGS; break; case 0xf9: /* stc */ if (s->cc_op != CC_OP_DYNAMIC) op_set_cc_op(s->cc_op); gen_op_stc(); s->cc_op = CC_OP_EFLAGS; break; case 0xfc: /* cld */ gen_op_cld(); break; case 0xfd: /* std */ gen_op_std(); break; /************************/ /* misc */ case 0x90: /* nop */ break; #if 0 case 0x1a2: /* cpuid */ gen_insn0(OP_ASM); break; #endif default: error("unknown opcode %x", b); return -1; } return (long)s->pc; }