qemu/translate-i386.c
bellard 367e86e847 new x86 CPU core
git-svn-id: svn://svn.savannah.nongnu.org/qemu/trunk@14 c046a42c-6fe2-441c-8c8c-71466251a162
2003-03-01 17:13:26 +00:00

2134 lines
55 KiB
C

#include <stdarg.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <assert.h>
#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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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, &reg_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;
}