+/*
+ * ia32rtools
+ * (C) notaz, 2013,2014
+ *
+ * This work is licensed under the terms of 3-clause BSD license.
+ * See COPYING file in the top-level directory.
+ */
+
#define _GNU_SOURCE
#include <stdio.h>
#include <stdlib.h>
OPF_ATAIL = (1 << 14), /* tail call with reused arg frame */
OPF_32BIT = (1 << 15), /* 32bit division */
OPF_LOCK = (1 << 16), /* op has lock prefix */
+ OPF_VAPUSH = (1 << 17), /* vararg ptr push (as call arg) */
};
enum op_op {
OP_SHL,
OP_SHR,
OP_SAR,
+ OP_SHRD,
OP_ROL,
OP_ROR,
OP_RCL,
OP_JECXZ,
OP_JCC,
OP_SCC,
+ // x87
+ // mmx
+ OP_EMMS,
};
enum opr_type {
OPLM_BYTE,
OPLM_WORD,
OPLM_DWORD,
+ OPLM_QWORD,
};
#define MAX_OPERANDS 3
unsigned char pfo;
unsigned char pfo_inv;
unsigned char operand_cnt;
- unsigned char pad;
+ unsigned char p_argnum; // push: altered before call arg #
+ unsigned char p_argpass;// push: arg of host func
+ unsigned char pad[3];
int regmask_src; // all referensed regs
int regmask_dst;
int pfomask; // flagop: parsed_flag_op that can't be delayed
- int argnum; // push: altered before call arg #
int cc_scratch; // scratch storage during analysis
int bt_i; // branch target for branches
struct parsed_data *btj;// branch targets for jumptables
printf("%s:%d: note: [%s] '%s': " fmt, asmfn, (op_)->asmln, g_func, \
dump_op(op_), ##__VA_ARGS__)
-#define MAX_REGS 8
-
-const char *regs_r32[] = { "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp" };
+const char *regs_r32[] = {
+ "eax", "ebx", "ecx", "edx", "esi", "edi", "ebp", "esp",
+ // not r32, but list here for easy parsing and printing
+ "mm0", "mm1", "mm2", "mm3", "mm4", "mm5", "mm6", "mm7",
+};
const char *regs_r16[] = { "ax", "bx", "cx", "dx", "si", "di", "bp", "sp" };
const char *regs_r8l[] = { "al", "bl", "cl", "dl" };
const char *regs_r8h[] = { "ah", "bh", "ch", "dh" };
PFO_LE, // e ZF=1||SF!=OF
};
+#define PFOB_O (1 << PFO_O)
+#define PFOB_C (1 << PFO_C)
+#define PFOB_Z (1 << PFO_Z)
+#define PFOB_S (1 << PFO_S)
+
static const char *parsed_flag_op_names[] = {
"o", "c", "z", "be", "s", "p", "l", "le"
};
snprintf(buf, buf_size, number < 10 ? "%lu" : "0x%02lx", number);
}
+static int check_segment_prefix(const char *s)
+{
+ if (s[0] == 0 || s[1] != 's' || s[2] != ':')
+ return 0;
+
+ switch (s[0]) {
+ case 'c': return 1;
+ case 'd': return 2;
+ case 's': return 3;
+ case 'e': return 4;
+ case 'f': return 5;
+ case 'g': return 6;
+ default: return 0;
+ }
+}
+
static int parse_reg(enum opr_lenmod *reg_lmod, const char *s)
{
int reg;
reg = char_array_i(regs_r32, ARRAY_SIZE(regs_r32), s);
+ if (reg >= 8) {
+ *reg_lmod = OPLM_QWORD;
+ return reg;
+ }
if (reg >= 0) {
*reg_lmod = OPLM_DWORD;
return reg;
s++;
*d = 0;
- // skip 'ds:' prefix
- if (IS_START(s, "ds:"))
+ // skip '?s:' prefixes
+ if (check_segment_prefix(s))
s += 3;
s = next_idt(w, sizeof(w), s);
return 0;
}
-static const char *parse_stack_el(const char *name, char *extra_reg)
+static const char *parse_stack_el(const char *name, char *extra_reg,
+ int early_try)
{
const char *p, *p2, *s;
char *endp = NULL;
long val;
int len;
- p = name;
- if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
- p += 4;
- if (extra_reg != NULL) {
- strncpy(extra_reg, name, 3);
- extra_reg[4] = 0;
+ if (g_bp_frame || early_try)
+ {
+ p = name;
+ if (IS_START(p + 3, "+ebp+") && is_reg_in_str(p)) {
+ p += 4;
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, name, 3);
+ extra_reg[4] = 0;
+ }
}
- }
- if (IS_START(p, "ebp+")) {
- p += 4;
+ if (IS_START(p, "ebp+")) {
+ p += 4;
- p2 = strchr(p, '+');
- if (p2 != NULL && is_reg_in_str(p)) {
- if (extra_reg != NULL) {
- strncpy(extra_reg, p, p2 - p);
- extra_reg[p2 - p] = 0;
+ p2 = strchr(p, '+');
+ if (p2 != NULL && is_reg_in_str(p)) {
+ if (extra_reg != NULL) {
+ strncpy(extra_reg, p, p2 - p);
+ extra_reg[p2 - p] = 0;
+ }
+ p = p2 + 1;
}
- p = p2 + 1;
- }
- if (!('0' <= *p && *p <= '9'))
- return p;
+ if (!('0' <= *p && *p <= '9'))
+ return p;
- return NULL;
+ return NULL;
+ }
}
if (!IS_START(name, "esp+"))
opr->lmod = OPLM_BYTE;
return 1;
}
+ if (!strncmp(opr->name, "qword_", 6)) {
+ opr->lmod = OPLM_QWORD;
+ return 1;
+ }
return 0;
}
static const char *dword_types[] = {
"int", "_DWORD", "UINT_PTR", "DWORD",
"WPARAM", "LPARAM", "UINT", "__int32",
- "LONG", "HIMC",
+ "LONG", "HIMC", "BOOL", "size_t",
+ "float",
};
static const char *word_types[] = {
- "uint16_t", "int16_t", "_WORD",
+ "uint16_t", "int16_t", "_WORD", "WORD",
"unsigned __int16", "__int16",
};
static const char *byte_types[] = {
if (label != NULL) {
opr->type = OPT_LABEL;
- if (IS_START(label, "ds:")) {
+ ret = check_segment_prefix(label);
+ if (ret != 0) {
+ if (ret >= 5)
+ aerr("fs/gs used\n");
opr->had_ds = 1;
label += 3;
}
opr->lmod = OPLM_WORD;
else if (IS(words[w], "byte"))
opr->lmod = OPLM_BYTE;
+ else if (IS(words[w], "qword"))
+ opr->lmod = OPLM_QWORD;
else
aerr("type parsing failed\n");
w += 2;
if (wordc_in != 1)
aerr("parse_operand 1 word expected\n");
- if (IS_START(words[w], "ds:")) {
+ ret = check_segment_prefix(words[w]);
+ if (ret != 0) {
+ if (ret >= 5)
+ aerr("fs/gs used\n");
opr->had_ds = 1;
memmove(words[w], words[w] + 3, strlen(words[w]) - 2);
}
aerr("[] parse failure\n");
parse_indmode(opr->name, regmask_indirect, 1);
- if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL)) {
+ if (opr->lmod == OPLM_UNSPEC && parse_stack_el(opr->name, NULL, 1))
+ {
// might be an equ
struct parsed_equ *eq =
- equ_find(NULL, parse_stack_el(opr->name, NULL), &i);
+ equ_find(NULL, parse_stack_el(opr->name, NULL, 1), &i);
if (eq)
opr->lmod = eq->lmod;
}
{ "shr", OP_SHR, 2, 2, OPF_DATA|OPF_FLAGS },
{ "sal", OP_SHL, 2, 2, OPF_DATA|OPF_FLAGS },
{ "sar", OP_SAR, 2, 2, OPF_DATA|OPF_FLAGS },
+ { "shrd", OP_SHRD, 3, 3, OPF_DATA|OPF_FLAGS },
{ "rol", OP_ROL, 2, 2, OPF_DATA|OPF_FLAGS },
{ "ror", OP_ROR, 2, 2, OPF_DATA|OPF_FLAGS },
{ "rcl", OP_RCL, 2, 2, OPF_DATA|OPF_FLAGS|OPF_CC, PFO_C },
{ "setng", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 0 },
{ "setg", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
{ "setnle", OP_SCC, 1, 1, OPF_DATA|OPF_CC, PFO_LE, 1 },
+ // x87
+ // mmx
+ { "emms", OP_EMMS, 0, 0, OPF_DATA },
+ { "movq", OP_MOV, 2, 2, OPF_DATA },
};
static void parse_op(struct parsed_op *op, char words[16][256], int wordc)
op->operand[1].lmod = OPLM_BYTE;
break;
+ case OP_SHRD:
+ if (op->operand[2].lmod == OPLM_UNSPEC)
+ op->operand[2].lmod = OPLM_BYTE;
+ break;
+
case OP_PUSH:
if (op->operand[0].lmod == OPLM_UNSPEC
&& (op->operand[0].type == OPT_CONST
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "u64";
case OPLM_DWORD:
return "u32";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "";
case OPLM_DWORD:
return "";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "*(u64 *)";
case OPLM_DWORD:
return "*(u32 *)";
case OPLM_WORD:
enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return "(s64)";
case OPLM_DWORD:
return "(s32)";
case OPLM_WORD:
static int lmod_bytes(struct parsed_op *po, enum opr_lenmod lmod)
{
switch (lmod) {
+ case OPLM_QWORD:
+ return 8;
case OPLM_DWORD:
return 4;
case OPLM_WORD:
static const char *opr_reg_p(struct parsed_op *po, struct parsed_opr *popr)
{
- if ((unsigned int)popr->reg >= MAX_REGS)
+ if ((unsigned int)popr->reg >= ARRAY_SIZE(regs_r32))
ferr(po, "invalid reg: %d\n", popr->reg);
return regs_r32[popr->reg];
}
static int is_stack_access(struct parsed_op *po,
const struct parsed_opr *popr)
{
- return (parse_stack_el(popr->name, NULL)
+ return (parse_stack_el(popr->name, NULL, 0)
|| (g_bp_frame && !(po->flags & OPF_EBP_S)
&& IS_START(popr->name, "ebp")));
}
ferr(po, "ebp- parse of '%s' failed\n", name);
}
else {
- bp_arg = parse_stack_el(name, ofs_reg);
+ bp_arg = parse_stack_el(name, ofs_reg, 0);
snprintf(g_comment, sizeof(g_comment), "%s", bp_arg);
eq = equ_find(po, bp_arg, &offset);
if (eq == NULL)
*bp_arg_out = bp_arg;
}
-static void stack_frame_access(struct parsed_op *po,
+static int stack_frame_access(struct parsed_op *po,
struct parsed_opr *popr, char *buf, size_t buf_size,
const char *name, const char *cast, int is_src, int is_lea)
{
int unaligned = 0;
int stack_ra = 0;
int offset = 0;
+ int retval = -1;
int sf_ofs;
int lim;
if (cast[0] == 0)
cast = "(u32)";
snprintf(buf, buf_size, "%sap", cast);
- return;
+ return -1;
}
ferr(po, "offset %d (%s,%d) doesn't map to any arg\n",
offset, bp_arg, arg_i);
ferr(po, "arg %d not in prototype?\n", arg_i);
popr->is_ptr = g_func_pp->arg[i].type.is_ptr;
+ retval = i;
switch (popr->lmod)
{
// common problem
guess_lmod_from_c_type(&tmp_lmod, &g_func_pp->arg[i].type);
if (tmp_lmod != OPLM_DWORD
- && (unaligned || (!is_src && tmp_lmod < popr->lmod)))
+ && (unaligned || (!is_src && lmod_bytes(po, tmp_lmod)
+ < lmod_bytes(po, popr->lmod) + (offset & 3))))
{
ferr(po, "bp_arg arg%d/w offset %d and type '%s' is too small\n",
i + 1, offset, g_func_pp->arg[i].type.name);
ferr(po, "bp_stack bad lmod: %d\n", popr->lmod);
}
}
+
+ return retval;
}
static void check_func_pp(struct parsed_op *po,
const struct parsed_proto *pp, const char *pfx)
{
+ enum opr_lenmod tmp_lmod;
char buf[256];
+ int ret, i;
if (pp->argc_reg != 0) {
if (/*!g_allow_regfunc &&*/ !pp->is_fastcall) {
ferr(po, "%s: %d reg arg(s) with %d stack arg(s)\n",
pfx, pp->argc_reg, pp->argc_stack);
}
+
+ // fptrs must use 32bit args, callsite might have no information and
+ // lack a cast to smaller types, which results in incorrectly masked
+ // args passed (callee may assume masked args, it does on ARM)
+ if (!pp->is_oslib) {
+ for (i = 0; i < pp->argc; i++) {
+ ret = guess_lmod_from_c_type(&tmp_lmod, &pp->arg[i].type);
+ if (ret && tmp_lmod != OPLM_DWORD)
+ ferr(po, "reference to %s with arg%d '%s'\n", pp->name,
+ i + 1, pp->arg[i].type.name);
+ }
+ }
}
static const char *check_label_read_ref(struct parsed_op *po,
ferr(po, "lea from reg?\n");
switch (popr->lmod) {
+ case OPLM_QWORD:
+ snprintf(buf, buf_size, "%s%s.q", cast, opr_reg_p(po, popr));
+ break;
case OPLM_DWORD:
snprintf(buf, buf_size, "%s%s", cast, opr_reg_p(po, popr));
break;
switch (popr->type) {
case OPT_REG:
switch (popr->lmod) {
+ case OPLM_QWORD:
+ snprintf(buf, buf_size, "%s.q", opr_reg_p(po, popr));
+ break;
case OPLM_DWORD:
snprintf(buf, buf_size, "%s", opr_reg_p(po, popr));
break;
char buf1[256], buf2[256];
enum opr_lenmod lmod;
- if (po->operand[0].lmod != po->operand[1].lmod)
+ if (po->op != OP_DEC && po->operand[0].lmod != po->operand[1].lmod)
ferr(po, "%s: lmod mismatch: %d %d\n", __func__,
po->operand[0].lmod, po->operand[1].lmod);
lmod = po->operand[0].lmod;
}
out_src_opr(buf1, sizeof(buf1), po, &po->operand[0], cast_use, 0);
- out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0);
+ if (po->op == OP_DEC)
+ snprintf(buf2, sizeof(buf2), "1");
+ else
+ out_src_opr(buf2, sizeof(buf2), po, &po->operand[1], cast_use, 0);
switch (pfo) {
case PFO_C:
buf1, is_inv ? ">=" : "<", buf2);
break;
- case PFO_LE:
+ case PFO_LE: // !g
snprintf(buf, buf_size, "(%s %s %s)",
buf1, is_inv ? ">" : "<=", buf2);
break;
}
if ((po->flags & OPF_RMD)
- || (po->op == OP_PUSH && po->argnum != 0)) // arg push
+ || (po->op == OP_PUSH && po->p_argnum != 0)) // arg push
continue;
if ((po->flags & OPF_JMP) && po->op != OP_CALL) {
return pp;
}
-static int try_resolve_const(int i, const struct parsed_opr *opr,
- int magic, unsigned int *val)
+// find an instruction that changed opr before i op
+// *op_i must be set to -1
+static int resolve_origin(int i, const struct parsed_opr *opr,
+ int magic, int *op_i)
{
struct label_ref *lr;
int ret = 0;
if (g_labels[i][0] != 0) {
lr = &g_label_refs[i];
for (; lr != NULL; lr = lr->next)
- ret |= try_resolve_const(lr->i, opr, magic, val);
+ ret |= resolve_origin(lr->i, opr, magic, op_i);
if (i > 0 && LAST_OP(i - 1))
return ret;
}
continue;
if (!is_opr_modified(opr, &ops[i]))
continue;
+
+ if (*op_i >= 0) {
+ if (*op_i == i)
+ return 1;
+ // XXX: could check if the other op does the same
+ return -1;
+ }
+
+ *op_i = i;
+ return 1;
+ }
+}
+
+static int try_resolve_const(int i, const struct parsed_opr *opr,
+ int magic, unsigned int *val)
+{
+ int s_i = -1;
+ int ret = 0;
+
+ ret = resolve_origin(i, opr, magic, &s_i);
+ if (ret == 1) {
+ i = s_i;
if (ops[i].op != OP_MOV && ops[i].operand[1].type != OPT_CONST)
return -1;
*val = ops[i].operand[1].val;
return 1;
}
+
+ return -1;
}
static int collect_call_args_r(struct parsed_op *po, int i,
struct parsed_proto *pp_tmp;
struct label_ref *lr;
int need_to_save_current;
+ int save_args;
int ret = 0;
- int j;
+ int reg;
+ char buf[32];
+ int j, k;
if (i < 0) {
ferr(po, "dead label encountered\n");
ferr(po, "arg collect %d/%d hit '%s' with %d stack args\n",
arg, pp->argc, opr_name(&ops[j], 0), pp_tmp->argc_stack);
}
- else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP) {
+ // esp adjust of 0 means we collected it before
+ else if (ops[j].op == OP_ADD && ops[j].operand[0].reg == xSP
+ && (ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != 0))
+ {
if (pp->is_unresolved)
break;
- ferr(po, "arg collect %d/%d hit esp adjust\n",
- arg, pp->argc);
+ ferr(po, "arg collect %d/%d hit esp adjust of %d\n",
+ arg, pp->argc, ops[j].operand[1].val);
}
else if (ops[j].op == OP_POP) {
if (pp->is_unresolved)
pp->arg[arg].datap = &ops[j];
need_to_save_current = 0;
+ save_args = 0;
+ reg = -1;
+ if (ops[j].operand[0].type == OPT_REG)
+ reg = ops[j].operand[0].reg;
+
if (!need_op_saving) {
ret = scan_for_mod(&ops[j], j + 1, i, 1);
need_to_save_current = (ret >= 0);
if (need_op_saving || need_to_save_current) {
// mark this push as one that needs operand saving
ops[j].flags &= ~OPF_RMD;
- if (ops[j].argnum == 0) {
- ops[j].argnum = arg + 1;
- *save_arg_vars |= 1 << arg;
+ if (ops[j].p_argnum == 0) {
+ ops[j].p_argnum = arg + 1;
+ save_args |= 1 << arg;
+ }
+ else if (ops[j].p_argnum < arg + 1) {
+ // XXX: might kill valid var..
+ //*save_arg_vars &= ~(1 << (ops[j].p_argnum - 1));
+ ops[j].p_argnum = arg + 1;
+ save_args |= 1 << arg;
}
- else if (ops[j].argnum < arg + 1)
- ferr(&ops[j], "argnum conflict (%d<%d) for '%s'\n",
- ops[j].argnum, arg + 1, pp->name);
}
- else if (ops[j].argnum == 0)
+ else if (ops[j].p_argnum == 0)
ops[j].flags |= OPF_RMD;
// some PUSHes are reused by different calls on other branches,
ops[j].flags &= ~OPF_RSAVE;
+ // check for __VALIST
+ if (!pp->is_unresolved && pp->arg[arg].type.is_va_list) {
+ k = -1;
+ ret = resolve_origin(j, &ops[j].operand[0], magic + 1, &k);
+ if (ret == 1 && k >= 0)
+ {
+ if (ops[k].op == OP_LEA) {
+ snprintf(buf, sizeof(buf), "arg_%X",
+ g_func_pp->argc_stack * 4);
+ if (!g_func_pp->is_vararg
+ || strstr(ops[k].operand[1].name, buf))
+ {
+ ops[k].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD | OPF_VAPUSH;
+ save_args &= ~(1 << arg);
+ reg = -1;
+ }
+ else
+ ferr(&ops[j], "lea va_list used, but no vararg?\n");
+ }
+ // check for va_list from g_func_pp arg too
+ else if (ops[k].op == OP_MOV
+ && is_stack_access(&ops[k], &ops[k].operand[1]))
+ {
+ ret = stack_frame_access(&ops[k], &ops[k].operand[1],
+ buf, sizeof(buf), ops[k].operand[1].name, "", 1, 0);
+ if (ret >= 0) {
+ ops[k].flags |= OPF_RMD;
+ ops[j].flags |= OPF_RMD;
+ ops[j].p_argpass = ret + 1;
+ save_args &= ~(1 << arg);
+ reg = -1;
+ }
+ }
+ }
+ }
+
+ *save_arg_vars |= save_args;
+
+ // tracking reg usage
+ if (reg >= 0)
+ *regmask |= 1 << reg;
+
arg++;
if (!pp->is_unresolved) {
// next arg
break;
}
magic = (magic & 0xffffff) | (arg << 24);
-
- // tracking reg usage
- if (ops[j].operand[0].type == OPT_REG)
- *regmask |= 1 << ops[j].operand[0].reg;
}
}
} while (i < opcnt);
}
else {
- for (i = 0; i < opcnt; i++) {
+ int ecx_push = 0, esp_sub = 0;
+
+ i = 0;
+ while (ops[i].op == OP_PUSH && IS(opr_name(&ops[i], 0), "ecx")) {
+ ops[i].flags |= OPF_RMD;
+ g_stack_fsz += 4;
+ ecx_push++;
+ i++;
+ }
+
+ for (; i < opcnt; i++) {
if (ops[i].op == OP_PUSH || (ops[i].flags & (OPF_JMP|OPF_TAIL)))
break;
if (ops[i].op == OP_SUB && ops[i].operand[0].reg == xSP
&& ops[i].operand[1].type == OPT_CONST)
{
- g_sp_frame = 1;
+ g_stack_fsz = ops[i].operand[1].val;
+ ops[i].flags |= OPF_RMD;
+ esp_sub = 1;
break;
}
}
found = 0;
- if (g_sp_frame)
+ if (ecx_push || esp_sub)
{
- g_stack_fsz = ops[i].operand[1].val;
- ops[i].flags |= OPF_RMD;
+ g_sp_frame = 1;
i++;
do {
j--;
}
- if (ops[j].op != OP_ADD
- || !IS(opr_name(&ops[j], 0), "esp")
- || ops[j].operand[1].type != OPT_CONST
- || ops[j].operand[1].val != g_stack_fsz)
- ferr(&ops[j], "'add esp' expected\n");
- ops[j].flags |= OPF_RMD;
+ if (ecx_push > 0) {
+ for (l = 0; l < ecx_push; l++) {
+ if (ops[j].op != OP_POP
+ || !IS(opr_name(&ops[j], 0), "ecx"))
+ {
+ ferr(&ops[j], "'pop ecx' expected\n");
+ }
+ ops[j].flags |= OPF_RMD;
+ j--;
+ }
+
+ found = 1;
+ }
+
+ if (esp_sub) {
+ if (ops[j].op != OP_ADD
+ || !IS(opr_name(&ops[j], 0), "esp")
+ || ops[j].operand[1].type != OPT_CONST
+ || ops[j].operand[1].val != g_stack_fsz)
+ ferr(&ops[j], "'add esp' expected\n");
+ ops[j].flags |= OPF_RMD;
+
+ found = 1;
+ }
- found = 1;
i++;
} while (i < opcnt);
}
// indirect call
pp_c = resolve_icall(i, opcnt, &l);
if (pp_c != NULL) {
+ if (!pp_c->is_func && !pp_c->is_fptr)
+ ferr(po, "call to non-func: %s\n", pp_c->name);
pp = proto_clone(pp_c);
my_assert_not(pp, NULL);
if (l)
regmask_save |= 1 << reg;
}
- if (po->op == OP_PUSH && po->argnum == 0
+ if (po->op == OP_PUSH && po->p_argnum == 0
&& !(po->flags & OPF_RSAVE) && !g_func_pp->is_userstack)
{
if (po->operand[0].type == OPT_REG)
tmp_op = pp->arg[arg].datap;
if (tmp_op == NULL)
ferr(po, "parsed_op missing for arg%d\n", arg);
- if (tmp_op->argnum == 0 && tmp_op->operand[0].type == OPT_REG)
+ if (tmp_op->p_argnum == 0 && tmp_op->operand[0].type == OPT_REG)
regmask_stack |= 1 << tmp_op->operand[0].reg;
}
regmask_now = regmask & ~regmask_arg;
regmask_now &= ~(1 << xSP);
- if (regmask_now) {
+ if (regmask_now & 0x00ff) {
for (reg = 0; reg < 8; reg++) {
if (regmask_now & (1 << reg)) {
fprintf(fout, " u32 %s", regs_r32[reg]);
}
}
}
+ if (regmask_now & 0xff00) {
+ for (reg = 8; reg < 16; reg++) {
+ if (regmask_now & (1 << reg)) {
+ fprintf(fout, " mmxr %s", regs_r32[reg]);
+ if (regmask_init & (1 << reg))
+ fprintf(fout, " = { 0, }");
+ fprintf(fout, ";\n");
+ had_decl = 1;
+ }
+ }
+ }
if (regmask_save) {
for (reg = 0; reg < 8; reg++) {
delayed_flag_op = NULL;
break;
+ case OP_SHRD:
+ assert_operand_cnt(3);
+ propagate_lmod(po, &po->operand[0], &po->operand[1]);
+ l = lmod_bytes(po, po->operand[0].lmod) * 8;
+ out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
+ out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]);
+ out_src_opr_u32(buf3, sizeof(buf3), po, &po->operand[2]);
+ fprintf(fout, " %s >>= %s; %s |= %s << (%d - %s);",
+ buf1, buf3, buf1, buf2, l, buf3);
+ strcpy(g_comment, "shrd");
+ output_std_flags(fout, po, &pfomask, buf1);
+ last_arith_dst = &po->operand[0];
+ delayed_flag_op = NULL;
+ break;
+
case OP_ROL:
case OP_ROR:
assert_operand_cnt(2);
case OP_SUB:
assert_operand_cnt(2);
propagate_lmod(po, &po->operand[0], &po->operand[1]);
- if (pfomask & (1 << PFO_C)) {
- fprintf(fout, " cond_c = %s < %s;\n",
- out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]),
- out_src_opr_u32(buf2, sizeof(buf2), po, &po->operand[1]));
- pfomask &= ~(1 << PFO_C);
+ if (pfomask & ~((1 << PFO_Z) | (1 << PFO_S))) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
}
goto dualop_arith;
strcat(g_comment, "bsf");
break;
- case OP_INC:
case OP_DEC:
+ if (pfomask & ~(PFOB_S | PFOB_S | PFOB_C)) {
+ for (j = 0; j <= PFO_LE; j++) {
+ if (!(pfomask & (1 << j)))
+ continue;
+ if (j == PFO_Z || j == PFO_S || j == PFO_C)
+ continue;
+
+ out_cmp_for_cc(buf1, sizeof(buf1), po, j, 0);
+ fprintf(fout, " cond_%s = %s;\n",
+ parsed_flag_op_names[j], buf1);
+ pfomask &= ~(1 << j);
+ }
+ }
+ // fallthrough
+
+ case OP_INC:
+ if (pfomask & (1 << PFO_C))
+ // carry is unaffected by inc/dec.. wtf?
+ ferr(po, "carry propagation needed\n");
+
out_dst_opr(buf1, sizeof(buf1), po, &po->operand[0]);
if (po->operand[0].type == OPT_REG) {
strcpy(buf2, po->op == OP_INC ? "++" : "--");
tmp_op = pp->arg[arg].datap;
if (tmp_op == NULL)
ferr(po, "parsed_op missing for arg%d\n", arg);
- if (tmp_op->argnum != 0) {
- fprintf(fout, "%ss_a%d", cast, tmp_op->argnum);
+
+ if (tmp_op->flags & OPF_VAPUSH) {
+ fprintf(fout, "ap");
+ }
+ else if (tmp_op->p_argpass != 0) {
+ fprintf(fout, "a%d", tmp_op->p_argpass);
+ }
+ else if (tmp_op->p_argnum != 0) {
+ fprintf(fout, "%ss_a%d", cast, tmp_op->p_argnum);
}
else {
fprintf(fout, "%s",
case OP_PUSH:
out_src_opr_u32(buf1, sizeof(buf1), po, &po->operand[0]);
- if (po->argnum != 0) {
+ if (po->p_argnum != 0) {
// special case - saved func arg
- fprintf(fout, " s_a%d = %s;", po->argnum, buf1);
+ fprintf(fout, " s_a%d = %s;", po->p_argnum, buf1);
break;
}
else if (po->flags & OPF_RSAVE) {
no_output = 1;
break;
+ // mmx
+ case OP_EMMS:
+ strcpy(g_comment, "(emms)");
+ break;
+
default:
no_output = 1;
ferr(po, "unhandled op type %d, flags %x\n",
g_eqs[g_eqcnt].lmod = OPLM_WORD;
else if (IS(words[2], "byte"))
g_eqs[g_eqcnt].lmod = OPLM_BYTE;
+ else if (IS(words[2], "qword"))
+ g_eqs[g_eqcnt].lmod = OPLM_QWORD;
else
aerr("bad lmod: '%s'\n", words[2]);